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')?> =format_float($vessel->lwl, 2, 'm')?>
=_('Beam')?> =format_float($vessel->beam, 2, 'm')?>
=_('Draught')?> =format_float($vessel->draught, 2, 'm')?>
-=_('Draught, min.')?> =format_float($vessel->draught_min, 2, 'm')?>
+draught_min) {
+ echo '', _('Draught, min.'), ' ';
+ format_float($vessel->draught_min, 2, 'm');
+ echo " \n";
+}
+?>
=_('Displacement')?> =$vessel->displacement?> kg
=_('Ballast')?> =$vessel->ballast?> kg
=_('Belt position aft')?> =format_float($vessel->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);
=_('Model')?>
+
+ =_('Year built')?>
+
+
=_('LoA')?>, m
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, "
echo "", _('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)) {
?>
- =_('Login')?>
+ =_('Login'), "\n"?>
-
-
-
-
+ ="\n" /* card body */ ?>
+ ="\n" /* columns */ ?>
+ ="\n" /* rows */ ?>
+ ="\n" /* card */ ?>