diff --git a/README b/README index 9d2b6a1..9f607ac 100644 --- a/README +++ b/README @@ -1,7 +1,14 @@ -YMS - Yacht Management System -Prototype +YMS - Open Source Yacht Management System +Preview Version -Many features are incomplete, buggy or missing. +Free, self-hosted software for managing your boat's equipment, +inventory, documents, maintenance and tasks. + +Your boat. Your data. Your software. + + +This is work in progress: +Many features can be incomplete, buggy or missing. Use at your own risk! If inserting a new user you have also to insert a setting value @@ -9,8 +16,8 @@ to assign boat 0. To set your own passwords via console there is a helper script ymspass.py - Use it to get an SQL-statement for changing passwords. For further information go to https://computerclub.hoogi.de/yms (currently german only) + diff --git a/db/mariadb.sql b/db/mariadb.sql index 4bbcb22..1d171e1 100644 --- a/db/mariadb.sql +++ b/db/mariadb.sql @@ -5,8 +5,10 @@ /* TODO - check all values if unsigned can be used or not - - implement foreign keys where possible + - implement foreign keys where missing and possible + - add referential actions where appropriate - rename provisions to provision(?) + - review defaults */ /* User @@ -125,9 +127,17 @@ CREATE TABLE storage ( angle smallint(4) NOT NULL DEFAULT 0, color char(6) DEFAULT NULL, remarks varchar(150) DEFAULT NULL, - PRIMARY KEY (sid) + PRIMARY KEY (sid), + FOREIGN KEY fk_storage_vessel (vid) + REFERENCES vessel(vid) ) ENGINE=InnoDB; +/* intentionally after storage creation */ +ALTER TABLE vessel + ADD CONSTRAINT fk_vessel_storage + FOREIGN KEY (sid_default) + REFERENCES storage(sid); + CREATE TABLE tag ( tagid smallint(6) UNSIGNED NOT NULL AUTO_INCREMENT, tagname varchar(20) NOT NULL, @@ -141,7 +151,10 @@ CREATE TABLE tagref ( tagid smallint(6) UNSIGNED NOT NULL, objid int(10) UNSIGNED NOT NULL, objtype enum('equip','inv','prov','doc', 'proj', 'task', 'maint') NOT NULL, - PRIMARY KEY (tagid, objid, objtype) + PRIMARY KEY (tagid, objid, objtype), + FOREIGN KEY fk_tagref_tag (tagid) + REFERENCES tag(tagid) + ON DELETE CASCADE ) ENGINE=InnoDB; /* TODO Boxes can be nested via parent @@ -157,7 +170,9 @@ CREATE TABLE box ( weight float(5,2) UNSIGNED DEFAULT NULL, remarks varchar(150) DEFAULT NULL, PRIMARY KEY (boxid), - INDEX ix_label (label) + INDEX ix_label (label), + FOREIGN KEY fk_box_storage (sid) + REFERENCES storage(sid) ) ENGINE=InnoDB; CREATE TABLE equipment ( @@ -165,7 +180,7 @@ CREATE TABLE equipment ( vid smallint(6) NOT NULL DEFAULT 1, ename varchar(80) NOT NULL, shortname varchar(20) DEFAULT NULL, - model varchar(40), + model varchar(40) DEFAULT NULL, ecat tinyint(3) DEFAULT NULL, serial varchar(30) DEFAULT NULL, supplier smallint(6) DEFAULT NULL, @@ -175,25 +190,21 @@ CREATE TABLE equipment ( weight float(5,2) UNSIGNED DEFAULT NULL, remarks varchar(150) DEFAULT NULL, flags set('ordered','removed','deleted') DEFAULT NULL, - PRIMARY KEY(eid) + PRIMARY KEY(eid), + FOREIGN KEY fk_equipment_vessel (vid) + REFERENCES vessel(vid) + ON DELETE RESTRICT, + FOREIGN KEY fk_equipment_supplier (supplier) + REFERENCES company(compid) + ON DELETE RESTRICT ) 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 ( invid int(10) UNSIGNED NOT NULL AUTO_INCREMENT, invname varchar(80) NOT NULL, conttype enum('storage','box') NOT NULL DEFAULT 'storage', - sid smallint(6) UNSIGNED NOT NULL DEFAULT 1, - boxid smallint(6) UNSIGNED NOT NULL DEFAULT 1, + sid smallint(6) UNSIGNED DEFAULT NULL, + boxid smallint(6) UNSIGNED DEFAULT NULL, eid smallint(6) UNSIGNED DEFAULT NULL, number smallint(6) UNSIGNED NOT NULL DEFAULT 1, weight float(5,2) UNSIGNED DEFAULT NULL, @@ -203,7 +214,18 @@ CREATE TABLE inventory ( remarks varchar(150) DEFAULT NULL, flags set('ordered','removed','deleted') DEFAULT NULL, PRIMARY KEY (invid), - INDEX ix_invname (invname) + INDEX ix_invname (invname), + FOREIGN KEY fk_inventory_storage (sid) + REFERENCES storage(sid), + FOREIGN KEY fk_inventory_box (boxid) + REFERENCES box(boxid), + FOREIGN KEY fk_inventory_equipment (eid) + REFERENCES equipment(eid) + CHECK ( + (sid IS NOT NULL AND boxid IS NULL) + OR + (sid IS NULL AND boxid IS NOT NULL) + ) ) ENGINE=InnoDB; CREATE TABLE cable ( @@ -222,7 +244,13 @@ CREATE TABLE cable ( cablecond enum('unknown','new','excellent','good','fair','bad','repairable','defect') NOT NULL default 'unknown', remarks varchar(150) DEFAULT NULL, PRIMARY KEY (cableid), - INDEX ix_cablename (vid, cablename) + INDEX ix_cablename (vid, cablename), + FOREIGN KEY fk_cable_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_cable_supplier (supplier) + REFERENCES company(compid), + FOREIGN KEY fk_cable_manufacturer (manufacturer) + REFERENCES company(compid) ) ENGINE=InnoDB; CREATE table fuse ( @@ -237,7 +265,13 @@ CREATE table fuse ( description varchar(60) DEFAULT NULL, remarks varchar(150) DEFAULT NULL, PRIMARY KEY (fuseid), - UNIQUE INDEX ix_fusenumber (vid, fnumber) + UNIQUE INDEX ix_fusenumber (vid, fnumber), + FOREIGN KEY fk_fuse_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_fuse_cable (cableid) + REFERENCES cable(cableid), + FOREIGN KEY fk_fuse_equipment (eid) + REFERENCES equipment(eid) ) ENGINE=InnoDB; -- change vals to decimal(12,4)? @@ -245,6 +279,7 @@ CREATE TABLE measurement ( mid smallint(6) NOT NULL AUTO_INCREMENT, mname varchar(80) NOT NULL, vid smallint(6) NOT NULL DEFAULT 1, + eid smallint(6) UNSIGNED DEFAULT NULL, nval tinyint(1) UNSIGNED NOT NULL DEFAULT 1, val1 int(10) NOT NULL, val2 int(10) DEFAULT NULL, @@ -253,8 +288,11 @@ CREATE TABLE measurement ( accuracy enum('unknown','precise','normal','rough','estimated') NOT NULL DEFAULT 'unknown', mdate date DEFAULT NULL, note varchar(80) DEFAULT NULL, - eid smallint(6) UNSIGNED DEFAULT NULL, - PRIMARY KEY (mid) + PRIMARY KEY (mid), + FOREIGN KEY fk_measurement_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_measurement_equipment (eid) + REFERENCES equipment(eid) ) ENGINE=InnoDB; CREATE TABLE provisions ( @@ -273,7 +311,11 @@ CREATE TABLE provisions ( shelflife date DEFAULT NULL, price decimal(12,2) DEFAULT NULL, remarks varchar(150) DEFAULT NULL, - PRIMARY KEY (provid) + PRIMARY KEY (provid), + FOREIGN KEY fk_provisions_storage (sid) + REFERENCES storage(sid), + FOREIGN KEY fk_provisions_box (boxid) + REFERENCES box(boxid) ) ENGINE=InnoDB; /* TODO @@ -322,9 +364,17 @@ CREATE TABLE maintenance ( eid smallint(6) UNSIGNED DEFAULT NULL, series smallint(6) DEFAULT NULL, activities varchar(40) NOT NULL, + plan_date DATE DEFAULT NULL, + completion_date DATE DEFAULT NULL, + interval_hours smallint(6) DEFAULT NULL, + last_hours int(10) DEFAULT NULL, remarks varchar(150) DEFAULT NULL, maintstate enum('new','planned','ongoing','done') NOT NULL DEFAULT 'new', - PRIMARY KEY (maintid) + PRIMARY KEY (maintid), + FOREIGN KEY fk_maintenance_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_maintenance_equipment (eid) + REFERENCES equipment(eid) ) ENGINE=InnoDB; /* TODO? time recording */ @@ -340,9 +390,14 @@ CREATE TABLE project ( costs_final decimal(12,2) DEFAULT NULL, remarks varchar(150) DEFAULT NULL, projstate enum('new','plan','ongoing','paused','finished') NOT NULL DEFAULT 'new', - PRIMARY KEY (projid) + PRIMARY KEY (projid), + FOREIGN KEY fk_project_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_project_user (responsible) + REFERENCES user(userid) ) ENGINE=InnoDB; +/* TODO executed_by : company or user? or both? */ CREATE TABLE task ( taskid int(10) NOT NULL AUTO_INCREMENT, vid smallint(6) DEFAULT NULL, @@ -356,13 +411,16 @@ CREATE TABLE task ( started datetime DEFAULT NULL, finished datetime DEFAULT NULL, responsible smallint(6) NOT NULL, - /* TODO executed_by : company or user? or both? */ PRIMARY KEY (taskid), FOREIGN KEY fk_task_user (responsible) - REFERENCES user(userid) + REFERENCES user(userid), + FOREIGN KEY fk_task_vessel (vid) + REFERENCES vessel(vid), + FOREIGN KEY fk_task_project (projid) + REFERENCES project(projid) ) ENGINE=InnoDB; -/* Notes can be assigned to tasks,maintenances and equipment */ +/* Notes can be assigned to tasks, maintenances and equipment */ CREATE TABLE note ( noteid int(10) NOT NULL AUTO_INCREMENT, notetype enum('task','maint','equip') NOT NULL DEFAULT 'task', @@ -376,28 +434,38 @@ CREATE TABLE checklist ( title varchar(30) NOT NULL, parent smallint(6) DEFAULT NULL, clstate enum('new','open','closed') NOT NULL DEFAULT 'new', - PRIMARY KEY (clid) + PRIMARY KEY (clid), + FOREIGN KEY fk_checklist_parent (parent) + REFERENCES checklist(clid) +) ENGINE=InnoDB; + +CREATE TABLE checkgroup ( + groupid smallint(6) UNSIGNED NOT NULL, + title varchar(30) NOT NULL, + PRIMARY KEY (groupid) ) ENGINE=InnoDB; CREATE TABLE checkitem ( checkid int(10) NOT NULL AUTO_INCREMENT, checkname varchar(60) NOT NULL, - groupid tinyint(3) DEFAULT NULL, + groupid smallint(6) UNSIGNED DEFAULT NULL, checkresult enum('unchecked','checked','unused') NOT NULL DEFAULT 'unused', - PRIMARY KEY (checkid) + PRIMARY KEY (checkid), + FOREIGN KEY fk_checkitem_group (groupid) + REFERENCES checkgroup(groupid) ) ENGINE=InnoDB; CREATE TABLE checkref ( clid smallint(6) NOT NULL, checkid int(10) NOT NULL, sort smallint(6) DEFAULT NULL, - PRIMARY KEY (clid, checkid) -) ENGINE=InnoDB; - -CREATE TABLE checkgroup ( - groupid int(10) NOT NULL, - title varchar(30) NOT NULL, - PRIMARY KEY (groupid) + PRIMARY KEY (clid, checkid), + FOREIGN KEY fk_checkref_checklist (clid) + REFERENCES checklist(clid) + ON DELETE CASCADE, + FOREIGN KEY fk_checkref_checkitem (checkid) + REFERENCES checkitem(checkid) + ON DELETE CASCADE ) ENGINE=InnoDB; /* Expansion for future use: signalk mapping (just an idea) */ @@ -410,5 +478,7 @@ CREATE TABLE signalk ( skpath varchar(255) NOT NULL, PRIMARY KEY (skid), UNIQUE KEY ix_signalk - (vid, objtype, objid, datatype) + (vid, objtype, objid, datatype), + FOREIGN KEY fk_signalk_vessel (vid) + REFERENCES vessel(vid) ) ENGINE=InnoDB; diff --git a/install.sh b/install.sh index 7f67c76..c838c94 100755 --- a/install.sh +++ b/install.sh @@ -7,6 +7,7 @@ BINDIR="${PREFIX}/bin" DESKTOPDIR="${PREFIX}/share/applications" ICONDIR="${PREFIX}/share/icons/hicolor" DOCDIR="${PREFIX}/share/doc/yms" +MANDIR="${PREFIX}/usr/local/share/man/man1" LOCALEDIR="${PREFIX}/share/locale" SOURCEDIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) @@ -76,6 +77,9 @@ install -m 0644 \ install -m 0644 \ "${SOURCEDIR}/COPYING" \ "${DOCDIR}/COPYING" +install -m 0644 \ + "${SOURCEDIR}/yms.1" \ + "${MANDIR}/yms.1" echo "Installing translations..." make translation diff --git a/webgui/globals.inc b/webgui/globals.inc index 8a70c25..d7fe4e5 100644 --- a/webgui/globals.inc +++ b/webgui/globals.inc @@ -783,7 +783,7 @@ function db_get_options($ddid, $orderby = '', $short = False, $default = NULL) { $sth = $pdo->prepare($sql); $sth->execute([$ddid]); foreach ($sth->fetchAll(PDO::FETCH_NUM) as $rec) { - $list[$rec[0]] = $rec[1]; + $list[$rec[0]] = h($rec[1]); } return $list; } @@ -793,7 +793,7 @@ function db_get_opt_vessel() { $list = array(); $sth = $pdo->query("SELECT vid, vesselname, model FROM vessel ORDER BY vid"); foreach ($sth->fetchAll(PDO::FETCH_NUM) as $row) { - $list[$rec[0]] = $rec[1]; + $list[$row[0]] = h($row[1]); } return $list; } @@ -811,7 +811,7 @@ function db_get_opt_storage($vid) { $g_error->Add($e->getMessage()); } foreach ($sth->fetchAll(PDO::FETCH_NUM) as $rec) { - $list[$rec[0]] = $rec[1]; + $list[$rec[0]] = h($rec[1]); } return $list; } @@ -832,7 +832,7 @@ function db_get_opt_box($vid, $exclude=[]) { } foreach ($sth->fetchAll(PDO::FETCH_NUM) as $rec) { if (! in_array($rec[0], $exclude)) { - $list[$rec[0]] = $rec[1] . ($rec[2] ? ' - '. $rec[2] : ''); + $list[$rec[0]] = h($rec[1]) . ($rec[2] ? ' - '. h($rec[2]) : ''); } } return $list; @@ -855,7 +855,7 @@ function db_get_opt_equip($vid, $default=NULL, $exclude=[]) { } foreach ($sth->fetchAll(PDO::FETCH_NUM) as $rec) { if (! in_array($rec[0], $exclude)) { - $list[$rec[0]] = $rec[1]; + $list[$rec[0]] = h($rec[1]); } } return $list; @@ -871,7 +871,7 @@ function db_get_opt_manuf($default=NULL) { $sql = "SELECT compid, compname FROM company WHERE comptype=2 ORDER BY compname"; $sth = $pdo->query($sql); foreach ($sth->fetchAll(PDO::FETCH_NUM) as $row) { - $list[$row[0]] = $row[1]; + $list[$row[0]] = h($row[1]); } return $list; } @@ -886,7 +886,7 @@ function db_get_opt_supp($default=NULL) { $sql = "SELECT compid, compname FROM company WHERE comptype=1 ORDER BY compname"; $sth = $pdo->query($sql); foreach ($sth->fetchAll(PDO::FETCH_NUM) as $row) { - $list[$row[0]] = $row[1]; + $list[$row[0]] = h($row[1]); } return $list; } @@ -901,7 +901,7 @@ function db_get_opt_user($userid, $default=NULL) { $sql = "SELECT userid, displayname FROM user WHERE userid>0 ORDER BY displayname"; $sth = $pdo->query($sql); foreach ($sth->fetchAll(PDO::FETCH_NUM) as $row) { - $list[$row[0]] = $row[1]; + $list[$row[0]] = h($row[1]); } return $list; } @@ -931,7 +931,7 @@ function db_get_opt_proj($vid, $exclude=[], $default=NULL) { $g_error->Add($e->getMessage()); } foreach ($sth->fetchAll(PDO::FETCH_NUM) as $rec) { - $list[$rec[0]] = $rec[1]; + $list[$rec[0]] = h($rec[1]); } return $list; } @@ -1296,6 +1296,11 @@ function format_measurement($n, $unit, $v1, $v2, $v3) { // ========== COMMON FUNCTIONS ================================================ +function h($value) { + // escape value coming from db for safe html output + return htmlspecialchars($value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); +} + function header_location($location, $message=NULL) { if (is_array($message)) { $valid_keys = array('succ', 'info', 'warn', 'err'); diff --git a/webgui/inventory.php b/webgui/inventory.php index 8888bc8..d4670e8 100644 --- a/webgui/inventory.php +++ b/webgui/inventory.php @@ -226,7 +226,7 @@ $opt_special = array( -2 => _('― all ―'), ); foreach ($opt_special + $opt_invcond as $k => $v) { - echo '