User management with native gui and further improvements

This commit is contained in:
2026-08-17 10:37:19 +02:00
parent 41556cfe73
commit a3c8b4083d
24 changed files with 1004 additions and 159 deletions
+9 -2
View File
@@ -15,6 +15,7 @@ Yacht Management Software (YMS) Installation
- Python GTK3 - Python GTK3
- python3-gi - python3-gi
- gir1.2-gtk-3.0 - gir1.2-gtk-3.0
- gir-rsvg-2.0
1.3 Web GUI 1.3 Web GUI
- Webserver - Webserver
@@ -69,7 +70,7 @@ Customize path and access rights to your needs
To grant access for the local gui the directory has to be writeable by a special To grant access for the local gui the directory has to be writeable by a special
group (here as example 'yms'). The user which runs the local gui (here group (here as example 'yms'). The user which runs the local gui (here
'guiuser') has also to be a member of that group as well as the webserver user 'guiuser') has also to be a member of that group as well as the webserver user
''www-data'. 'www-data'.
groupadd yms groupadd yms
usermod -aG yms www-data usermod -aG yms www-data
@@ -107,14 +108,20 @@ Configfile should be readable by webserver but not writeable.
Create a local database user with same name as your computer user and allow Create a local database user with same name as your computer user and allow
database acces with current unix credentials: database acces with current unix credentials:
mysql> CREATE USER 'ymsgui'@'localhost' IDENTIFIED VIA unix_socket; mysql> CREATE USER 'ymsgui'@'localhost' IDENTIFIED VIA unix_socket;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON yms.* TO 'ymsgui'@'localhost';
Copy ymsgui.py to /usr/local/bin
Copy directory yms to /usr/local/share
Start ymsgui.py, default configuration file is automatically created Start ymsgui.py, default configuration file is automatically created
7. Start using YMS 7. Start using YMS
Use webgui: Use webgui:
Start your browser and login to YMS with the default username/password: Start your browser and login to YMS with the initial username/password:
captain/captain captain/captain
Use local client: Use local client:
Start ymsgui.py Start ymsgui.py
Change the password using the local GUI before normal use.
+70 -30
View File
@@ -3,6 +3,12 @@
/* MariaDB Scheme /* MariaDB Scheme
Version: 1 */ Version: 1 */
/* TODO
- check all values if unsigned can be used or not
- implement foreign keys where possible
- rename provisions to provision(?)
*/
/* User /* User
userid is signed because nevative values are required for settings userid is signed because nevative values are required for settings
The user table must not be writeable by the webgui user! The user table must not be writeable by the webgui user!
@@ -58,6 +64,28 @@ CREATE TABLE dropdown (
PRIMARY KEY (ddid, ddval) PRIMARY KEY (ddid, ddval)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
CREATE TABLE company (
compid smallint(6) NOT NULL AUTO_INCREMENT,
compname varchar(60) NOT NULL,
comptype tinyint(3) UNSIGNED NOT NULL DEFAULT 1,
comptype2 tinyint(3) UNSIGNED DEFAULT NULL,
shortname varchar(20) DEFAULT NULL,
street varchar(40),
zip varchar(10),
city varchar(30),
country varchar(30),
contact varchar(30),
phone varchar(30),
email varchar(50),
web varchar(40),
customerno varchar(20),
contractno varchar(20),
remarks varchar(150) DEFAULT NULL,
flags set('deleted','historic') DEFAULT NULL,
PRIMARY KEY (compid),
UNIQUE INDEX ix_compname (compname, comptype)
) ENGINE=InnoDB;
CREATE TABLE vessel ( CREATE TABLE vessel (
vid smallint(6) NOT NULL AUTO_INCREMENT, vid smallint(6) NOT NULL AUTO_INCREMENT,
vtype enum('mono','cat','tri') NOT NULL DEFAULT 'mono', vtype enum('mono','cat','tri') NOT NULL DEFAULT 'mono',
@@ -76,14 +104,16 @@ CREATE TABLE vessel (
shapefile varchar(40) DEFAULT NULL, shapefile varchar(40) DEFAULT NULL,
sid_default smallint(6) UNSIGNED DEFAULT NULL, -- default storage sid_default smallint(6) UNSIGNED DEFAULT NULL, -- default storage
remarks varchar(150) DEFAULT NULL, remarks varchar(150) DEFAULT NULL,
PRIMARY KEY (vid) PRIMARY KEY (vid),
FOREIGN KEY fk_vessel_shipyard (shipyard)
REFERENCES company(compid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
CREATE TABLE storage ( CREATE TABLE storage (
sid smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT, sid smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT,
vid smallint(6) NOT NULL DEFAULT 1, vid smallint(6) NOT NULL DEFAULT 1,
sname varchar(40) NOT NULL, sname varchar(40) NOT NULL,
stype tinyint(3) NOT NULL DEFAULT 0, stype tinyint(3) NOT NULL DEFAULT 0, -- ddid=3
capacity smallint(6) DEFAULT NULL, capacity smallint(6) DEFAULT NULL,
capaunit enum('kg','l') DEFAULT NULL, capaunit enum('kg','l') DEFAULT NULL,
x smallint(6) UNSIGNED NOT NULL DEFAULT 0, x smallint(6) UNSIGNED NOT NULL DEFAULT 0,
@@ -148,6 +178,16 @@ CREATE TABLE equipment (
PRIMARY KEY(eid) PRIMARY KEY(eid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
/*
convert to
sid SMALLINT UNSIGNED NULL,
boxid SMALLINT UNSIGNED NULL,
CHECK (
(sid IS NOT NULL AND boxid IS NULL)
OR
(sid IS NULL AND boxid IS NOT NULL)
)
*/
CREATE TABLE inventory ( CREATE TABLE inventory (
invid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, invid int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
invname varchar(80) NOT NULL, invname varchar(80) NOT NULL,
@@ -161,6 +201,7 @@ CREATE TABLE inventory (
purchdate date DEFAULT NULL, purchdate date DEFAULT NULL,
invcond enum('unknown','excellent','good','fair','bad','repairable','defect') NOT NULL default 'unknown', invcond enum('unknown','excellent','good','fair','bad','repairable','defect') NOT NULL default 'unknown',
remarks varchar(150) DEFAULT NULL, remarks varchar(150) DEFAULT NULL,
flags set('ordered','removed','deleted') DEFAULT NULL,
PRIMARY KEY (invid), PRIMARY KEY (invid),
INDEX ix_invname (invname) INDEX ix_invname (invname)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
@@ -199,6 +240,7 @@ CREATE table fuse (
UNIQUE INDEX ix_fusenumber (vid, fnumber) UNIQUE INDEX ix_fusenumber (vid, fnumber)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
-- change vals to decimal(12,4)?
CREATE TABLE measurement ( CREATE TABLE measurement (
mid smallint(6) NOT NULL AUTO_INCREMENT, mid smallint(6) NOT NULL AUTO_INCREMENT,
mname varchar(80) NOT NULL, mname varchar(80) NOT NULL,
@@ -226,8 +268,10 @@ CREATE TABLE provisions (
weight float(5,2) UNSIGNED DEFAULT NULL, weight float(5,2) UNSIGNED DEFAULT NULL,
weight_net float(5,2) UNSIGNED DEFAULT NULL, weight_net float(5,2) UNSIGNED DEFAULT NULL,
unit tinyint(3) DEFAULT NULL, unit tinyint(3) DEFAULT NULL,
calories smallint(6) UNSIGNED DEFAULT NULL,
storedate date DEFAULT CURRENT_DATE, storedate date DEFAULT CURRENT_DATE,
shelflife date DEFAULT NULL, shelflife date DEFAULT NULL,
price decimal(12,2) DEFAULT NULL,
remarks varchar(150) DEFAULT NULL, remarks varchar(150) DEFAULT NULL,
PRIMARY KEY (provid) PRIMARY KEY (provid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
@@ -259,38 +303,19 @@ CREATE TABLE docref (
drid int(10) NOT NULL AUTO_INCREMENT, drid int(10) NOT NULL AUTO_INCREMENT,
reftype enum('vessel','box','equipment','project','task','user','company') NOT NULL DEFAULT 'vessel', reftype enum('vessel','box','equipment','project','task','user','company') NOT NULL DEFAULT 'vessel',
docid smallint(6) NOT NULL, docid smallint(6) NOT NULL,
refid smallint(6) NOT NULL, refid int(10) NOT NULL,
PRIMARY KEY (drid), PRIMARY KEY (drid),
UNIQUE INDEX ix_docref (docid, refid), UNIQUE INDEX ix_docref (docid, refid, reftype),
FOREIGN KEY fx_docref_doc (docid) FOREIGN KEY fk_docref_doc (docid)
REFERENCES document (docid) REFERENCES document (docid)
ON DELETE RESTRICT ON DELETE RESTRICT
ON UPDATE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB; ) ENGINE=InnoDB;
CREATE TABLE company ( /* TODO optional fields: plan date, target date, completion date
compid smallint(6) NOT NULL AUTO_INCREMENT, interval_hours, last_hours
compname varchar(60) NOT NULL, signalk: operating hours current vs maint every n operating hours in separate
comptype tinyint(3) UNSIGNED NOT NULL DEFAULT 1, */
comptype2 tinyint(3) UNSIGNED DEFAULT NULL,
shortname varchar(20) DEFAULT NULL,
street varchar(40),
zip varchar(10),
city varchar(30),
country varchar(30),
contact varchar(30),
phone varchar(30),
email varchar(50),
web varchar(40),
customerno varchar(20),
contractno varchar(20),
remarks varchar(150) DEFAULT NULL,
flags set('deleted','historic') DEFAULT NULL,
PRIMARY KEY (compid),
UNIQUE INDEX ix_compname (compname, comptype)
) ENGINE=InnoDB;
/* TODO optional field: plan date, target date, completion date */
CREATE TABLE maintenance ( CREATE TABLE maintenance (
maintid int(10) NOT NULL AUTO_INCREMENT, maintid int(10) NOT NULL AUTO_INCREMENT,
vid smallint(6) NOT NULL DEFAULT 1, vid smallint(6) NOT NULL DEFAULT 1,
@@ -332,14 +357,16 @@ CREATE TABLE task (
finished datetime DEFAULT NULL, finished datetime DEFAULT NULL,
responsible smallint(6) NOT NULL, responsible smallint(6) NOT NULL,
/* TODO executed_by : company or user? or both? */ /* TODO executed_by : company or user? or both? */
PRIMARY KEY (taskid) PRIMARY KEY (taskid),
FOREIGN KEY fk_task_user (responsible)
REFERENCES user(userid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
/* Notes can be assigned to tasks,maintenances and equipment */ /* Notes can be assigned to tasks,maintenances and equipment */
CREATE TABLE note ( CREATE TABLE note (
noteid int(10) NOT NULL AUTO_INCREMENT, noteid int(10) NOT NULL AUTO_INCREMENT,
notetype enum('task','maint','equip') NOT NULL DEFAULT 'task', notetype enum('task','maint','equip') NOT NULL DEFAULT 'task',
refid smallint(6) NOT NULL, refid int(10) NOT NULL,
annotation text NOT NULL, annotation text NOT NULL,
PRIMARY KEY (noteid) PRIMARY KEY (noteid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
@@ -372,3 +399,16 @@ CREATE TABLE checkgroup (
title varchar(30) NOT NULL, title varchar(30) NOT NULL,
PRIMARY KEY (groupid) PRIMARY KEY (groupid)
) ENGINE=InnoDB; ) ENGINE=InnoDB;
/* Expansion for future use: signalk mapping (just an idea) */
CREATE TABLE signalk (
skid int(10) NOT NULL AUTO_INCREMENT,
vid smallint(6) NOT NULL,
objtype enum('equipment','storage') NOT NULL,
objid int(10) NOT NULL,
datatype enum('engine_hours','tank_level') NOT NULL,
skpath varchar(255) NOT NULL,
PRIMARY KEY (skid),
UNIQUE KEY ix_signalk
(vid, objtype, objid, datatype)
) ENGINE=InnoDB;
+2 -1
View File
@@ -76,4 +76,5 @@ INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES
INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES
(3, 0, 'Stauraum allgemein', 'fixed'), (3, 0, 'Stauraum allgemein', 'fixed'),
(3, 1, 'Tank', 'fixed'); (3, 1, 'Tank', 'fixed'),
(3, 2, 'Extern', 'fixed'); -- not on board
+1 -1
View File
@@ -17,7 +17,7 @@ define('APP_ILLUSTRATION', 'sailboat.svg');
// database connection // database connection
$g_db_host = 'localhost'; $g_db_host = 'localhost';
$g_db_username = 'yms'; $g_db_username = 'ymsweb';
$g_db_password = 'changeme!'; $g_db_password = 'changeme!';
$g_db_schema = 'yms'; $g_db_schema = 'yms';
+1
View File
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -57,6 +57,7 @@ switch ($submit = form_get_action()) {
// WIP New standalone document // WIP New standalone document
// debug $g_message->Add(print_r($_FILES, true)); // debug $g_message->Add(print_r($_FILES, true));
// check for possible errors // check for possible errors
// TODO use "noscale" option for images
if (!isset($_FILES['files']['error']) || is_array($_FILES['files']['error'])) { if (!isset($_FILES['files']['error']) || is_array($_FILES['files']['error'])) {
$g_error->Add(_("Invalid file upload parameters.")); $g_error->Add(_("Invalid file upload parameters."));
$action = ACT_ADD; $action = ACT_ADD;
@@ -434,7 +435,7 @@ echo '<h2>', _('Add Document'), "</h2>\n";
<label for="files" class="form-label"><?=_('Document')?></label> <label for="files" class="form-label"><?=_('Document')?></label>
<input class="form-control" type="file" id="files" name="files" onchange="preview()"> <input class="form-control" type="file" id="files" name="files" onchange="preview()">
<img id="frame" src="" class="img-fluid"> <img id="frame" src="" class="img-fluid">
<button type="button" class="btn btn-secondary" onclick="clearimg();return false;"><?=_('Clear selection')?></button> <button type="button" class="btn btn-secondary mt-2" onclick="clearimg();return false;"><?=_('Clear selection')?></button>
<script> <script>
function preview() { function preview() {
var images = ["image/png", "image/jpeg", "image/svg+xml"]; var images = ["image/png", "image/jpeg", "image/svg+xml"];
@@ -451,6 +452,9 @@ echo '<h2>', _('Add Document'), "</h2>\n";
} }
</script> </script>
</div> </div>
<?php
form_create_check('noscale', _('Do not scale image'), FALSE);
?>
<div class="container-fluid px-0 my-3"> <div class="container-fluid px-0 my-3">
<button type="submit" name="submit[insert]" class="btn btn-primary"><?=_('Save')?></button> <button type="submit" name="submit[insert]" class="btn btn-primary"><?=_('Save')?></button>
<a href="<?=$g_scriptname?>" class="btn btn-secondary"><?=_('Back')?></a> <a href="<?=$g_scriptname?>" class="btn btn-secondary"><?=_('Back')?></a>
+1 -1
View File
@@ -420,7 +420,7 @@ $flt = db_get_filter($user->id, 200);
<input type="text" class="form-control" id="ename" name="ename"> <input type="text" class="form-control" id="ename" name="ename">
</div> </div>
<?php <?php
form_create_select('category', _('Category'), [-1 => '--- none ---'] + $opt_ecat, $flt->cat); form_create_select('category', _('Category'), $g_opt_none + $opt_ecat, $flt->cat);
form_create_select('manufacturer', _('Manufacturer'), $opt_manufacturer, $flt->manuf); form_create_select('manufacturer', _('Manufacturer'), $opt_manufacturer, $flt->manuf);
?> ?>
<div class="mb-3"> <div class="mb-3">
+24 -2
View File
@@ -904,7 +904,8 @@ function db_get_opt_user($userid, $default=NULL) {
return $list; return $list;
} }
function db_get_opt_proj($vid, $default=NULL) { function db_get_opt_proj($vid, $exclude=[], $default=NULL) {
// exclude is a list of project states
global $pdo; global $pdo;
global $g_error; global $g_error;
if (isset($default)) { if (isset($default)) {
@@ -912,7 +913,15 @@ function db_get_opt_proj($vid, $default=NULL) {
} else { } else {
$list = array(); $list = array();
} }
$sql = "SELECT projid, projname FROM project WHERE vid=? ORDER BY projname"; $sql = "SELECT projid, projname FROM project WHERE vid=?";
$where = [];
foreach ($exclude as $excl) {
$where[] = "NOT FIND_IN_SET('$excl',projstate)";
}
if (! empty($where)) {
$sql .= ' AND '. implode(' AND ', $where);
}
$sql .= " ORDER BY projname";
$sth = $pdo->prepare($sql); $sth = $pdo->prepare($sql);
try { try {
$sth->execute([$vid]); $sth->execute([$vid]);
@@ -1047,6 +1056,18 @@ function form_create_text($fieldname, $label, $value) {
echo "</div>\n"; echo "</div>\n";
} }
function form_create_check($fieldname, $label, $checked) {
// create single checkbox with assigned label
echo '<div class="mb-3">', "\n";
echo '<input type="checkbox" class="form-check-input" name="', $fieldname, '" id="', $fieldname, '"';
if ($checked) {
echo ' checked';
}
echo ">\n";
echo '<label for="', $fieldname, '">', $label, "</label>\n";
echo "</div>\n";
}
function form_create_select($fieldname, $label, $optlist, $selval=NULL, $multiple=FALSE) { function form_create_select($fieldname, $label, $optlist, $selval=NULL, $multiple=FALSE) {
echo '<div class="mb-3">', "\n"; echo '<div class="mb-3">', "\n";
echo '<label for="', $fieldname, '">', $label, "</label>\n"; echo '<label for="', $fieldname, '">', $label, "</label>\n";
@@ -1063,6 +1084,7 @@ function form_create_select($fieldname, $label, $optlist, $selval=NULL, $multipl
} }
function form_create_checks($fieldname, $label, $optlist, $selected = []) { function form_create_checks($fieldname, $label, $optlist, $selected = []) {
// create multiple combined checkboxes with title
echo '<fieldset class="mb-3">'; echo '<fieldset class="mb-3">';
echo '<legend class="fs-6 mb-1 border-bottom pb-1">', $label, "</legend>\n"; echo '<legend class="fs-6 mb-1 border-bottom pb-1">', $label, "</legend>\n";
echo '<div class="d-flex flex-wrap gap-3">'; echo '<div class="d-flex flex-wrap gap-3">';
+2
View File
File diff suppressed because one or more lines are too long
+167
View File
@@ -0,0 +1,167 @@
let db;
const request = indexedDB.open("shopping", 1);
request.onupgradeneeded = function(event) {
db = event.target.result;
const store = db.createObjectStore(
"items",
{ keyPath: "provid" }
);
};
request.onsuccess = function(event) {
db = event.target.result;
checkDatabase();
};
function checkDatabase()
{
let tx = db.transaction(
"items",
"readonly"
);
let store = tx.objectStore("items");
let request = store.count();
request.onsuccess = function(){
if (request.result > 0) {
renderLists();
} else {
importList(initialList);
}
};
}
function importList(list)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
list.forEach(item => {
item.done = false;
store.put(item);
});
tx.oncomplete = function(){
renderLists();
};
}
function renderLists()
{
let tx = db.transaction(
"items",
"readonly"
);
let store = tx.objectStore("items");
let request = store.getAll();
request.onsuccess = function(){
let pending = "";
let completed = "";
request.result.forEach(item => {
let html = `
<div class="item">
<span class="name">
${item.provname}
</span>
<span>
${item.buy} ${item.unit}
</span>
<button onclick="completeItem(${item.provid})">
</button>
</div>
`;
if (item.completed) {
completed += html;
} else {
pending += html;
}
});
document.getElementById("shoppingList").innerHTML = pending || "No items";
document.getElementById("completedList").innerHTML = completed || "No completed items";
};
}
function plus(id)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
let req = store.get(id);
req.onsuccess = function(){
let item = req.result;
item.buy++;
store.put(item);
renderLists();
};
}
function minus(id)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
let req = store.get(id);
req.onsuccess = function(){
let item = req.result;
item.buy--;
store.put(item);
renderLists();
};
}
function completeItem(id)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
let request = store.get(id);
request.onsuccess = function(){
let item = request.result;
item.completed = true;
store.put(item);
renderLists();
};
}
function undo(id)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
let req = store.get(id);
req.onsuccess = function(){
let item = req.result;
item.done = false;
store.put(item);
showList();
};
}
function done(id)
{
let tx = db.transaction(
"items",
"readwrite"
);
let store = tx.objectStore("items");
let req = store.get(id);
req.onsuccess = function(){
let item = req.result;
item.done = true;
store.put(item);
showList();
};
}
+58 -58
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: YMS 0.1\n" "Project-Id-Version: YMS 0.1\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-07-27 08:02+0200\n" "POT-Creation-Date: 2026-08-14 08:33+0200\n"
"PO-Revision-Date: 2024-03-15 13:50+0100\n" "PO-Revision-Date: 2024-03-15 13:50+0100\n"
"Last-Translator: Thomas Hooge <thomas@hoogi.de>\n" "Last-Translator: Thomas Hooge <thomas@hoogi.de>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -16,7 +16,7 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: box.php:19 index.php:353 globals.inc:1536 #: box.php:19 index.php:357 globals.inc:1536
msgid "Boxes" msgid "Boxes"
msgstr "Kisten" msgstr "Kisten"
@@ -48,7 +48,7 @@ msgid "Content"
msgstr "Inhalt" msgstr "Inhalt"
#: box.php:98 box.php:183 box.php:248 cable.php:211 equipment.php:464 #: box.php:98 box.php:183 box.php:248 cable.php:211 equipment.php:464
#: index.php:361 index.php:377 inventory.php:381 provisions.php:284 #: index.php:365 index.php:381 inventory.php:384 provisions.php:284
msgid "Weight" msgid "Weight"
msgstr "Gewicht" msgstr "Gewicht"
@@ -64,7 +64,7 @@ msgid "View"
msgstr "Ansehen" msgstr "Ansehen"
#: box.php:115 cable.php:151 company.php:231 documents.php:407 dropdown.php:162 #: box.php:115 cable.php:151 company.php:231 documents.php:407 dropdown.php:162
#: equipment.php:399 equipment_js.php:247 fuse.php:176 index.php:318 #: equipment.php:399 equipment_js.php:247 fuse.php:176 index.php:322
#: inventory.php:284 maintenance.php:197 maintenance.php:318 #: inventory.php:284 maintenance.php:197 maintenance.php:318
#: measurement.php:235 projects.php:219 projects.php:264 projects.php:351 #: measurement.php:235 projects.php:219 projects.php:264 projects.php:351
#: provisions.php:219 storage.php:143 storage.php:182 tag.php:136 task.php:256 #: provisions.php:219 storage.php:143 storage.php:182 tag.php:136 task.php:256
@@ -77,7 +77,7 @@ msgid "Add Box"
msgstr "Kiste hinzufügen" msgstr "Kiste hinzufügen"
#: box.php:138 box.php:184 box.php:311 inventory.php:208 inventory.php:313 #: box.php:138 box.php:184 box.php:311 inventory.php:208 inventory.php:313
#: inventory.php:320 inventory.php:374 inventory.php:424 provisions.php:260 #: inventory.php:320 inventory.php:374 inventory.php:427 provisions.php:260
#: provisions.php:290 provisions.php:344 storage.php:12 storage.php:225 #: provisions.php:290 provisions.php:344 storage.php:12 storage.php:225
#: globals.inc:564 globals.inc:1532 #: globals.inc:564 globals.inc:1532
msgid "Storage" msgid "Storage"
@@ -87,7 +87,7 @@ msgstr "Stauraum"
#: company.php:206 company.php:295 company.php:420 documents.php:430 #: company.php:206 company.php:295 company.php:420 documents.php:430
#: documents.php:538 documents.php:778 equipment.php:360 equipment.php:431 #: documents.php:538 documents.php:778 equipment.php:360 equipment.php:431
#: equipment.php:469 equipment.php:684 equipment_js.php:69 fuse.php:237 #: equipment.php:469 equipment.php:684 equipment_js.php:69 fuse.php:237
#: index.php:445 index.php:555 inventory.php:385 inventory.php:452 #: index.php:449 index.php:559 inventory.php:388 inventory.php:455
#: maintenance.php:180 maintenance.php:280 maintenance.php:350 projects.php:176 #: maintenance.php:180 maintenance.php:280 maintenance.php:350 projects.php:176
#: projects.php:200 projects.php:290 projects.php:317 projects.php:403 #: projects.php:200 projects.php:290 projects.php:317 projects.php:403
#: provisions.php:291 provisions.php:347 storage.php:122 storage.php:234 #: provisions.php:291 provisions.php:347 storage.php:122 storage.php:234
@@ -97,7 +97,7 @@ msgstr "Bemerkungen"
#: box.php:156 company.php:255 company.php:427 documents.php:455 #: box.php:156 company.php:255 company.php:427 documents.php:455
#: documents.php:781 equipment.php:435 equipment.php:691 equipment_js.php:116 #: documents.php:781 equipment.php:435 equipment.php:691 equipment_js.php:116
#: equipment_js.php:283 index.php:449 inventory.php:324 maintenance.php:238 #: equipment_js.php:283 index.php:453 inventory.php:324 maintenance.php:238
#: measurement.php:284 note.php:158 projects.php:293 projects.php:406 #: measurement.php:284 note.php:158 projects.php:293 projects.php:406
#: provisions.php:262 settings.php:146 storage.php:208 storage.php:363 #: provisions.php:262 settings.php:146 storage.php:208 storage.php:363
#: task.php:286 globals.inc:1112 globals.inc:1119 #: task.php:286 globals.inc:1112 globals.inc:1119
@@ -107,7 +107,7 @@ msgstr "Speichern"
#: box.php:157 company.php:256 company.php:428 company.php:454 #: box.php:157 company.php:256 company.php:428 company.php:454
#: documents.php:456 documents.php:782 dropdown.php:214 dropdown.php:281 #: documents.php:456 documents.php:782 dropdown.php:214 dropdown.php:281
#: equipment.php:436 equipment.php:692 equipment_js.php:117 #: equipment.php:436 equipment.php:692 equipment_js.php:117
#: equipment_js.php:251 equipment_js.php:284 index.php:450 inventory.php:325 #: equipment_js.php:251 equipment_js.php:284 index.php:454 inventory.php:325
#: maintenance.php:239 measurement.php:285 note.php:159 projects.php:294 #: maintenance.php:239 measurement.php:285 note.php:159 projects.php:294
#: projects.php:407 provisions.php:263 provisions.php:385 search.php:36 #: projects.php:407 provisions.php:263 provisions.php:385 search.php:36
#: settings.php:147 storage.php:209 storage.php:364 task.php:287 #: settings.php:147 storage.php:209 storage.php:364 task.php:287
@@ -139,12 +139,12 @@ msgstr "Keine verknüpften Bilder"
msgid "Inventory in box" msgid "Inventory in box"
msgstr "Inventar in der Kiste" msgstr "Inventar in der Kiste"
#: box.php:246 index.php:375 inventory.php:12 search.php:80 globals.inc:1500 #: box.php:246 index.php:379 inventory.php:12 search.php:80 globals.inc:1500
msgid "Inventory" msgid "Inventory"
msgstr "Inventar" msgstr "Inventar"
#: box.php:247 dropdown.php:150 inventory.php:261 inventory.php:307 #: box.php:247 dropdown.php:150 inventory.php:261 inventory.php:307
#: inventory.php:380 inventory.php:414 provisions.php:183 provisions.php:248 #: inventory.php:383 inventory.php:417 provisions.php:183 provisions.php:248
#: provisions.php:282 provisions.php:316 #: provisions.php:282 provisions.php:316
msgid "Number" msgid "Number"
msgstr "Anzahl" msgstr "Anzahl"
@@ -162,13 +162,13 @@ msgstr "&horbar; keine &horbar;"
msgid "Edit Box" msgid "Edit Box"
msgstr "Kiste bearbeiten" msgstr "Kiste bearbeiten"
#: box.php:308 equipment.php:668 inventory.php:418 provisions.php:324 #: box.php:308 equipment.php:668 inventory.php:421 provisions.php:324
msgid "Weight, kg" msgid "Weight, kg"
msgstr "Gewicht, kg" msgstr "Gewicht, kg"
#: box.php:327 cable.php:298 checklist.php:170 company.php:465 #: box.php:327 cable.php:298 checklist.php:170 company.php:465
#: documents.php:815 dropdown.php:287 equipment.php:718 equipment_js.php:293 #: documents.php:815 dropdown.php:287 equipment.php:718 equipment_js.php:293
#: fuse.php:290 index.php:566 inventory.php:480 maintenance.php:371 #: fuse.php:290 index.php:570 inventory.php:483 maintenance.php:371
#: measurement.php:369 note.php:226 projects.php:419 provisions.php:391 #: measurement.php:369 note.php:226 projects.php:419 provisions.php:391
#: settings.php:153 storage.php:390 tag.php:294 task.php:415 #: settings.php:153 storage.php:390 tag.php:294 task.php:415
msgid "Unknown function call: Please report to system development!" msgid "Unknown function call: Please report to system development!"
@@ -222,8 +222,8 @@ msgstr "Kabel"
#: cable.php:127 cable.php:169 cable.php:235 checklist.php:153 company.php:204 #: cable.php:127 cable.php:169 cable.php:235 checklist.php:153 company.php:204
#: company.php:251 company.php:274 company.php:355 dropdown.php:185 #: company.php:251 company.php:274 company.php:355 dropdown.php:185
#: dropdown.php:233 equipment.php:419 equipment.php:651 equipment_js.php:101 #: dropdown.php:233 equipment.php:419 equipment.php:651 equipment_js.php:101
#: equipment_js.php:268 index.php:437 inventory.php:259 inventory.php:303 #: equipment_js.php:268 index.php:441 inventory.php:259 inventory.php:303
#: inventory.php:410 measurement.php:256 measurement.php:303 #: inventory.php:413 measurement.php:256 measurement.php:303
#: measurement.php:333 projects.php:169 projects.php:196 projects.php:245 #: measurement.php:333 projects.php:169 projects.php:196 projects.php:245
#: projects.php:283 projects.php:310 projects.php:377 provisions.php:244 #: projects.php:283 projects.php:310 projects.php:377 provisions.php:244
#: provisions.php:312 storage.php:120 storage.php:202 storage.php:316 #: provisions.php:312 storage.php:120 storage.php:202 storage.php:316
@@ -244,7 +244,7 @@ msgid "Cross section"
msgstr "Querschnitt" msgstr "Querschnitt"
#: cable.php:132 cable.php:213 cable.php:266 inventory.php:222 #: cable.php:132 cable.php:213 cable.php:266 inventory.php:222
#: inventory.php:262 inventory.php:384 inventory.php:436 #: inventory.php:262 inventory.php:387 inventory.php:439
msgid "Condition" msgid "Condition"
msgstr "Zustand" msgstr "Zustand"
@@ -429,7 +429,7 @@ msgstr "Stadt"
msgid "Delete Company" msgid "Delete Company"
msgstr "Firma löschen" msgstr "Firma löschen"
#: company.php:441 documents.php:802 equipment.php:707 inventory.php:470 #: company.php:441 documents.php:802 equipment.php:707 inventory.php:473
#: note.php:217 storage.php:379 #: note.php:217 storage.php:379
#, php-format #, php-format
msgid "Record no. %d" msgid "Record no. %d"
@@ -455,7 +455,7 @@ msgstr "Neuer GD Imagestream kann nicht initialisiert werden"
msgid "Resource not found!" msgid "Resource not found!"
msgstr "Resource nicht gefunden!" msgstr "Resource nicht gefunden!"
#: documents.php:21 index.php:381 task.php:343 globals.inc:1516 #: documents.php:21 index.php:385 task.php:343 globals.inc:1516
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
@@ -515,7 +515,7 @@ msgstr "SQL-Fehler: %s"
msgid "Removed document reference" msgid "Removed document reference"
msgstr "Dokumentreferenz wurde entfernt" msgstr "Dokumentreferenz wurde entfernt"
#: documents.php:345 note.php:103 #: documents.php:345 note.php:103 note.php:178
msgid "Reference" msgid "Reference"
msgstr "Referenz" msgstr "Referenz"
@@ -537,7 +537,7 @@ msgstr "Dokument"
#: documents.php:437 #: documents.php:437
msgid "Clear selection" msgid "Clear selection"
msgstr "Auswah leeren" msgstr "Auswahl leeren"
#: documents.php:473 #: documents.php:473
msgid "View Document" msgid "View Document"
@@ -597,14 +597,14 @@ msgid "Add new reference"
msgstr "Neue Referenz hinzufügen" msgstr "Neue Referenz hinzufügen"
#: documents.php:654 equipment.php:13 equipment_js.php:14 fuse.php:236 #: documents.php:654 equipment.php:13 equipment_js.php:14 fuse.php:236
#: index.php:359 inventory.php:450 maintenance.php:95 maintenance.php:179 #: index.php:363 inventory.php:453 maintenance.php:95 maintenance.php:179
#: maintenance.php:225 maintenance.php:274 maintenance.php:348 #: maintenance.php:225 maintenance.php:274 maintenance.php:348
#: measurement.php:281 measurement.php:312 measurement.php:358 search.php:52 #: measurement.php:281 measurement.php:312 measurement.php:358 search.php:52
#: globals.inc:532 globals.inc:1496 #: globals.inc:532 globals.inc:1496
msgid "Equipment" msgid "Equipment"
msgstr "Ausrüstung" msgstr "Ausrüstung"
#: documents.php:700 inventory.php:314 inventory.php:321 inventory.php:425 #: documents.php:700 inventory.php:314 inventory.php:321 inventory.php:428
#: storage.php:155 globals.inc:522 #: storage.php:155 globals.inc:522
msgid "Box" msgid "Box"
msgstr "Kiste" msgstr "Kiste"
@@ -750,7 +750,7 @@ msgstr ""
#: equipment.php:357 equipment.php:427 equipment.php:462 equipment.php:660 #: equipment.php:357 equipment.php:427 equipment.php:462 equipment.php:660
#: equipment_js.php:67 equipment_js.php:109 equipment_js.php:138 #: equipment_js.php:67 equipment_js.php:109 equipment_js.php:138
#: equipment_js.php:276 index.php:441 index.php:505 #: equipment_js.php:276 index.php:445 index.php:509
msgid "Model" msgid "Model"
msgstr "Modell" msgstr "Modell"
@@ -785,11 +785,11 @@ msgstr "Entfernt"
msgid "Add Equipment" msgid "Add Equipment"
msgstr "Ausrüstung hinzufügen" msgstr "Ausrüstung hinzufügen"
#: equipment.php:465 inventory.php:382 #: equipment.php:465 inventory.php:385
msgid "Price" msgid "Price"
msgstr "Preis" msgstr "Preis"
#: equipment.php:466 equipment.php:676 inventory.php:383 inventory.php:432 #: equipment.php:466 equipment.php:676 inventory.php:386 inventory.php:435
msgid "Purchase date" msgid "Purchase date"
msgstr "Kaufdatum" msgstr "Kaufdatum"
@@ -841,7 +841,7 @@ msgstr "Keine Ersatzteile gefunden."
msgid "Edit Equipment" msgid "Edit Equipment"
msgstr "Ausrüstung bearbeiten" msgstr "Ausrüstung bearbeiten"
#: equipment.php:672 inventory.php:428 #: equipment.php:672 inventory.php:431
#, php-format #, php-format
msgid "Price, %s" msgid "Price, %s"
msgstr "Preis, %s" msgstr "Preis, %s"
@@ -968,99 +968,99 @@ msgstr "Eigenbau"
msgid "Built by %s" msgid "Built by %s"
msgstr "Gebaut von %s" msgstr "Gebaut von %s"
#: index.php:303 #: index.php:307
msgid "Boat data" msgid "Boat data"
msgstr "Bootsdaten" msgstr "Bootsdaten"
#: index.php:305 #: index.php:309
msgid "Loa" msgid "Loa"
msgstr "LüA" msgstr "LüA"
#: index.php:306 #: index.php:310
msgid "Lwl" msgid "Lwl"
msgstr "LWL" msgstr "LWL"
#: index.php:307 index.php:517 #: index.php:311 index.php:521
msgid "Beam" msgid "Beam"
msgstr "Breite" msgstr "Breite"
#: index.php:308 index.php:521 #: index.php:312 index.php:525
msgid "Draught" msgid "Draught"
msgstr "Tiefgang" msgstr "Tiefgang"
#: index.php:309 index.php:525 #: index.php:313 index.php:529
msgid "Draught, min." msgid "Draught, min."
msgstr "Tiefgang, min." msgstr "Tiefgang, min."
#: index.php:310 index.php:529 #: index.php:314 index.php:533
msgid "Displacement" msgid "Displacement"
msgstr "Verdrängung" msgstr "Verdrängung"
#: index.php:311 index.php:533 #: index.php:315 index.php:537
msgid "Ballast" msgid "Ballast"
msgstr "Ballast" msgstr "Ballast"
#: index.php:312 index.php:537 #: index.php:316 index.php:541
msgid "Belt position aft" msgid "Belt position aft"
msgstr "Gurtposition achtern" msgstr "Gurtposition achtern"
#: index.php:313 index.php:541 #: index.php:317 index.php:545
msgid "Belt position bow" msgid "Belt position bow"
msgstr "Gurtposition vorne" msgstr "Gurtposition vorne"
#: index.php:329 #: index.php:333
msgid "Statistics" msgid "Statistics"
msgstr "Statistik" msgstr "Statistik"
#: index.php:345 #: index.php:349
msgid "Storage type" msgid "Storage type"
msgstr "Stauraumart" msgstr "Stauraumart"
#: index.php:360 index.php:376 index.php:382 #: index.php:364 index.php:380 index.php:386
msgid "Count" msgid "Count"
msgstr "Anzahl" msgstr "Anzahl"
#: index.php:397 #: index.php:401
msgid "Base data" msgid "Base data"
msgstr "Stammdaten" msgstr "Stammdaten"
#: index.php:409 #: index.php:413
msgid "Additional modules" msgid "Additional modules"
msgstr "Zusatzmodule" msgstr "Zusatzmodule"
#: index.php:432 #: index.php:436
msgid "Add additional yacht" msgid "Add additional yacht"
msgstr "Zusätzliche Yacht hinzufügen" msgstr "Zusätzliche Yacht hinzufügen"
#: index.php:481 #: index.php:485
msgid "&horbar; Custom-built &horbar;" msgid "&horbar; Custom-built &horbar;"
msgstr "&horbar; Eigenbau &horbar;" msgstr "&horbar; Eigenbau &horbar;"
#: index.php:497 #: index.php:501
msgid "Edit Yacht" msgid "Edit Yacht"
msgstr "Yacht bearbeiten" msgstr "Yacht bearbeiten"
#: index.php:501 #: index.php:505
msgid "Vesselname" msgid "Vesselname"
msgstr "Yachtname" msgstr "Yachtname"
#: index.php:509 #: index.php:513
msgid "LoA" msgid "LoA"
msgstr "LüA" msgstr "LüA"
#: index.php:513 #: index.php:517
msgid "LwL" msgid "LwL"
msgstr "LWL" msgstr "LWL"
#: index.php:549 #: index.php:553
msgid "Shape file" msgid "Shape file"
msgstr "Rumpfform-Datei" msgstr "Rumpfform-Datei"
#: index.php:550 #: index.php:554
msgid "Shipyard" msgid "Shipyard"
msgstr "Werft" msgstr "Werft"
#: index.php:551 #: index.php:555
msgid "Default Storage" msgid "Default Storage"
msgstr "Standard-Stauraum" msgstr "Standard-Stauraum"
@@ -1085,7 +1085,7 @@ msgstr "Keine Tags zugeordnet"
msgid "Add Inventory" msgid "Add Inventory"
msgstr "Inventar hinzufügen" msgstr "Inventar hinzufügen"
#: inventory.php:311 inventory.php:423 #: inventory.php:311 inventory.php:426
msgid "Container type" msgid "Container type"
msgstr "Behälterart" msgstr "Behälterart"
@@ -1094,19 +1094,23 @@ msgstr "Behälterart"
msgid "%s in %s" msgid "%s in %s"
msgstr "" msgstr ""
#: inventory.php:379 #: inventory.php:378
msgid "View Box"
msgstr "Box ansehen"
#: inventory.php:382
msgid "Location ID" msgid "Location ID"
msgstr "Position (x, y, z)" msgstr "Position (x, y, z)"
#: inventory.php:406 #: inventory.php:409
msgid "Edit Inventory" msgid "Edit Inventory"
msgstr "Inventar bearbeiten" msgstr "Inventar bearbeiten"
#: inventory.php:469 #: inventory.php:472
msgid "Delete Inventory" msgid "Delete Inventory"
msgstr "Inventar löschen" msgstr "Inventar löschen"
#: inventory.php:473 #: inventory.php:476
msgid "" msgid ""
"Deleting an inventory item is final. There is no way back. Only delete if " "Deleting an inventory item is final. There is no way back. Only delete if "
"you are absolute sure." "you are absolute sure."
@@ -1323,10 +1327,6 @@ msgstr "Notiz"
msgid "View Note" msgid "View Note"
msgstr "Notiz ansehen" msgstr "Notiz ansehen"
#: note.php:178
msgid "Referenz"
msgstr ""
#: note.php:196 #: note.php:196
msgid "Edit Note" msgid "Edit Note"
msgstr "Notiz bearbeiten" msgstr "Notiz bearbeiten"
Binary file not shown.
+2
View File
@@ -75,6 +75,8 @@ Es wird das Einlagerungsdatum erfaßt und das Haltbarkeitsdatum</p>
oder aber <em>frei</em>. oder aber <em>frei</em>.
Freier Proviant kann später zugeordnet Freier Proviant kann später zugeordnet
werden. Er ist erkennbar am Einkaufswagen-Symbol <i class="bi bi-cart3"></i>.<p> werden. Er ist erkennbar am Einkaufswagen-Symbol <i class="bi bi-cart3"></i>.<p>
<p>Proviant wird mit Brutto- und Nettogewicht geführt, hat einen Energiegehalt in kCal
und ggf. einen Preis</p>
<p>TODO/Noch nichtimplementiert: Proviant kann auch in eine Kiste gelegt werden</p> <p>TODO/Noch nichtimplementiert: Proviant kann auch in eine Kiste gelegt werden</p>
+1 -1
View File
@@ -175,7 +175,7 @@ $note = $sth->fetch(PDO::FETCH_OBJ);
// edit button // edit button
// list where annotaion used // list where annotaion used
echo '<table class="table">', "\n"; echo '<table class="table">', "\n";
echo '<tr><th>', _('Referenz'),"</th><td>", $note->refid, "</td></tr>\n"; echo '<tr><th>', _('Reference'),"</th><td>", $note->refid, "</td></tr>\n";
echo '<tr><th>', _('Type'),"</th><td>", $note->notetype, "</td></tr>\n"; echo '<tr><th>', _('Type'),"</th><td>", $note->notetype, "</td></tr>\n";
echo '<tr><th>', _('Annotation'),"</th><td>", nl2br($note->annotation), "</td></tr>\n"; echo '<tr><th>', _('Annotation'),"</th><td>", nl2br($note->annotation), "</td></tr>\n";
echo "</table>\n"; echo "</table>\n";
+16 -2
View File
@@ -61,8 +61,10 @@ switch ($submit = form_get_action()) {
$p[':weight'] = min(gpc_get_float($_POST, 'weight'), 999.99); // limit max value $p[':weight'] = min(gpc_get_float($_POST, 'weight'), 999.99); // limit max value
$p[':weight_net'] = min(gpc_get_float($_POST, 'weight_net'), 999.99); $p[':weight_net'] = min(gpc_get_float($_POST, 'weight_net'), 999.99);
$p[':unit'] = gpc_get_int($_POST, 'unit'); $p[':unit'] = gpc_get_int($_POST, 'unit');
$p[':calories'] = gpc_get_uint($_POST, 'calories');
$p[':storedate'] = gpc_get_string($_POST, 'storedate'); $p[':storedate'] = gpc_get_string($_POST, 'storedate');
$p[':shelflife'] = gpc_get_string($_POST, 'shelflife'); $p[':shelflife'] = gpc_get_string($_POST, 'shelflife');
$p[':price'] = gpc_get_currency($_POST, 'price');
$p[':sid'] = gpc_get_int($_POST, 'sid'); $p[':sid'] = gpc_get_int($_POST, 'sid');
if ($p[':sid'] <= 0) $p[':sid'] = NULL; if ($p[':sid'] <= 0) $p[':sid'] = NULL;
$p[':remarks'] = gpc_get_string($_POST, 'remarks', 150); $p[':remarks'] = gpc_get_string($_POST, 'remarks', 150);
@@ -269,7 +271,7 @@ elseif ($action == ACT_VIEW):
// ========== VARIANT: view single record ===================================== // ========== VARIANT: view single record =====================================
$sql = "SELECT provname, provcat, number, target, unit, weight, weight_net," $sql = "SELECT provname, provcat, number, target, unit, weight, weight_net,"
. " storedate, shelflife, sid, remarks " . " calories, storedate, shelflife, price, sid, remarks "
. "FROM provisions " . "FROM provisions "
. "WHERE provid=?"; . "WHERE provid=?";
$sth = $pdo->prepare($sql); $sth = $pdo->prepare($sql);
@@ -284,9 +286,11 @@ echo "<tr><th>", _('Target'), "</th><td>", $prov->target, "</td></tr>\n";
echo "<tr><th>", _('Weight'), "</th><td>", format_float($prov->weight, 2, 'kg'), "</td></tr>\n"; echo "<tr><th>", _('Weight'), "</th><td>", format_float($prov->weight, 2, 'kg'), "</td></tr>\n";
echo "<tr><th>", _('Weight net'), "</th><td>", format_float($prov->weight_net, 2, 'kg'), "</td></tr>\n"; echo "<tr><th>", _('Weight net'), "</th><td>", format_float($prov->weight_net, 2, 'kg'), "</td></tr>\n";
echo '<tr><th>', _('Unit'),"</th><td>", $opt_unit[$prov->unit], "</td></tr>\n"; echo '<tr><th>', _('Unit'),"</th><td>", $opt_unit[$prov->unit], "</td></tr>\n";
echo "<tr><th>", _('Calories'), "</th><td>", $prov->calories, " kcal</td></tr>\n";
echo '<tr><th>', _('Category'),"</th><td>", $opt_category[$prov->provcat] ?? 'n/a', "</td></tr>\n"; echo '<tr><th>', _('Category'),"</th><td>", $opt_category[$prov->provcat] ?? 'n/a', "</td></tr>\n";
echo '<tr><th>', _('Storedate'),"</th><td>", $prov->storedate, "</td></tr>\n"; echo '<tr><th>', _('Storedate'),"</th><td>", $prov->storedate, "</td></tr>\n";
echo '<tr><th>', _('Shelflife'),"</th><td>", $prov->shelflife, "</td></tr>\n"; echo '<tr><th>', _('Shelflife'),"</th><td>", $prov->shelflife, "</td></tr>\n";
echo '<tr><th>', _('Price'), "</th><td>", format_currency($prov->price), "</td></tr>\n";
echo '<tr><th>', _('Storage'),"</th><td>", ($opt_storage[$prov->sid] ?? ''), "</td></tr>\n"; echo '<tr><th>', _('Storage'),"</th><td>", ($opt_storage[$prov->sid] ?? ''), "</td></tr>\n";
echo '<tr><th>', _('Remarks'),"</th><td>", nl2br($prov->remarks), "</td></tr>\n"; echo '<tr><th>', _('Remarks'),"</th><td>", nl2br($prov->remarks), "</td></tr>\n";
echo "</table>\n"; echo "</table>\n";
@@ -297,7 +301,7 @@ elseif ($action == ACT_EDIT):
// ========== VARIANT: edit single record ===================================== // ========== VARIANT: edit single record =====================================
$sql = "SELECT provname, provcat, number, target, unit, weight, weight_net," $sql = "SELECT provname, provcat, number, target, unit, weight, weight_net,"
. " storedate, shelflife, sid, remarks " . " calories, storedate, shelflife, price, sid, remarks "
. "FROM provisions " . "FROM provisions "
. "WHERE provid=?"; . "WHERE provid=?";
$sth = $pdo->prepare($sql); $sth = $pdo->prepare($sql);
@@ -330,6 +334,12 @@ echo '<h2>', _('Edit provision'), "</h2>\n";
</div> </div>
<?php <?php
form_create_select('unit', _('Unit'), $opt_unit, $prov->unit); form_create_select('unit', _('Unit'), $opt_unit, $prov->unit);
?>
<div class="mb-3">
<label for="target"><?=_('Calories per Unit (kcal)')?></label>
<input type="number" min="0" max="65000" class="form-control" id="calories" name="calories" value="<?=$prov->calories ?>">
</div>
<?php
form_create_select('provcat', _('Category'), $g_opt_none + $opt_category, $prov->provcat); form_create_select('provcat', _('Category'), $g_opt_none + $opt_category, $prov->provcat);
?> ?>
<div class="mb-3"> <div class="mb-3">
@@ -340,6 +350,10 @@ form_create_select('provcat', _('Category'), $g_opt_none + $opt_category, $prov-
<label for="shelflife"><?=_('Shelflife')?></label> <label for="shelflife"><?=_('Shelflife')?></label>
<input type="date" class="form-control" id="shelflife" name="shelflife" value="<?=$prov->shelflife ?>"> <input type="date" class="form-control" id="shelflife" name="shelflife" value="<?=$prov->shelflife ?>">
</div> </div>
<div class="mb-3">
<label for="price" class="form-label"><?=sprintf(_('Price, %s'), $g_lconv['currency_symbol']); ?></label>
<input type="text" class="form-control" id="price" name="price" value="<?=format_float($prov->price) ?>">
</div>
<?php <?php
form_create_select('sid', _('Storage'), $g_opt_none + $opt_storage, $prov->sid); form_create_select('sid', _('Storage'), $g_opt_none + $opt_storage, $prov->sid);
?> ?>
+1 -1
View File
@@ -16,7 +16,7 @@
style="display:inline"><path style="display:inline"><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00001;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:8, 4, 2.00001, 4;stroke-dashoffset:0;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2.00001;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:8, 4, 2.00001, 4;stroke-dashoffset:0;stroke-opacity:1"
d="M 42.346999,421.58603 H 1066.347" d="M 42.346999,421.58603 H 1066.347"
id="path399" /><path id="centerline" /><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 43.434741,421.43502 c 0,0 0.413166,-22.42089 3.894359,-39.92496 8.859879,-44.54909 33.485118,-67.94835 93.87163,-87.80423 71.79884,-23.60841 231.35367,-47.44953 338.07331,-45.17591 58.4656,1.24559 128.71431,3.06837 239.1874,30.59247 125.0131,31.14672 271.98631,88.99119 345.78696,137.98517 0,0 0.7865,1.03131 0.972,1.63871 0.3052,0.99905 0.1801,3.12866 0.1801,3.12866" d="m 43.434741,421.43502 c 0,0 0.413166,-22.42089 3.894359,-39.92496 8.859879,-44.54909 33.485118,-67.94835 93.87163,-87.80423 71.79884,-23.60841 231.35367,-47.44953 338.07331,-45.17591 58.4656,1.24559 128.71431,3.06837 239.1874,30.59247 125.0131,31.14672 271.98631,88.99119 345.78696,137.98517 0,0 0.7865,1.03131 0.972,1.63871 0.3052,0.99905 0.1801,3.12866 0.1801,3.12866"
id="path1004" /><use id="path1004" /><use

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

+138
View File
@@ -0,0 +1,138 @@
<svg
width="1024"
height="325"
viewBox="0 0 1024 325"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
id="title134">Colin Archer 40</title><defs
id="defs2" /><g
id="layer1"
style="display:inline"><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 32.902587,163.24723 c 0,0 -6.666293,-22.17724 51.732501,-67.565006 C 187.52515,15.715671 366.50013,5.7999235 433.65337,3.9666602 542.97458,0.98222366 704.13522,4.0275731 830.22893,58.523616 908.62334,92.40465 969.7182,149.13425 978.673,155.26415 c 5.4153,3.70695 4.5761,8.14666 4.5761,8.14666"
id="path951" /><use
x="0"
y="0"
xlink:href="#path951"
id="use1049"
transform="matrix(1,0,0,-1,-0.05781152,326.36779)" /><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 895.985,92.86984 V 232.55335"
id="path1051" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 689.37014,17.637239 V 195.72401"
id="path1051-36" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 540.10735,3.0789488 V 91.21857"
id="path1051-36-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 690.02348,237.42732 V 307.8727"
id="path1051-36-7-2" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 416.49715,4.2620683 V 132.78882"
id="path1051-36-5" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 310.10943,14.105407 V 136.19705"
id="path1051-36-3" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 113.52266,76.302173 V 251.83248"
id="path1051-36-56" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 690.08485,105.08884 28.07694,0.60163"
id="path1051-36-7-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 786.68796,42.130597 0.25367,46.194127 -16.3841,16.832756 -12.41255,0.48582"
id="path1051-36-7-1" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 502.13905,196.92602 6.32096,0.0627 31.73427,26.11546 -0.69919,99.59481"
id="path1051-36-7-27" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 417.82431,321.48565 0.004,-123.99946"
id="path1051-36-7-0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 308.66858,312.05474 0.81755,-70.25663"
id="path1051-36-7-0-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 310.24697,200.53963 0.007,-13.8616"
id="path1051-36-7-0-6-0" /><rect
style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
id="rect625"
width="152.16988"
height="11.532784"
x="866.78735"
y="157.73856"
rx="1.3628241"
ry="1.3045897" /><rect
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect640"
width="43.70451"
height="5.1411266"
x="2.7056627"
y="160.75401"
rx="1.3628241"
ry="1.3045897" /><ellipse
style="fill:#ffffff;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="path1248"
cx="657.82275"
cy="163.56593"
rx="12.162697"
ry="7.8855205" /><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 4, 2, 4;stroke-dashoffset:0;stroke-opacity:1"
d="M 3.2714902e-6,163.30965 1024,163.3063"
id="path271" /></g><g
id="layer2"
style="display:inline"
transform="translate(-25.319896,5.562848)"><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 565.67989,236.15227 148.94077,0.58894"
id="path1051-3" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 567.16163,86.445447 148.94077,0.58894"
id="path1051-3-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 811.98823,83.068113 57.1816,30.733537 48.6489,-26.735129"
id="path1051-3-2" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 854.02272,105.74204 876.63281,63.267145"
id="path1051-3-2-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 715.48091,236.62096 39.15964,0.46197 -0.0963,57.26552"
id="path1051-3-2-6-8" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 831.33681,93.198753 854.85906,52.701572"
id="path1051-3-2-1" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 334.88168,257.06299 108.33469,-0.0336"
id="path1051-3-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999994;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 335.54514,66.093576 106.51923,-0.0336"
id="path1051-3-7-23" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 304.78037,14.051838 0.25341,67.943863 30.47429,-0.462178"
id="path1051-3-7-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 248.70861,289.07127 14.00732,-51.92697 71.96665,5.09935"
id="path1051-3-7-2" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 298.75245,239.75719 -7.2046,59.2492"
id="path1051-3-7-0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:0.99999899,1.99999797;stroke-opacity:1;stroke-dashoffset:0"
d="m 444.22245,247.11088 63.11081,0.2124 13.52892,-40.80411 0.425,-100.58449 -13.78927,-39.435166 -65.58989,0.129781"
id="path1051-3-7-7" /><rect
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:1, 2;stroke-dashoffset:0"
id="rect1606"
width="74.914055"
height="69.12413"
x="570.22064"
y="122.47643"
rx="1.3628241"
ry="1.3045897" /></g><metadata
id="metadata132"><rdf:RDF><cc:Work
rdf:about=""><dc:title>Colin Archer 40</dc:title><dc:creator><cc:Agent><dc:title>Thomas Hooge</dc:title></cc:Agent></dc:creator></cc:Work></rdf:RDF></metadata></svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

+1 -1
View File
@@ -30,7 +30,7 @@
<path <path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.268;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.072, 0.536, 0.268, 0.536;stroke-dashoffset:0;stroke-opacity:1" style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.268;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.072, 0.536, 0.268, 0.536;stroke-dashoffset:0;stroke-opacity:1"
d="m 297.50974,51.957452 c -2.54837,0.522337 -297.50950858727,0 -297.50950858727,0" d="m 297.50974,51.957452 c -2.54837,0.522337 -297.50950858727,0 -297.50950858727,0"
id="path857" /> id="centerline" />
<text <text
xml:space="preserve" xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;line-height:98%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.93889px;line-height:98%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

+1 -1
View File
@@ -28,7 +28,7 @@
transform="matrix(1,0,0,-1,-0.02583644,109.41506)" /><path transform="matrix(1,0,0,-1,-0.02583644,109.41506)" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.529169;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:2.11667, 1.05833, 0.529169, 1.05833;stroke-dashoffset:0;stroke-opacity:1" style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.529169;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:2.11667, 1.05833, 0.529169, 1.05833;stroke-dashoffset:0;stroke-opacity:1"
d="M 0.21617824,54.888403 270.69991,54.888402" d="M 0.21617824,54.888403 270.69991,54.888402"
id="path271" /><path id="centerline" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.529168;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1" style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.529168;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 141.00077,5.7751708 V 53.289576" d="M 141.00077,5.7751708 V 53.289576"
id="path1051-5-20-3-5-3" /><path id="path1051-5-20-3-5-3" /><path

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

+289
View File
@@ -0,0 +1,289 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="1024"
height="305"
viewBox="0 0 1024 305"
version="1.1"
id="svg5"
xml:space="preserve"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"><title
id="title134">Gulfstar 41</title><defs
id="defs2" /><g
id="layer1"
style="display:inline"><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:8, 4, 2, 4;stroke-dashoffset:0;stroke-opacity:1"
d="M 3.3e-6,152.00334 1023.9982,151.99999"
id="path271" /><path
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 4.541505,152.82638 c 0,0 0.729165,-54.491109 25.243635,-88.018502 C 94.775098,50.967238 140.69217,39.927662 199.55731,31.275058 297.4149,16.890941 397.62677,5.6293124 462.03098,4.859982 568.15897,3.5922467 688.37772,16.318765 809.42487,53.979712 912.75153,86.127351 1008.139,138.58129 1017.0525,144.71119 c 5.3903,3.70695 4.555,8.14666 4.555,8.14666"
id="path951" /><use
x="0"
y="0"
xlink:href="#path951"
id="use1049"
transform="matrix(1,0,0,-1,0.14093269,304.94033)" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 296.62649,18.557857 V 286.46486"
id="path1051-5" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 415.8873,227.21891 v 71.23063"
id="path1051-5-20" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 296.3826,153.91187 75.74474,30.18682 72.23129,0.21079 -0.0479,-57.74576 -27.76098,-0.14802 -0.69918,-119.2298423"
id="path1051-5-0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 671.07416,20.286177 0.0415,128.557953 56.89431,-0.10936 29.62768,-23.18612 -0.78991,-85.78406"
id="path1051-5-2-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 220.55901,28.151512 v 92.906228 l 17.65895,30.72635 58.41003,0.0579"
id="path1051-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 296.53346,226.79659 -75.83471,-11.69391 -0.24255,61.15704"
id="path1051-35" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 914.04719,93.338688 V 212.03375"
id="path1051" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 755.60805,266.13836 -0.2572,-69.86532 -84.93468,-0.33711 -0.018,87.43233"
id="path1051-5-2" /><ellipse
style="display:inline;fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0"
id="path1640"
cx="594.63507"
cy="152.02142"
rx="8.7870979"
ry="4.8806257" /></g><g
id="layer2"
style="display:inline"><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:1.99999, 0.999997;stroke-dashoffset:0;stroke-opacity:1"
d="m 756.00672,238.37946 c 0,0 59.16733,-20.02492 85.38293,-30.44142 26.2156,-10.41649 71.66524,-31.86272 71.66524,-31.86272"
id="path1051-3" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 669.46008,195.69652 508.5147,213.65213"
id="path1051-3-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 822.22439,152.3697 758.17324,125.24424"
id="path1051-3-9-62" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 822.09326,152.28781 -66.36093,29.18905 -0.0239,14.78286"
id="path1051-3-9-62-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 508.55542,213.45397 0.0642,86.3025"
id="path1051-3-9-97" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 670.76127,237.09813 508.69631,255.16318"
id="path1051-3-9-75" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 670.66554,254.20123 508.64908,272.20918"
id="path1051-3-9-92" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 661.4627,142.51176 -0.7724,-79.741048 c 0,0 -37.56032,-7.039363 -56.42139,-6.896387 -18.86107,0.142976 -55.78157,7.771263 -55.78157,7.771263 l -0.14541,79.791092"
id="path1051-3-9-2" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 415.442,253.04762 295.11292,242.76125"
id="path1051-3-9-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 460.1918,231.34815 -8.69345,-0.0532"
id="path1051-3-9-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 416.39973,52.794624 297.21078,62.947673"
id="path1051-3-9-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 296.55075,62.8079 -76.00147,0.07026"
id="path1051-3-9-6-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 271.79139,151.82674 271.63843,63.050532"
id="path1051-3-9-6-7-0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 271.73415,111.91673 -51.28978,0.0489"
id="path1051-3-9-6-7-0-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 671.34001,43.684548 541.56327,28.616168"
id="path1051-3-9-6-1" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 670.65715,142.55208 -39.25248,0.44377 -1.31417,-55.619742 -48.91307,-0.79758 v 57.313592 l -40.07981,0.0484 L 540.4774,7.3776096"
id="path1051-3-9-6-1-8" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:2, 0.999999;stroke-dashoffset:0;stroke-opacity:1"
d="m 416.30812,74.92521 c 0,0 -82.47253,15.144404 -122.81009,25.9362 -40.33756,10.7918 -119.21527,38.81458 -119.21527,38.81458 l -0.48224,29.55534 c 0,0 80.54391,27.12128 120.73218,37.21131 40.18827,10.09004 121.27862,23.76758 121.27862,23.76758"
id="path1051-3-7" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 219.67776,109.0587 93.855492,151.80189"
id="path1051-3-7-3" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 219.86139,195.03714 94.039117,152.29395"
id="path1051-3-7-3-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 220.0363,213.44097 219.71324,194.9243"
id="path1051-3-7-3-6-6" /><use
x="0"
y="0"
xlink:href="#path1051-6"
id="use457"
transform="matrix(1,0,0,-1,-0.13850787,304.9487)"
style="display:inline" /><rect
style="display:inline;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect1255"
width="28.664709"
height="25.4055"
x="444.12122"
y="150.14844" /><rect
style="display:inline;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect1255-1"
width="48.177208"
height="62.083755"
x="460.24216"
y="224.74203" /><rect
style="display:inline;fill:none;stroke:#000000;stroke-width:0.999998;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect1255-1-2"
width="35.377628"
height="52.031517"
x="416.01917"
y="228.25325" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 220.17782,38.804009 c 0,0 -56.08046,11.503545 -98.40722,29.919685 C 79.443836,87.139833 58.501345,112.47149 58.501345,112.47149 l -0.185798,40.1905"
id="path1051-6" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 671.50207,60.229759 84.47664,0.731761"
id="path1051-3-70" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 728.31329,60.955674 0.21143,87.895066"
id="path1051-3-70-9" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999999;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 671.31947,114.80366 57.0344,0.0153"
id="path1051-3-70-9-5" /><rect
style="display:inline;fill:none;stroke:#000000;stroke-width:0.999995;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect1255-3"
width="28.496277"
height="25.182354"
x="506.32599"
y="47.019566" /><rect
style="display:inline;fill:none;stroke:#000000;stroke-width:0.999995;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
id="rect1255-3-6"
width="40.192474"
height="30.994183"
x="453.94885"
y="42.663792" /><circle
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0"
id="path1537"
cx="473.59415"
cy="50.722347"
r="4.1049838" /><circle
style="display:inline;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0"
id="path1537-2"
cx="484.03549"
cy="64.60498"
r="4.1049838" /><circle
style="display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.999999;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0"
id="path1537-2-3"
cx="511.54593"
cy="59.438629"
r="1.4208409" /><circle
style="display:inline;fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0"
id="path1537-6"
cx="463.9982"
cy="64.60498"
r="4.1049838" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 670.94118,54.557571 c 0,0 -43.64416,-7.676983 -65.60103,-7.709096 -21.95686,-0.03211 -65.08605,7.813561 -65.08605,7.813561"
id="path1051-3-9-3" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 670.27867,245.85044 508.57702,264.03781"
id="path1051-3-9-28" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="M 540.61416,41.075906 500.33278,41.27855 V 14.68724 h -51.75124 v 54.26063 l -32.33807,0.248933"
id="path1051-3-9-36" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 541.01887,131.38337 -38.36889,-0.14689 0.35135,-55.159487 -58.43273,0.0059 0.12857,50.081065"
id="path1051-3-9-1" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.999997;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 444.57231,76.176669 -0.002,-7.190214"
id="path1051-3-9-29" /><rect
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2020"
width="22.906216"
height="18.748451"
x="507.5379"
y="82.955887"
ry="1.3045897"
rx="1.3628241" /><rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2020-1"
width="22.906216"
height="18.748451"
x="507.51715"
y="106.56944"
ry="1.3045897"
rx="1.3628241" /><rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2020-1-3"
width="12.380301"
height="18.748451"
x="276.67719"
y="118.84782"
ry="1.3045897"
rx="1.3628241" /><rect
style="display:inline;fill:none;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2020-1-3-0"
width="12.380301"
height="18.748451"
x="736.40729"
y="103.76661"
ry="1.3045897"
rx="1.3628241" /><g
id="g1346"
transform="translate(1.6711347,1.1925367)"><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 711.61753,105.88116 2.65465,-0.01"
id="path1051-3-70-9-6" /><rect
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2591-3"
width="8.0729084"
height="10.706293"
x="714.31329"
y="101.32455"
ry="0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 716.92538,107.04318 2.79647,-0.003"
id="path1051-3-70-9-6-7" /><path
id="rect2588"
style="fill:#ffffff;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;fill-opacity:1"
d="m 686.24942,90.899539 20.82037,0.08492 c 0,0 5.06651,8.402799 4.39879,16.424111 -0.93076,11.18123 -8.47355,16.69152 -14.7092,16.79516 -6.23564,0.1036 -13.73785,-6.42007 -14.55567,-15.91907 -0.83507,-9.699525 4.04571,-17.385082 4.04571,-17.385082 z" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2591"
width="22.45204"
height="2.0430894"
x="685.52374"
y="88.79052" /></g><g
id="g1346-5"
transform="translate(-452.44821,-6.2064984)"
style="display:inline"><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 711.61753,105.88116 2.65465,-0.01"
id="path1051-3-70-9-6-6" /><rect
style="display:inline;fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2591-3-2"
width="8.0729084"
height="10.706293"
x="714.31329"
y="101.32455"
ry="0" /><path
style="display:inline;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
d="m 716.92538,107.04318 2.79647,-0.003"
id="path1051-3-70-9-6-7-9" /><path
id="rect2588-1"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round"
d="m 686.24942,90.899539 20.82037,0.08492 c 0,0 5.06651,8.402799 4.39879,16.424111 -0.93076,11.18123 -8.47355,16.69152 -14.7092,16.79516 -6.23564,0.1036 -13.73785,-6.42007 -14.55567,-15.91907 -0.83507,-9.699525 4.04571,-17.385082 4.04571,-17.385082 z" /><rect
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect2591-2"
width="22.45204"
height="2.0430894"
x="685.52374"
y="88.79052" /></g></g><metadata
id="metadata132"><rdf:RDF><cc:Work
rdf:about=""><dc:title>Shape template</dc:title><dc:creator><cc:Agent><dc:title>Thomas Hooge</dc:title></cc:Agent></dc:creator></cc:Work></rdf:RDF></metadata></svg>

After

Width:  |  Height:  |  Size: 20 KiB

+11 -7
View File
@@ -13,7 +13,7 @@ $pagetitle = _('Task');
$id = gpc_get_int($_REQUEST, 'id', 0); $id = gpc_get_int($_REQUEST, 'id', 0);
$opt_projects = db_get_opt_proj($user->vid, array(-1 => _('unassigned'))); $opt_projects = db_get_opt_proj($user->vid, [], array(-1 => _('unassigned')));
$opt_priority = db_load_enum('task', 'priority', true, true); $opt_priority = db_load_enum('task', 'priority', true, true);
$opt_state = db_load_enum('task', 'taskstate', true, true); $opt_state = db_load_enum('task', 'taskstate', true, true);
@@ -30,6 +30,7 @@ switch ($submit = form_get_action()) {
case 'filter': case 'filter':
$flt = array(); $flt = array();
$flt['proj'] = gpc_get_int($_POST, 'flt_proj');
$flt['prio'] = gpc_get_enum($_POST, 'flt_prio', array_merge(array_keys($opt_priority), ['-2', '-1'])); $flt['prio'] = gpc_get_enum($_POST, 'flt_prio', array_merge(array_keys($opt_priority), ['-2', '-1']));
$flt['txt'] = gpc_get_string($_POST, 'flt_txt'); $flt['txt'] = gpc_get_string($_POST, 'flt_txt');
$allowed = db_load_enum('task', 'taskstate'); $allowed = db_load_enum('task', 'taskstate');
@@ -125,7 +126,7 @@ if ($action == ACT_DEFAULT):
page_caption_search($pagetitle); page_caption_search($pagetitle);
// load filter from db // load filter from db
$flt = db_get_filter($user->id, 206, ['txt','prio', 'stat']); $flt = db_get_filter($user->id, 206, ['txt', 'prio', 'stat', 'proj']);
$w = array('(t.vid=:vid OR t.vid IS NULL)'); $w = array('(t.vid=:vid OR t.vid IS NULL)');
$p = array(':vid' => $user->vid); $p = array(':vid' => $user->vid);
@@ -133,16 +134,18 @@ if (strlen($flt->txt) > 1) {
$w[] = '(taskname LIKE :txt)'; $w[] = '(taskname LIKE :txt)';
$p[':txt'] = '%'.$flt->txt.'%'; $p[':txt'] = '%'.$flt->txt.'%';
} }
if ($flt->proj > 0) {
$w[] = 'projid=:projid';
$p[':projid'] = $flt->proj;
} elseif ($flt->proj == -1) {
$w[] = 'projid IS NULL';
}
if (strlen($flt->prio) > 2 ) { if (strlen($flt->prio) > 2 ) {
$w[] = 'priority=:priority'; $w[] = 'priority=:priority';
$p[':priority'] = $flt->prio; $p[':priority'] = $flt->prio;
} elseif ($flt->prio == -1) { } elseif ($flt->prio == -1) {
$w[] = 'priority IS NULL'; $w[] = 'priority IS NULL';
} }
/*if (strlen($flt->stat) > 2 ) {
$w[] = 'taskstate=:taskstate';
$p[':taskstate'] = $flt->stat;
} */
$parts = ["taskstate=''", 'taskstate IS NULL']; $parts = ["taskstate=''", 'taskstate IS NULL'];
foreach ($flt->stat as $f) { foreach ($flt->stat as $f) {
$parts[] = "FIND_IN_SET('$f', taskstate)"; $parts[] = "FIND_IN_SET('$f', taskstate)";
@@ -202,8 +205,9 @@ $res = $sth->fetchAll();
<div class="card-header"><i class="bi bi-funnel me-2"></i>Filter</div> <div class="card-header"><i class="bi bi-funnel me-2"></i>Filter</div>
<div class="card-body d-flex flex-wrap gap-3"> <div class="card-body d-flex flex-wrap gap-3">
<?php <?php
$opt_proj_filter = db_get_opt_proj($user->vid, ['finished']);
filter_create_select('flt_proj', _('Project'), $g_opt_all + $g_opt_none + $opt_proj_filter, $flt->proj);
filter_create_select('flt_prio', _('Priority'), $g_opt_all + $g_opt_none + $opt_priority, $flt->prio); filter_create_select('flt_prio', _('Priority'), $g_opt_all + $g_opt_none + $opt_priority, $flt->prio);
//filter_create_select('flt_stat', _('State'), $g_opt_all + $opt_state, $flt->stat);
?> ?>
<div class="d-flex flex-nowrap gap-2"> <div class="d-flex flex-nowrap gap-2">
<label for="flt_txt">Text</label> <label for="flt_txt">Text</label>
+35 -32
View File
@@ -163,26 +163,25 @@
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="orientation">vertical</property> <property name="orientation">vertical</property>
<child> <child>
<object class="GtkListBox" id="lvw_user"> <object class="GtkScrolledWindow">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">True</property>
</object> <property name="shadow-type">in</property>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkSeparator"> <object class="GtkTreeView" id="tvw_user">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">True</property>
<property name="orientation">vertical</property> <signal name="row-activated" handler="on_tvw_user_row_activated" swapped="no"/>
<child internal-child="selection">
<object class="GtkTreeSelection"/>
</child>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">0</property>
</packing> </packing>
</child> </child>
<child> <child>
@@ -191,11 +190,12 @@
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="layout-style">end</property> <property name="layout-style">end</property>
<child> <child>
<object class="GtkButton"> <object class="GtkButton" id="but_user_new">
<property name="label" translatable="yes">New</property> <property name="label" translatable="yes">New</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<signal name="clicked" handler="on_but_user_new_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -204,11 +204,12 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton"> <object class="GtkButton" id="but_user_del">
<property name="label" translatable="yes">Delete</property> <property name="label" translatable="yes">Delete</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<signal name="clicked" handler="on_but_user_del_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -220,7 +221,7 @@
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">True</property>
<property name="position">3</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
@@ -262,7 +263,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry"> <object class="GtkEntry" id="ent_user_login">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
</object> </object>
@@ -272,7 +273,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry"> <object class="GtkEntry" id="ent_user_displayname">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
</object> </object>
@@ -304,7 +305,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkComboBox"> <object class="GtkComboBox" id="cmb_user_role">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
</object> </object>
@@ -314,7 +315,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkComboBox"> <object class="GtkComboBox" id="cmb_user_lang">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
</object> </object>
@@ -335,7 +336,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkEntry"> <object class="GtkEntry" id="ent_user_pass">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
</object> </object>
@@ -345,11 +346,12 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton"> <object class="GtkButton" id="but_user_newpass">
<property name="label" translatable="yes">Create</property> <property name="label" translatable="yes">Create</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<signal name="clicked" handler="on_but_user_newpass_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="left-attach">2</property> <property name="left-attach">2</property>
@@ -357,7 +359,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton"> <object class="GtkCheckButton" id="chk_user_locked">
<property name="label" translatable="yes">Locked</property> <property name="label" translatable="yes">Locked</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
@@ -370,7 +372,7 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkCheckButton"> <object class="GtkCheckButton" id="chk_user_deleted">
<property name="label" translatable="yes">Deleted</property> <property name="label" translatable="yes">Deleted</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
@@ -446,11 +448,12 @@
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="layout-style">end</property> <property name="layout-style">end</property>
<child> <child>
<object class="GtkButton"> <object class="GtkButton" id="but_user_save">
<property name="label" translatable="yes">Save</property> <property name="label" translatable="yes">Save</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="receives-default">True</property> <property name="receives-default">True</property>
<signal name="clicked" handler="on_but_user_save_clicked" swapped="no"/>
</object> </object>
<packing> <packing>
<property name="expand">True</property> <property name="expand">True</property>
@@ -480,7 +483,7 @@
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">Lists</property> <property name="label" translatable="yes">User</property>
</object> </object>
<packing> <packing>
<property name="position">2</property> <property name="position">2</property>
@@ -560,7 +563,7 @@
<object class="GtkLabel"> <object class="GtkLabel">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">False</property> <property name="can-focus">False</property>
<property name="label" translatable="yes">User</property> <property name="label" translatable="yes">Lists</property>
</object> </object>
<packing> <packing>
<property name="position">2</property> <property name="position">2</property>
@@ -846,7 +849,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="shadow-type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView"> <object class="GtkTreeView" id="tvw_equipment">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<child internal-child="selection"> <child internal-child="selection">
@@ -1831,7 +1834,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="shadow-type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView"> <object class="GtkTreeView" id="tvw_provisions">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<child internal-child="selection"> <child internal-child="selection">
@@ -2040,7 +2043,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="shadow-type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView"> <object class="GtkTreeView" id="tvw_maintenance">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<child internal-child="selection"> <child internal-child="selection">
@@ -2270,7 +2273,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="shadow-type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView"> <object class="GtkTreeView" id="tvw_projects">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<child internal-child="selection"> <child internal-child="selection">
@@ -2500,7 +2503,7 @@
<property name="can-focus">True</property> <property name="can-focus">True</property>
<property name="shadow-type">in</property> <property name="shadow-type">in</property>
<child> <child>
<object class="GtkTreeView"> <object class="GtkTreeView" id="tvw_documents">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can-focus">True</property> <property name="can-focus">True</property>
<child internal-child="selection"> <child internal-child="selection">
+78 -9
View File
@@ -1,13 +1,20 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import MySQLdb.cursors import MySQLdb.cursors
import bcrypt
import random
import string
class User(object): class User(object):
@classmethod
def genpass(cls, length=12):
return ''.join(random.choices(string.ascii_letters + string.digits, k=length))
def __init__(self, dbconn, user_id=None): def __init__(self, dbconn, user_id=None):
self.conn = dbconn self.conn = dbconn
self.cur = dbconn.cursor(MySQLdb.cursors.DictCursor) self.cur = dbconn.cursor(MySQLdb.cursors.DictCursor)
if user_id: if user_id is not None:
self.user_id = int(user_id) self.user_id = int(user_id)
self.load() self.load()
else: else:
@@ -15,13 +22,13 @@ class User(object):
self.displayname = None self.displayname = None
self.language = 'en' self.language = 'en'
self.role = 'undef' self.role = 'undef'
self.flags = '' self.flags = []
def load(self): def load(self, user_id=0):
sql = ( sql = (
"SELECT login, displayname, language, role, flags " "SELECT login, displayname, language, role, flags "
"FROM user " "FROM user "
"WHERE user_id=%s") "WHERE userid=%s")
self.cur.execute(sql, (self.user_id, )) self.cur.execute(sql, (self.user_id, ))
row = self.cur.fetchone() row = self.cur.fetchone()
if row: if row:
@@ -30,15 +37,32 @@ class User(object):
self.displayname = row['displayname'] self.displayname = row['displayname']
self.language = row['language'] self.language = row['language']
self.role = row['role'] self.role = row['role']
self.flags = row['flags'] self.flags = row['flags'].split(',') if row['flags'] else []
else: else:
self.data = None self.data = None
def save(self): def save(self):
""" fields = ('login', 'displayname', 'language', 'role')
Speichern eines Benutzers ist momentan nicht vorgesehen sqlfield = []
""" values = []
pass for field in fields:
newdata = getattr(self, field)
if self.data[field] != newdata:
sqlfield.append(f"{field}=%s")
values.append(newdata)
self.data[field] = newdata
# flags set handled separately
newflags = ','.join(self.flags) if self.flags else None
if self.data['flags'] != newflags:
sqlfield.append(f"flags=%s")
values.append(newflags)
self.data['flags'] = newflags
if values:
# only save if changed values exist
sql = "UPDATE user SET " + ",".join(sqlfield) + " WHERE userid=%s"
values.append(self.user_id)
self.cur.execute(sql, values)
self.conn.commit()
""" unused at the moment """ unused at the moment
def mark_login(self): def mark_login(self):
@@ -52,6 +76,51 @@ class User(object):
self.conn.commit() self.conn.commit()
""" """
def create(self):
"""
Creates a new user with the first free id > 0
The role is the lowest possible one
"""
sql = (
"SELECT MIN(u1.userid+1) AS newid "
"FROM user AS u1 LEFT JOIN user AS u2 ON u2.userid=u1.userid+1 "
"WHERE u1.userid>=0 AND u2.userid IS NULL")
self.cur.execute(sql)
newid = self.cur.fetchone()['newid']
self.user_id = newid
# TODO check if login is unused else create other name
# add function for that feature
self.login = f"user{newid}"
self.displayname = f"User #{newid}"
self.language = 'en'
self.role = 'sailor'
self.flags = None
sql = (
"INSERT INTO user (userid, login, pass, displayname, role, flags, language) "
"VALUES (%s,%s,%s,%s,%s,%s,%s)"
)
cryptpass = bcrypt.hashpw(self.genpass().encode(), bcrypt.gensalt())
self.cur.execute(sql, (newid, self.login, cryptpass, self.displayname, self.role, self.flags, self.language))
# create mandatory settings
sql = "INSERT INTO settings (userid, sno, valint) VALUES (%s, 1, 0)"
self.cur.execute(sql, (newid,))
self.conn.commit()
return newid
def delete(self):
sql = "DELETE FROM settings WHERE userid=%s"
self.cur.execute(sql, (self.user_id,))
sql = "DELETE FROM user WHERE userid=%s"
self.cur.execute(sql, (self.user_id,))
self.conn.commit()
def setpass(self, newpass):
sql = "UPDATE user SET pass=%s WHERE userid=%s"
cryptpass = bcrypt.hashpw(newpass.encode(), bcrypt.gensalt())
print(cryptpass, self.user_id)
self.cur.execute(sql, (cryptpass, self.user_id))
self.conn.commit()
def __repr__(self): def __repr__(self):
# Unique description # Unique description
out = "User(%d)" % self.user_id out = "User(%d)" % self.user_id
+88 -6
View File
@@ -30,7 +30,8 @@ gi.require_version('Rsvg', '2.0')
from gi.repository import GLib, Gtk, Rsvg from gi.repository import GLib, Gtk, Rsvg
from yms import database from yms import database
from yms import gtkutils from yms import gtkutils, combobox, treeview
from yms import user
cfg = { cfg = {
'cfgfile': '~/.config/ymsgui.conf', 'cfgfile': '~/.config/ymsgui.conf',
@@ -69,10 +70,11 @@ class Frontend():
self.vname = "Ghost(0)" self.vname = "Ghost(0)"
# get list of vessels # get list of vessels
db.cur.execute("SELECT vid, vesselname, model FROM vessel ORDER BY vid") vessel = {}
for row in db.cur.fetchall(): db.dcur.execute("SELECT vid, vesselname, model FROM vessel ORDER BY vid")
print(row) for row in db.dcur.fetchall():
print(type(self.combo['vessel'])) vessel[row['vid']] = row['vesselname'] + " - " + row['model']
combobox.load_from_dict(self.combo['vessel'], vessel, self.vid)
# load shape of vessel if available # load shape of vessel if available
handle = Rsvg.Handle() handle = Rsvg.Handle()
@@ -84,17 +86,97 @@ class Frontend():
#viewport.height = 240 #viewport.height = 240
#self._svg.render_document(ctx, viewport) #self._svg.render_document(ctx, viewport)
# combos
roles = {'captain': 'Captain', 'helmsman': 'Helmsman', 'sailor': 'Sailor'}
combobox.load_from_dict(self.combo['user_role'], roles)
langs = {'de': 'Deutsch', 'en': 'English'}
combobox.load_from_dict(self.combo['user_lang'], langs)
# prepare lists # prepare lists
self.lst_user = Gtk.ListStore(int, str) self.lst_user = Gtk.ListStore(int, str)
db.cur.execute("SELECT userid, login FROM user ORDER BY login") db.cur.execute("SELECT userid, login FROM user WHERE userid > 0 ORDER BY login")
for row in db.cur.fetchall(): for row in db.cur.fetchall():
self.lst_user.append(row) self.lst_user.append(row)
treeview.add_column(self.tree['user'], 'ID', 0)
treeview.add_column(self.tree['user'], 'Login', 1)
self.tree['user'].set_model(self.lst_user)
# current selected objects
self._user = user.User(db.conn, 0)
print(self._user)
def run(self): def run(self):
self.label['vessel'].set_text(self.vname) self.label['vessel'].set_text(self.vname)
self.window.show_all() self.window.show_all()
Gtk.main() Gtk.main()
def user_display(self)
self.entry['user_login'].set_text(self._user.login)
self.entry['user_displayname'].set_text(self._user.displayname)
combobox.activate_row(self.combo['user_role'], self._user.role)
combobox.activate_row(self.combo['user_lang'], self._user.language)
self.check['user_locked'].set_active('locked' in self._user.flags)
self.check['user_deleted'].set_active('deleted' in self._user.flags)
self.entry['user_pass'].set_text('')
def on_tvw_user_row_activated(self, widget, path, column):
# load selected user
self._user.user_id = treeview.get_value(widget, 0)
self._user.load()
self.user_display()
def on_but_user_new_clicked(self, widget):
# get first free user id and create a new record with name "user<nnn>"
self._user.create()
self.lst_user.append((self._user.user_id, self._user.login))
def on_but_user_del_clicked(self, widget):
print("Delete user")
self._user.delete()
selection = self.tree['user'].get_selection()
model, tree_iter = selection.get_selected()
model.remove(tree_iter)
# TODO Select some other user from new list selection
self._user.user_id = treeview.get_value(self.tree['user'])
self._user.load()
self.user_display()
def on_but_user_newpass_clicked(self, widget):
self.entry['user_pass'].set_text(user.User.genpass())
# self.lst_user.
def on_but_user_save_clicked(self, widget):
newlogin = self.entry['user_login'].get_text()
# check if changed login exists and throw error in that case
if newlogin != self._user.login:
sql = "SELECT COUNT(login) FROM user WHERE login=%s"
db.cur.execute(sql, (newlogin, ))
if db.cur.fetchone()[0] > 0:
# TODO set error indicator
print("Error: Login already exists!")
return
sql = "UPDATE user SET login=%s WHERE userid=%s"
db.cur.execute(sql, (newlogin, self._user.user_id))
treeview.set_value(self.tree['user'], 1, newlogin)
self._user.displayname = self.entry['user_displayname'].get_text()
self._user.role = combobox.get_list_value(self.combo['user_role'])
self._user.language = combobox.get_list_value(self.combo['user_lang'])
self._user.flags = []
if self.check['user_locked'].get_active(): self._user.flags.append('locked')
if self.check['user_deleted'].get_active(): self._user.flags.append('deleted')
self._user.save()
# password field is different. if filled set new password
newpass = self.entry['user_pass'].get_text()
if len(newpass) >= 8:
try:
self._user.setpass(newpass)
finally:
newpass = None
self.entry['user_pass'].set_text('')
else:
# Password to short
pass
def on_but_info_clicked(self, widget): def on_but_info_clicked(self, widget):
# Userlist # Userlist
sql = ("SELECT userid, login, displayname FROM user ORDER BY login") sql = ("SELECT userid, login, displayname FROM user ORDER BY login")