From 4bc8dd8280882d22eebf38cd2017f1d2d8406b72 Mon Sep 17 00:00:00 2001 From: Thomas Hooge Date: Sat, 12 Sep 2026 11:05:09 +0200 Subject: [PATCH] Some improvements and fixes for webgui --- .gitignore | 1 + db/mariadb.sql | 1 + db/minimal.sql | 3 ++ db/minimal_en.sql | 10 ++++-- webgui/documents.php | 15 ++++++++- webgui/equipment.php | 3 +- webgui/globals.inc | 49 ++++++++++++++++++++++------ webgui/index.php | 59 +++++++++++++++++++++++++++++---- webgui/inventory.php | 2 +- webgui/lib/fileutils.inc | 70 ++++++++++++++++++++++++++++++++++++++++ webgui/lib/svg.inc | 1 + webgui/login.php | 10 +++--- webgui/measurement.php | 2 +- webgui/projects.php | 12 +++---- webgui/provisions.php | 4 +-- 15 files changed, 206 insertions(+), 36 deletions(-) diff --git a/.gitignore b/.gitignore index dd672a5..85de01a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ __pycache__/ *.txt *.bak +*.pot compare_* # Debian package build artifacts diff --git a/db/mariadb.sql b/db/mariadb.sql index ad5ed72..67e1eb0 100644 --- a/db/mariadb.sql +++ b/db/mariadb.sql @@ -94,6 +94,7 @@ CREATE TABLE vessel ( vesselname varchar(40) NOT NULL, model varchar(20) DEFAULT NULL, shipyard smallint(6) DEFAULT NULL, + year_built year DEFAULT NULL, loa float(4,2) UNSIGNED DEFAULT NULL, lwl float(4,2) UNSIGNED DEFAULT NULL, beam float(4,2) UNSIGNED DEFAULT NULL, diff --git a/db/minimal.sql b/db/minimal.sql index 13832f2..27cb5d1 100644 --- a/db/minimal.sql +++ b/db/minimal.sql @@ -80,3 +80,6 @@ INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES (3, 0, 'Stauraum allgemein', 'fixed'), (3, 1, 'Tank', 'fixed'), (3, 2, 'Extern', 'fixed'); -- not on board + +INSERT INTO company (compid, compname, comptype) VALUES +(-3, 'Eigenbau', 3); diff --git a/db/minimal_en.sql b/db/minimal_en.sql index 4500033..61331f5 100644 --- a/db/minimal_en.sql +++ b/db/minimal_en.sql @@ -63,10 +63,11 @@ INSERT INTO dropdown (ddid, ddval, ddtext) VALUES (0, 5, 'provision category'), (0, 6, 'provision unit'), (0, 7, 'fuse type'), -(0, 8 , 'unit'); +(0, 8 , 'unit'), (0, 9, 'box types'), -(0.10, 'fuse blocks'), -(0,11, 'cable types'); +(0, 10, 'fuse blocks'), +(0, 11, 'cable types'), +(0, 12, 'cable desc. technical'); INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES (2, 1, 'Supplier', 'fixed'), @@ -78,3 +79,6 @@ INSERT INTO dropdown (ddid, ddval, ddtext, flags) VALUES (3, 0, 'Generic storage', 'fixed'), (3, 1, 'Tank', 'fixed'), (3, 2, 'External', 'fixed'); + +INSERT INTO company (compid, compname, comptype) VALUES +(-3, 'Custom built', 3); diff --git a/webgui/documents.php b/webgui/documents.php index 821d248..99d15ab 100644 --- a/webgui/documents.php +++ b/webgui/documents.php @@ -15,6 +15,7 @@ doctype icons */ require 'globals.inc'; +require 'lib/db.inc'; require 'lib/gpc.inc'; require 'lib/fileutils.inc'; @@ -89,7 +90,19 @@ switch ($submit = form_get_action()) { $g_warning->Add(sprintf(_("Mimetype mismatch: '%s'."), $file_type)); } - // TODO for images check maximum size and scale down if needed + // for images check maximum size and scale down if needed + if (isset($_POST['noscale'])) { + $g_message->Add(_('Image has not been scaled down')); + } else { + // scale down if dimensions exceed limits + $img_maxx= $pdo->query("SELECT valint FROM settings WHERE userid=0 AND sno=20")->fetchColumn(); + $img_maxy= $pdo->query("SELECT valint FROM settings WHERE userid=0 AND sno=21")->fetchColumn(); + [$width, $height] = getimagesize($file_tmp); + $factor = min($img_maxx / $width, $img_maxy / $height); + if ($factor < 1) { + image_scale($file_tmp, $file_mimetype, $width, $height, $factor); + } + } // ok let's go $file_hash = md5_file($file_tmp); diff --git a/webgui/equipment.php b/webgui/equipment.php index 8e8d941..4acd4dc 100644 --- a/webgui/equipment.php +++ b/webgui/equipment.php @@ -7,6 +7,7 @@ ******************************************************************************/ require 'globals.inc'; +require 'lib/db.inc'; require 'lib/gpc.inc'; require 'lib/fileutils.inc'; @@ -463,7 +464,7 @@ echo '', _('Model'),"", h($equipment->model), "\n"; echo '', _('Serial'),"", h($equipment->serial), "\n"; echo '', _('Weight'),"", format_float($equipment->weight, 2, 'kg'), "\n"; echo '', _('Price'),"", format_currency($equipment->price), "\n"; -echo '', _('Purchase date'),"", $equipment->purchdate, "\n"; +echo '', _('Purchase date'),"", format_date($equipment->purchdate), "\n"; echo '', _('Supplier'),"", $equipment->supplier ? $opt_supplier[$equipment->supplier] : 'n/a', "\n"; echo '', _('Category'),"", $opt_ecat[$equipment->ecat], "\n"; echo '', _('Remarks'),"", h($equipment->remarks, br:true), "\n"; diff --git a/webgui/globals.inc b/webgui/globals.inc index b44a406..3c1e40f 100644 --- a/webgui/globals.inc +++ b/webgui/globals.inc @@ -56,6 +56,9 @@ $g_scriptname = basename($_SERVER['SCRIPT_NAME']); require 'config.inc'; // Localization + +$lang = get_browser_lang(['de', 'en']); + setlocale(LC_ALL, $g_locale); $g_lconv = localeconv(); $g_lang = substr($g_locale, 0, 2); // short language identifier @@ -244,6 +247,7 @@ class User { public $datefmt; public $rows_pp; public $menu; + public $language; private function __construct() { global $g_rows_pp; @@ -255,6 +259,7 @@ class User { $this->datefmt = 'Y-m-d'; $this->rows_pp = $g_rows_pp; $this->menu = [1, 2, 3, 4, 5, 6]; + $this->language = 'en'; } public static function getInstance() { @@ -317,13 +322,14 @@ class User { } $this->id = $_SESSION['userid']; - $sql = "SELECT displayname, role FROM user WHERE userid=?"; + $sql = "SELECT displayname, role, language FROM user WHERE userid=?"; $sth = $pdo->prepare($sql); $sth->execute([$this->id]); $row = $sth->fetch(); if ($row) { $this->displayname = $row['displayname']; $this->role = $row['role']; + $this->language = $row['language']; } } @@ -1251,15 +1257,16 @@ function format_currency($val) { } function format_date($date) { + // date is string in ISO format global $user; - // $user->datefmt; - /*'Y-m-d' => _('YYYY-MM-DD'). $curdate->format(' - Y-m-d'), - 'Y/m/d' => _('YYYY/MM/DD'). $curdate->format(' - Y/m/d'), - 'd.m.Y' => _('DD.MM.YYYY'). $curdate->format(' - d.m.Y'), - 'd/m/Y' => _('DD/MM/YYYY'). $curdate->format(' - d/m/Y'), - 'm-d-Y' => _('MM/DD/YYYY'). $curdate->format(' - m-d-Y') */ - - return ''; + if (empty($date)) { + return ''; + } + $timestamp = strtotime($date); + if ($timestamp === false) { + return ''; + } + return date($user->datefmt, $timestamp); } function format_color($color) { @@ -1345,6 +1352,30 @@ function header_location($location, $message=NULL) { exit; } +function get_browser_lang($allowed, $default='en') { + $h = $_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? ''; + if (!$h) { + return $def; + } + $langs = []; + foreach (explode(',', $h) as $p) { + $p = explode(';q=', trim($p)); + $l = strtolower($p[0]); + $q = isset($p[1]) ? (float) $p[1] : 1.0; + $langs[$l] = $q; + } + arsort($langs); + foreach ($langs as $bl => $q) { + foreach ($allowed as $l) { + $l = strtolower($l); + if ($bl === $l || str_starts_with($bl, $l . '-')) { + return $l; + } + } + } + return $default; +} + function pgettext($context, $msgid) { // implement missing function global $g_textdomain; diff --git a/webgui/index.php b/webgui/index.php index f6ce651..a7a9b72 100644 --- a/webgui/index.php +++ b/webgui/index.php @@ -80,6 +80,10 @@ switch ($submit = form_get_action()) { $p[':shapefile'] = NULL; } $p[':shipyard'] = gpc_get_int($_POST, 'shipyard'); + if ($p[':shipyard'] == '-1') { + $p[':shipyard'] = NULL; + } + $p[':year_built'] = gpc_get_int($_POST, 'year_built'); $p[':sid_default'] = gpc_get_uint($_POST, 'sid'); $p[':remarks'] = gpc_get_string($_POST, 'remarks', 150); db_exec_update('vessel', $p, 'vid'); @@ -115,7 +119,7 @@ $vid = ($res[0]); // Get current vessel data $sql = "SELECT vesselname, model, loa, lwl, beam, draught, draught_min," . " displacement, ballast, beltpos_aft, beltpos_bow, shapefile," - . " shipyard, remarks " + . " shipyard, year_built, remarks " . "FROM vessel " . "WHERE vid=?"; $sth = $pdo->prepare($sql); @@ -161,15 +165,19 @@ echo '
', "\n"; echo '
'; if ($vessel->shipyard && $vessel->shipyard > 0) { if ($vessel->shipyard == -3) { - echo _('Custom-build'); + echo sprintf(_('Custom-build %d'), ($vessel->year_built ?? '')); } else { $sql = "SELECT compname FROM company WHERE compid=?"; $sth = $pdo->prepare($sql); $sth->execute([$vessel->shipyard]); - echo sprintf(_('Built by %s'), $sth->fetchColumn()); - // TODO year of build? + echo sprintf(_('Built %d by %s'), ($vessel->year_built ?? ''), $sth->fetchColumn()); + } +} else { + if ($vessel->year_built && $vessel->year_built >= 1901) { + echo sprintf(_('Year built %d'), $vessel->year_built); } } + if (strlen($vessel->remarks) > 0) { echo h($vessel->remarks, br:true); } @@ -310,7 +318,13 @@ echo substr($xml, strpos($xml, "\n") + 1); lwl, 2, 'm')?> beam, 2, 'm')?> draught, 2, 'm')?> -draught_min, 2, 'm')?> +draught_min) { + echo '', _('Draught, min.'), ''; + format_float($vessel->draught_min, 2, 'm'); + echo "\n"; +} +?> displacement?> kg ballast?> kg beltpos_aft, 2, 'm')?> @@ -360,6 +374,8 @@ echo ' ', _('Boxes'), '', $sth->fetchColumn(), "< $sth = $pdo->prepare("SELECT COUNT(*), SUM(weight) FROM equipment WHERE vid=?"); $sth->execute([$user->vid]); $row = $sth->fetch(PDO::FETCH_NUM); +$weight_equip = $row[1]; + echo '', _('Equipment') , ""; echo ' ', _('Count'), '', $row[0], "\n"; echo ' ', _('Weight'), '', format_float($row[1], 1, 'kg'), "\n"; @@ -375,16 +391,41 @@ $sth = $pdo->prepare($sql); $sth->bindValue(':vid', $user->vid, PDO::PARAM_INT); $sth->execute(); $row = $sth->fetch(PDO::FETCH_NUM); +$weight_inv = $row[1]; echo '', _('Inventory') , ""; echo ' ', _('Count'), '', $row[0], "\n"; echo ' ', _('Weight'), '', format_float($row[1], 1, 'kg'), "\n"; +// Provisions +/*$sql = "SELECT SUM(n), SUM(w) FROM (" + . "SELECT COUNT(*) AS n, SUM(p.weight * p.number) as w FROM provisions AS p JOIN storage USING (sid) WHERE conttype='storage' AND vid=:vid " + . "UNION " + . "SELECT COUNT(*) AS n, SUM(p.weight * p.number) as w FROM provisions AS p JOIN box USING (boxid) JOIN storage ON (box.sid=storage.sid) WHERE conttype='box' AND vid=:vid" + . ") AS x"; */ + +// only storage assignment at the moment +$sql = "SELECT SUM(n), SUM(w) FROM (" + . "SELECT COUNT(*) AS n, SUM(p.weight * p.number) as w FROM provisions AS p JOIN storage USING (sid) WHERE storage.vid=:vid " + . ") AS x"; +$sth = $pdo->prepare($sql); +$sth->bindValue(':vid', $user->vid, PDO::PARAM_INT); +$sth->execute(); +$row = $sth->fetch(PDO::FETCH_NUM); +$weight_prov = $row[1]; + +echo '', _('Provisions') , ""; +echo ' ', _('Count'), '', $row[0], "\n"; +echo ' ', _('Weight'), '', format_float($row[1], 1, 'kg'), "\n"; + // Documents $sth = $pdo->query("SELECT COUNT(*) FROM document"); echo '', _('Documents') , ""; echo ' ', _('Count'), '', $sth->fetchColumn(), "\n"; +$weight_total = $weight_equip + $weight_inv + $weight_prov; +echo '', _('Total weight'), ''; +echo '', format_float($weight_total, 1, 'kg'), "\n"; echo "\n"; @@ -478,7 +519,7 @@ if (!file_exists($shapeindexfile)) { } } } -$opt_shapefiles = $g_opt_none + $shapeindex; +$opt_shapefiles = $g_opt_none + ($shapeindex ?? []); $opt_storage = db_get_opt_storage($user->vid); @@ -491,7 +532,7 @@ foreach ($sth->fetchAll(PDO::FETCH_NUM) as $row) { $sql = "SELECT vesselname, vtype, model, loa, lwl, beam, draught, draught_min," . " displacement, ballast, beltpos_aft, beltpos_bow, shapefile," - . " shipyard, sid_default, remarks " + . " shipyard, year_built, sid_default, remarks " . "FROM vessel " . "WHERE vid=?"; $sth = $pdo->prepare($sql); @@ -509,6 +550,10 @@ $vessel = $sth->fetch(PDO::FETCH_OBJ);
+
+ + +
diff --git a/webgui/inventory.php b/webgui/inventory.php index 40a6898..bd1aef2 100644 --- a/webgui/inventory.php +++ b/webgui/inventory.php @@ -394,7 +394,7 @@ echo "", _('Location ID'), "", $inventory->locationid, "", _('Number'), "", $inventory->number, "\n"; echo "", _('Weight'), "", format_float($inventory->weight, 2, 'kg'), "\n"; echo "", _('Price'), "", format_currency($inventory->price), "\n"; -echo "", _('Purchase date'), "", $inventory->purchdate, "\n"; +echo "", _('Purchase date'), "", format_date($inventory->purchdate), "\n"; echo "", _('Condition'), "", $opt_invcond[$inventory->invcond], "\n"; echo "", _('Remarks'), "", h($inventory->remarks, br:true), "\n"; diff --git a/webgui/lib/fileutils.inc b/webgui/lib/fileutils.inc index ae0ed33..1806832 100644 --- a/webgui/lib/fileutils.inc +++ b/webgui/lib/fileutils.inc @@ -43,3 +43,73 @@ function dir_get_size($path) { } return $total; } + +function image_scale($imagefile, $mimetype, $width, $height, $factor) { + // scales down image in place + // returns size in bytes for new scaled image or -1 in case of error + global $g_message, $g_error; + + if ($factor <= 0 || $factor >= 1) { + $g_error->Add("Internal error: Invalid image scale factor"); + return -1; + } + $width = (int)($width * $factor); + $height = (int)($height * $factor); + if ($width < 1 || $height < 1) { + $g_error->Add("Internal error: Scaled image dimensions are too small"); + return -1; + } + if ($mimetype == 'image/jpeg') { + $orig = imagecreatefromjpeg($imagefile); + if ($orig === false) { + $g_error->Add("Internal error: imagecreatefromjpeg failed"); + return -1; + } + $scaled = imagescale($orig, $width, $height, IMG_BICUBIC); // TODO IMG_BICUBIC_FIXED? + if ($scaled === false) { + imagedestroy($orig); + $g_error->Add("Internal error: jpeg imagescale failed"); + return -1; + } + if (!imagejpeg($scaled, $imagefile, 85)) { + imagedestroy($scaled); + imagedestroy($orig); + $g_error->Add("Internal error: imagejpeg failed"); + return -1; + } + } else if ($mimetype == 'image/png') { + $orig = imagecreatefrompng($imagefile); + if ($orig === false) { + $g_error->Add("Internal error: imagecreatefrompng failed"); + return -1; + } + $scaled = imagescale($orig, $width, $height, IMG_BICUBIC); + if ($scaled === false) { + imagedestroy($orig); + $g_error->Add("Internal error: png imagescale failed"); + return -1; + } + // Preserve PNG transparency + imagealphablending($scaled, false); + imagesavealpha($scaled, true); + if (!imagepng($scaled, $imagefile, 9)) { + imagedestroy($scaled); + imagedestroy($orig); + $g_error->Add("Internal error: imagepng failed"); + return -1; + } + } else { + $g_error->Add(sprintf(_('Internal error: Unknown mimetype %s!'), $mimetype)); + return -1; + } + $g_message->add(sprintf(_('Image scaled down to %dx%d'), $width, $height)); + imagedestroy($scaled); + imagedestroy($orig); + clearstatcache(true, $imagefile); + $size = filesize($imagefile); + if ($size === false) { + $g_error->Add("Internal error: filesize failed"); + return -1; + } + return $size; +} diff --git a/webgui/lib/svg.inc b/webgui/lib/svg.inc index 49b8c60..75a7e51 100644 --- a/webgui/lib/svg.inc +++ b/webgui/lib/svg.inc @@ -13,6 +13,7 @@ function create_shapeindex($shapeindexfile) { // an existing index file will be overwritten global $g_shapedir; + $shapeindex = array(); foreach (glob($g_shapedir.'/*.svg') as $f) { $fh = fopen($f, 'r'); $fname = basename($f); diff --git a/webgui/login.php b/webgui/login.php index 622bdd3..fed75c8 100644 --- a/webgui/login.php +++ b/webgui/login.php @@ -71,13 +71,13 @@ if (isset($err)) { ?> -
- - - + + + + diff --git a/webgui/measurement.php b/webgui/measurement.php index 04292e2..368dade 100644 --- a/webgui/measurement.php +++ b/webgui/measurement.php @@ -307,7 +307,7 @@ echo '', _('Value 1'), '', $measurement->val1, "\n"; echo '', _('Value 2'), '', $measurement->val2, "\n"; echo '', _('Value 3'), '', $measurement->val3, "\n"; echo '', _('Accuracy'), '', get_enum($measurement->accuracy), "\n"; -echo '', _('Date'), '', $measurement->mdate, "\n"; +echo '', _('Date'), '', format_date($measurement->mdate), "\n"; echo '', _('Note'), '', h($measurement->note), "\n"; echo '', _('Equipment'), '', $opt_equipment[$measurement->eid], "\n"; echo "\n"; diff --git a/webgui/projects.php b/webgui/projects.php index b124070..3d430a6 100644 --- a/webgui/projects.php +++ b/webgui/projects.php @@ -209,13 +209,13 @@ foreach ($res as $row) { echo '', $i; echo '', "\n"; echo "\n"; - echo "". $row['projname'] ."\n"; - echo "". $row['startdate'] ."\n"; - echo "". $row['duration'] ."\n"; - echo "". get_enum($row['projstate']) ."\n"; - echo "". $row['remarks'] ."\n"; + echo '', $row['projname'], "\n"; + echo '', format_date($row['startdate']), "\n"; + echo '', $row['duration'], "\n"; + echo '', get_enum($row['projstate']), "\n"; + echo '', h($row['remarks']), "\n"; // Edit current record button - echo ""; + echo ''; echo '', "\n"; echo "\n"; diff --git a/webgui/provisions.php b/webgui/provisions.php index 93a4131..f686c55 100644 --- a/webgui/provisions.php +++ b/webgui/provisions.php @@ -288,8 +288,8 @@ echo "", _('Weight net'), "", format_float($prov->weight_net, 2 echo '', _('Unit'),"", $opt_unit[$prov->unit], "\n"; echo "", _('Calories'), "", $prov->calories, " kcal\n"; echo '', _('Category'),"", $opt_category[$prov->provcat] ?? 'n/a', "\n"; -echo '', _('Storedate'),"", $prov->storedate, "\n"; -echo '', _('Shelflife'),"", $prov->shelflife, "\n"; +echo '', _('Storedate'),"", format_date($prov->storedate), "\n"; +echo '', _('Shelflife'),"", format_date($prov->shelflife), "\n"; echo '', _('Price'), "", format_currency($prov->price), "\n"; echo '', _('Storage'),"", ($opt_storage[$prov->sid] ?? ''), "\n"; echo '', _('Remarks'),"", h($prov->remarks, br:true), "\n";