Webgui programming

This commit is contained in:
2025-05-09 10:22:08 +02:00
parent c293ca1caa
commit fb93c83388
11 changed files with 340 additions and 85 deletions
+260 -50
View File
@@ -50,9 +50,9 @@ setlocale(LC_ALL, $g_locale);
$g_lconv = localeconv();
$g_lang = substr($g_locale, 0, 2); // short language identifier
bindtextdomain('yms', 'locale');
bind_textdomain_codeset('yms', 'UTF-8');
textdomain('yms');
bindtextdomain($g_textdomain, 'locale');
bind_textdomain_codeset($g_textdomain, 'UTF-8');
textdomain($g_textdomain);
date_default_timezone_set($g_timezone);
@@ -63,12 +63,18 @@ if ($g_scriptname != 'login.php') {
$_SESSION['prelogin'] = $_SERVER['REQUEST_URI'];
header_location('login.php');
}
$g_referrer = $_SESSION['coming_from'];
$g_referrer = $_SESSION['coming_from'] ?? $g_scriptname;
$_SESSION['coming_from'] = $g_scriptname;
}
// database
$pdo = new PDO("mysql:host=$g_db_host;dbname=$g_db_schema;charset=utf8mb4", $g_db_username, $g_db_password);
try {
$pdo = new PDO("mysql:host=$g_db_host;dbname=$g_db_schema;charset=utf8mb4", $g_db_username, $g_db_password);
} catch (PDOException $e) {
echo '<pre>';
print_r($e);
echo '</pre>';
}
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
# $pdo->query("SET lc_time_names='de_DE'");
@@ -87,6 +93,10 @@ $valid = FALSE;
// document storage
$g_doc_mimetypes = ['image/png', 'image/jpeg', 'image/svg+xml', 'application/pdf'];
// form option lists
$g_opt_all = array(-2 => _('&horbar; all &horbar;'));
$g_opt_none = array(-1 => _('&horbar; none &horbar;'));
// Initialize message system
$g_message = new Message;
$g_success = new MessageSuccess;
@@ -105,6 +115,10 @@ class Message {
function __construct() {
$this->caption = _('Information');
$this->alertclass = 'alert-info';
if (isset($_SESSION['message']['info'])) {
$this->Add($_SESSION['message']['info']);
unset($_SESSION['message']['info']);
}
}
function SetCaption($str) {
@@ -140,6 +154,10 @@ class MessageSuccess extends Message {
function __construct() {
$this->caption = _('Success');
$this->alertclass = 'alert-success';
if (isset($_SESSION['message']['succ'])) {
$this->Add($_SESSION['message']['succ']);
unset($_SESSION['message']['succ']);
}
}
}
@@ -147,6 +165,10 @@ class MessageWarning extends Message {
function __construct() {
$this->caption = _('Warning');
$this->alertclass = 'alert-warning';
if (isset($_SESSION['message']['warn'])) {
$this->Add($_SESSION['message']['warn']);
unset($_SESSION['message']['warn']);
}
}
}
@@ -154,6 +176,10 @@ class MessageError extends Message {
function __construct() {
$this->caption = _('Error');
$this->alertclass = 'alert-danger';
if (isset($_SESSION['message']['err'])) {
$this->Add($_SESSION['message']['err']);
unset($_SESSION['message']['err']);
}
}
}
@@ -209,7 +235,9 @@ class User {
// password check successful
$_SESSION['userid'] = $row->userid;
$this->loggedin = TRUE;
header_location($_SESSION['prelogin'] ?? 'index.php');
$target = $_SESSION['prelogin'] ?? 'index.php';
unset($_SESSION['prelogin']);
header_location($target);
} else {
$this->errormessage = _('Username or password invalid');
}
@@ -344,13 +372,17 @@ function db_save_filter($flt, $sno) {
}
function get_enum($enum) {
// Translate and beautify enums
// translate and beautify enums
$lookup = array(
'NULL' => _('n/a'),
'bad' => _('Bad'),
'box' => _('Box'),
'cat' => _('Catamaran'),
'completed' => _('Completed'),
'closed' => _('Closed'),
'company' => _('Company'),
'defect' => _('Defect'),
'deleted' => _('Deleted'),
'done' => _('Done'),
'drawing' => _('Drawing'),
'equipment' => _('Equipment'),
@@ -364,40 +396,52 @@ function get_enum($enum) {
'locked' => _('Locked'),
'low' => _('Low'),
'manual' => _('Manual'),
'mediim' => _('Medium'),
'medium' => _('Medium'),
'mono' => _('Monohull'),
'new' => _('New'),
'none' => _('None'),
'normal' => _('Normal'),
'ongoing' => _('Ongoing'),
'open' => _('Open'),
'paused' => _('Paused'),
'pending' => _('Pending'),
'picture' => _('Picture'),
'planned' => _('Planned'),
'precise' => _('Precise'),
'project' => _('Project'),
'repairable' => _('Repairable'),
'rough' => _('Rough'),
'storage' => _('Storage'),
'task' => _('Task'),
'tri' => _('Trimaran'),
'unknown' => _('Unknown'),
'user' => _('User'),
'vessel' => _('Vessel'),
'waiting' => _('waiting')
);
return $lookup[$enum] ?? $enum;
}
function db_load_enum($table, $column, $lookup=false, $as_dict=false) {
function db_load_enum($table, $column, $lookup=false, $as_dict=false, $check_null=false) {
// returns array of enum-values as defined in database
// if lookup is true the name lookup and translaton is applied
// if as_dict is true the enum value is returned as the array key
// if check_null is true the nullable-check is applied and returned
global $pdo;
$sql = "SELECT TRIM(TRAILING ')' FROM SUBSTRING(column_type,6)) "
$sql = "SELECT TRIM(TRAILING ')' FROM SUBSTRING(column_type,6)), is_nullable "
. "FROM information_schema.columns "
. "WHERE table_name=? AND column_name=?";
. "WHERE table_schema=DATABASE() AND table_name=? AND column_name=?";
$sth = $pdo->prepare($sql);
$sth->execute([$table, $column]);
$row = $sth->fetch(PDO::FETCH_NUM);
// for PHP < 7.4
// return array_map(function($x) { return trim($x, "'"); }, explode(',', $sth->fetchColumn()));
// return array_map(function($x) { return trim($x, "'"); }, explode(',', $row[0]));
// for PHP => 7.4
$arr = array_map(fn($x) => trim($x, "'"), explode(',', $sth->fetchColumn()));
$arr = array_map(fn($x) => trim($x, "'"), explode(',', $row[0]));
// prepend special value NULL if column is nullable
if ($check_null and $row[1] == 'YES') {
array_unshift($arr, 'NULL');
}
if ($lookup) {
if ($as_dict) {
$dict = array();
@@ -413,6 +457,98 @@ function db_load_enum($table, $column, $lookup=false, $as_dict=false) {
}
}
function db_load_taglist($objtype, $objid) {
// returns comma separated list for object
global $pdo;
global $g_error;
$taglist = '';
$sql = "SELECT GROUP_CONCAT(tagname) "
. "FROM tagref JOIN tag USING (tagid) "
. "WHERE objtype=? AND objid=?";
$sth = $pdo->prepare($sql);
try {
$sth->execute([$objtype, $objid]);
$taglist = $sth->fetchColumn();
} catch(PDOException $e) {
$g_error->Add($e->getMessage());
}
return $taglist;
}
function db_save_taglist($objtype, $objid, $taglist) {
global $pdo;
global $g_error;
// WIP
// - taglist is comma separated string
// - create not existing tags
// - update tagref: create new, remove unused
$tags = array_filter(explode(',', preg_replace('/\s+/u', '', $taglist)));
if (empty($tags)) {
// remove all from tagref
$sth = $pdo->prepare("DELETE FROM tagref WHERE objtype=? AND objid=?");
try {
$sth->execute([$objtype, $objid]);
} catch(PDOException $e) {
$g_error->Add($e->getMessage());
}
} else {
// get list of non existing tags
$in = str_repeat('?,', count($tags) - 1) . '?';
$sql = "SELECT tagid, tagname FROM tag WHERE tagname IN ($in)";
$sth = $pdo->prepare($sql);
try {
$sth->execute(array_values($tags));
} catch(PDOException $e) {
$g_error->Add($e->getMessage());
}
$tagids = array();
$existing = array();
foreach ($sth->fetchAll() as $row) {
$existing[] = $row['tagname'];
$tagids[] = $row['tagid'];
}
$newtags = array_diff($tags, $existing);
$sth = $pdo->prepare("INSERT INTO tag (tagname) VALUES (?)");
foreach ($newtags as $t) {
// maxlength 20 for each tag!
$sth->execute([substr($t, 0, 20)]);
$tagids[] = $pdo->LastInsertId();
}
// update references
$sql = "SELECT tagid FROM tagref WHERE objtype=? AND objid=?";
$sth = $pdo->prepare($sql);
$sth->execute([$objtype, $objid]);
$refids = array();
$obsolete = array();
foreach ($sth->fetchAll() as $row) {
if (in_array($row['tagid'], $tagids)) {
// mark existing used references
$refids[] = $row['tagid'];
} else {
// obsolete reference
$obsolete[] = $row['tagid'];
}
}
// create missing references
$missing = array_diff($tagids, $refids);
if (! empty($missing)) {
$sql = "INSERT INTO tagref (tagid, objid, objtype) VALUES (?, ?, ?)";
$sth = $pdo->prepare($sql);
foreach ($missing as $m) {
$sth->execute([$m, $objid, $objtype]);
}
}
// remove obsolete references
if (! empty($obsolete)) {
$sql = "DELETE FROM tagref WHERE tagid=? AND objid=? and objtype=?";
$sth = $pdo->prepare($sql);
foreach ($obsolete as $o) {
$sth->execute([$o, $objid, $objtype]);
}
}
}
}
function db_get_ddtext($ddid, $ddval) {
// returns single value for dropdown
global $pdo;
@@ -428,6 +564,8 @@ function db_get_ddtext($ddid, $ddval) {
return $sth->fetchColumn();
}
// Functions to get option lists (key/value)
function db_get_options($ddid, $orderby = '', $default = NULL) {
// returns list for dropdown
global $pdo;
@@ -461,7 +599,6 @@ function db_get_opt_vessel() {
return $list;
}
// Functions to get option lists (key/value)
function db_get_opt_storage($vid) {
global $pdo;
@@ -619,10 +756,31 @@ function float_to_sql($var) {
// ========== FORM FUNCTIONS ==================================================
function form_get_action() {
if (!isset($_POST['submit'])) {
if (isset($_GET['f'])) {
$submit = $_GET['f'];
// strip parameters
/* $pos = strpos($submit, '?');
if ($pos > 2) {
$submit = substr($submit, 0, $pos);
} */
} else {
$submit = NULL;
}
} else {
$submit = $_POST['submit'];
}
if (is_array($submit)) {
$submit = key($submit);
}
return strtolower($submit);
}
function form_create_select($fieldname, $label, $optlist, $selval=NULL) {
echo '<div class="mb-3">', "\n";
echo '<label for="', $fieldname, '">', $label, "</label>\n";
echo '<select class="form-select" name="', $fieldname,'" id="', $fieldname,'">';
echo '<select class="form-select" name="', $fieldname,'" id="', $fieldname, '">', "\n";
foreach ($optlist as $k => $v) {
echo '<option value="', $k, '"';
if (isset($selval) and ($k == $selval)) {
@@ -686,60 +844,40 @@ function form_delete_buttons($script, $id, $token) {
echo "</div>\n";
}
function form_tag_assignment($script, $id) {
function form_tag_assignment($script, $id, $tags) {
echo '<tr>';
echo '<th>', _('Tags'), "</th>\n";
echo '<td>', $tags, '</td>';
echo "</tr>\n<tr>";
echo '<th>', _('Tag assignment'), "</th>\n";
echo '<td><form method="post" action="', $script, '">', "\n";
echo '<div class="form-floating">';
echo '<input type="hidden" name="id" value="', $id .'">', "\n";
echo '<label>', _('(Separate by comma)'), '</label>', "\n";
echo '<input type="text" maxlength="80" class="form-control" name="tagstring">', "\n";
echo '<input type="text" maxlength="80" class="form-control" name="tagstring" value="', $tags, '">', "\n";
echo '<input type="submit">', "\n";
echo "</div></form></td></td>\n";
echo "</tr>";
}
// ========== COMMON FUNCTIONS ================================================
function header_location($location) {
header("Location: $location");
exit;
}
function form_get_action() {
if (!isset($_POST['submit'])) {
if (isset($_GET['f'])) {
$submit = $_GET['f'];
// strip parameters
/* $pos = strpos($submit, '?');
if ($pos > 2) {
$submit = substr($submit, 0, $pos);
} */
} else {
$submit = NULL;
function filter_create_select($fieldname, $label, $optlist, $selval=NULL) {
echo '<label for="', $fieldname,'">', $label,'</label>', "\n";
echo '<select id="', $fieldname, '" name="', $fieldname, '">', "\n";
foreach ($optlist as $k => $v) {
echo '<option value="', $k, '"';
if ($k == $selval) {
echo ' selected';
}
} else {
$submit = $_POST['submit'];
echo '>', $v, '</option>', "\n";
}
if (is_array($submit)) {
$submit = key($submit);
}
return strtolower($submit);
echo "</select>\n";
}
function get_color_brightness($color) {
// returns a value between 0 and 1
// color is a 6 char hex string
list($red, $green, $blue) = array_map('hexdec', str_split($color, 2));
$red = $red / 255 * 0.2126;
$green = $green / 255 * 0.7152;
$blue = $blue / 255 * 0.0722;
return $red + $green + $blue;
}
// ========== GPC FUNCTIONS ===================================================
function gpc_get_string(&$GPC, $varname, $maxlen = NULL, $default = NULL) {
// TODO Parameter nullval: single value or array of values which considered as null
// Default value for nullval is "-1"
if (!isset($GPC[$varname]) or (strlen(trim($GPC[$varname])) == 0)) {
return $default;
}
@@ -799,6 +937,15 @@ function gpc_get_bool(&$GPC, $varname, $default = FALSE) {
}
function gpc_get_date(&$GPC, $varname, $default = NULL) {
// TODO improve
if (!isset($GPC[$varname]) || $GPC[$varname] == '') {
return $default;
}
return $GPC[$varname];
}
function gpc_get_datetime(&$GPC, $varname, $default = NULL) {
// TODO improve
if (!isset($GPC[$varname]) || $GPC[$varname] == '') {
return $default;
}
@@ -829,6 +976,8 @@ function gpc_get_enum(&$GPC, $varname, $values, $default = NULL) {
return $GPC[$varname];
}
// ========== FORMAT FUNCTIONS ================================================
function format_float($val, $decimals = 2, $suffix = '') {
global $g_lconv;
if (!isset($val) || $val == '') {
@@ -910,6 +1059,67 @@ function format_measurement($n, $unit, $v1, $v2, $v3) {
return $m;
}
// ========== COMMON FUNCTIONS ================================================
function header_location($location, $message=NULL) {
if (is_array($message)) {
$valid_keys = array('succ', 'info', 'warn', 'err');
foreach ($message as $k => $v) {
if (in_array($k, $valid_keys)) {
$_SESSION['message'][$k] = $v;
}
}
}
header("Location: $location");
exit;
}
function pgettext($context, $msgid) {
// implement missing function
global $g_textdomain;
$ctxstr = "{$context}\004{$msgid}";
$translation = dcgettext($g_textdomain, $ctxstr, LC_MESSAGES);
if ($translation == $ctxstr) {
return $msgid;
} else {
return $translation;
}
}
function get_color_brightness($color, $default=0.5) {
// returns a value between 0 and 1
// color is a 6 char hex string
if (strlen($color) != 6) {
return $default;
}
list($red, $green, $blue) = array_map('hexdec', str_split($color, 2));
$red = $red / 255 * 0.2126;
$green = $green / 255 * 0.7152;
$blue = $blue / 255 * 0.0722;
return $red + $green + $blue;
}
function page_caption_search($caption) {
// print page caption with searchbox
?>
<div class="row align-items-center">
<div class="col">
<h1><?=$caption?></h1>
</div>
<div class="col-4">
<form class="form-inline" method="post" action="search.php">
<div class="input-group input-group-sm">
<input name="needle" type="search" class="form-control" placeholder="<?=_('Search')?>">
<div class="input-group-append">
<button class="btn btn-sm btn-primary" type="submit"><i class="bi bi-search"></i></button>
</div>
</div>
</form>
</div>
</div>
<?php
}
function get_pagination($script, $param, $page, $lastpage, $small=false) {
// no direct output, returns string for multiple output