Some improvements and fixes for webgui

This commit is contained in:
2026-09-12 11:05:09 +02:00
parent 82dea5800d
commit 4bc8dd8280
15 changed files with 206 additions and 36 deletions
+1
View File
@@ -2,6 +2,7 @@
__pycache__/
*.txt
*.bak
*.pot
compare_*
# Debian package build artifacts
+1
View File
@@ -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,
+3
View File
@@ -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);
+7 -3
View File
@@ -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);
+14 -1
View File
@@ -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);
+2 -1
View File
@@ -7,6 +7,7 @@
******************************************************************************/
require 'globals.inc';
require 'lib/db.inc';
require 'lib/gpc.inc';
require 'lib/fileutils.inc';
@@ -463,7 +464,7 @@ echo '<tr><th>', _('Model'),"</th><td>", h($equipment->model), "</td></tr>\n";
echo '<tr><th>', _('Serial'),"</th><td>", h($equipment->serial), "</td></tr>\n";
echo '<tr><th>', _('Weight'),"</th><td>", format_float($equipment->weight, 2, 'kg'), "</td></tr>\n";
echo '<tr><th>', _('Price'),"</th><td>", format_currency($equipment->price), "</td></tr>\n";
echo '<tr><th>', _('Purchase date'),"</th><td>", $equipment->purchdate, "</td></tr>\n";
echo '<tr><th>', _('Purchase date'),"</th><td>", format_date($equipment->purchdate), "</td></tr>\n";
echo '<tr><th>', _('Supplier'),"</th><td>", $equipment->supplier ? $opt_supplier[$equipment->supplier] : 'n/a', "</td></tr>\n";
echo '<tr><th>', _('Category'),"</th><td>", $opt_ecat[$equipment->ecat], "</td></tr>\n";
echo '<tr><th>', _('Remarks'),"</th><td>", h($equipment->remarks, br:true), "</td></tr>\n";
+39 -8
View File
@@ -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,16 +1257,17 @@ 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') */
if (empty($date)) {
return '';
}
$timestamp = strtotime($date);
if ($timestamp === false) {
return '';
}
return date($user->datefmt, $timestamp);
}
function format_color($color) {
$colstr = '<span style="';
@@ -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;
+52 -7
View File
@@ -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 '<div class="col-1"></div>', "\n";
echo '<div class="col">';
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);
<tr><td><?=_('Lwl')?></td><td><?=format_float($vessel->lwl, 2, 'm')?></td></tr>
<tr><td><?=_('Beam')?></td><td><?=format_float($vessel->beam, 2, 'm')?></td></tr>
<tr><td><?=_('Draught')?></td><td><?=format_float($vessel->draught, 2, 'm')?></td></tr>
<tr><td><?=_('Draught, min.')?></td><td><?=format_float($vessel->draught_min, 2, 'm')?></td></tr>
<?php
if ($vessel->draught_min) {
echo '<tr><td>', _('Draught, min.'), '</td><td>';
format_float($vessel->draught_min, 2, 'm');
echo "</td></tr>\n";
}
?>
<tr><td><?=_('Displacement')?></td><td><?=$vessel->displacement?>&thinsp;kg</td></tr>
<tr><td><?=_('Ballast')?></td><td><?=$vessel->ballast?>&thinsp;kg</td></tr>
<tr><td><?=_('Belt position aft')?></td><td><?=format_float($vessel->beltpos_aft, 2, 'm')?></td></tr>
@@ -360,6 +374,8 @@ echo '<tr><td>&nbsp;</td><td>', _('Boxes'), '</td><td>', $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 '<tr><td colspan="3">', _('Equipment') , "</td>";
echo '<tr><td>&nbsp;</td><td>', _('Count'), '</td><td>', $row[0], "</td></tr>\n";
echo '<tr><td>&nbsp;</td><td>', _('Weight'), '</td><td>', format_float($row[1], 1, 'kg'), "</td></tr>\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 '<tr><td colspan="3">', _('Inventory') , "</td>";
echo '<tr><td>&nbsp;</td><td>', _('Count'), '</td><td>', $row[0], "</td></tr>\n";
echo '<tr><td>&nbsp;</td><td>', _('Weight'), '</td><td>', format_float($row[1], 1, 'kg'), "</td></tr>\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 '<tr><td colspan="3">', _('Provisions') , "</td>";
echo '<tr><td>&nbsp;</td><td>', _('Count'), '</td><td>', $row[0], "</td></tr>\n";
echo '<tr><td>&nbsp;</td><td>', _('Weight'), '</td><td>', format_float($row[1], 1, 'kg'), "</td></tr>\n";
// Documents
$sth = $pdo->query("SELECT COUNT(*) FROM document");
echo '<tr><td colspan="3">', _('Documents') , "</td>";
echo '<tr><td>&nbsp;</td><td>', _('Count'), '</td><td>', $sth->fetchColumn(), "</td></tr>\n";
$weight_total = $weight_equip + $weight_inv + $weight_prov;
echo '<tr><td colspan="2">', _('Total weight'), '</td>';
echo '<td>', format_float($weight_total, 1, 'kg'), "</td></tr>\n";
echo "</table>\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);
<label for="model" class="form-label"><?=_('Model')?></label>
<input type="text" class="form-control" id="model" name="model" value="<?=$vessel->model ?>">
</div>
<div class="mb-3">
<label for="year_built" class="form-label"><?=_('Year built')?></label>
<input type="number" class="form-control" id="year_built" name="year_built" value="<?=$vessel->year_built ?>">
</div>
<div class="mb-3">
<label for="loa" class="form-label"><?=_('LoA')?>, m</label>
<input type="text" class="form-control" id="loa" name="loa" value="<?=format_float($vessel->loa, 2) ?>">
+1 -1
View File
@@ -394,7 +394,7 @@ echo "<tr><th>", _('Location ID'), "</th><td>", $inventory->locationid, "</td></
echo "<tr><th>", _('Number'), "</th><td>", $inventory->number, "</td></tr>\n";
echo "<tr><th>", _('Weight'), "</th><td>", format_float($inventory->weight, 2, 'kg'), "</td></tr>\n";
echo "<tr><th>", _('Price'), "</th><td>", format_currency($inventory->price), "</td></tr>\n";
echo "<tr><th>", _('Purchase date'), "</th><td>", $inventory->purchdate, "</td></tr>\n";
echo "<tr><th>", _('Purchase date'), "</th><td>", format_date($inventory->purchdate), "</td></tr>\n";
echo "<tr><th>", _('Condition'), "</th><td>", $opt_invcond[$inventory->invcond], "</td></tr>\n";
echo "<tr><th>", _('Remarks'), "</th><td>", h($inventory->remarks, br:true), "</td></tr>\n";
+70
View File
@@ -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;
}
+1
View File
@@ -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);
+5 -5
View File
@@ -71,13 +71,13 @@ if (isset($err)) {
?>
<button type="submit" class="btn btn-primary" title="<?php printf(_('Logon to %s'), APP_TITLE_SHORT); ?>" name="login" value="1">
<span class="bi bi-caret-right"></span>
<?=_('Login')?>
<?=_('Login'), "\n"?>
</button>
</form>
</div> <?php /* card body */?>
</div> <?php /* columns */?>
</div> <?php /* rows */?>
</div> <?phph /* card */?>
</div><?="\n" /* card body */ ?>
</div><?="\n" /* columns */ ?>
</div><?="\n" /* rows */ ?>
</div><?="\n" /* card */ ?>
</section>
</div>
</body>
+1 -1
View File
@@ -307,7 +307,7 @@ echo '<tr><th>', _('Value 1'), '</th><td>', $measurement->val1, "</td></tr>\n";
echo '<tr><th>', _('Value 2'), '</th><td>', $measurement->val2, "</td></tr>\n";
echo '<tr><th>', _('Value 3'), '</th><td>', $measurement->val3, "</td></tr>\n";
echo '<tr><th>', _('Accuracy'), '</th><td>', get_enum($measurement->accuracy), "</td></tr>\n";
echo '<tr><th>', _('Date'), '</th><td>', $measurement->mdate, "</td></tr>\n";
echo '<tr><th>', _('Date'), '</th><td>', format_date($measurement->mdate), "</td></tr>\n";
echo '<tr><th>', _('Note'), '</th><td>', h($measurement->note), "</td></tr>\n";
echo '<tr><th>', _('Equipment'), '</th><td>', $opt_equipment[$measurement->eid], "</td></tr>\n";
echo "</table>\n";
+6 -6
View File
@@ -209,13 +209,13 @@ foreach ($res as $row) {
echo '<td>', $i;
echo '<a class="text-nowrap" title="', _('View'), '" href="', $g_scriptname, '?f=view&id=', $row['projid'], '"><i class="bi bi-eye ms-2"></i></a>', "\n";
echo "</td>\n";
echo "<td>". $row['projname'] ."</td>\n";
echo "<td>". $row['startdate'] ."</td>\n";
echo "<td>". $row['duration'] ."</td>\n";
echo "<td>". get_enum($row['projstate']) ."</td>\n";
echo "<td>". $row['remarks'] ."</td>\n";
echo '<td>', $row['projname'], "</td>\n";
echo '<td>', format_date($row['startdate']), "</td>\n";
echo '<td>', $row['duration'], "</td>\n";
echo '<td>', get_enum($row['projstate']), "</td>\n";
echo '<td>', h($row['remarks']), "</td>\n";
// Edit current record button
echo "<td>";
echo '<td>';
echo '<a title="', _('Edit'), '" href="', $g_scriptname, '?f=edit&id=', $row['projid'], '"><i class="bi-pencil"></i></a>', "\n";
echo "</td>\n";
+2 -2
View File
@@ -288,8 +288,8 @@ echo "<tr><th>", _('Weight net'), "</th><td>", format_float($prov->weight_net, 2
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>', _('Storedate'),"</th><td>", $prov->storedate, "</td></tr>\n";
echo '<tr><th>', _('Shelflife'),"</th><td>", $prov->shelflife, "</td></tr>\n";
echo '<tr><th>', _('Storedate'),"</th><td>", format_date($prov->storedate), "</td></tr>\n";
echo '<tr><th>', _('Shelflife'),"</th><td>", format_date($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>', _('Remarks'),"</th><td>", h($prov->remarks, br:true), "</td></tr>\n";