Very big commit on the way to version 3 backend
This commit is contained in:
parent
809fa2d1a4
commit
e9b3a8d313
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[submodule "ext/PHPMailer"]
|
||||||
|
path = ext/PHPMailer
|
||||||
|
url = https://github.com/PHPMailer/PHPMailer.git
|
||||||
|
[submodule "ext/php-jwt"]
|
||||||
|
path = ext/php-jwt
|
||||||
|
url = https://github.com/firebase/php-jwt.git
|
||||||
|
[submodule "ext/php-qrcode"]
|
||||||
|
path = ext/php-qrcode
|
||||||
|
url = https://github.com/chillerlan/php-qrcode
|
||||||
|
[submodule "ext/php-settings-container"]
|
||||||
|
path = ext/php-settings-container
|
||||||
|
url = https://github.com/chillerlan/php-settings-container.git
|
||||||
119
LICENSE
119
LICENSE
@ -1,119 +0,0 @@
|
|||||||
Creative Commons Legal Code
|
|
||||||
|
|
||||||
CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES
|
|
||||||
NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE
|
|
||||||
AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION
|
|
||||||
ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE
|
|
||||||
OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS
|
|
||||||
LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION
|
|
||||||
OR WORKS PROVIDED HEREUNDER.
|
|
||||||
|
|
||||||
Statement of Purpose
|
|
||||||
|
|
||||||
The laws of most jurisdictions throughout the world automatically confer exclusive
|
|
||||||
Copyright and Related Rights (defined below) upon the creator and subsequent
|
|
||||||
owner(s) (each and all, an "owner") of an original work of authorship and/or
|
|
||||||
a database (each, a "Work").
|
|
||||||
|
|
||||||
Certain owners wish to permanently relinquish those rights to a Work for the
|
|
||||||
purpose of contributing to a commons of creative, cultural and scientific
|
|
||||||
works ("Commons") that the public can reliably and without fear of later claims
|
|
||||||
of infringement build upon, modify, incorporate in other works, reuse and
|
|
||||||
redistribute as freely as possible in any form whatsoever and for any purposes,
|
|
||||||
including without limitation commercial purposes. These owners may contribute
|
|
||||||
to the Commons to promote the ideal of a free culture and the further production
|
|
||||||
of creative, cultural and scientific works, or to gain reputation or greater
|
|
||||||
distribution for their Work in part through the use and efforts of others.
|
|
||||||
|
|
||||||
For these and/or other purposes and motivations, and without any expectation
|
|
||||||
of additional consideration or compensation, the person associating CC0 with
|
|
||||||
a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
|
||||||
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
|
||||||
and publicly distribute the Work under its terms, with knowledge of his or
|
|
||||||
her Copyright and Related Rights in the Work and the meaning and intended
|
|
||||||
legal effect of CC0 on those rights.
|
|
||||||
|
|
||||||
1. Copyright and Related Rights. A Work made available under CC0 may be protected
|
|
||||||
by copyright and related or neighboring rights ("Copyright and Related Rights").
|
|
||||||
Copyright and Related Rights include, but are not limited to, the following:
|
|
||||||
|
|
||||||
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
|
||||||
and translate a Work;
|
|
||||||
|
|
||||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
|
||||||
|
|
||||||
iii. publicity and privacy rights pertaining to a person's image or likeness
|
|
||||||
depicted in a Work;
|
|
||||||
|
|
||||||
iv. rights protecting against unfair competition in regards to a Work, subject
|
|
||||||
to the limitations in paragraph 4(a), below;
|
|
||||||
|
|
||||||
v. rights protecting the extraction, dissemination, use and reuse of data
|
|
||||||
in a Work;
|
|
||||||
|
|
||||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
|
||||||
European Parliament and of the Council of 11 March 1996 on the legal protection
|
|
||||||
of databases, and under any national implementation thereof, including any
|
|
||||||
amended or successor version of such directive); and
|
|
||||||
|
|
||||||
vii. other similar, equivalent or corresponding rights throughout the world
|
|
||||||
based on applicable law or treaty, and any national implementations thereof.
|
|
||||||
|
|
||||||
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
|
||||||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
|
||||||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
|
||||||
and Related Rights and associated claims and causes of action, whether now
|
|
||||||
known or unknown (including existing as well as future claims and causes of
|
|
||||||
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
|
||||||
duration provided by applicable law or treaty (including future time extensions),
|
|
||||||
(iii) in any current or future medium and for any number of copies, and (iv)
|
|
||||||
for any purpose whatsoever, including without limitation commercial, advertising
|
|
||||||
or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the
|
|
||||||
benefit of each member of the public at large and to the detriment of Affirmer's
|
|
||||||
heirs and successors, fully intending that such Waiver shall not be subject
|
|
||||||
to revocation, rescission, cancellation, termination, or any other legal or
|
|
||||||
equitable action to disrupt the quiet enjoyment of the Work by the public
|
|
||||||
as contemplated by Affirmer's express Statement of Purpose.
|
|
||||||
|
|
||||||
3. Public License Fallback. Should any part of the Waiver for any reason be
|
|
||||||
judged legally invalid or ineffective under applicable law, then the Waiver
|
|
||||||
shall be preserved to the maximum extent permitted taking into account Affirmer's
|
|
||||||
express Statement of Purpose. In addition, to the extent the Waiver is so
|
|
||||||
judged Affirmer hereby grants to each affected person a royalty-free, non
|
|
||||||
transferable, non sublicensable, non exclusive, irrevocable and unconditional
|
|
||||||
license to exercise Affirmer's Copyright and Related Rights in the Work (i)
|
|
||||||
in all territories worldwide, (ii) for the maximum duration provided by applicable
|
|
||||||
law or treaty (including future time extensions), (iii) in any current or
|
|
||||||
future medium and for any number of copies, and (iv) for any purpose whatsoever,
|
|
||||||
including without limitation commercial, advertising or promotional purposes
|
|
||||||
(the "License"). The License shall be deemed effective as of the date CC0
|
|
||||||
was applied by Affirmer to the Work. Should any part of the License for any
|
|
||||||
reason be judged legally invalid or ineffective under applicable law, such
|
|
||||||
partial invalidity or ineffectiveness shall not invalidate the remainder of
|
|
||||||
the License, and in such case Affirmer hereby affirms that he or she will
|
|
||||||
not (i) exercise any of his or her remaining Copyright and Related Rights
|
|
||||||
in the Work or (ii) assert any associated claims and causes of action with
|
|
||||||
respect to the Work, in either case contrary to Affirmer's express Statement
|
|
||||||
of Purpose.
|
|
||||||
|
|
||||||
4. Limitations and Disclaimers.
|
|
||||||
|
|
||||||
a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered,
|
|
||||||
licensed or otherwise affected by this document.
|
|
||||||
|
|
||||||
b. Affirmer offers the Work as-is and makes no representations or warranties
|
|
||||||
of any kind concerning the Work, express, implied, statutory or otherwise,
|
|
||||||
including without limitation warranties of title, merchantability, fitness
|
|
||||||
for a particular purpose, non infringement, or the absence of latent or other
|
|
||||||
defects, accuracy, or the present or absence of errors, whether or not discoverable,
|
|
||||||
all to the greatest extent permissible under applicable law.
|
|
||||||
|
|
||||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
|
||||||
that may apply to the Work or any use thereof, including without limitation
|
|
||||||
any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims
|
|
||||||
responsibility for obtaining any necessary consents, permissions or other
|
|
||||||
rights required for any use of the Work.
|
|
||||||
|
|
||||||
d. Affirmer understands and acknowledges that Creative Commons is not a party
|
|
||||||
to this document and has no duty or obligation with respect to this CC0 or
|
|
||||||
use of the Work.
|
|
||||||
0
bin/errors.php → bin/__errors.php
Normal file → Executable file
0
bin/errors.php → bin/__errors.php
Normal file → Executable file
0
bin/navi.php → bin/__navi.php
Normal file → Executable file
0
bin/navi.php → bin/__navi.php
Normal file → Executable file
63
bin/__print.php
Executable file
63
bin/__print.php
Executable file
@ -0,0 +1,63 @@
|
|||||||
|
<?php #bin/print.php
|
||||||
|
|
||||||
|
//array $page = (int main, string sub, array css = [strings], array js = [strings])
|
||||||
|
|
||||||
|
//Include CSS
|
||||||
|
$tplcss = tplExtrSection($output["stat"], "###CSS###");
|
||||||
|
$css = "";
|
||||||
|
$x = false;
|
||||||
|
foreach ($output["css"] as $val)
|
||||||
|
{
|
||||||
|
$css.= tplReplMarker($tplcss, "###CSSFILE###", $val."?id=".lgnGenSalt()).($x ? "\n" : "");
|
||||||
|
$x = true;
|
||||||
|
}
|
||||||
|
$output["stat"] = tplReplSection($output["stat"], "###CSS###", $css);
|
||||||
|
|
||||||
|
//Include Javascript
|
||||||
|
$tpljs = tplExtrSection($output["stat"], "###JS###");
|
||||||
|
$js = "";
|
||||||
|
$x= false;
|
||||||
|
foreach ($output["js"] as $val)
|
||||||
|
{
|
||||||
|
$js.= tplReplMarker($tpljs, "###JSFILE###", $val).($x ? "\n" : "");
|
||||||
|
$x = true;
|
||||||
|
}
|
||||||
|
$output["stat"] = tplReplSection($output["stat"], "###JS###", $js);
|
||||||
|
|
||||||
|
//Additional
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###NAV###", $output["navi"]);
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###MAIN###", $output["main"]);
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###PAGETYPE###", $page["main"]=="" ? "frontpage" : "content");
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###TITLE###", TITLE);
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###LINKSTART###", "");
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###PAGEMAIN###", $page["main"]);
|
||||||
|
$url = parse_url("https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###PAGEURL###", "https://".$url["host"].$url["path"]);
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###PAGEDOMAIN###", str_replace("www.", "", $url["host"]));
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###ERRORS###", $output["error"]);
|
||||||
|
|
||||||
|
//Print login/logout forms
|
||||||
|
if ($output["showlogin"]=="in") {
|
||||||
|
//$tpllgn = tplExtrSection($output["stat"], "###LOGIN###");
|
||||||
|
$output["stat"] = tplReplSection($output["stat"], "###LOGOFF###", "");
|
||||||
|
$gets = "";
|
||||||
|
foreach ($_GET as $key => $value) {
|
||||||
|
$gets.= ($gets=="" ? "?" : "&").$key."=".$value;
|
||||||
|
}
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###PAGEGET###", $gets);
|
||||||
|
} elseif ($output["showlogin"]=="off") {
|
||||||
|
//$tpllgn = tplExtrSection($output["stat"], "###LOGOFF###");
|
||||||
|
$output["stat"] = tplReplSection($output["stat"], "###LOGIN###", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($lang as $key => $value) {
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], $key, $value[$user["address.plural"]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$output["stat"] = tplReplMarker($output["stat"], "###SECTOKEN###", $_SESSION["secTokenUse"]);
|
||||||
|
|
||||||
|
//Print page
|
||||||
|
echo $output["stat"];
|
||||||
|
|
||||||
|
|
||||||
|
?>
|
||||||
3
bin/exit.php
Normal file → Executable file
3
bin/exit.php
Normal file → Executable file
@ -1,5 +1,4 @@
|
|||||||
<?php #bin/exit.php
|
<?php #bin/exit.php
|
||||||
|
|
||||||
$mysqli->close();
|
$mysqli->close();
|
||||||
|
$redis->close();
|
||||||
?>
|
|
||||||
|
|||||||
79
bin/init.php
Normal file → Executable file
79
bin/init.php
Normal file → Executable file
@ -1,70 +1,25 @@
|
|||||||
<?php #bin/init.php
|
<?php #bin/init.php
|
||||||
|
|
||||||
|
//Include external libraries (excl. hidden [.file])
|
||||||
|
$files["ext"] = scandir("ext");
|
||||||
|
foreach ($files["ext"] as $val) {
|
||||||
|
if ("."!=substr($val,0,1) && ".php"==substr($val,strlen($val)-4,4)) {
|
||||||
|
include "ext/".$val;
|
||||||
|
} }
|
||||||
|
|
||||||
//Include libraries (excl. hidden [.file])
|
//Include libraries (excl. hidden [.file])
|
||||||
$files["lib"] = scandir("lib");
|
$files["lib"] = scandir("lib");
|
||||||
foreach ($files["lib"] as $val)
|
foreach ($files["lib"] as $val) {
|
||||||
if (substr($val,0,1)!= ".") include "lib/".$val;
|
if (substr($val,0,1)!= "." && $val!="99_manager_new.php") {
|
||||||
|
include "lib/".$val;
|
||||||
|
} }
|
||||||
|
|
||||||
//Import configuration files (excl. hidden [.file])
|
//Import configuration files (excl. hidden [.file])
|
||||||
$files["var"] = scandir("var");
|
$files["var"] = scandir("var");
|
||||||
foreach ($files["var"] as $val)
|
foreach ($files["var"] as $val) {
|
||||||
if (substr($val,0,1)!= ".") include "var/".$val;
|
if (substr($val,0,1)!= ".") {
|
||||||
|
include "var/".$val;
|
||||||
|
} }
|
||||||
|
|
||||||
error_reporting(ERRORLVL);
|
// Authenticate user and prepare input/output
|
||||||
ini_set("display_errors", 1);
|
$man = new Manager($mysqli, $redis, JWT_KEY);
|
||||||
|
|
||||||
lgnSecSessionStart(); // Unsere selbstgemachte sichere Funktion um eine PHP-Sitzung zu starten
|
|
||||||
lgnRegenerateToken(); // Erzeugt neuen Token
|
|
||||||
|
|
||||||
//Clean input
|
|
||||||
function cleanInput($array) {
|
|
||||||
$ret = array();
|
|
||||||
foreach ($array as $key => $value) {
|
|
||||||
$ret[$key] = is_array($value) ? cleanInput($value) : (is_string($value) ? SS($value) : SI($value));
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
$input = cleanInput($_REQUEST);
|
|
||||||
|
|
||||||
//Check login attempt
|
|
||||||
if (isset($input["login"],$input["pass"],$input["submit"]) && $input["secToken"]==$_SESSION["secTokenVerify"])
|
|
||||||
{
|
|
||||||
//Login attempt
|
|
||||||
if (!lgnLogin($mysqli, $input["login"], $input["pass"]))
|
|
||||||
{
|
|
||||||
addError("Login Failed: ", $mysqli->error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//Init main variables
|
|
||||||
//array $page = (string main, string sub, array css = [strings], array js = [strings])
|
|
||||||
//array $output = (string stat, string navi, string main)
|
|
||||||
//array $temp = (EMPTY) - can be used for temporary variables
|
|
||||||
$page = array
|
|
||||||
(
|
|
||||||
"main" => "",
|
|
||||||
"sub" => ""
|
|
||||||
);
|
|
||||||
$output = array
|
|
||||||
(
|
|
||||||
"stat" => tplLoadFile(FRAMETPL), //Load Mainframe
|
|
||||||
"main" => "",
|
|
||||||
"navi" => "",
|
|
||||||
"error" => "",
|
|
||||||
"css" => array(),
|
|
||||||
"js" => array(),
|
|
||||||
"showlogin" => "in"
|
|
||||||
);
|
|
||||||
addStyle(FRAMECSS, FRAMECSS!="");
|
|
||||||
addJScript(FRAMEJS, FRAMEJS!="");
|
|
||||||
$temp = array();
|
|
||||||
$user = array(
|
|
||||||
"address.plural" => 1
|
|
||||||
);
|
|
||||||
|
|
||||||
if (isset($input["userMod"]) && $input["userMod"]=="Ausloggen")
|
|
||||||
{
|
|
||||||
lgnLogout();
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|||||||
22
bin/init_new.php
Normal file
22
bin/init_new.php
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?php #bin/init.php
|
||||||
|
|
||||||
|
//Include external libraries (excl. hidden [.file])
|
||||||
|
$files["ext"] = scandir("ext");
|
||||||
|
foreach ($files["ext"] as $val) {
|
||||||
|
if ("."!=substr($val,0,1) && ".php"==substr($val,strlen($val)-4,4)) {
|
||||||
|
include "ext/".$val;
|
||||||
|
} }
|
||||||
|
|
||||||
|
//Include libraries (excl. hidden [.file])
|
||||||
|
$files["lib"] = scandir("lib_new");
|
||||||
|
foreach ($files["lib"] as $val) {
|
||||||
|
if (substr($val,0,1)!= ".") {
|
||||||
|
include "lib_new/".$val;
|
||||||
|
} }
|
||||||
|
|
||||||
|
//Import configuration files (excl. hidden [.file])
|
||||||
|
$files["conf"] = scandir("conf");
|
||||||
|
foreach ($files["conf"] as $val) {
|
||||||
|
if (substr($val,0,1)!= ".") {
|
||||||
|
include "conf/".$val;
|
||||||
|
} }
|
||||||
298
bin/print.php
298
bin/print.php
@ -1,63 +1,259 @@
|
|||||||
<?php #bin/print.php
|
<?php #ajax/print.php
|
||||||
|
|
||||||
//array $page = (int main, string sub, array css = [strings], array js = [strings])
|
require_once "../bin/init_ajax.php";
|
||||||
|
require_once "../lib/content.php";
|
||||||
|
|
||||||
//Include CSS
|
function getContent($key, $getText = false, $demoMode = false) {
|
||||||
$tplcss = tplExtrSection($output["stat"], "###CSS###");
|
global $content, $data, $links, $refs, $options;
|
||||||
$css = "";
|
|
||||||
$x = false;
|
|
||||||
foreach ($output["css"] as $val)
|
|
||||||
{
|
|
||||||
$css.= tplReplMarker($tplcss, "###CSSFILE###", $val."?id=".lgnGenSalt()).($x ? "\n" : "");
|
|
||||||
$x = true;
|
|
||||||
}
|
|
||||||
$output["stat"] = tplReplSection($output["stat"], "###CSS###", $css);
|
|
||||||
|
|
||||||
//Include Javascript
|
$ret = "";
|
||||||
$tpljs = tplExtrSection($output["stat"], "###JS###");
|
if (array_key_exists($key, $content)) {
|
||||||
$js = "";
|
if ($getText && isset($options[$key])) {
|
||||||
$x= false;
|
if (is_array($content[$key])) {
|
||||||
foreach ($output["js"] as $val)
|
$ret = array();
|
||||||
{
|
foreach ($options[$key] as $option) {
|
||||||
$js.= tplReplMarker($tpljs, "###JSFILE###", $val).($x ? "\n" : "");
|
if (in_array($option["value"], $content[$key])) {
|
||||||
$x = true;
|
$ret[] = $option["display"];
|
||||||
}
|
|
||||||
$output["stat"] = tplReplSection($output["stat"], "###JS###", $js);
|
|
||||||
|
|
||||||
//Additional
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###NAV###", $output["navi"]);
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###MAIN###", $output["main"]);
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###PAGETYPE###", $page["main"]=="" ? "frontpage" : "content");
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###TITLE###", TITLE);
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###LINKSTART###", "");
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###PAGEMAIN###", $page["main"]);
|
|
||||||
$url = parse_url("https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###PAGEURL###", "https://".$url["host"].$url["path"]);
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###PAGEDOMAIN###", str_replace("www.", "", $url["host"]));
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###ERRORS###", $output["error"]);
|
|
||||||
|
|
||||||
//Print login/logout forms
|
|
||||||
if ($output["showlogin"]=="in") {
|
|
||||||
//$tpllgn = tplExtrSection($output["stat"], "###LOGIN###");
|
|
||||||
$output["stat"] = tplReplSection($output["stat"], "###LOGOFF###", "");
|
|
||||||
$gets = "";
|
|
||||||
foreach ($_GET as $key => $value) {
|
|
||||||
$gets.= ($gets=="" ? "?" : "&").$key."=".$value;
|
|
||||||
}
|
}
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###PAGEGET###", $gets);
|
}
|
||||||
} elseif ($output["showlogin"]=="off") {
|
} else {
|
||||||
//$tpllgn = tplExtrSection($output["stat"], "###LOGOFF###");
|
foreach ($options[$key] as $option) {
|
||||||
$output["stat"] = tplReplSection($output["stat"], "###LOGIN###", "");
|
$ret = " ";
|
||||||
|
if ($option["value"]==$content[$key]) {
|
||||||
|
$ret = $option["display"];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ret = $content[$key];
|
||||||
|
}
|
||||||
|
} else if (isset($data[$key])) {
|
||||||
|
if (isset($data[$key]["default"]) && $data[$key]["default"]!="") {
|
||||||
|
$ret = $data[$key]["default"];
|
||||||
|
} else if ($demoMode) {
|
||||||
|
$ret = $data[$key]["title"];
|
||||||
|
}
|
||||||
|
} else if (isset($links[$key])) {
|
||||||
|
$ret = array();
|
||||||
|
} else if (isset($refs[$key])) {
|
||||||
|
$ret = array();
|
||||||
|
} else {
|
||||||
|
$ret = $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($data[$key]["prefix"]) && !is_array($ret)) {
|
||||||
|
$ret = $data[$key]["prefix"].$ret;
|
||||||
|
}
|
||||||
|
if (isset($data[$key]["postfix"]) && !is_array($ret)) {
|
||||||
|
$ret.= $data[$key]["postfix"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($lang as $key => $value) {
|
function moveIfOverlap(&$floating, &$left, &$top, $width, $height, $dir = "none", $lastdir = "none") {
|
||||||
$output["stat"] = tplReplMarker($output["stat"], $key, $value[$user["address.plural"]]);
|
$moved = false;
|
||||||
|
foreach ($floating as $key => $float) {
|
||||||
|
if (
|
||||||
|
$left < $float["right"] &&
|
||||||
|
$left+$width > $float["left"] &&
|
||||||
|
$top < $float["bottom"] &&
|
||||||
|
$top+$height > $float["top"]
|
||||||
|
) {
|
||||||
|
if ($float["dir"]=="left" && $lastdir!="right") {
|
||||||
|
$left = $float["right"];
|
||||||
|
} else if ($float["dir"]=="right" && $lastdir!="left") {
|
||||||
|
$left = $float["left"] - $width;
|
||||||
|
} else if ($float["dir"]=="up" && $lastdir!="down") {
|
||||||
|
$top = $float["bottom"];
|
||||||
|
} else if ($float["dir"]=="down" && $lastdir!="up") {
|
||||||
|
$top = $float["top"] - $height;
|
||||||
|
}
|
||||||
|
$lastdir = $float["dir"];
|
||||||
|
$moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($moved) {
|
||||||
|
moveIfOverlap($floating, $left, $top, $width, $height, $dir, $lastdir);
|
||||||
|
} else if ($dir!="none") {
|
||||||
|
$floating[] = array(
|
||||||
|
"left" => $left,
|
||||||
|
"right" => $left + $width,
|
||||||
|
"top" => $top,
|
||||||
|
"bottom" => $top + $height,
|
||||||
|
"dir" => $dir
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$output["stat"] = tplReplMarker($output["stat"], "###SECTOKEN###", $_SESSION["secTokenUse"]);
|
if (lgnCheckLogin($mysqli) && isset($_POST["page"], $_POST["layout"]) && $_POST["layout"]!="") {
|
||||||
|
$page = $_POST["page"];
|
||||||
|
$data = $_POST["data"];
|
||||||
|
$links = $_POST["links"] ?? array();
|
||||||
|
$refs = $_POST["refs"] ?? array();
|
||||||
|
$options = $_POST["options"];
|
||||||
|
|
||||||
//Print page
|
$content = array();
|
||||||
echo $output["stat"];
|
$id = null;
|
||||||
|
if (isset($_POST["ID"]) && $_POST["ID"]!="") {
|
||||||
|
$id = $_POST["ID"];
|
||||||
|
$cntRow = cntGetRow($mysqli, $page, $id, $links, $refs);
|
||||||
|
$content = $cntRow["erg"];
|
||||||
|
if ($cntRow["err"]!="") {
|
||||||
|
echo "<div>".$cntRow["err"]."</div>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$res = $mysqli->query("select * from print_layouts where ID='".$_POST["layout"]."'");
|
||||||
|
$layout = $res->fetch_assoc();
|
||||||
|
$floating = array();
|
||||||
|
|
||||||
|
echo "<div style='float: none; position: relative; display: inline-block; vertical-align: top; width: ".($layout["width"]/10)."mm; height: ".($layout["height"]/10)."mm; border: 1px solid #000000; background-color: #ffffff;'>";
|
||||||
|
|
||||||
|
$res = $mysqli->query("select * from print_elements where key_layout_ID='".$layout["ID"]."' order by ord asc");
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$do_print = false;
|
||||||
|
$cond = explode("|", $row["cond"]);
|
||||||
|
if ($row["params"]=="") {
|
||||||
|
$row["params"] = "{}";
|
||||||
|
}
|
||||||
|
$params = json_decode($row["params"], true);
|
||||||
|
switch ($cond[0]) {
|
||||||
|
case null:
|
||||||
|
case "":
|
||||||
|
case "0": //Print always
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "1": //Field has to be NOT null and not empty text
|
||||||
|
if (sizeof($cond)>1 && (getContent($cond[1])!=null && getContent($cond[1], true)!=""))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "2": //Field has to be null or empty text
|
||||||
|
if (sizeof($cond)>1 && (getContent($cond[1])==null || getContent($cond[1], true)==""))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "3": //Field has to be value (second parameter) or include value if is link
|
||||||
|
if (sizeof($cond)>2 && ((array_key_exists($cond[1], $links) && in_array($cond[2], getContent($cond[1]))) || (getContent($cond[1])==$cond[2])))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "4": //Field has to be NOT value (second parameter) or DONT include value if is link
|
||||||
|
if (sizeof($cond)>2 && ((array_key_exists($cond[1], $links) && !in_array($cond[2], getContent($cond[1]))) && (getContent($cond[1])!=$cond[2])))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "5": //Field has to be text (second parameter) or include text if it is link
|
||||||
|
if (sizeof($cond)>2 && ((array_key_exists($cond[1], $links) && in_array($cond[2], getContent($cond[1], true))) || (getContent($cond[1], true)==$cond[2])))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "6": //Field has to be NOT text (second parameter) or DONT include text if it is link
|
||||||
|
if (sizeof($cond)>2 && ((array_key_exists($cond[1], $links) && !in_array($cond[2], getContent($cond[1], true))) && (getContent($cond[1], true)!=$cond[2])))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($id===null) {//Demo mode
|
||||||
|
$do_print = true;
|
||||||
|
}
|
||||||
|
$print_count = 0;
|
||||||
|
if ($do_print) {
|
||||||
|
$print_count = $params["repeat"] ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($print_count>0) {
|
||||||
|
$print_count--;
|
||||||
|
$border = array();
|
||||||
|
$border["top"] = isset($params["border-top"]) ? $params["border-top"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["bottom"] = isset($params["border-bottom"]) ? $params["border-bottom"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["left"] = isset($params["border-left"]) ? $params["border-left"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["right"] = isset($params["border-right"]) ? $params["border-right"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["color"] = isset($params["border-color"]) ? $params["border-color"] : "#000000";
|
||||||
|
$border["string"] = "";
|
||||||
|
if ($border["left"]!="0") {
|
||||||
|
$border["string"].= "border-left: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["right"]!="0") {
|
||||||
|
$border["string"].= "border-right: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["top"]!="0") {
|
||||||
|
$border["string"].= "border-top: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["bottom"]!="0") {
|
||||||
|
$border["string"].= "border-bottom: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
|
||||||
|
$cnt = $row["content"];
|
||||||
|
$fields = array();
|
||||||
|
if (preg_match_all("/#([^#]+)#/", $cnt, $matches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
$replace = "";
|
||||||
|
$fields[] = $match[1];
|
||||||
|
if (array_key_exists($match[1], $links)) {
|
||||||
|
$replace = implode(", ", getContent($match[1], true, $id===null));
|
||||||
|
} else if (array_key_exists($match[1], $data)) {
|
||||||
|
$replace = getContent($match[1], true, $id===null);
|
||||||
|
} else {
|
||||||
|
foreach ($data as $key => $val) {
|
||||||
|
if ($val["group"]==$match[1]) {
|
||||||
|
$replace.=getContent($key, true, $id===null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cnt = str_replace("#".$match[1]."#", $replace, $cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$left = $row["posl"];
|
||||||
|
$top = $row["post"];
|
||||||
|
$width = $row["dimw"];
|
||||||
|
if ($width == 0) {
|
||||||
|
$width = 10;
|
||||||
|
}
|
||||||
|
$height = $row["dimh"];
|
||||||
|
if ($height == 0) {
|
||||||
|
$height = 10;
|
||||||
|
}
|
||||||
|
moveIfOverlap($floating, $left, $top, $width, $height, isset($params["float"]) ? $params["float"] : "none");
|
||||||
|
|
||||||
|
switch ($row["type"]) {
|
||||||
|
case "img":
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="keep") {
|
||||||
|
$imgsize = "max-width: ".$width."mm; max-height: ".$height."mm; width: auto; height: auto; ";
|
||||||
|
}
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="height") {
|
||||||
|
$imgsize = "height: ".$height."mm; width: auto; ";
|
||||||
|
} else {
|
||||||
|
$imgsize = "width: ".$width."mm; height: ".$height."mm; ";
|
||||||
|
}
|
||||||
|
$path = $cnt;
|
||||||
|
if (count($fields)) {
|
||||||
|
$path = "uploads/".$page."/".$fields[0]."/".$cnt;
|
||||||
|
}
|
||||||
|
if (file_exists("../".$path)) {
|
||||||
|
echo "<img src='".$path."' style='position: absolute; left: ".$left."mm; top: ".$top."mm; ".$imgsize."' />";
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
$rotate = "";
|
||||||
|
if (isset($params["rotate"]) && $params["rotate"]=="1") {
|
||||||
|
$rotate = "transform: rotate(-90deg); ";
|
||||||
|
$left = "calc(".($left + ($height - $width)/2)."mm - ".$border["left"]."px); ";
|
||||||
|
$top = "calc(".($top + ($width - $height)/2)."mm - ".$border["top"]."px); ";
|
||||||
|
} else {
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
}
|
||||||
|
$font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
|
||||||
|
$font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
|
||||||
|
if (isset($params["weight"])) {
|
||||||
|
$font.= "font-weight: ".$params["weight"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["align"])) {
|
||||||
|
$font.= "text-align: ".$params["align"]."; ";
|
||||||
|
}
|
||||||
|
echo "<span style='position: absolute; left: ".$left."top: ".$top."width: ".$width."mm; height: ".$height."mm; ".$font.$border["string"].$rotate."'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "</div>";
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
1
client/dropzone.min.js
vendored
Normal file
1
client/dropzone.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
16
client/filter.js
Normal file
16
client/filter.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
class APIFilter // This holds ONE filter element
|
||||||
|
{
|
||||||
|
constructor(data, type) {
|
||||||
|
this.Set(data, type);
|
||||||
|
}
|
||||||
|
Get() {
|
||||||
|
return {and: this.and, field: this.field, op: this.op, value: this.value};
|
||||||
|
}
|
||||||
|
Set(data, type) {
|
||||||
|
this.and = typeof(data.and)!="undefined" ? data.and : "and";
|
||||||
|
this.field = typeof(data.field)!="undefined" ? data.field : "none";
|
||||||
|
this.op = typeof(data.op)!="undefined" ? data.op : "";
|
||||||
|
this.value = typeof(data.value)!="undefined" ? data.value : ("Multiple"==type ? [] : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
81
client/index.html
Normal file
81
client/index.html
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="expires" content="0">
|
||||||
|
<meta name="author" content="Nils Otterpohl" />
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
||||||
|
<title>OF56-App (Client v3)</title>
|
||||||
|
<link rel="icon" href="/wappen48.png" type="image/png">
|
||||||
|
<link rel="stylesheet" type="text/css" href="/client/main.css" />
|
||||||
|
<link rel="stylesheet" type="text/css" href="/client/toastify.css" />
|
||||||
|
<script src="/client/toastify.js"></script>
|
||||||
|
<script src="/client/dropzone.min.js"></script>
|
||||||
|
<script src="/client/qr-scanner.umd.min.js"></script>
|
||||||
|
<script src="/client/template.js"></script>
|
||||||
|
<script src="/client/scanner.js"></script>
|
||||||
|
<script src="/client/filter.js"></script>
|
||||||
|
<script src="/client/page.js"></script>
|
||||||
|
<!--<script src="/client/pages/start.js"></script>-->
|
||||||
|
<!--<script src="/client/pages/termine.js"></script>-->
|
||||||
|
<!--<script src="/client/pages/dienstpläne.js"></script>-->
|
||||||
|
<script src="/client/pages/personal.js"></script>
|
||||||
|
<!--<script src="/client/pages/prozesse.js"></script>-->
|
||||||
|
<!--<script src="/client/pages/spinde.js"></script>-->
|
||||||
|
<!--<script src="/client/pages/einsätze.js"></script>-->
|
||||||
|
<!--<script src="/client/pages/anwesenheiten.js"></script>-->
|
||||||
|
<script src="/client/pages/fahrzeuge.js"></script>
|
||||||
|
<script src="/client/root.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="header" style="display: none; opacity: 0;">
|
||||||
|
<div id="loginbox">
|
||||||
|
<form>
|
||||||
|
<center>
|
||||||
|
<input id="loginUser" class="login" type="text" name="username" autocomplete="username" /><br />
|
||||||
|
<input id="loginPassword" class="login" type="password" autocomplete="current-password" /><br />
|
||||||
|
<input id="loginButton" type="submit" class="login" value="Einloggen" />
|
||||||
|
</center>
|
||||||
|
</form>
|
||||||
|
<p>
|
||||||
|
Willkommen! Bitte Anmeldedaten von IServ verwenden! (vorname.nachname)<br />
|
||||||
|
Sollte der Login nicht klappen, bitte versuchen ob die Zugangsdaten auf
|
||||||
|
<a href="https://feuerwehr-bs.net">feuerwehr-bs.net</a> funktionieren und danach bei Nils melden.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<nav>
|
||||||
|
<ul id="navigation">
|
||||||
|
<li id="nav_start" class="current"><img src="/wappen48.png" title="Start"></li>
|
||||||
|
<li id="nav_termine" style="display: none;">Termine</li>
|
||||||
|
<li id="nav_dienstpläne" style="display: none;">Dienstpläne</li>
|
||||||
|
<li id="nav_personal" style="display: none;">Personal</li>
|
||||||
|
<li id="nav_prozesse" style="display: none;">Prozesse</li>
|
||||||
|
<li id="nav_spinde" style="display: none;">Spinde</li>
|
||||||
|
<li id="nav_einsätze" style="display: none;">(Einsätze)</li>
|
||||||
|
<li id="nav_anwesenheiten" style="display: none;">(Anwesenheiten)</li>
|
||||||
|
<li id="nav_fahrzeuge" style="display: none;">(Fahrzeuge)</li>
|
||||||
|
<li id="nav_logout"><img src="/res/dark/logout.png" title="Ausloggen"></li>
|
||||||
|
</ul>
|
||||||
|
<select id="navigation_select">
|
||||||
|
<option id="nav_sel_start" value="">☰ Start</option>
|
||||||
|
<option id="nav_sel_termine" value="Termine" style="display: none;">☰ Termine</option>
|
||||||
|
<option id="nav_sel_dienstpläne" value="Dienstpläne" style="display: none;">☰ Dienstpläne</option>
|
||||||
|
<option id="nav_sel_personal" value="Personal" style="display: none;">☰ Personal</option>
|
||||||
|
<option id="nav_sel_prozesse" value="Prozesse" style="display: none;">☰ Prozesse</option>
|
||||||
|
<option id="nav_sel_spinde" value="Spinde" style="display: none;">☰ Spinde</option>
|
||||||
|
<option id="nav_sel_einsätze" value="Einsätze" style="display: none;">☰ (Einsätze)</option>
|
||||||
|
<option id="nav_sel_anwesenheiten" value="Anwesenheiten" style="display: none;">☰ (Anwesenheiten)</option>
|
||||||
|
<option id="nav_sel_fahrzeuge" value="Fahrzeuge" style="display: none;">☰ (Fahrzeuge)</option>
|
||||||
|
<option id="nav_sel_logout" value="_LOGOUT_">Logout</option>
|
||||||
|
</select>
|
||||||
|
</nav>
|
||||||
|
<main id="content"></main>
|
||||||
|
<div id="qrscanner">
|
||||||
|
<img id="qrscanner_close" src="/res/dark/cancel.png">
|
||||||
|
<img id="qrscanner_camera" src="/res/dark/refresh.png">
|
||||||
|
<div id="qrscanner_text"></div>
|
||||||
|
<video id="qrscanner_video"></video>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
247
client/main.css
Normal file
247
client/main.css
Normal file
@ -0,0 +1,247 @@
|
|||||||
|
/* tpl/main.css */
|
||||||
|
body {
|
||||||
|
font-family: arial;
|
||||||
|
font-size: 20px;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
header {
|
||||||
|
background: #000 url(/res/frontpage.jpg) center no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
||||||
|
#loginbox {
|
||||||
|
position: absolute;
|
||||||
|
width: 18em;
|
||||||
|
left: calc(50% - 9em);
|
||||||
|
bottom: 5em;
|
||||||
|
}
|
||||||
|
#loginbox p {
|
||||||
|
background-color: #000;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.2em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
a {color: #fff; text-decoration: underline;}
|
||||||
|
ul {list-style: none; padding: 0; margin: 0;}
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2em;
|
||||||
|
margin: 0.1em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
li img {height: 0.8em;}
|
||||||
|
li > img {margin: 0 0.4em 0 0.2em;}
|
||||||
|
.button > img, li > .button {margin: 0 0.2em 0 0.2em;}
|
||||||
|
li .button {
|
||||||
|
padding: 0 0.1em;
|
||||||
|
background-color: #000;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
border: 0.05em solid #fff;
|
||||||
|
}
|
||||||
|
.button {border: 0.05em solid #fff; background-color: #000; cursor: pointer;}
|
||||||
|
.button:hover {background-color: #333;}
|
||||||
|
nav {
|
||||||
|
vertical-align: middle;
|
||||||
|
text-align: left;
|
||||||
|
background-color: #9b0000;
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
nav ul {padding: 0 0.5em;}
|
||||||
|
nav li {
|
||||||
|
padding: 0.5em 0.75em;
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 0;
|
||||||
|
background-color: #9b0000;
|
||||||
|
border: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
nav li:hover {background-color: #f00;}
|
||||||
|
nav li.current {background-color: #000;}
|
||||||
|
#navigation_select {
|
||||||
|
display:none;
|
||||||
|
background-color: #9b0000;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0.5em 0.75em;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
main {padding: 0 1.75em;}
|
||||||
|
article {margin: 0 0 2em 0; max-width: 60em;}
|
||||||
|
article:first-of-type {margin: 2em 0 2em 0;}
|
||||||
|
article img {max-width: 100%;}
|
||||||
|
h1 img {height: 0.8em;}
|
||||||
|
input, select {
|
||||||
|
background-color: #000;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0.1em;
|
||||||
|
border: 1px solid #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.list_entry select {height: 1.4em;}
|
||||||
|
input[type="submit"], button {background-color: #9b0000; color: #fff; font-size: 100%;}
|
||||||
|
.login {
|
||||||
|
width: 12em;
|
||||||
|
text-align: center;
|
||||||
|
border: 0.1em solid #fff;
|
||||||
|
padding: 0.2em;
|
||||||
|
margin: 0.1em;
|
||||||
|
box-sizing: content-box;
|
||||||
|
}
|
||||||
|
.disabled, input[type="submit"].disabled {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
a.toggleVisibility {text-decoration: none;}
|
||||||
|
h1 {
|
||||||
|
background-color: #333;
|
||||||
|
display: inline;
|
||||||
|
padding: 0.3em 0.6em;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
div.toggleVisibility {
|
||||||
|
padding: 1em;
|
||||||
|
border: 0.3em solid #333;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
.group_title {
|
||||||
|
border-bottom: none;
|
||||||
|
border-left: 0.3em solid #333;
|
||||||
|
border-right: 0.3em solid #333;
|
||||||
|
padding: 0.5em;
|
||||||
|
border-top: 0.2em solid #333;
|
||||||
|
border-top-right-radius: 0.3em;
|
||||||
|
border-top-left-radius: 0.3em;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.group_title h1 {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
.list_entry_row {
|
||||||
|
border-bottom: 0.05em dashed #333;
|
||||||
|
border-left: 0.3em solid #333;
|
||||||
|
border-right: 0.3em solid #333;
|
||||||
|
border-top: 0;
|
||||||
|
padding: 0.2em;
|
||||||
|
min-height: 1.733em;
|
||||||
|
}
|
||||||
|
.list_entry_row ul {display: inline;}
|
||||||
|
.list_entry_row .block {display: block;}
|
||||||
|
.list_entry {border-bottom: 0.05em dotted #333;}
|
||||||
|
.list_entry:last-of-type {border-bottom: none;}
|
||||||
|
.list_entry_title {
|
||||||
|
border-bottom: none;
|
||||||
|
border-left: 0.3em solid #333;
|
||||||
|
border-right: 0.3em solid #333;
|
||||||
|
padding: 0.2em;
|
||||||
|
border-top: 0.1em solid #333;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
.list_entry .list_entry_row:last-of-type {border-bottom: none;}
|
||||||
|
.list_entry:last-of-type .list_entry_row:last-of-type {border-bottom: 0.3em solid #333;}
|
||||||
|
/*.list_entry:last-of-type .list_entry_title:last-of-type {border-bottom: 0.05em solid #888;}*/
|
||||||
|
.list_entry_title li {background-color: #000;}
|
||||||
|
.list_entry:first-of-type .list_entry_title {border-top-right-radius: 0.2em;}
|
||||||
|
.list_entry:last-of-type div:last-of-type {border-bottom-right-radius: 0.2em; border-bottom-left-radius: 0.2em;}
|
||||||
|
.list_selected div {border-color: #9b0000;}
|
||||||
|
.list_selected .list_entry_title {background-color: #9b0000;}
|
||||||
|
|
||||||
|
.list_entry .bild {
|
||||||
|
position: relative;
|
||||||
|
float: right;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 4em;
|
||||||
|
min-width: 4em;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #fff;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.list_entry .bild img {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
max-height: 4em;
|
||||||
|
max-width: 4em;
|
||||||
|
}
|
||||||
|
.list_entry .bild form {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 10;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 4em;
|
||||||
|
width: 4em;
|
||||||
|
opacity: 0.6;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
.list_entry .bild form button {
|
||||||
|
height: 4em;
|
||||||
|
width: 4em;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.list_entry .bild .deleter {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 20;
|
||||||
|
bottom: -0.2em;
|
||||||
|
right: -0.2em;
|
||||||
|
background-color: #000;
|
||||||
|
border: 0.05em solid #fff;
|
||||||
|
border-radius: 0.2em;
|
||||||
|
}
|
||||||
|
.list_entry .bild .deleter img {position: relative;height: 0.8em;}
|
||||||
|
.list_entry .bild .dz-default {max-height: 100%;}
|
||||||
|
|
||||||
|
#qrscanner {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
left: 2em;
|
||||||
|
right: 2em;
|
||||||
|
top: 2em;
|
||||||
|
bottom: 2em;
|
||||||
|
width: auto;
|
||||||
|
height: auto;
|
||||||
|
z-index: 100;
|
||||||
|
background-color: #555;
|
||||||
|
padding: 2em;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
|
#qrscanner_text, #qrscanner_video {/*width:100%;*/}
|
||||||
|
#qrscanner_close {position: fixed; z-index: 999; right: 2.5em; top: 2.5em;}
|
||||||
|
#qrscanner_video {max-height: 80%; max-width: 80%;}
|
||||||
|
|
||||||
|
.chocolate {background-color: #840;}
|
||||||
|
.merlot {background-color: #804;}
|
||||||
|
.mughal {background-color: #480;}
|
||||||
|
.philippine {background-color: #084;}
|
||||||
|
.blue {background-color: #048;}
|
||||||
|
.indigo {background-color: #408;}
|
||||||
|
.purple {background-color: #404;}
|
||||||
|
.olive {background-color: #440;}
|
||||||
|
.teal {background-color: #044;}
|
||||||
|
.red {background-color: #400;}
|
||||||
|
.pacific {background-color: #004;}
|
||||||
|
.green {background-color: #040;}
|
||||||
|
|
||||||
|
@media only screen and (orientation: portrait) {
|
||||||
|
body {font-size: 4vw;}
|
||||||
|
nav {font-size: 6vw;}
|
||||||
|
#navigation_select {display:inline;font-size: 6vw;}
|
||||||
|
#navigation {display:none;}
|
||||||
|
main {padding: 0 1em;}
|
||||||
|
nav li {padding: 1em 0.35em;}
|
||||||
|
table {font-size: 4vw;}
|
||||||
|
input,button,select {font-size: 4vw;}
|
||||||
|
input[type=radio] {border: 0; height: 1em;}
|
||||||
|
#qrscanner {flex-direction: column; padding: 0;}
|
||||||
|
#qrscanner_video {max-height: 40%; max-width: 100%;}
|
||||||
|
}
|
||||||
399
client/page.js
Normal file
399
client/page.js
Normal file
@ -0,0 +1,399 @@
|
|||||||
|
class Page
|
||||||
|
{
|
||||||
|
constructor(groupName, json, route, array, groups, subs, selects, globalAdmin) {
|
||||||
|
this.group = groups[groupName];
|
||||||
|
this.groupName = groupName;
|
||||||
|
this.ID = json.ID;
|
||||||
|
this.route = route;
|
||||||
|
this.array = array;
|
||||||
|
this.groups = groups;
|
||||||
|
this.subs = subs;
|
||||||
|
this.selects = selects;
|
||||||
|
this.admin = globalAdmin || false;//json.admin; // Local admin rights
|
||||||
|
|
||||||
|
this.init(json);
|
||||||
|
this.initSubs(json);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Static functions */
|
||||||
|
|
||||||
|
static Open() {
|
||||||
|
document.getElementById("nav_" + Root.instance.page.toLowerCase()).classList.remove("current");
|
||||||
|
document.getElementById("nav_" + this.route.toLowerCase()).classList.add("current");
|
||||||
|
Root.instance.page = this.route;
|
||||||
|
document.getElementById("navigation_select").value = this.route;
|
||||||
|
//this.FetchOptions();
|
||||||
|
this.Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
static SetVisibility(visible) {
|
||||||
|
this.visible = visible;
|
||||||
|
document.getElementById("nav_"+this.route.toLowerCase()).style.display = visible ? "inline" : "none";
|
||||||
|
document.getElementById("nav_sel_"+this.route.toLowerCase()).style.display = visible ? "inline" : "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
static UpdateData(json) {
|
||||||
|
//this.meta = json.meta;
|
||||||
|
this.prints = json.prints;
|
||||||
|
this.admin = json.admin;
|
||||||
|
for (let sub in this.subs) {
|
||||||
|
this.subs[sub].entries = json.options[sub.toUpperCase()];
|
||||||
|
}
|
||||||
|
for (let sel in this.selects) {
|
||||||
|
this.selects[sel] = json.options[sel.toUpperCase()];
|
||||||
|
}
|
||||||
|
this.visible = true;
|
||||||
|
/*this.selected = [];
|
||||||
|
if ("undefined"!=typeof(json.selected)) {
|
||||||
|
this.selected = json.selected;
|
||||||
|
}*/
|
||||||
|
for (var k in this.filter) {
|
||||||
|
delete this.filter[k];
|
||||||
|
}
|
||||||
|
this.filter = [];
|
||||||
|
for (var l in json.filter) {
|
||||||
|
this.filter.push(new APIFilter(json.filter[l], "none"!=json.filter[l].field ? this.mainFields[json.filter[l].field].type : "none"));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Render() {
|
||||||
|
let html = "";
|
||||||
|
if (this.admin) {
|
||||||
|
html+= "<article id='" + this.route + "/Add'>" + this.RenderAdd() + "</article>";
|
||||||
|
}
|
||||||
|
for (const group in this.groups) {
|
||||||
|
const groupHtml = this.RenderGroup(group);
|
||||||
|
html+= groupHtml.begin;
|
||||||
|
|
||||||
|
|
||||||
|
for (const e in this.groups[group]) {
|
||||||
|
const entry = this.groups[group][e];
|
||||||
|
html+= entry.renderContainer()/*Entry(false, true)*/;
|
||||||
|
}
|
||||||
|
html+= "</article>";
|
||||||
|
}
|
||||||
|
document.getElementById("content").innerHTML = html;
|
||||||
|
for (const ID in this.array) {
|
||||||
|
this.array[ID].renderEntry();
|
||||||
|
this.array[ID].addEntryEvents();
|
||||||
|
this.array[ID].renderSubs();
|
||||||
|
this.array[ID].addSubEvents();
|
||||||
|
}
|
||||||
|
const _this = this;
|
||||||
|
Root.AddEventListenerIfButtonExists(this.route + "/Add/Add", function (event) {_this.Add();});
|
||||||
|
}
|
||||||
|
|
||||||
|
static Value(id, name) {
|
||||||
|
const e = document.getElementById(this.route+"/"+id+"/"+name);
|
||||||
|
return (e!==null ? e.value : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Add(contextId = null) {
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
let json = this.Collect("Add", contextId);
|
||||||
|
json.secToken = Root.instance.secToken;
|
||||||
|
const _this = this;
|
||||||
|
|
||||||
|
request.open("POST", Root.instance.url + this.route);
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
//_this.UpdateData(json);
|
||||||
|
if (typeof(_this.groups[json.content.GROUP])=="undefined") {
|
||||||
|
_this.groups[json.content.GROUP] = [];
|
||||||
|
const groupHtml = _this.RenderGroup(json.content.GROUP);
|
||||||
|
document.getElementById("content").insertAdjacentHTML("beforeend", groupHtml.begin + groupHtml.end);
|
||||||
|
}
|
||||||
|
const group = _this.groups[json.content.GROUP];
|
||||||
|
_this.array[json.content.ID] = _this.New(json.content.GROUP, json.content);
|
||||||
|
group.push(_this.array[json.content.ID]);
|
||||||
|
// Add html
|
||||||
|
var elList = document.getElementById("Group/" + json.content.GROUP);
|
||||||
|
elList.insertAdjacentHTML("beforeend", _this.array[json.content.ID].renderEntry(false, true));
|
||||||
|
_this.array[json.content.ID].addEntryEvents();
|
||||||
|
} };
|
||||||
|
|
||||||
|
request.send(JSON.stringify(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
static Load(id = null) {
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
const _this = this;
|
||||||
|
let path = Root.instance.url + this.route + (id !== null ? "/"+id : "");
|
||||||
|
|
||||||
|
if (this.filter.length>0) {
|
||||||
|
path += "?filter="+encodeURI(JSON.stringify(this.getFilter()));
|
||||||
|
}
|
||||||
|
|
||||||
|
request.open("GET", path);
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
if (id!==null) {
|
||||||
|
_this.array[id].init(json.content);
|
||||||
|
_this.Reset();
|
||||||
|
} else {
|
||||||
|
_this.UpdateData(json);
|
||||||
|
for (let g in json.content) {
|
||||||
|
const groupID = json.content[g].ID;
|
||||||
|
_this.groups[groupID] = [];
|
||||||
|
let group = _this.groups[groupID];
|
||||||
|
for (let i in json.content[g].ENTRIES) {
|
||||||
|
_this.array[json.content[g].ENTRIES[i].ID] = _this.New(groupID, json.content[g].ENTRIES[i]);
|
||||||
|
group.push(_this.array[json.content[g].ENTRIES[i].ID]);
|
||||||
|
} } } }
|
||||||
|
_this.Render();
|
||||||
|
}
|
||||||
|
request.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Public functions */
|
||||||
|
|
||||||
|
Save() {
|
||||||
|
const _this = this;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
|
||||||
|
const json = this.collect();
|
||||||
|
json.secToken = Root.instance.secToken;
|
||||||
|
request.open("PATCH", Root.instance.url + this.marker());
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
//_this.UpdateData(json);
|
||||||
|
if (typeof(_this.groups[json.content.GROUP])=="undefined") {
|
||||||
|
_this.groups[json.content.GROUP] = [];
|
||||||
|
const groupHtml = _this.RenderGroup(json.content.GROUP);
|
||||||
|
document.getElementById("content").insertAdjacentHTML("beforeend", groupHtml.begin + groupHtml.end);
|
||||||
|
}
|
||||||
|
if (_this.groupName!=json.content.GROUP) {
|
||||||
|
_this.removeFromGroup();
|
||||||
|
let element = document.getElementById(_this.marker());
|
||||||
|
let groupel = document.getElementById("Group/" + json.content.GROUP);
|
||||||
|
_this.groups[json.content.GROUP].push(_this);
|
||||||
|
groupel.appendChild(element);
|
||||||
|
}
|
||||||
|
_this.init(json.content);
|
||||||
|
_this.Show(false);
|
||||||
|
} };
|
||||||
|
request.send(JSON.stringify(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
Delete() {
|
||||||
|
const _this = this;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
|
||||||
|
request.open("DELETE", Root.instance.url + this.marker());
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
_this.removeFromGroup();
|
||||||
|
delete _this.array[_this.ID];
|
||||||
|
let element = document.getElementById(_this.marker());
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
} };
|
||||||
|
|
||||||
|
request.send(JSON.stringify({secToken: Root.instance.secToken}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Show(edit = false) {
|
||||||
|
this.renderEntry(edit, false);
|
||||||
|
this.addEntryEvents(edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubShow(sub, edit) {
|
||||||
|
document.getElementById(this.marker(sub, "Add")).innerHTML = this.renderSubAdd(sub, edit);
|
||||||
|
this.addSubEvents(edit);
|
||||||
|
}
|
||||||
|
|
||||||
|
SubAdd(sub) {
|
||||||
|
const id = document.getElementById(this.marker(sub, "Add", sub)).value;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
const _this = this;
|
||||||
|
|
||||||
|
request.open("POST", Root.instance.url + this.marker(sub, id));
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
_this.updateSubs(sub, json.content.SUB[sub.toUpperCase()]);
|
||||||
|
} };
|
||||||
|
request.send(JSON.stringify({secToken: Root.instance.secToken}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SubDelete(sub, id) {
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
const _this = this;
|
||||||
|
|
||||||
|
request.open("DELETE", Root.instance.url + this.marker(sub, id));
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + Root.instance.jwt);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
const {ok, json} = Root.instance.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
_this.updateSubs(sub, json.content.SUB[sub.toUpperCase()]);
|
||||||
|
} };
|
||||||
|
request.send(JSON.stringify({secToken: Root.instance.secToken}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Private" functions */
|
||||||
|
|
||||||
|
initSubs(json) {
|
||||||
|
for (const s in this.subs) {
|
||||||
|
this[s] = new Set();
|
||||||
|
for (let i in json.SUB[s.toUpperCase()]) {
|
||||||
|
this[s].add(json.SUB[s.toUpperCase()][i].ID);
|
||||||
|
} } }
|
||||||
|
|
||||||
|
updateSubs(sub, newSubList) {
|
||||||
|
this[sub].clear();
|
||||||
|
for (let i in newSubList) {
|
||||||
|
this[sub].add(newSubList[i].ID);
|
||||||
|
}
|
||||||
|
this.renderSubs(sub);
|
||||||
|
this.addSubEvents(sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
// first = action or subroute, second = subID, third = subAction
|
||||||
|
marker(firstAppend = null, secondAppend = null, thirdAppend = null) {
|
||||||
|
let id = this.route + "/" + this.ID;
|
||||||
|
if (firstAppend!==null) {
|
||||||
|
id+= "/" + firstAppend;
|
||||||
|
if (secondAppend!==null) {
|
||||||
|
id+= "/" + secondAppend;
|
||||||
|
if (thirdAppend!==null) {
|
||||||
|
id+= "/" + thirdAppend;
|
||||||
|
} } }
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderContainer() {
|
||||||
|
let html = "<div id='" + this.marker() + "' class='list_entry'>"
|
||||||
|
+ "<div id='" + this.marker("Main") + "' class='list_entry_title'></div>";
|
||||||
|
if (Object.keys(this.subs).length>0) {
|
||||||
|
html+= "<div class='list_entry_row'>";
|
||||||
|
for (let s in this.subs) {
|
||||||
|
html+= "<ul id='" + this.marker(s) + "'></ul>";
|
||||||
|
}
|
||||||
|
html+= "</div>";
|
||||||
|
}
|
||||||
|
html+= "</div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderField(icon, value) {
|
||||||
|
return "<li><img src='/res/dark/" + icon + "' />" + value + "</li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderInput(name, value, width = 8, maxLength = null) {
|
||||||
|
return "<input id='" + this.marker(name) + "' value='" + value + "' "
|
||||||
|
+ (maxLength!==null ? " maxlength=" + maxLength : "")
|
||||||
|
+ " style='width:"+ width + "em;'/>";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderButton(icon, name, title) {
|
||||||
|
return "<li id='" + this.marker(name) + "' class='button' title='" + title + "'><img src='/res/dark/" + icon + "' /></li>"
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSelect(name, selected, width = 8) {
|
||||||
|
let html = "<select id='" + this.marker(name) + "' style='width: " + width + "em;'>";
|
||||||
|
for (const e in this.selects[name]) {
|
||||||
|
const entry = this.selects[name][e];
|
||||||
|
html+= "<option value='" + entry.ID + "'" + (entry.ID==selected ? " selected" : "") + ">" + (typeof(entry.KÜRZEL)!="undefined" ? entry.KÜRZEL : entry.NAME) + "</option>";
|
||||||
|
}
|
||||||
|
return html+"</select>";
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSubs(onlyOneSub = null) {
|
||||||
|
// TODO: SUBS
|
||||||
|
let subs = {};
|
||||||
|
if (onlyOneSub===null) {
|
||||||
|
subs = this.subs;
|
||||||
|
} else {
|
||||||
|
subs[onlyOneSub] = this.subs[onlyOneSub];
|
||||||
|
}
|
||||||
|
for (let s in subs) {
|
||||||
|
let html = "";
|
||||||
|
if (typeof(this.subs[s])!="undefined") {
|
||||||
|
for (let e in this.subs[s].entries) {
|
||||||
|
const entry = this.subs[s].entries[e];
|
||||||
|
if (this[s].has(entry.ID)) {
|
||||||
|
html+= "<li class='" + this.subs[s].color + "' title='" + entry.NAME + "'><img src='/res/dark/" + this.subs[s].icon + "' />"
|
||||||
|
+ (typeof(entry.KÜRZEL)!="undefined" ? entry.KÜRZEL : entry.NAME)
|
||||||
|
+ (this.admin ? " <img id='" + this.marker(s, entry.ID, "Delete") + "' src='/res/dark/delete.png' class='button' />" : "")
|
||||||
|
+ "</li>";
|
||||||
|
} }
|
||||||
|
if (this.admin) {
|
||||||
|
html+= "<li class='" + this.subs[s].color + "' id='" + this.marker(s, "Add") + "'>" + this.renderSubAdd(s, false) + "</li>";
|
||||||
|
}
|
||||||
|
document.getElementById(this.marker(s)).innerHTML = html;
|
||||||
|
} } }
|
||||||
|
|
||||||
|
renderSubAdd(sub, add) {
|
||||||
|
let html = "<img src='/res/dark/" + this.subs[sub].icon + "' />";
|
||||||
|
if (add) {
|
||||||
|
html+= "<select id='" + this.marker(sub, "Add", sub) + "'>";
|
||||||
|
for (let i in this.subs[sub].entries) {
|
||||||
|
const entry = this.subs[sub].entries[i];
|
||||||
|
if (!this[sub].has(entry.ID)) {
|
||||||
|
html+= "<option value='" + entry.ID + "'>" + entry.NAME + "</option>";
|
||||||
|
} }
|
||||||
|
html+= "</select>";
|
||||||
|
html+= "<img class='button' id='" + this.marker(sub, "Add/Add") + "' src='/res/dark/save.png' />";
|
||||||
|
html+= "<img class='button' id='" + this.marker(sub, "Add/Reset") + "' src='/res/dark/cancel.png' />";
|
||||||
|
} else {
|
||||||
|
html+= "<img class='button' id='" + this.marker(sub, "Add/Show") + "' src='/res/dark/add.png' />";
|
||||||
|
}
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
addSubEvents() {
|
||||||
|
const _this = this;
|
||||||
|
for (let sub in this.subs) {
|
||||||
|
for (const id of this[sub]) {
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker(sub, id, "Delete"), function (event) {_this.SubDelete(sub, id);});
|
||||||
|
}
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker(sub, "Add/Show"), function (event) {_this.SubShow(sub, true);});
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker(sub, "Add/Add"), function (event) {_this.SubAdd(sub);});
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker(sub, "Add/Reset"), function (event) {_this.SubShow(sub, false);});
|
||||||
|
} }
|
||||||
|
|
||||||
|
removeFromGroup() {
|
||||||
|
for (let i in this.groups[this.group]) {
|
||||||
|
if (this.groups[this.group][i].ID==this.ID) {
|
||||||
|
delete _this.groups[this.group][i];
|
||||||
|
break;
|
||||||
|
} } }
|
||||||
|
|
||||||
|
addEntryEvents(edit) {
|
||||||
|
const _this = this;
|
||||||
|
if (edit) {
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker("Save"), function (event) {_this.Save();});
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker("Delete"), function (event) {_this.Delete();});
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker("Reset"), function (event) {_this.Show(false);});
|
||||||
|
} else {
|
||||||
|
Root.AddEventListenerIfButtonExists(this.marker("Edit"), function (event) {_this.Show(true);});
|
||||||
|
} }
|
||||||
|
}
|
||||||
119
client/pages/fahrzeuge.js
Normal file
119
client/pages/fahrzeuge.js
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
class Fahrzeuge extends Page
|
||||||
|
{
|
||||||
|
static route = "Fahrzeuge";
|
||||||
|
static array = {};
|
||||||
|
static groups = {};
|
||||||
|
static subs = {
|
||||||
|
"Eingewiesene": {color: "chocolate", icon: "person.png", entries: []}
|
||||||
|
};
|
||||||
|
static selects = {};
|
||||||
|
static admin = false; // global page admin rights
|
||||||
|
static prints = [];
|
||||||
|
static filter = [];
|
||||||
|
//static selected = [];
|
||||||
|
//static dropzones = {};
|
||||||
|
static visible = false;
|
||||||
|
|
||||||
|
constructor(groupName, json) {
|
||||||
|
super(
|
||||||
|
groupName,
|
||||||
|
json,
|
||||||
|
Fahrzeuge.route,
|
||||||
|
Fahrzeuge.array,
|
||||||
|
Fahrzeuge.groups,
|
||||||
|
Fahrzeuge.subs,
|
||||||
|
Fahrzeuge.selects,
|
||||||
|
Fahrzeuge.admin/*,
|
||||||
|
Fahrzeuge.prints,
|
||||||
|
Fahrzeuge.filter,
|
||||||
|
Fahrzeuge.selected,
|
||||||
|
Fahrzeuge.dropzones*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(json) {
|
||||||
|
this.Kürzel = json.MAIN.KÜRZEL;
|
||||||
|
this.Name = json.MAIN.NAME;
|
||||||
|
this.Bild = json.MAIN.BILD;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEntry(drawEdit) {
|
||||||
|
let html = "<div class='bild'>";
|
||||||
|
if (this.Bild.EXISTIERT) {
|
||||||
|
html+= "<img src='/pic.php/Fahrzeuge/Bild/" + this.Bild.ADRESSE + "?h=260' />";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<div id='" + this.marker("Bild/Delete") + "' class='deleter'><img src='/res/dark/delete.png' /></div>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html+= "<img src='/upl/Fahrzeuge/Bild/Nopic.svg' />";
|
||||||
|
}
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<form action='/" + this.marker("Bilder") + "' class='dropzone' id='dropzone_" + this.ID + "'></form>";
|
||||||
|
}
|
||||||
|
html += "</div>";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<ul>"
|
||||||
|
+ "<li><img src='/res/dark/truck.png' /><input id='" + this.marker("Name") + "' value='" + this.Name + "' style='width: 8cm;'></li>"
|
||||||
|
+ "<li><img src='/res/dark/truck.png' /><input id='" + this.marker("Kürzel") + "' value='" + this.Kürzel + "' style='width: 4cm;'></li>"
|
||||||
|
+ "<li id='" + this.marker("Save") + "' class='button'><img src='/res/dark/save.png' /></li>"
|
||||||
|
+ "<li id='" + this.marker("Delete") + "' class='button'><img src='/res/dark/delete.png' /></li>"
|
||||||
|
+ "<li id='" + this.marker("Reset") + "' class='button'><img src='/res/dark/cancel.png' /></li>"
|
||||||
|
+ "</ul>";
|
||||||
|
} else {
|
||||||
|
html+= "<ul><li><img src='/res/dark/truck.png' />" + this.Name + "</li>"
|
||||||
|
"<li><img src='/res/dark/truck.png' />" + this.Kürzel + "</li>";
|
||||||
|
if (this.admin) {
|
||||||
|
html += "<li id='" + this.marker("Edit") + "' class='button'><img src='/res/dark/edit.png' title='Bearbeiten' /></li>";
|
||||||
|
}
|
||||||
|
html+= "</ul>";
|
||||||
|
}
|
||||||
|
document.getElementById(this.marker("Main")).innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderAdd() {
|
||||||
|
let html = "<h1>Fahrzeug hinzufügen</h1>"
|
||||||
|
+ "<div class='toggleVisibility'><ul><li>Name: <input id='Fahrzeuge/Add/Name' style='width: 8em;' /></li>"
|
||||||
|
+ "<li>Kürzel: <input id='Fahrzeuge/Add/Kürzel' style='width: 4em;' /></li>"
|
||||||
|
+ "<li id='Fahrzeuge/Add/Add' class='button'>Hinzufügen <img src='/res/dark/add.png' /></li></ul></div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroup() {
|
||||||
|
return Fahrzeuge.RenderGroup(this.groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderGroup(groupName) {
|
||||||
|
return {
|
||||||
|
begin: "<article id='Group/" + groupName + "'><div class='group_title'><h1>" + groupName + "</h1></div>",
|
||||||
|
end: "</article>"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static New(groupName, json) {
|
||||||
|
return new Fahrzeuge(groupName, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect() {
|
||||||
|
return Fahrzeuge.Collect(this.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collect(id, contextId = null) {
|
||||||
|
return {
|
||||||
|
ID: id,
|
||||||
|
KÜRZEL: this.Value(id, "Kürzel"),
|
||||||
|
NAME: this.Value(id, "Name")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderSelect(selectId, fahrzeugId, nullable = true) {
|
||||||
|
let html = "<select id='" + selectId + "'>" + (nullable ? "<option value='null'></option>" : "");
|
||||||
|
for (let group in Fahrzeuge.groups) {
|
||||||
|
for (let e in Fahrzeuge.groups[group]) {
|
||||||
|
const entry = Fahrzeuge.groups[group][e];
|
||||||
|
html+= "<option value='" + entry.ID + "'" + (entry.ID==fahrzeugId ? " selected" : "") + ">" + entry.Kürzel + "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</select>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
160
client/pages/personal.js
Normal file
160
client/pages/personal.js
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
class PersonalPage extends Page
|
||||||
|
{
|
||||||
|
static routes = {
|
||||||
|
"Personal": {needRead: true, needWrite: false}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static route = "Personal";
|
||||||
|
static array = {};
|
||||||
|
static groups = {};
|
||||||
|
static subs = {
|
||||||
|
"Abteilungen": {color: "purple", icon: "department.png", entries: []},
|
||||||
|
"Gruppen": {color: "olive", icon: "group.png", entries: []},
|
||||||
|
"Lehrgänge": {color: "teal", icon: "skill.png", entries: []},
|
||||||
|
"Einweisungen": {color: "chocolate", icon: "person.png", entries: []}
|
||||||
|
};
|
||||||
|
static selects = {
|
||||||
|
"Kategorie" : [],
|
||||||
|
"Funktion" : []
|
||||||
|
};
|
||||||
|
static admin = true; // global page admin rights
|
||||||
|
static prints = [];
|
||||||
|
static filter = [];
|
||||||
|
//static selected = [];
|
||||||
|
//static dropzones = {};
|
||||||
|
static visible = false;
|
||||||
|
|
||||||
|
constructor(groupName, json) {
|
||||||
|
super(
|
||||||
|
groupName,
|
||||||
|
json,
|
||||||
|
Personal.route,
|
||||||
|
Personal.array,
|
||||||
|
Personal.groups,
|
||||||
|
Personal.subs,
|
||||||
|
Personal.selects,
|
||||||
|
Personal.admin/*,
|
||||||
|
Personal.prints,
|
||||||
|
Personal.filter,
|
||||||
|
Personal.selected,
|
||||||
|
Personal.dropzones*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(json) {
|
||||||
|
this.OFNr = json.MAIN.OFNR;
|
||||||
|
this.PNr = json.MAIN.PNR;
|
||||||
|
this.Nr = json.MAIN.NR;
|
||||||
|
this.Login = json.MAIN.LOGIN;
|
||||||
|
this.Email = json.MAIN.EMAIL;
|
||||||
|
this.Vornamen = json.MAIN.VORNAMEN;
|
||||||
|
this.Nachnamen = json.MAIN.NACHNAMEN;
|
||||||
|
this.Name = json.MAIN.NAME;
|
||||||
|
this.Bild = json.MAIN.BILD;
|
||||||
|
this.Kategorie = json.MAIN.KATEGORIE;
|
||||||
|
this.Funktion = json.MAIN.FUNKTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEntry(drawEdit) {
|
||||||
|
let html = "<div class='bild'>";
|
||||||
|
if (this.Bild.EXISTIERT) {
|
||||||
|
html+= "<img src='/pic.php/Personal/Bildadresse/" + this.Bild.ADRESSE + "?h=260' />";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<div id='" + this.marker("Bild/Delete") + "' class='deleter'><img src='/res/dark/delete.png' /></div>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html+= "<img src='/upl/Personal/Bildadresse/Nopic.svg' />";
|
||||||
|
}
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<form action='/" + this.marker("Bilder") + "' class='dropzone' id='dropzone_" + this.ID + "'></form>";
|
||||||
|
}
|
||||||
|
html += "</div><ul>";
|
||||||
|
|
||||||
|
// TODO: Draw selectbox
|
||||||
|
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= this.renderField("number.png", this.renderInput("OFNr", this.OFNr, 1.5, 2) + "-" + this.renderInput("PNr", this.PNr, 2, 3))
|
||||||
|
+ this.renderField("person.png", this.renderInput("Nachnamen", this.Nachnamen) + ", " + this.renderInput("Vornamen", this.Vornamen))
|
||||||
|
+ this.renderField("type.png", this.renderSelect("Kategorie", this.Kategorie.ID))
|
||||||
|
+ this.renderField("note.png", this.renderInput("Login", this.Login) + "@feuerwehr-bs.net")
|
||||||
|
+ this.renderField("star.png", this.renderSelect("Funktion", this.Funktion.ID, 2.5))
|
||||||
|
+ this.renderButton("save.png", "Save", "Speichern")
|
||||||
|
+ this.renderButton("delete.png", "Delete", "Löschen")
|
||||||
|
+ this.renderButton("cancel.png", "Reset", "Zurücksetzen");
|
||||||
|
} else {
|
||||||
|
html+= this.renderField("number.png", this.Nr)
|
||||||
|
+ this.renderField("person.png", "<b>" + this.Nachnamen + ", " + this.Vornamen + "</b>")
|
||||||
|
+ (this.Login=="" || this.Login===null ? this.renderField("note.png", "<span class='red'>Kein Login!</span>") : "")
|
||||||
|
+ this.renderField("star.png", this.Funktion!==null ? this.Funktion.KÜRZEL : "-");
|
||||||
|
if (this.admin) {
|
||||||
|
html += this.renderButton("edit.png", "Edit", "Bearbeiten");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</ul>";
|
||||||
|
document.getElementById(this.marker("Main")).innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderAdd() {
|
||||||
|
let html = "<h1>Fahrzeug hinzufügen</h1>"
|
||||||
|
+ "<div class='toggleVisibility'><ul><li>Name: <input id='Personal/Add/Name' style='width: 8em;' /></li>"
|
||||||
|
+ "<li>Kürzel: <input id='Personal/Add/Kürzel' style='width: 4em;' /></li>"
|
||||||
|
+ "<li id='Personal/Add/Add' class='button'>Hinzufügen <img src='/res/dark/add.png' /></li></ul></div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroup() {
|
||||||
|
return Personal.RenderGroup(this.groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderGroup(groupName) {
|
||||||
|
return {
|
||||||
|
begin: "<article id='Group/" + groupName + "'><div class='group_title'><h1>" + groupName + "</h1></div>",
|
||||||
|
end: "</article>"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static New(groupName, json) {
|
||||||
|
return new Personal(groupName, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect() {
|
||||||
|
return Personal.Collect(this.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collect(id, contextId = null) {
|
||||||
|
return {
|
||||||
|
ID: id,
|
||||||
|
OFNR: this.Value(id, "OFNr"),
|
||||||
|
PNR: this.Value(id, "PNr"),
|
||||||
|
NACHNAMEN: this.Value(id, "Nachnamen"),
|
||||||
|
VORNAMEN: this.Value(id, "Vornamen"),
|
||||||
|
KATEGORIE: this.Value(id, "Kategorie"),
|
||||||
|
LOGIN: this.Value(id, "Login"),
|
||||||
|
FUNKTION: this.Value(id, "Funktion")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderSelect(selectId, fahrzeugId, nullable = true) {
|
||||||
|
let html = "<select id='" + selectId + "'>" + (nullable ? "<option value='null'></option>" : "");
|
||||||
|
for (let group in Personal.groups) {
|
||||||
|
for (let e in Personal.groups[group]) {
|
||||||
|
const entry = Personal.groups[group][e];
|
||||||
|
html+= "<option value='" + entry.ID + "'" + (entry.ID==fahrzeugId ? " selected" : "") + ">" + entry.Kürzel + "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</select>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
98
client/qr-scanner-worker.min.js
vendored
Normal file
98
client/qr-scanner-worker.min.js
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
export const createWorker=()=>new Worker(URL.createObjectURL(new Blob([`class x{constructor(a,b){this.width=b;this.height=a.length/b;this.data=a}static createEmpty(a,b){return new x(new Uint8ClampedArray(a*b),a)}get(a,b){return 0>a||a>=this.width||0>b||b>=this.height?!1:!!this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c?1:0}setRegion(a,b,c,d,e){for(let f=b;f<b+d;f++)for(let g=a;g<a+c;g++)this.set(g,f,!!e)}}
|
||||||
|
class A{constructor(a,b,c){this.width=a;a*=b;if(c&&c.length!==a)throw Error("Wrong buffer size");this.data=c||new Uint8ClampedArray(a)}get(a,b){return this.data[b*this.width+a]}set(a,b,c){this.data[b*this.width+a]=c}}
|
||||||
|
class ba{constructor(a){this.bitOffset=this.byteOffset=0;this.bytes=a}readBits(a){if(1>a||32<a||a>this.available())throw Error("Cannot read "+a.toString()+" bits");var b=0;if(0<this.bitOffset){b=8-this.bitOffset;var c=a<b?a:b;b-=c;b=(this.bytes[this.byteOffset]&255>>8-c<<b)>>b;a-=c;this.bitOffset+=c;8===this.bitOffset&&(this.bitOffset=0,this.byteOffset++)}if(0<a){for(;8<=a;)b=b<<8|this.bytes[this.byteOffset]&255,this.byteOffset++,a-=8;0<a&&(c=8-a,b=b<<a|(this.bytes[this.byteOffset]&255>>c<<c)>>c,
|
||||||
|
this.bitOffset+=a)}return b}available(){return 8*(this.bytes.length-this.byteOffset)-this.bitOffset}}var B,C=B||(B={});C.Numeric="numeric";C.Alphanumeric="alphanumeric";C.Byte="byte";C.Kanji="kanji";C.ECI="eci";C.StructuredAppend="structuredappend";var D,E=D||(D={});E[E.Terminator=0]="Terminator";E[E.Numeric=1]="Numeric";E[E.Alphanumeric=2]="Alphanumeric";E[E.Byte=4]="Byte";E[E.Kanji=8]="Kanji";E[E.ECI=7]="ECI";E[E.StructuredAppend=3]="StructuredAppend";let F="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:".split("");
|
||||||
|
function ca(a,b){let c=[],d="";b=a.readBits([8,16,16][b]);for(let e=0;e<b;e++){let f=a.readBits(8);c.push(f)}try{d+=decodeURIComponent(c.map(e=>\`%\${("0"+e.toString(16)).substr(-2)}\`).join(""))}catch(e){}return{bytes:c,text:d}}
|
||||||
|
function da(a,b){a=new ba(a);let c=9>=b?0:26>=b?1:2;for(b={text:"",bytes:[],chunks:[],version:b};4<=a.available();){var d=a.readBits(4);if(d===D.Terminator)return b;if(d===D.ECI)0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(7)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(14)}):0===a.readBits(1)?b.chunks.push({type:B.ECI,assignmentNumber:a.readBits(21)}):b.chunks.push({type:B.ECI,assignmentNumber:-1});else if(d===D.Numeric){var e=a,f=[];d="";for(var g=
|
||||||
|
e.readBits([10,12,14][c]);3<=g;){var h=e.readBits(10);if(1E3<=h)throw Error("Invalid numeric value above 999");var k=Math.floor(h/100),m=Math.floor(h/10)%10;h%=10;f.push(48+k,48+m,48+h);d+=k.toString()+m.toString()+h.toString();g-=3}if(2===g){g=e.readBits(7);if(100<=g)throw Error("Invalid numeric value above 99");e=Math.floor(g/10);g%=10;f.push(48+e,48+g);d+=e.toString()+g.toString()}else if(1===g){e=e.readBits(4);if(10<=e)throw Error("Invalid numeric value above 9");f.push(48+e);d+=e.toString()}b.text+=
|
||||||
|
d;b.bytes.push(...f);b.chunks.push({type:B.Numeric,text:d})}else if(d===D.Alphanumeric){e=a;f=[];d="";for(g=e.readBits([9,11,13][c]);2<=g;)m=e.readBits(11),k=Math.floor(m/45),m%=45,f.push(F[k].charCodeAt(0),F[m].charCodeAt(0)),d+=F[k]+F[m],g-=2;1===g&&(e=e.readBits(6),f.push(F[e].charCodeAt(0)),d+=F[e]);b.text+=d;b.bytes.push(...f);b.chunks.push({type:B.Alphanumeric,text:d})}else if(d===D.Byte)d=ca(a,c),b.text+=d.text,b.bytes.push(...d.bytes),b.chunks.push({type:B.Byte,bytes:d.bytes,text:d.text});
|
||||||
|
else if(d===D.Kanji){f=a;d=[];e=f.readBits([8,10,12][c]);for(g=0;g<e;g++)k=f.readBits(13),k=Math.floor(k/192)<<8|k%192,k=7936>k?k+33088:k+49472,d.push(k>>8,k&255);f=(new TextDecoder("shift-jis")).decode(Uint8Array.from(d));b.text+=f;b.bytes.push(...d);b.chunks.push({type:B.Kanji,bytes:d,text:f})}else d===D.StructuredAppend&&b.chunks.push({type:B.StructuredAppend,currentSequence:a.readBits(4),totalSequence:a.readBits(4),parity:a.readBits(8)})}if(0===a.available()||0===a.readBits(a.available()))return b}
|
||||||
|
class G{constructor(a,b){if(0===b.length)throw Error("No coefficients.");this.field=a;let c=b.length;if(1<c&&0===b[0]){let d=1;for(;d<c&&0===b[d];)d++;if(d===c)this.coefficients=a.zero.coefficients;else for(this.coefficients=new Uint8ClampedArray(c-d),a=0;a<this.coefficients.length;a++)this.coefficients[a]=b[d+a]}else this.coefficients=b}degree(){return this.coefficients.length-1}isZero(){return 0===this.coefficients[0]}getCoefficient(a){return this.coefficients[this.coefficients.length-1-a]}addOrSubtract(a){if(this.isZero())return a;
|
||||||
|
if(a.isZero())return this;let b=this.coefficients;a=a.coefficients;b.length>a.length&&([b,a]=[a,b]);let c=new Uint8ClampedArray(a.length),d=a.length-b.length;for(var e=0;e<d;e++)c[e]=a[e];for(e=d;e<a.length;e++)c[e]=b[e-d]^a[e];return new G(this.field,c)}multiply(a){if(0===a)return this.field.zero;if(1===a)return this;let b=this.coefficients.length,c=new Uint8ClampedArray(b);for(let d=0;d<b;d++)c[d]=this.field.multiply(this.coefficients[d],a);return new G(this.field,c)}multiplyPoly(a){if(this.isZero()||
|
||||||
|
a.isZero())return this.field.zero;let b=this.coefficients,c=b.length;a=a.coefficients;let d=a.length,e=new Uint8ClampedArray(c+d-1);for(let f=0;f<c;f++){let g=b[f];for(let h=0;h<d;h++)e[f+h]=H(e[f+h],this.field.multiply(g,a[h]))}return new G(this.field,e)}multiplyByMonomial(a,b){if(0>a)throw Error("Invalid degree less than 0");if(0===b)return this.field.zero;let c=this.coefficients.length;a=new Uint8ClampedArray(c+a);for(let d=0;d<c;d++)a[d]=this.field.multiply(this.coefficients[d],b);return new G(this.field,
|
||||||
|
a)}evaluateAt(a){let b=0;if(0===a)return this.getCoefficient(0);let c=this.coefficients.length;if(1===a)return this.coefficients.forEach(d=>{b^=d}),b;b=this.coefficients[0];for(let d=1;d<c;d++)b=H(this.field.multiply(a,b),this.coefficients[d]);return b}}function H(a,b){return a^b}
|
||||||
|
class ea{constructor(a,b,c){this.primitive=a;this.size=b;this.generatorBase=c;this.expTable=Array(this.size);this.logTable=Array(this.size);a=1;for(b=0;b<this.size;b++)this.expTable[b]=a,a*=2,a>=this.size&&(a=(a^this.primitive)&this.size-1);for(a=0;a<this.size-1;a++)this.logTable[this.expTable[a]]=a;this.zero=new G(this,Uint8ClampedArray.from([0]));this.one=new G(this,Uint8ClampedArray.from([1]))}multiply(a,b){return 0===a||0===b?0:this.expTable[(this.logTable[a]+this.logTable[b])%(this.size-1)]}inverse(a){if(0===
|
||||||
|
a)throw Error("Can't invert 0");return this.expTable[this.size-this.logTable[a]-1]}buildMonomial(a,b){if(0>a)throw Error("Invalid monomial degree less than 0");if(0===b)return this.zero;a=new Uint8ClampedArray(a+1);a[0]=b;return new G(this,a)}log(a){if(0===a)throw Error("Can't take log(0)");return this.logTable[a]}exp(a){return this.expTable[a]}}
|
||||||
|
function fa(a,b,c,d){b.degree()<c.degree()&&([b,c]=[c,b]);let e=a.zero;for(var f=a.one;c.degree()>=d/2;){var g=b;let h=e;b=c;e=f;if(b.isZero())return null;c=g;f=a.zero;g=b.getCoefficient(b.degree());for(g=a.inverse(g);c.degree()>=b.degree()&&!c.isZero();){let k=c.degree()-b.degree(),m=a.multiply(c.getCoefficient(c.degree()),g);f=f.addOrSubtract(a.buildMonomial(k,m));c=c.addOrSubtract(b.multiplyByMonomial(k,m))}f=f.multiplyPoly(e).addOrSubtract(h);if(c.degree()>=b.degree())return null}d=f.getCoefficient(0);
|
||||||
|
if(0===d)return null;a=a.inverse(d);return[f.multiply(a),c.multiply(a)]}
|
||||||
|
function ha(a,b){let c=new Uint8ClampedArray(a.length);c.set(a);a=new ea(285,256,0);var d=new G(a,c),e=new Uint8ClampedArray(b),f=!1;for(var g=0;g<b;g++){var h=d.evaluateAt(a.exp(g+a.generatorBase));e[e.length-1-g]=h;0!==h&&(f=!0)}if(!f)return c;d=new G(a,e);d=fa(a,a.buildMonomial(b,1),d,b);if(null===d)return null;b=d[0];g=b.degree();if(1===g)b=[b.getCoefficient(1)];else{e=Array(g);f=0;for(h=1;h<a.size&&f<g;h++)0===b.evaluateAt(h)&&(e[f]=a.inverse(h),f++);b=f!==g?null:e}if(null==b)return null;e=d[1];
|
||||||
|
f=b.length;d=Array(f);for(g=0;g<f;g++){h=a.inverse(b[g]);let k=1;for(let m=0;m<f;m++)g!==m&&(k=a.multiply(k,H(1,a.multiply(b[m],h))));d[g]=a.multiply(e.evaluateAt(h),a.inverse(k));0!==a.generatorBase&&(d[g]=a.multiply(d[g],h))}for(e=0;e<b.length;e++){f=c.length-1-a.log(b[e]);if(0>f)return null;c[f]^=d[e]}return c}
|
||||||
|
let I=[{infoBits:null,versionNumber:1,alignmentPatternCenters:[],errorCorrectionLevels:[{ecCodewordsPerBlock:7,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:13,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:13}]},{ecCodewordsPerBlock:17,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:9}]}]},{infoBits:null,versionNumber:2,alignmentPatternCenters:[6,18],errorCorrectionLevels:[{ecCodewordsPerBlock:10,ecBlocks:[{numBlocks:1,
|
||||||
|
dataCodewordsPerBlock:34}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:28}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:16}]}]},{infoBits:null,versionNumber:3,alignmentPatternCenters:[6,22],errorCorrectionLevels:[{ecCodewordsPerBlock:15,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:55}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:18,
|
||||||
|
ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:13}]}]},{infoBits:null,versionNumber:4,alignmentPatternCenters:[6,26],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:80}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:32}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:9}]}]},
|
||||||
|
{infoBits:null,versionNumber:5,alignmentPatternCenters:[6,30],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:43}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:11},{numBlocks:2,dataCodewordsPerBlock:12}]}]},{infoBits:null,versionNumber:6,alignmentPatternCenters:[6,
|
||||||
|
34],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68}]},{ecCodewordsPerBlock:16,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:27}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:31892,versionNumber:7,alignmentPatternCenters:[6,22,38],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:78}]},{ecCodewordsPerBlock:18,
|
||||||
|
ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:31}]},{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:13},{numBlocks:1,dataCodewordsPerBlock:14}]}]},{infoBits:34236,versionNumber:8,alignmentPatternCenters:[6,24,42],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:97}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:38},
|
||||||
|
{numBlocks:2,dataCodewordsPerBlock:39}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:18},{numBlocks:2,dataCodewordsPerBlock:19}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:14},{numBlocks:2,dataCodewordsPerBlock:15}]}]},{infoBits:39577,versionNumber:9,alignmentPatternCenters:[6,26,46],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:36},
|
||||||
|
{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:12},{numBlocks:4,dataCodewordsPerBlock:13}]}]},{infoBits:42195,versionNumber:10,alignmentPatternCenters:[6,28,50],errorCorrectionLevels:[{ecCodewordsPerBlock:18,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:68},{numBlocks:2,dataCodewordsPerBlock:69}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,
|
||||||
|
dataCodewordsPerBlock:43},{numBlocks:1,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:15},{numBlocks:2,dataCodewordsPerBlock:16}]}]},{infoBits:48118,versionNumber:11,alignmentPatternCenters:[6,30,54],errorCorrectionLevels:[{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:81}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,
|
||||||
|
dataCodewordsPerBlock:50},{numBlocks:4,dataCodewordsPerBlock:51}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:22},{numBlocks:4,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:12},{numBlocks:8,dataCodewordsPerBlock:13}]}]},{infoBits:51042,versionNumber:12,alignmentPatternCenters:[6,32,58],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:92},{numBlocks:2,dataCodewordsPerBlock:93}]},
|
||||||
|
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:36},{numBlocks:2,dataCodewordsPerBlock:37}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:20},{numBlocks:6,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:14},{numBlocks:4,dataCodewordsPerBlock:15}]}]},{infoBits:55367,versionNumber:13,alignmentPatternCenters:[6,34,62],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:107}]},
|
||||||
|
{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:37},{numBlocks:1,dataCodewordsPerBlock:38}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:20},{numBlocks:4,dataCodewordsPerBlock:21}]},{ecCodewordsPerBlock:22,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:11},{numBlocks:4,dataCodewordsPerBlock:12}]}]},{infoBits:58893,versionNumber:14,alignmentPatternCenters:[6,26,46,66],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:115},
|
||||||
|
{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:40},{numBlocks:5,dataCodewordsPerBlock:41}]},{ecCodewordsPerBlock:20,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:16},{numBlocks:5,dataCodewordsPerBlock:17}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:5,dataCodewordsPerBlock:13}]}]},{infoBits:63784,versionNumber:15,alignmentPatternCenters:[6,26,48,70],errorCorrectionLevels:[{ecCodewordsPerBlock:22,
|
||||||
|
ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:87},{numBlocks:1,dataCodewordsPerBlock:88}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:41},{numBlocks:5,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:12},{numBlocks:7,dataCodewordsPerBlock:13}]}]},{infoBits:68472,versionNumber:16,alignmentPatternCenters:[6,26,50,
|
||||||
|
74],errorCorrectionLevels:[{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:98},{numBlocks:1,dataCodewordsPerBlock:99}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:19},{numBlocks:2,dataCodewordsPerBlock:20}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:70749,
|
||||||
|
versionNumber:17,alignmentPatternCenters:[6,30,54,78],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:1,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:22},{numBlocks:15,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:14},{numBlocks:17,
|
||||||
|
dataCodewordsPerBlock:15}]}]},{infoBits:76311,versionNumber:18,alignmentPatternCenters:[6,30,56,82],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:120},{numBlocks:1,dataCodewordsPerBlock:121}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:43},{numBlocks:4,dataCodewordsPerBlock:44}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},{numBlocks:1,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,
|
||||||
|
dataCodewordsPerBlock:14},{numBlocks:19,dataCodewordsPerBlock:15}]}]},{infoBits:79154,versionNumber:19,alignmentPatternCenters:[6,30,58,86],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:113},{numBlocks:4,dataCodewordsPerBlock:114}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:44},{numBlocks:11,dataCodewordsPerBlock:45}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:21},{numBlocks:4,dataCodewordsPerBlock:22}]},
|
||||||
|
{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:9,dataCodewordsPerBlock:13},{numBlocks:16,dataCodewordsPerBlock:14}]}]},{infoBits:84390,versionNumber:20,alignmentPatternCenters:[6,34,62,90],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:107},{numBlocks:5,dataCodewordsPerBlock:108}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:41},{numBlocks:13,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},
|
||||||
|
{numBlocks:5,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:15},{numBlocks:10,dataCodewordsPerBlock:16}]}]},{infoBits:87683,versionNumber:21,alignmentPatternCenters:[6,28,50,72,94],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:116},{numBlocks:4,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:42}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:22},
|
||||||
|
{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:16},{numBlocks:6,dataCodewordsPerBlock:17}]}]},{infoBits:92361,versionNumber:22,alignmentPatternCenters:[6,26,50,74,98],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:111},{numBlocks:7,dataCodewordsPerBlock:112}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},
|
||||||
|
{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:24,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:13}]}]},{infoBits:96236,versionNumber:23,alignmentPatternCenters:[6,30,54,74,102],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:121},{numBlocks:5,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:47},{numBlocks:14,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},
|
||||||
|
{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:16,dataCodewordsPerBlock:15},{numBlocks:14,dataCodewordsPerBlock:16}]}]},{infoBits:102084,versionNumber:24,alignmentPatternCenters:[6,28,54,80,106],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:45},{numBlocks:14,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,
|
||||||
|
ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:24},{numBlocks:16,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:30,dataCodewordsPerBlock:16},{numBlocks:2,dataCodewordsPerBlock:17}]}]},{infoBits:102881,versionNumber:25,alignmentPatternCenters:[6,32,58,84,110],errorCorrectionLevels:[{ecCodewordsPerBlock:26,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:106},{numBlocks:4,dataCodewordsPerBlock:107}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:47},{numBlocks:13,
|
||||||
|
dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:13,dataCodewordsPerBlock:16}]}]},{infoBits:110507,versionNumber:26,alignmentPatternCenters:[6,30,58,86,114],errorCorrectionLevels:[{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:114},{numBlocks:2,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,
|
||||||
|
dataCodewordsPerBlock:46},{numBlocks:4,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:28,dataCodewordsPerBlock:22},{numBlocks:6,dataCodewordsPerBlock:23}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:33,dataCodewordsPerBlock:16},{numBlocks:4,dataCodewordsPerBlock:17}]}]},{infoBits:110734,versionNumber:27,alignmentPatternCenters:[6,34,62,90,118],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
|
||||||
|
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:45},{numBlocks:3,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:8,dataCodewordsPerBlock:23},{numBlocks:26,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:15},{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:117786,versionNumber:28,alignmentPatternCenters:[6,26,50,74,98,122],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:117},
|
||||||
|
{numBlocks:10,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:3,dataCodewordsPerBlock:45},{numBlocks:23,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,dataCodewordsPerBlock:24},{numBlocks:31,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:31,dataCodewordsPerBlock:16}]}]},{infoBits:119615,versionNumber:29,alignmentPatternCenters:[6,30,54,78,102,126],errorCorrectionLevels:[{ecCodewordsPerBlock:30,
|
||||||
|
ecBlocks:[{numBlocks:7,dataCodewordsPerBlock:116},{numBlocks:7,dataCodewordsPerBlock:117}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:21,dataCodewordsPerBlock:45},{numBlocks:7,dataCodewordsPerBlock:46}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:1,dataCodewordsPerBlock:23},{numBlocks:37,dataCodewordsPerBlock:24}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:15},{numBlocks:26,dataCodewordsPerBlock:16}]}]},{infoBits:126325,versionNumber:30,alignmentPatternCenters:[6,
|
||||||
|
26,52,78,104,130],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:5,dataCodewordsPerBlock:115},{numBlocks:10,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:47},{numBlocks:10,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:15,dataCodewordsPerBlock:24},{numBlocks:25,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},{numBlocks:25,dataCodewordsPerBlock:16}]}]},
|
||||||
|
{infoBits:127568,versionNumber:31,alignmentPatternCenters:[6,30,56,82,108,134],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:3,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:46},{numBlocks:29,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:24},{numBlocks:1,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:23,dataCodewordsPerBlock:15},
|
||||||
|
{numBlocks:28,dataCodewordsPerBlock:16}]}]},{infoBits:133589,versionNumber:32,alignmentPatternCenters:[6,34,60,86,112,138],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:24},{numBlocks:35,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,
|
||||||
|
dataCodewordsPerBlock:15},{numBlocks:35,dataCodewordsPerBlock:16}]}]},{infoBits:136944,versionNumber:33,alignmentPatternCenters:[6,30,58,86,114,142],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:115},{numBlocks:1,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:21,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:24},{numBlocks:19,dataCodewordsPerBlock:25}]},
|
||||||
|
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:11,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:141498,versionNumber:34,alignmentPatternCenters:[6,34,62,90,118,146],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:115},{numBlocks:6,dataCodewordsPerBlock:116}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:14,dataCodewordsPerBlock:46},{numBlocks:23,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:44,
|
||||||
|
dataCodewordsPerBlock:24},{numBlocks:7,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:59,dataCodewordsPerBlock:16},{numBlocks:1,dataCodewordsPerBlock:17}]}]},{infoBits:145311,versionNumber:35,alignmentPatternCenters:[6,30,54,78,102,126,150],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:121},{numBlocks:7,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:12,dataCodewordsPerBlock:47},{numBlocks:26,dataCodewordsPerBlock:48}]},
|
||||||
|
{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:39,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:22,dataCodewordsPerBlock:15},{numBlocks:41,dataCodewordsPerBlock:16}]}]},{infoBits:150283,versionNumber:36,alignmentPatternCenters:[6,24,50,76,102,128,154],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:6,dataCodewordsPerBlock:121},{numBlocks:14,dataCodewordsPerBlock:122}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:6,
|
||||||
|
dataCodewordsPerBlock:47},{numBlocks:34,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:46,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:2,dataCodewordsPerBlock:15},{numBlocks:64,dataCodewordsPerBlock:16}]}]},{infoBits:152622,versionNumber:37,alignmentPatternCenters:[6,28,54,80,106,132,158],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:17,dataCodewordsPerBlock:122},{numBlocks:4,dataCodewordsPerBlock:123}]},
|
||||||
|
{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:29,dataCodewordsPerBlock:46},{numBlocks:14,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:49,dataCodewordsPerBlock:24},{numBlocks:10,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:24,dataCodewordsPerBlock:15},{numBlocks:46,dataCodewordsPerBlock:16}]}]},{infoBits:158308,versionNumber:38,alignmentPatternCenters:[6,32,58,84,110,136,162],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:4,
|
||||||
|
dataCodewordsPerBlock:122},{numBlocks:18,dataCodewordsPerBlock:123}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:13,dataCodewordsPerBlock:46},{numBlocks:32,dataCodewordsPerBlock:47}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:48,dataCodewordsPerBlock:24},{numBlocks:14,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:42,dataCodewordsPerBlock:15},{numBlocks:32,dataCodewordsPerBlock:16}]}]},{infoBits:161089,versionNumber:39,alignmentPatternCenters:[6,26,54,82,110,138,166],
|
||||||
|
errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:117},{numBlocks:4,dataCodewordsPerBlock:118}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:40,dataCodewordsPerBlock:47},{numBlocks:7,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:43,dataCodewordsPerBlock:24},{numBlocks:22,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:10,dataCodewordsPerBlock:15},{numBlocks:67,dataCodewordsPerBlock:16}]}]},{infoBits:167017,
|
||||||
|
versionNumber:40,alignmentPatternCenters:[6,30,58,86,114,142,170],errorCorrectionLevels:[{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:19,dataCodewordsPerBlock:118},{numBlocks:6,dataCodewordsPerBlock:119}]},{ecCodewordsPerBlock:28,ecBlocks:[{numBlocks:18,dataCodewordsPerBlock:47},{numBlocks:31,dataCodewordsPerBlock:48}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:34,dataCodewordsPerBlock:24},{numBlocks:34,dataCodewordsPerBlock:25}]},{ecCodewordsPerBlock:30,ecBlocks:[{numBlocks:20,dataCodewordsPerBlock:15},
|
||||||
|
{numBlocks:61,dataCodewordsPerBlock:16}]}]}];function J(a,b){a^=b;for(b=0;a;)b++,a&=a-1;return b}function K(a,b){return b<<1|a}
|
||||||
|
let ia=[{bits:21522,formatInfo:{errorCorrectionLevel:1,dataMask:0}},{bits:20773,formatInfo:{errorCorrectionLevel:1,dataMask:1}},{bits:24188,formatInfo:{errorCorrectionLevel:1,dataMask:2}},{bits:23371,formatInfo:{errorCorrectionLevel:1,dataMask:3}},{bits:17913,formatInfo:{errorCorrectionLevel:1,dataMask:4}},{bits:16590,formatInfo:{errorCorrectionLevel:1,dataMask:5}},{bits:20375,formatInfo:{errorCorrectionLevel:1,dataMask:6}},{bits:19104,formatInfo:{errorCorrectionLevel:1,dataMask:7}},{bits:30660,formatInfo:{errorCorrectionLevel:0,
|
||||||
|
dataMask:0}},{bits:29427,formatInfo:{errorCorrectionLevel:0,dataMask:1}},{bits:32170,formatInfo:{errorCorrectionLevel:0,dataMask:2}},{bits:30877,formatInfo:{errorCorrectionLevel:0,dataMask:3}},{bits:26159,formatInfo:{errorCorrectionLevel:0,dataMask:4}},{bits:25368,formatInfo:{errorCorrectionLevel:0,dataMask:5}},{bits:27713,formatInfo:{errorCorrectionLevel:0,dataMask:6}},{bits:26998,formatInfo:{errorCorrectionLevel:0,dataMask:7}},{bits:5769,formatInfo:{errorCorrectionLevel:3,dataMask:0}},{bits:5054,
|
||||||
|
formatInfo:{errorCorrectionLevel:3,dataMask:1}},{bits:7399,formatInfo:{errorCorrectionLevel:3,dataMask:2}},{bits:6608,formatInfo:{errorCorrectionLevel:3,dataMask:3}},{bits:1890,formatInfo:{errorCorrectionLevel:3,dataMask:4}},{bits:597,formatInfo:{errorCorrectionLevel:3,dataMask:5}},{bits:3340,formatInfo:{errorCorrectionLevel:3,dataMask:6}},{bits:2107,formatInfo:{errorCorrectionLevel:3,dataMask:7}},{bits:13663,formatInfo:{errorCorrectionLevel:2,dataMask:0}},{bits:12392,formatInfo:{errorCorrectionLevel:2,
|
||||||
|
dataMask:1}},{bits:16177,formatInfo:{errorCorrectionLevel:2,dataMask:2}},{bits:14854,formatInfo:{errorCorrectionLevel:2,dataMask:3}},{bits:9396,formatInfo:{errorCorrectionLevel:2,dataMask:4}},{bits:8579,formatInfo:{errorCorrectionLevel:2,dataMask:5}},{bits:11994,formatInfo:{errorCorrectionLevel:2,dataMask:6}},{bits:11245,formatInfo:{errorCorrectionLevel:2,dataMask:7}}],ja=[a=>0===(a.y+a.x)%2,a=>0===a.y%2,a=>0===a.x%3,a=>0===(a.y+a.x)%3,a=>0===(Math.floor(a.y/2)+Math.floor(a.x/3))%2,a=>0===a.x*a.y%
|
||||||
|
2+a.x*a.y%3,a=>0===(a.y*a.x%2+a.y*a.x%3)%2,a=>0===((a.y+a.x)%2+a.y*a.x%3)%2];
|
||||||
|
function ka(a,b,c){c=ja[c.dataMask];let d=a.height;var e=17+4*b.versionNumber;let f=x.createEmpty(e,e);f.setRegion(0,0,9,9,!0);f.setRegion(e-8,0,8,9,!0);f.setRegion(0,e-8,9,8,!0);for(var g of b.alignmentPatternCenters)for(var h of b.alignmentPatternCenters)6===g&&6===h||6===g&&h===e-7||g===e-7&&6===h||f.setRegion(g-2,h-2,5,5,!0);f.setRegion(6,9,1,e-17,!0);f.setRegion(9,6,e-17,1,!0);6<b.versionNumber&&(f.setRegion(e-11,0,3,6,!0),f.setRegion(0,e-11,6,3,!0));b=[];h=g=0;e=!0;for(let k=d-1;0<k;k-=2){6===
|
||||||
|
k&&k--;for(let m=0;m<d;m++){let l=e?d-1-m:m;for(let n=0;2>n;n++){let q=k-n;if(!f.get(q,l)){h++;let r=a.get(q,l);c({y:l,x:q})&&(r=!r);g=g<<1|r;8===h&&(b.push(g),g=h=0)}}}e=!e}return b}
|
||||||
|
function la(a){var b=a.height,c=Math.floor((b-17)/4);if(6>=c)return I[c-1];c=0;for(var d=5;0<=d;d--)for(var e=b-9;e>=b-11;e--)c=K(a.get(e,d),c);d=0;for(e=5;0<=e;e--)for(let g=b-9;g>=b-11;g--)d=K(a.get(e,g),d);a=Infinity;let f;for(let g of I){if(g.infoBits===c||g.infoBits===d)return g;b=J(c,g.infoBits);b<a&&(f=g,a=b);b=J(d,g.infoBits);b<a&&(f=g,a=b)}if(3>=a)return f}
|
||||||
|
function ma(a){let b=0;for(var c=0;8>=c;c++)6!==c&&(b=K(a.get(c,8),b));for(c=7;0<=c;c--)6!==c&&(b=K(a.get(8,c),b));var d=a.height;c=0;for(var e=d-1;e>=d-7;e--)c=K(a.get(8,e),c);for(e=d-8;e<d;e++)c=K(a.get(e,8),c);a=Infinity;d=null;for(let {bits:f,formatInfo:g}of ia){if(f===b||f===c)return g;e=J(b,f);e<a&&(d=g,a=e);b!==c&&(e=J(c,f),e<a&&(d=g,a=e))}return 3>=a?d:null}
|
||||||
|
function na(a,b,c){let d=b.errorCorrectionLevels[c],e=[],f=0;d.ecBlocks.forEach(h=>{for(let k=0;k<h.numBlocks;k++)e.push({numDataCodewords:h.dataCodewordsPerBlock,codewords:[]}),f+=h.dataCodewordsPerBlock+d.ecCodewordsPerBlock});if(a.length<f)return null;a=a.slice(0,f);b=d.ecBlocks[0].dataCodewordsPerBlock;for(c=0;c<b;c++)for(var g of e)g.codewords.push(a.shift());if(1<d.ecBlocks.length)for(g=d.ecBlocks[0].numBlocks,b=d.ecBlocks[1].numBlocks,c=0;c<b;c++)e[g+c].codewords.push(a.shift());for(;0<a.length;)for(let h of e)h.codewords.push(a.shift());
|
||||||
|
return e}function L(a){let b=la(a);if(!b)return null;var c=ma(a);if(!c)return null;a=ka(a,b,c);var d=na(a,b,c.errorCorrectionLevel);if(!d)return null;c=d.reduce((e,f)=>e+f.numDataCodewords,0);c=new Uint8ClampedArray(c);a=0;for(let e of d){d=ha(e.codewords,e.codewords.length-e.numDataCodewords);if(!d)return null;for(let f=0;f<e.numDataCodewords;f++)c[a++]=d[f]}try{return da(c,b.versionNumber)}catch(e){return null}}
|
||||||
|
function M(a,b,c,d){var e=a.x-b.x+c.x-d.x;let f=a.y-b.y+c.y-d.y;if(0===e&&0===f)return{a11:b.x-a.x,a12:b.y-a.y,a13:0,a21:c.x-b.x,a22:c.y-b.y,a23:0,a31:a.x,a32:a.y,a33:1};let g=b.x-c.x;var h=d.x-c.x;let k=b.y-c.y,m=d.y-c.y;c=g*m-h*k;h=(e*m-h*f)/c;e=(g*f-e*k)/c;return{a11:b.x-a.x+h*b.x,a12:b.y-a.y+h*b.y,a13:h,a21:d.x-a.x+e*d.x,a22:d.y-a.y+e*d.y,a23:e,a31:a.x,a32:a.y,a33:1}}
|
||||||
|
function oa(a,b,c,d){a=M(a,b,c,d);return{a11:a.a22*a.a33-a.a23*a.a32,a12:a.a13*a.a32-a.a12*a.a33,a13:a.a12*a.a23-a.a13*a.a22,a21:a.a23*a.a31-a.a21*a.a33,a22:a.a11*a.a33-a.a13*a.a31,a23:a.a13*a.a21-a.a11*a.a23,a31:a.a21*a.a32-a.a22*a.a31,a32:a.a12*a.a31-a.a11*a.a32,a33:a.a11*a.a22-a.a12*a.a21}}
|
||||||
|
function pa(a,b){var c=oa({x:3.5,y:3.5},{x:b.dimension-3.5,y:3.5},{x:b.dimension-6.5,y:b.dimension-6.5},{x:3.5,y:b.dimension-3.5}),d=M(b.topLeft,b.topRight,b.alignmentPattern,b.bottomLeft),e=d.a11*c.a11+d.a21*c.a12+d.a31*c.a13,f=d.a12*c.a11+d.a22*c.a12+d.a32*c.a13,g=d.a13*c.a11+d.a23*c.a12+d.a33*c.a13,h=d.a11*c.a21+d.a21*c.a22+d.a31*c.a23,k=d.a12*c.a21+d.a22*c.a22+d.a32*c.a23,m=d.a13*c.a21+d.a23*c.a22+d.a33*c.a23,l=d.a11*c.a31+d.a21*c.a32+d.a31*c.a33,n=d.a12*c.a31+d.a22*c.a32+d.a32*c.a33,q=d.a13*
|
||||||
|
c.a31+d.a23*c.a32+d.a33*c.a33;c=x.createEmpty(b.dimension,b.dimension);d=(r,u)=>{const p=g*r+m*u+q;return{x:(e*r+h*u+l)/p,y:(f*r+k*u+n)/p}};for(let r=0;r<b.dimension;r++)for(let u=0;u<b.dimension;u++){let p=d(u+.5,r+.5);c.set(u,r,a.get(Math.floor(p.x),Math.floor(p.y)))}return{matrix:c,mappingFunction:d}}let N=(a,b)=>Math.sqrt(Math.pow(b.x-a.x,2)+Math.pow(b.y-a.y,2));function O(a){return a.reduce((b,c)=>b+c)}
|
||||||
|
function qa(a,b,c){let d=N(a,b),e=N(b,c),f=N(a,c),g,h,k;e>=d&&e>=f?[g,h,k]=[b,a,c]:f>=e&&f>=d?[g,h,k]=[a,b,c]:[g,h,k]=[a,c,b];0>(k.x-h.x)*(g.y-h.y)-(k.y-h.y)*(g.x-h.x)&&([g,k]=[k,g]);return{bottomLeft:g,topLeft:h,topRight:k}}
|
||||||
|
function ra(a,b,c,d){d=(O(P(a,c,d,5))/7+O(P(a,b,d,5))/7+O(P(c,a,d,5))/7+O(P(b,a,d,5))/7)/4;if(1>d)throw Error("Invalid module size");b=Math.round(N(a,b)/d);a=Math.round(N(a,c)/d);a=Math.floor((b+a)/2)+7;switch(a%4){case 0:a++;break;case 2:a--}return{dimension:a,moduleSize:d}}
|
||||||
|
function Q(a,b,c,d){let e=[{x:Math.floor(a.x),y:Math.floor(a.y)}];var f=Math.abs(b.y-a.y)>Math.abs(b.x-a.x);if(f){var g=Math.floor(a.y);var h=Math.floor(a.x);a=Math.floor(b.y);b=Math.floor(b.x)}else g=Math.floor(a.x),h=Math.floor(a.y),a=Math.floor(b.x),b=Math.floor(b.y);let k=Math.abs(a-g),m=Math.abs(b-h),l=Math.floor(-k/2),n=g<a?1:-1,q=h<b?1:-1,r=!0;for(let u=g,p=h;u!==a+n;u+=n){g=f?p:u;h=f?u:p;if(c.get(g,h)!==r&&(r=!r,e.push({x:g,y:h}),e.length===d+1))break;l+=m;if(0<l){if(p===b)break;p+=q;l-=k}}c=
|
||||||
|
[];for(f=0;f<d;f++)e[f]&&e[f+1]?c.push(N(e[f],e[f+1])):c.push(0);return c}function P(a,b,c,d){let e=b.y-a.y,f=b.x-a.x;b=Q(a,b,c,Math.ceil(d/2));a=Q(a,{x:a.x-f,y:a.y-e},c,Math.ceil(d/2));c=b.shift()+a.shift()-1;return a.concat(c).concat(...b)}function R(a,b){let c=O(a)/O(b),d=0;b.forEach((e,f)=>{d+=Math.pow(a[f]-e*c,2)});return{averageSize:c,error:d}}
|
||||||
|
function S(a,b,c){try{let d=P(a,{x:-1,y:a.y},c,b.length),e=P(a,{x:a.x,y:-1},c,b.length),f=P(a,{x:Math.max(0,a.x-a.y)-1,y:Math.max(0,a.y-a.x)-1},c,b.length),g=P(a,{x:Math.min(c.width,a.x+a.y)+1,y:Math.min(c.height,a.y+a.x)+1},c,b.length),h=R(d,b),k=R(e,b),m=R(f,b),l=R(g,b),n=(h.averageSize+k.averageSize+m.averageSize+l.averageSize)/4;return Math.sqrt(h.error*h.error+k.error*k.error+m.error*m.error+l.error*l.error)+(Math.pow(h.averageSize-n,2)+Math.pow(k.averageSize-n,2)+Math.pow(m.averageSize-n,2)+
|
||||||
|
Math.pow(l.averageSize-n,2))/n}catch(d){return Infinity}}function T(a,b){for(var c=Math.round(b.x);a.get(c,Math.round(b.y));)c--;for(var d=Math.round(b.x);a.get(d,Math.round(b.y));)d++;c=(c+d)/2;for(d=Math.round(b.y);a.get(Math.round(c),d);)d--;for(b=Math.round(b.y);a.get(Math.round(c),b);)b++;return{x:c,y:(d+b)/2}}
|
||||||
|
function sa(a){var b=[],c=[];let d=[];var e=[];for(let p=0;p<=a.height;p++){var f=0,g=!1;let t=[0,0,0,0,0];for(let v=-1;v<=a.width;v++){var h=a.get(v,p);if(h===g)f++;else{t=[t[1],t[2],t[3],t[4],f];f=1;g=h;var k=O(t)/7;k=Math.abs(t[0]-k)<k&&Math.abs(t[1]-k)<k&&Math.abs(t[2]-3*k)<3*k&&Math.abs(t[3]-k)<k&&Math.abs(t[4]-k)<k&&!h;var m=O(t.slice(-3))/3;h=Math.abs(t[2]-m)<m&&Math.abs(t[3]-m)<m&&Math.abs(t[4]-m)<m&&h;if(k){let z=v-t[3]-t[4],y=z-t[2];k={startX:y,endX:z,y:p};m=c.filter(w=>y>=w.bottom.startX&&
|
||||||
|
y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<m.length?m[0].bottom=k:c.push({top:k,bottom:k})}if(h){let z=v-t[4],y=z-t[3];h={startX:y,y:p,endX:z};k=e.filter(w=>y>=w.bottom.startX&&y<=w.bottom.endX||z>=w.bottom.startX&&y<=w.bottom.endX||y<=w.bottom.startX&&z>=w.bottom.endX&&1.5>t[2]/(w.bottom.endX-w.bottom.startX)&&.5<t[2]/(w.bottom.endX-w.bottom.startX));0<k.length?
|
||||||
|
k[0].bottom=h:e.push({top:h,bottom:h})}}}b.push(...c.filter(v=>v.bottom.y!==p&&2<=v.bottom.y-v.top.y));c=c.filter(v=>v.bottom.y===p);d.push(...e.filter(v=>v.bottom.y!==p));e=e.filter(v=>v.bottom.y===p)}b.push(...c.filter(p=>2<=p.bottom.y-p.top.y));d.push(...e);c=[];for(var l of b)2>l.bottom.y-l.top.y||(b=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4,e=(l.top.y+l.bottom.y+1)/2,a.get(Math.round(b),Math.round(e))&&(f=[l.top.endX-l.top.startX,l.bottom.endX-l.bottom.startX,l.bottom.y-l.top.y+
|
||||||
|
1],f=O(f)/f.length,g=S({x:Math.round(b),y:Math.round(e)},[1,1,3,1,1],a),c.push({score:g,x:b,y:e,size:f})));if(3>c.length)return null;c.sort((p,t)=>p.score-t.score);l=[];for(b=0;b<Math.min(c.length,5);++b){e=c[b];f=[];for(var n of c)n!==e&&f.push(Object.assign(Object.assign({},n),{score:n.score+Math.pow(n.size-e.size,2)/e.size}));f.sort((p,t)=>p.score-t.score);l.push({points:[e,f[0],f[1]],score:e.score+f[0].score+f[1].score})}l.sort((p,t)=>p.score-t.score);let {topRight:q,topLeft:r,bottomLeft:u}=qa(...l[0].points);
|
||||||
|
l=U(a,d,q,r,u);n=[];l&&n.push({alignmentPattern:{x:l.alignmentPattern.x,y:l.alignmentPattern.y},bottomLeft:{x:u.x,y:u.y},dimension:l.dimension,topLeft:{x:r.x,y:r.y},topRight:{x:q.x,y:q.y}});l=T(a,q);b=T(a,r);c=T(a,u);(a=U(a,d,l,b,c))&&n.push({alignmentPattern:{x:a.alignmentPattern.x,y:a.alignmentPattern.y},bottomLeft:{x:c.x,y:c.y},topLeft:{x:b.x,y:b.y},topRight:{x:l.x,y:l.y},dimension:a.dimension});return 0===n.length?null:n}
|
||||||
|
function U(a,b,c,d,e){let f,g;try{({dimension:f,moduleSize:g}=ra(d,c,e,a))}catch(l){return null}var h=c.x-d.x+e.x,k=c.y-d.y+e.y;c=(N(d,e)+N(d,c))/2/g;e=1-3/c;let m={x:d.x+e*(h-d.x),y:d.y+e*(k-d.y)};b=b.map(l=>{const n=(l.top.startX+l.top.endX+l.bottom.startX+l.bottom.endX)/4;l=(l.top.y+l.bottom.y+1)/2;if(a.get(Math.floor(n),Math.floor(l))){var q=S({x:Math.floor(n),y:Math.floor(l)},[1,1,1],a)+N({x:n,y:l},m);return{x:n,y:l,score:q}}}).filter(l=>!!l).sort((l,n)=>l.score-n.score);return{alignmentPattern:15<=
|
||||||
|
c&&b.length?b[0]:m,dimension:f}}
|
||||||
|
function V(a){var b=sa(a);if(!b)return null;for(let e of b){b=pa(a,e);var c=b.matrix;if(null==c)c=null;else{var d=L(c);if(d)c=d;else{for(d=0;d<c.width;d++)for(let f=d+1;f<c.height;f++)c.get(d,f)!==c.get(f,d)&&(c.set(d,f,!c.get(d,f)),c.set(f,d,!c.get(f,d)));c=L(c)}}if(c)return{binaryData:c.bytes,data:c.text,chunks:c.chunks,version:c.version,location:{topRightCorner:b.mappingFunction(e.dimension,0),topLeftCorner:b.mappingFunction(0,0),bottomRightCorner:b.mappingFunction(e.dimension,e.dimension),bottomLeftCorner:b.mappingFunction(0,
|
||||||
|
e.dimension),topRightFinderPattern:e.topRight,topLeftFinderPattern:e.topLeft,bottomLeftFinderPattern:e.bottomLeft,bottomRightAlignmentPattern:e.alignmentPattern},matrix:b.matrix}}return null}let ta={inversionAttempts:"attemptBoth",greyScaleWeights:{red:.2126,green:.7152,blue:.0722,useIntegerApproximation:!1},canOverwriteImage:!0};function W(a,b){Object.keys(b).forEach(c=>{a[c]=b[c]})}
|
||||||
|
function X(a,b,c,d={}){let e=Object.create(null);W(e,ta);W(e,d);d="onlyInvert"===e.inversionAttempts||"invertFirst"===e.inversionAttempts;var f="attemptBoth"===e.inversionAttempts||d;var g=e.greyScaleWeights,h=e.canOverwriteImage,k=b*c;if(a.length!==4*k)throw Error("Malformed data passed to binarizer.");var m=0;if(h){var l=new Uint8ClampedArray(a.buffer,m,k);m+=k}l=new A(b,c,l);if(g.useIntegerApproximation)for(var n=0;n<c;n++)for(var q=0;q<b;q++){var r=4*(n*b+q);l.set(q,n,g.red*a[r]+g.green*a[r+1]+
|
||||||
|
g.blue*a[r+2]+128>>8)}else for(n=0;n<c;n++)for(q=0;q<b;q++)r=4*(n*b+q),l.set(q,n,g.red*a[r]+g.green*a[r+1]+g.blue*a[r+2]);g=Math.ceil(b/8);n=Math.ceil(c/8);q=g*n;if(h){var u=new Uint8ClampedArray(a.buffer,m,q);m+=q}u=new A(g,n,u);for(q=0;q<n;q++)for(r=0;r<g;r++){var p=Infinity,t=0;for(var v=0;8>v;v++)for(let w=0;8>w;w++){let aa=l.get(8*r+w,8*q+v);p=Math.min(p,aa);t=Math.max(t,aa)}v=(p+t)/2;v=Math.min(255,1.11*v);24>=t-p&&(v=p/2,0<q&&0<r&&(t=(u.get(r,q-1)+2*u.get(r-1,q)+u.get(r-1,q-1))/4,p<t&&(v=t)));
|
||||||
|
u.set(r,q,v)}h?(q=new Uint8ClampedArray(a.buffer,m,k),m+=k,q=new x(q,b)):q=x.createEmpty(b,c);r=null;f&&(h?(a=new Uint8ClampedArray(a.buffer,m,k),r=new x(a,b)):r=x.createEmpty(b,c));for(b=0;b<n;b++)for(a=0;a<g;a++){c=g-3;c=2>a?2:a>c?c:a;h=n-3;h=2>b?2:b>h?h:b;k=0;for(m=-2;2>=m;m++)for(p=-2;2>=p;p++)k+=u.get(c+m,h+p);c=k/25;for(h=0;8>h;h++)for(k=0;8>k;k++)m=8*a+h,p=8*b+k,t=l.get(m,p),q.set(m,p,t<=c),f&&r.set(m,p,!(t<=c))}f=f?{binarized:q,inverted:r}:{binarized:q};let {binarized:z,inverted:y}=f;(f=V(d?
|
||||||
|
y:z))||"attemptBoth"!==e.inversionAttempts&&"invertFirst"!==e.inversionAttempts||(f=V(d?z:y));return f}X.default=X;let Y="dontInvert",Z={red:77,green:150,blue:29,useIntegerApproximation:!0};
|
||||||
|
self.onmessage=a=>{let b=a.data.id,c=a.data.data;switch(a.data.type){case "decode":(a=X(c.data,c.width,c.height,{inversionAttempts:Y,greyScaleWeights:Z}))?self.postMessage({id:b,type:"qrResult",data:a.data,cornerPoints:[a.location.topLeftCorner,a.location.topRightCorner,a.location.bottomRightCorner,a.location.bottomLeftCorner]}):self.postMessage({id:b,type:"qrResult",data:null});break;case "grayscaleWeights":Z.red=c.red;Z.green=c.green;Z.blue=c.blue;Z.useIntegerApproximation=c.useIntegerApproximation;
|
||||||
|
break;case "inversionMode":switch(c){case "original":Y="dontInvert";break;case "invert":Y="onlyInvert";break;case "both":Y="attemptBoth";break;default:throw Error("Invalid inversion mode");}break;case "close":self.close()}}
|
||||||
|
`]),{type:"application/javascript"}))//# sourceMappingURL=qr-scanner-worker.min.js.map
|
||||||
1
client/qr-scanner-worker.min.js.map
Normal file
1
client/qr-scanner-worker.min.js.map
Normal file
File diff suppressed because one or more lines are too long
31
client/qr-scanner.umd.min.js
vendored
Normal file
31
client/qr-scanner.umd.min.js
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
'use strict';(function(e,a){"object"===typeof exports&&"undefined"!==typeof module?module.exports=a():"function"===typeof define&&define.amd?define(a):(e="undefined"!==typeof globalThis?globalThis:e||self,e.QrScanner=a())})(this,function(){class e{constructor(a,b,c,d,f){this._legacyCanvasSize=e.DEFAULT_CANVAS_SIZE;this._preferredCamera="environment";this._maxScansPerSecond=25;this._lastScanTimestamp=-1;this._destroyed=this._flashOn=this._paused=this._active=!1;this.$video=a;this.$canvas=document.createElement("canvas");
|
||||||
|
c&&"object"===typeof c?this._onDecode=b:(c||d||f?console.warn("You're using a deprecated version of the QrScanner constructor which will be removed in the future"):console.warn("Note that the type of the scan result passed to onDecode will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true."),this._legacyOnDecode=b);b="object"===typeof c?c:{};this._onDecodeError=b.onDecodeError||("function"===typeof c?c:this._onDecodeError);this._calculateScanRegion=
|
||||||
|
b.calculateScanRegion||("function"===typeof d?d:this._calculateScanRegion);this._preferredCamera=b.preferredCamera||f||this._preferredCamera;this._legacyCanvasSize="number"===typeof c?c:"number"===typeof d?d:this._legacyCanvasSize;this._maxScansPerSecond=b.maxScansPerSecond||this._maxScansPerSecond;this._onPlay=this._onPlay.bind(this);this._onLoadedMetaData=this._onLoadedMetaData.bind(this);this._onVisibilityChange=this._onVisibilityChange.bind(this);this._updateOverlay=this._updateOverlay.bind(this);
|
||||||
|
a.disablePictureInPicture=!0;a.playsInline=!0;a.muted=!0;let h=!1;a.hidden&&(a.hidden=!1,h=!0);document.body.contains(a)||(document.body.appendChild(a),h=!0);c=a.parentElement;if(b.highlightScanRegion||b.highlightCodeOutline){d=!!b.overlay;this.$overlay=b.overlay||document.createElement("div");f=this.$overlay.style;f.position="absolute";f.display="none";f.pointerEvents="none";this.$overlay.classList.add("scan-region-highlight");if(!d&&b.highlightScanRegion){this.$overlay.innerHTML='<svg class="scan-region-highlight-svg" viewBox="0 0 238 238" preserveAspectRatio="none" style="position:absolute;width:100%;height:100%;left:0;top:0;fill:none;stroke:#e9b213;stroke-width:4;stroke-linecap:round;stroke-linejoin:round"><path d="M31 2H10a8 8 0 0 0-8 8v21M207 2h21a8 8 0 0 1 8 8v21m0 176v21a8 8 0 0 1-8 8h-21m-176 0H10a8 8 0 0 1-8-8v-21"/></svg>';
|
||||||
|
try{this.$overlay.firstElementChild.animate({transform:["scale(.98)","scale(1.01)"]},{duration:400,iterations:Infinity,direction:"alternate",easing:"ease-in-out"})}catch(m){}c.insertBefore(this.$overlay,this.$video.nextSibling)}b.highlightCodeOutline&&(this.$overlay.insertAdjacentHTML("beforeend",'<svg class="code-outline-highlight" preserveAspectRatio="none" style="display:none;width:100%;height:100%;fill:none;stroke:#e9b213;stroke-width:5;stroke-dasharray:25;stroke-linecap:round;stroke-linejoin:round"><polygon/></svg>'),
|
||||||
|
this.$codeOutlineHighlight=this.$overlay.lastElementChild)}this._scanRegion=this._calculateScanRegion(a);requestAnimationFrame(()=>{let m=window.getComputedStyle(a);"none"===m.display&&(a.style.setProperty("display","block","important"),h=!0);"visible"!==m.visibility&&(a.style.setProperty("visibility","visible","important"),h=!0);h&&(console.warn("QrScanner has overwritten the video hiding style to avoid Safari stopping the playback."),a.style.opacity="0",a.style.width="0",a.style.height="0",this.$overlay&&
|
||||||
|
this.$overlay.parentElement&&this.$overlay.parentElement.removeChild(this.$overlay),delete this.$overlay,delete this.$codeOutlineHighlight);this.$overlay&&this._updateOverlay()});a.addEventListener("play",this._onPlay);a.addEventListener("loadedmetadata",this._onLoadedMetaData);document.addEventListener("visibilitychange",this._onVisibilityChange);window.addEventListener("resize",this._updateOverlay);this._qrEnginePromise=e.createQrEngine()}static set WORKER_PATH(a){console.warn("Setting QrScanner.WORKER_PATH is not required and not supported anymore. Have a look at the README for new setup instructions.")}static async hasCamera(){try{return!!(await e.listCameras(!1)).length}catch(a){return!1}}static async listCameras(a=
|
||||||
|
!1){if(!navigator.mediaDevices)return[];let b=async()=>(await navigator.mediaDevices.enumerateDevices()).filter(d=>"videoinput"===d.kind),c;try{a&&(await b()).every(d=>!d.label)&&(c=await navigator.mediaDevices.getUserMedia({audio:!1,video:!0}))}catch(d){}try{return(await b()).map((d,f)=>({id:d.deviceId,label:d.label||(0===f?"Default Camera":`Camera ${f+1}`)}))}finally{c&&(console.warn("Call listCameras after successfully starting a QR scanner to avoid creating a temporary video stream"),e._stopVideoStream(c))}}async hasFlash(){let a;
|
||||||
|
try{if(this.$video.srcObject){if(!(this.$video.srcObject instanceof MediaStream))return!1;a=this.$video.srcObject}else a=(await this._getCameraStream()).stream;return"torch"in a.getVideoTracks()[0].getSettings()}catch(b){return!1}finally{a&&a!==this.$video.srcObject&&(console.warn("Call hasFlash after successfully starting the scanner to avoid creating a temporary video stream"),e._stopVideoStream(a))}}isFlashOn(){return this._flashOn}async toggleFlash(){this._flashOn?await this.turnFlashOff():await this.turnFlashOn()}async turnFlashOn(){if(!this._flashOn&&
|
||||||
|
!this._destroyed&&(this._flashOn=!0,this._active&&!this._paused))try{if(!await this.hasFlash())throw"No flash available";await this.$video.srcObject.getVideoTracks()[0].applyConstraints({advanced:[{torch:!0}]})}catch(a){throw this._flashOn=!1,a;}}async turnFlashOff(){this._flashOn&&(this._flashOn=!1,await this._restartVideoStream())}destroy(){this.$video.removeEventListener("loadedmetadata",this._onLoadedMetaData);this.$video.removeEventListener("play",this._onPlay);document.removeEventListener("visibilitychange",
|
||||||
|
this._onVisibilityChange);window.removeEventListener("resize",this._updateOverlay);this._destroyed=!0;this._flashOn=!1;this.stop();e._postWorkerMessage(this._qrEnginePromise,"close")}async start(){if(this._destroyed)throw Error("The QR scanner can not be started as it had been destroyed.");if(!this._active||this._paused)if("https:"!==window.location.protocol&&console.warn("The camera stream is only accessible if the page is transferred via https."),this._active=!0,!document.hidden)if(this._paused=
|
||||||
|
!1,this.$video.srcObject)await this.$video.play();else try{let {stream:a,facingMode:b}=await this._getCameraStream();!this._active||this._paused?e._stopVideoStream(a):(this._setVideoMirror(b),this.$video.srcObject=a,await this.$video.play(),this._flashOn&&(this._flashOn=!1,this.turnFlashOn().catch(()=>{})))}catch(a){if(!this._paused)throw this._active=!1,a;}}stop(){this.pause();this._active=!1}async pause(a=!1){this._paused=!0;if(!this._active)return!0;this.$video.pause();this.$overlay&&(this.$overlay.style.display=
|
||||||
|
"none");let b=()=>{this.$video.srcObject instanceof MediaStream&&(e._stopVideoStream(this.$video.srcObject),this.$video.srcObject=null)};if(a)return b(),!0;await new Promise(c=>setTimeout(c,300));if(!this._paused)return!1;b();return!0}async setCamera(a){a!==this._preferredCamera&&(this._preferredCamera=a,await this._restartVideoStream())}static async scanImage(a,b,c,d,f=!1,h=!1){let m,n=!1;b&&("scanRegion"in b||"qrEngine"in b||"canvas"in b||"disallowCanvasResizing"in b||"alsoTryWithoutScanRegion"in
|
||||||
|
b||"returnDetailedScanResult"in b)?(m=b.scanRegion,c=b.qrEngine,d=b.canvas,f=b.disallowCanvasResizing||!1,h=b.alsoTryWithoutScanRegion||!1,n=!0):b||c||d||f||h?console.warn("You're using a deprecated api for scanImage which will be removed in the future."):console.warn("Note that the return type of scanImage will change in the future. To already switch to the new api today, you can pass returnDetailedScanResult: true.");b=!!c;try{let p,k;[c,p]=await Promise.all([c||e.createQrEngine(),e._loadImage(a)]);
|
||||||
|
[d,k]=e._drawToCanvas(p,m,d,f);let q;if(c instanceof Worker){let g=c;b||e._postWorkerMessageSync(g,"inversionMode","both");q=await new Promise((l,v)=>{let w,u,r,y=-1;u=t=>{t.data.id===y&&(g.removeEventListener("message",u),g.removeEventListener("error",r),clearTimeout(w),null!==t.data.data?l({data:t.data.data,cornerPoints:e._convertPoints(t.data.cornerPoints,m)}):v(e.NO_QR_CODE_FOUND))};r=t=>{g.removeEventListener("message",u);g.removeEventListener("error",r);clearTimeout(w);v("Scanner error: "+(t?
|
||||||
|
t.message||t:"Unknown Error"))};g.addEventListener("message",u);g.addEventListener("error",r);w=setTimeout(()=>r("timeout"),1E4);let x=k.getImageData(0,0,d.width,d.height);y=e._postWorkerMessageSync(g,"decode",x,[x.data.buffer])})}else q=await Promise.race([new Promise((g,l)=>window.setTimeout(()=>l("Scanner error: timeout"),1E4)),(async()=>{try{var [g]=await c.detect(d);if(!g)throw e.NO_QR_CODE_FOUND;return{data:g.rawValue,cornerPoints:e._convertPoints(g.cornerPoints,m)}}catch(l){g=l.message||l;
|
||||||
|
if(/not implemented|service unavailable/.test(g))return e._disableBarcodeDetector=!0,e.scanImage(a,{scanRegion:m,canvas:d,disallowCanvasResizing:f,alsoTryWithoutScanRegion:h});throw`Scanner error: ${g}`;}})()]);return n?q:q.data}catch(p){if(!m||!h)throw p;let k=await e.scanImage(a,{qrEngine:c,canvas:d,disallowCanvasResizing:f});return n?k:k.data}finally{b||e._postWorkerMessage(c,"close")}}setGrayscaleWeights(a,b,c,d=!0){e._postWorkerMessage(this._qrEnginePromise,"grayscaleWeights",{red:a,green:b,
|
||||||
|
blue:c,useIntegerApproximation:d})}setInversionMode(a){e._postWorkerMessage(this._qrEnginePromise,"inversionMode",a)}static async createQrEngine(a){a&&console.warn("Specifying a worker path is not required and not supported anymore.");a=()=>import("./qr-scanner-worker.min.js").then(c=>c.createWorker());if(!(!e._disableBarcodeDetector&&"BarcodeDetector"in window&&BarcodeDetector.getSupportedFormats&&(await BarcodeDetector.getSupportedFormats()).includes("qr_code")))return a();let b=navigator.userAgentData;
|
||||||
|
return b&&b.brands.some(({brand:c})=>/Chromium/i.test(c))&&/mac ?OS/i.test(b.platform)&&await b.getHighEntropyValues(["architecture","platformVersion"]).then(({architecture:c,platformVersion:d})=>/arm/i.test(c||"arm")&&13<=parseInt(d||"13")).catch(()=>!0)?a():new BarcodeDetector({formats:["qr_code"]})}_onPlay(){this._scanRegion=this._calculateScanRegion(this.$video);this._updateOverlay();this.$overlay&&(this.$overlay.style.display="");this._scanFrame()}_onLoadedMetaData(){this._scanRegion=this._calculateScanRegion(this.$video);
|
||||||
|
this._updateOverlay()}_onVisibilityChange(){document.hidden?this.pause():this._active&&this.start()}_calculateScanRegion(a){let b=Math.round(2/3*Math.min(a.videoWidth,a.videoHeight));return{x:Math.round((a.videoWidth-b)/2),y:Math.round((a.videoHeight-b)/2),width:b,height:b,downScaledWidth:this._legacyCanvasSize,downScaledHeight:this._legacyCanvasSize}}_updateOverlay(){requestAnimationFrame(()=>{if(this.$overlay){var a=this.$video,b=a.videoWidth,c=a.videoHeight,d=a.offsetWidth,f=a.offsetHeight,h=a.offsetLeft,
|
||||||
|
m=a.offsetTop,n=window.getComputedStyle(a),p=n.objectFit,k=b/c,q=d/f;switch(p){case "none":var g=b;var l=c;break;case "fill":g=d;l=f;break;default:("cover"===p?k>q:k<q)?(l=f,g=l*k):(g=d,l=g/k),"scale-down"===p&&(g=Math.min(g,b),l=Math.min(l,c))}var [v,w]=n.objectPosition.split(" ").map((r,y)=>{const x=parseFloat(r);return r.endsWith("%")?(y?f-l:d-g)*x/100:x});n=this._scanRegion.width||b;q=this._scanRegion.height||c;p=this._scanRegion.x||0;var u=this._scanRegion.y||0;k=this.$overlay.style;k.width=
|
||||||
|
`${n/b*g}px`;k.height=`${q/c*l}px`;k.top=`${m+w+u/c*l}px`;c=/scaleX\(-1\)/.test(a.style.transform);k.left=`${h+(c?d-v-g:v)+(c?b-p-n:p)/b*g}px`;k.transform=a.style.transform}})}static _convertPoints(a,b){if(!b)return a;let c=b.x||0,d=b.y||0,f=b.width&&b.downScaledWidth?b.width/b.downScaledWidth:1;b=b.height&&b.downScaledHeight?b.height/b.downScaledHeight:1;for(let h of a)h.x=h.x*f+c,h.y=h.y*b+d;return a}_scanFrame(){!this._active||this.$video.paused||this.$video.ended||("requestVideoFrameCallback"in
|
||||||
|
this.$video?this.$video.requestVideoFrameCallback.bind(this.$video):requestAnimationFrame)(async()=>{if(!(1>=this.$video.readyState)){var a=Date.now()-this._lastScanTimestamp,b=1E3/this._maxScansPerSecond;a<b&&await new Promise(d=>setTimeout(d,b-a));this._lastScanTimestamp=Date.now();try{var c=await e.scanImage(this.$video,{scanRegion:this._scanRegion,qrEngine:this._qrEnginePromise,canvas:this.$canvas})}catch(d){if(!this._active)return;this._onDecodeError(d)}!e._disableBarcodeDetector||await this._qrEnginePromise instanceof
|
||||||
|
Worker||(this._qrEnginePromise=e.createQrEngine());c?(this._onDecode?this._onDecode(c):this._legacyOnDecode&&this._legacyOnDecode(c.data),this.$codeOutlineHighlight&&(clearTimeout(this._codeOutlineHighlightRemovalTimeout),this._codeOutlineHighlightRemovalTimeout=void 0,this.$codeOutlineHighlight.setAttribute("viewBox",`${this._scanRegion.x||0} `+`${this._scanRegion.y||0} `+`${this._scanRegion.width||this.$video.videoWidth} `+`${this._scanRegion.height||this.$video.videoHeight}`),this.$codeOutlineHighlight.firstElementChild.setAttribute("points",
|
||||||
|
c.cornerPoints.map(({x:d,y:f})=>`${d},${f}`).join(" ")),this.$codeOutlineHighlight.style.display="")):this.$codeOutlineHighlight&&!this._codeOutlineHighlightRemovalTimeout&&(this._codeOutlineHighlightRemovalTimeout=setTimeout(()=>this.$codeOutlineHighlight.style.display="none",100))}this._scanFrame()})}_onDecodeError(a){a!==e.NO_QR_CODE_FOUND&&console.log(a)}async _getCameraStream(){if(!navigator.mediaDevices)throw"Camera not found.";let a=/^(environment|user)$/.test(this._preferredCamera)?"facingMode":
|
||||||
|
"deviceId",b=[{width:{min:1024}},{width:{min:768}},{}],c=b.map(d=>Object.assign({},d,{[a]:{exact:this._preferredCamera}}));for(let d of[...c,...b])try{let f=await navigator.mediaDevices.getUserMedia({video:d,audio:!1}),h=this._getFacingMode(f)||(d.facingMode?this._preferredCamera:"environment"===this._preferredCamera?"user":"environment");return{stream:f,facingMode:h}}catch(f){}throw"Camera not found.";}async _restartVideoStream(){let a=this._paused;await this.pause(!0)&&!a&&this._active&&await this.start()}static _stopVideoStream(a){for(let b of a.getTracks())b.stop(),
|
||||||
|
a.removeTrack(b)}_setVideoMirror(a){this.$video.style.transform="scaleX("+("user"===a?-1:1)+")"}_getFacingMode(a){return(a=a.getVideoTracks()[0])?/rear|back|environment/i.test(a.label)?"environment":/front|user|face/i.test(a.label)?"user":null:null}static _drawToCanvas(a,b,c,d=!1){c=c||document.createElement("canvas");let f=b&&b.x?b.x:0,h=b&&b.y?b.y:0,m=b&&b.width?b.width:a.videoWidth||a.width,n=b&&b.height?b.height:a.videoHeight||a.height;d||(d=b&&b.downScaledWidth?b.downScaledWidth:m,b=b&&b.downScaledHeight?
|
||||||
|
b.downScaledHeight:n,c.width!==d&&(c.width=d),c.height!==b&&(c.height=b));b=c.getContext("2d",{alpha:!1});b.imageSmoothingEnabled=!1;b.drawImage(a,f,h,m,n,0,0,c.width,c.height);return[c,b]}static async _loadImage(a){if(a instanceof Image)return await e._awaitImageLoad(a),a;if(a instanceof HTMLVideoElement||a instanceof HTMLCanvasElement||a instanceof SVGImageElement||"OffscreenCanvas"in window&&a instanceof OffscreenCanvas||"ImageBitmap"in window&&a instanceof ImageBitmap)return a;if(a instanceof
|
||||||
|
File||a instanceof Blob||a instanceof URL||"string"===typeof a){let b=new Image;b.src=a instanceof File||a instanceof Blob?URL.createObjectURL(a):a.toString();try{return await e._awaitImageLoad(b),b}finally{(a instanceof File||a instanceof Blob)&&URL.revokeObjectURL(b.src)}}else throw"Unsupported image type.";}static async _awaitImageLoad(a){a.complete&&0!==a.naturalWidth||await new Promise((b,c)=>{let d=f=>{a.removeEventListener("load",d);a.removeEventListener("error",d);f instanceof ErrorEvent?
|
||||||
|
c("Image load error"):b()};a.addEventListener("load",d);a.addEventListener("error",d)})}static async _postWorkerMessage(a,b,c,d){return e._postWorkerMessageSync(await a,b,c,d)}static _postWorkerMessageSync(a,b,c,d){if(!(a instanceof Worker))return-1;let f=e._workerMessageId++;a.postMessage({id:f,type:b,data:c},d);return f}}e.DEFAULT_CANVAS_SIZE=400;e.NO_QR_CODE_FOUND="No QR code found";e._disableBarcodeDetector=!1;e._workerMessageId=0;return e})
|
||||||
|
//# sourceMappingURL=qr-scanner.umd.min.js.map
|
||||||
1
client/qr-scanner.umd.min.js.map
Normal file
1
client/qr-scanner.umd.min.js.map
Normal file
File diff suppressed because one or more lines are too long
367
client/root.js
Normal file
367
client/root.js
Normal file
@ -0,0 +1,367 @@
|
|||||||
|
class Root
|
||||||
|
{
|
||||||
|
static instance = null;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Init values / parse cookies
|
||||||
|
if (Root.instance!==null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Root.instance = this;
|
||||||
|
this.jwt = this.getCookie("jwt");
|
||||||
|
this.secToken = this.getCookie("secToken");
|
||||||
|
this.loggedIn = false;
|
||||||
|
this.url = "https://app.fw-innenstadt.de/index_new.php/";
|
||||||
|
this.scanner = new Scanner(this);
|
||||||
|
this.page = "Start";
|
||||||
|
this.routesChecksum = "";
|
||||||
|
const _this = this;
|
||||||
|
Root.AddEventListenerIfButtonExists("loginButton", function (event) {_this.Login();});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_start", function (event) {_this.Open("Start");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_termine", function (event) {_this.Open("Termine");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_dienstpläne", function (event) {_this.Open("Dienstpläne");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_personal", function (event) {_this.Open("Personal");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_prozesse", function (event) {_this.Open("Prozesse");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_spinde", function (event) {_this.Open("Spinde");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_einsätze", function (event) {_this.Open("Einsätze");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_anwesenheiten", function (event) {_this.Open("Anwesenheiten");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_fahrzeuge", function (event) {_this.Open("Fahrzeuge");});
|
||||||
|
Root.AddEventListenerIfButtonExists("nav_logout", function (event) {_this.Logout();});
|
||||||
|
document.getElementById("navigation_select").addEventListener("change", function (event) {_this.Open(document.getElementById("navigation_select").value);});
|
||||||
|
this.FetchRoutes(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Open(page) {
|
||||||
|
if ("_LOGOUT_"==page) {
|
||||||
|
this.Logout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setCookie("page", page);
|
||||||
|
switch (page) {
|
||||||
|
case "Termine":
|
||||||
|
//TerminePage.Open();
|
||||||
|
break;
|
||||||
|
case "Dienstpläne":
|
||||||
|
//DienstplänePage.Open();
|
||||||
|
break;
|
||||||
|
case "Personal":
|
||||||
|
PersonalPage.Open();
|
||||||
|
break;
|
||||||
|
case "Prozesse":
|
||||||
|
//ProzessePage.Open();
|
||||||
|
break;
|
||||||
|
case "Spinde":
|
||||||
|
//SpindePage.Open();
|
||||||
|
break;
|
||||||
|
case "Einsätze":
|
||||||
|
//EinsätzePage.Open();
|
||||||
|
break;
|
||||||
|
case "Anwesenheiten":
|
||||||
|
//AnwesenheitenPage.Open();
|
||||||
|
break;
|
||||||
|
case "Fahrzeuge":
|
||||||
|
FahrzeugePage.Open();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
//StartPage.Open();
|
||||||
|
this.setCookie("page", "Start");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Login() {
|
||||||
|
var button = document.getElementById("loginButton");
|
||||||
|
button.disabled = true;
|
||||||
|
button.className = "login disabled";
|
||||||
|
const json = {
|
||||||
|
"login": document.getElementById("loginUser").value,
|
||||||
|
"password": document.getElementById("loginPassword").value/*,
|
||||||
|
"secToken": typeof(this.secToken)!="undefined" ? this.secToken : ""*/
|
||||||
|
};
|
||||||
|
|
||||||
|
const _this = this;
|
||||||
|
Root.SetLoadAnimation(true);
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.open("POST", this.url, true);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
Root.SetLoadAnimation(false);
|
||||||
|
const {ok, json, etag} = _this.FinishResponse(request);
|
||||||
|
_this.changeLoginScreen(ok);
|
||||||
|
if (ok) {
|
||||||
|
_this.updatePages(etag, json);
|
||||||
|
_this.Open(_this.getCookie("page"));
|
||||||
|
setTimeout(Root.instance.FetchPages, 10000, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
request.send(JSON.stringify(json));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logout() {
|
||||||
|
const _this = this;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
Root.SetLoadAnimation(true);
|
||||||
|
request.open("DELETE", this.url, true);
|
||||||
|
request.setRequestHeader("Content-Type", "application/json");
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
Root.SetLoadAnimation(false);
|
||||||
|
_this.FinishResponse(request);
|
||||||
|
_this.changeLoginScreen(false);
|
||||||
|
};
|
||||||
|
request.send({secToken: this.secToken});
|
||||||
|
}
|
||||||
|
|
||||||
|
FetchRoutes(callOpenAfterOk = false) {
|
||||||
|
if (this.jwt!="") {
|
||||||
|
Root.SetLoadAnimation(true);
|
||||||
|
const _this = this;
|
||||||
|
let request = new XMLHttpRequest();
|
||||||
|
request.open("OPTIONS", this.url);
|
||||||
|
request.setRequestHeader("Authorization", "Bearer " + this.jwt);
|
||||||
|
request.setRequestHeader("Accept", "application/json");
|
||||||
|
request.onreadystatechange = function() {
|
||||||
|
if (request.readyState != 4) return;
|
||||||
|
Root.SetLoadAnimation(false);
|
||||||
|
const {ok, json, etag} = _this.FinishResponse(request);
|
||||||
|
if (ok) {
|
||||||
|
_this.updatePages(etag, json);
|
||||||
|
if (callOpenAfterOk) {
|
||||||
|
_this.Open(_this.getCookie("page"));
|
||||||
|
}
|
||||||
|
setTimeout(Root.instance.FetchPages, 10000, false);
|
||||||
|
} }
|
||||||
|
request.send();
|
||||||
|
} else {
|
||||||
|
this.changeLoginScreen(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FinishResponse(request, isHead = false) {
|
||||||
|
const {ok, json} = isHead ? {ok: true, json: {}} : Root.ParseJson(request.responseText);
|
||||||
|
if (!ok) {
|
||||||
|
return {ok: false, json: {}, etag: null};
|
||||||
|
}
|
||||||
|
if (typeof(json.messages)!="undefined") {
|
||||||
|
Root.ShowMessages(json.messages);
|
||||||
|
}
|
||||||
|
if (typeof(json.secToken)!="undefined") {
|
||||||
|
this.secToken = json.secToken;
|
||||||
|
this.setCookie("secToken", json.secToken);
|
||||||
|
}
|
||||||
|
if (typeof(json.jwt)!="undefined") {
|
||||||
|
this.jwt = json.jwt;
|
||||||
|
this.setCookie("jwt", json.jwt);
|
||||||
|
}
|
||||||
|
if (request.status>=300) {
|
||||||
|
let statusString = "";
|
||||||
|
switch (request.status) {
|
||||||
|
//case 200: statusString = "OK"; break;
|
||||||
|
//case 201: statusString = "Erstellt"; break;
|
||||||
|
case 400: statusString = "Fehlerhafte Anfrage"; break;
|
||||||
|
case 401: statusString = "Anmeldung erforderlich"; this.changeLoginScreen(false); break;
|
||||||
|
case 403: statusString = "Keine Berechtigung"; break;
|
||||||
|
case 404: statusString = "Nicht gefunden"; break;
|
||||||
|
case 500: statusString = "Fehler auf Server"; break;
|
||||||
|
default: statusString = "" + request.status + " = Unbekannt";
|
||||||
|
}
|
||||||
|
Root.ShowMessages(["Anfrage wurde mit Code " + request.status + " abgelehnt.<br />(" + statusString + ")"]);
|
||||||
|
} else {
|
||||||
|
const etag = typeof(request.getResponseHeader) === 'function' ? request.getResponseHeader("ETag") : null;
|
||||||
|
return {ok: true, json: json, etag: etag};
|
||||||
|
}
|
||||||
|
return {ok: false, json: {}, etag: null};
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "Private" functions */
|
||||||
|
|
||||||
|
updatePages(etag, json) {
|
||||||
|
if (etag!=this.routesChecksum) {
|
||||||
|
//StartPage.SetVisibility(true);
|
||||||
|
//TerminePage.SetVisibility(json.pages.includes("Termine"));
|
||||||
|
//DienstplänePage.SetVisibility(json.pages.includes("Dienstpläne"));
|
||||||
|
PersonalPage.SetVisibility(json);
|
||||||
|
//ProzessePage.SetVisibility(json.pages.includes("Prozesse"));
|
||||||
|
//SpindePage.SetVisibility(json.pages.includes("Spinde"));
|
||||||
|
//EinsätzePage.SetVisibility(json.pages.includes("Einsätze"));
|
||||||
|
//AnwesenheitenPage.SetVisibility(json.pages.includes("Anwesenheiten"));
|
||||||
|
FahrzeugePage.SetVisibility(json);
|
||||||
|
this.pagesChecksum = etag;
|
||||||
|
} }
|
||||||
|
|
||||||
|
changeLoginScreen(newStatus) {
|
||||||
|
var s = document.getElementById('header').style;
|
||||||
|
if (this.loggedIn==null) {
|
||||||
|
if (!newStatus) {
|
||||||
|
s.display = "block";
|
||||||
|
s.opacity = 1;
|
||||||
|
}
|
||||||
|
} else if (newStatus) {
|
||||||
|
(function fadeOut() {(s.opacity-=0.1)<0 ? s.display="none" : setTimeout(fadeOut, 40)})();
|
||||||
|
} else {
|
||||||
|
var button = document.getElementById("loginButton");
|
||||||
|
button.disabled = false;
|
||||||
|
button.className = "login";
|
||||||
|
|
||||||
|
s.display = "block";
|
||||||
|
(function fadeIn(){(s.opacity-=-0.1)>1 ? s.opacity=1 : setTimeout(fadeIn, 40)})();
|
||||||
|
}
|
||||||
|
this.loggedIn = newStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*updateLoginStatus(loggedIn) {
|
||||||
|
if (loggedIn) {
|
||||||
|
if (loggedIn!=this.loggedIn) {
|
||||||
|
this.changeLoginScreen(true);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
this.changeLoginScreen(false);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}*/
|
||||||
|
|
||||||
|
setCookie(cname, cvalue, exdays) {
|
||||||
|
var d = new Date();
|
||||||
|
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||||
|
var expires = "expires="+ d.toUTCString();
|
||||||
|
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/;SameSite=Strict;Secure";
|
||||||
|
}
|
||||||
|
|
||||||
|
getCookie(cname){
|
||||||
|
var name = cname + "=";
|
||||||
|
var decodedCookie = decodeURIComponent(document.cookie);
|
||||||
|
var ca = decodedCookie.split(';');
|
||||||
|
for(var i = 0; i <ca.length; i++) {
|
||||||
|
var c = ca[i];
|
||||||
|
while (c.charAt(0) == ' ') {
|
||||||
|
c = c.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c.indexOf(name) == 0) {
|
||||||
|
return c.substring(name.length, c.length);
|
||||||
|
} }
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Static functions */
|
||||||
|
|
||||||
|
static ShowMessages(messages) {
|
||||||
|
for (var i in messages) {
|
||||||
|
Toastify({
|
||||||
|
text: messages[i],
|
||||||
|
duration: 3000,
|
||||||
|
close: true,
|
||||||
|
gravity: "top", // `top` or `bottom`
|
||||||
|
position: "right", // `left`, `center` or `right`
|
||||||
|
backgroundColor: "linear-gradient(to right, #00b09b, #96c93d)",
|
||||||
|
stopOnFocus: true, // Prevents dismissing of toast on hover
|
||||||
|
}).showToast();
|
||||||
|
} }
|
||||||
|
|
||||||
|
static ToggleVisibility(idImg, idDiv) {
|
||||||
|
let div = document.getElementById(idDiv);
|
||||||
|
let img = document.getElementById(idImg);
|
||||||
|
if (div.style.display === "none") {
|
||||||
|
div.style.display = "flex";
|
||||||
|
img.src = "res/dark/hide.png";
|
||||||
|
} else {
|
||||||
|
div.style.display = "none";
|
||||||
|
img.src = "res/dark/show.png";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static AddEventListenerIfButtonExists(button, func) {
|
||||||
|
let buttonElement = document.getElementById(button);
|
||||||
|
if (buttonElement!==null) {
|
||||||
|
buttonElement.addEventListener('click', func);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ParseJson(responseText) {
|
||||||
|
let json = {};
|
||||||
|
try {
|
||||||
|
json = JSON.parse(responseText);
|
||||||
|
} catch (SyntaxError) {
|
||||||
|
Root.ShowMessages(["Parsen der Antwort gescheitert. Es folgt die Antwort!", responseText]);
|
||||||
|
return {ok: false, json: {}};
|
||||||
|
}
|
||||||
|
return {ok: true, json: json};
|
||||||
|
}
|
||||||
|
|
||||||
|
static SetLoadAnimation(isLoading) {
|
||||||
|
var loadElement = document.getElementById("loadImage");
|
||||||
|
if (null!=loadElement) {
|
||||||
|
if (isLoading) {
|
||||||
|
loadElement.innerHTML = "<img src='/res/loading.gif' />";
|
||||||
|
} else {
|
||||||
|
loadElement.innerHTML = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*RenderMain(root) {
|
||||||
|
var html = "<table class='moneyplanner_accounts'>";
|
||||||
|
|
||||||
|
html+= "<tr><th>" + (this.viewFrom>0 ? "<button id='view_both_left'><</button>" : "") + (this.viewTo<this.view.length-1 ? "<button id='view_both_right'>></button>" : "") + "</th>";
|
||||||
|
for (var v=this.viewFrom; v<=this.viewTo; v++) {
|
||||||
|
html+= "<th>"
|
||||||
|
+ (v==this.viewFrom && this.viewFrom>0 ? "<button id='view_from_left'><</button>" : "")
|
||||||
|
+ (v==this.viewTo && this.viewTo>this.viewFrom ? "<button id='view_to_left'><</button>" : "")
|
||||||
|
+ this.view[v].title
|
||||||
|
+ (v==this.viewFrom && this.viewFrom<this.viewTo ? "<button id='view_from_right'>></button>" : "")
|
||||||
|
+ (v==this.viewTo && this.viewTo<this.view.length-1 ? "<button id='view_to_right'>></button>" : "")
|
||||||
|
+ "</th>";
|
||||||
|
}
|
||||||
|
html+= "</tr>";
|
||||||
|
|
||||||
|
for (let a in Account.byOrdering) {
|
||||||
|
html+= Account.byOrdering[a].Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.contentContainer.innerHTML = html+"</table>";
|
||||||
|
|
||||||
|
const _this = this;
|
||||||
|
addEventListenerIfButtonExists('view_from_left', function (event) {if (_this.viewFrom>0) {--_this.viewFrom; _this.RenderMain();}});
|
||||||
|
addEventListenerIfButtonExists('view_from_right', function (event) {if (_this.viewFrom<_this.viewTo) {++_this.viewFrom; _this.RenderMain();}});
|
||||||
|
addEventListenerIfButtonExists('view_to_left', function (event) {if (_this.viewTo>_this.viewFrom) {--_this.viewTo; _this.RenderMain();}});
|
||||||
|
addEventListenerIfButtonExists('view_to_right', function (event) {if (_this.viewTo<_this.view.length-1) {++_this.viewTo; _this.RenderMain();}});
|
||||||
|
addEventListenerIfButtonExists('view_both_left', function (event) {if (_this.viewFrom>0) {--_this.viewFrom; --_this.viewTo; _this.RenderMain();}});
|
||||||
|
addEventListenerIfButtonExists('view_both_right', function (event) {if (_this.viewTo<_this.view.length-1) {++_this.viewFrom; ++_this.viewTo; _this.RenderMain();}});
|
||||||
|
for (let a in Account.array) {
|
||||||
|
let accountId = Account.array[a].id;
|
||||||
|
addEventListenerIfButtonExists('balances_add_' + accountId, function (event) {Balance.Add(_this, _this.RenderMain, accountId);});
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*function ToggleEntryView(id) {api.activeRoute.ToggleEntryView(id);}
|
||||||
|
function Open(path) {api.Open(path);}
|
||||||
|
function OpenSelected() {
|
||||||
|
let s = document.getElementById("navigation_select").value;
|
||||||
|
if (s=="_LOGOUT_") {Logout();} else {api.Open(s);}
|
||||||
|
}
|
||||||
|
function Logout() {api.Open("/index.php/Logout");}
|
||||||
|
function FilterAdd() {api.activeRoute.FilterAdd();}
|
||||||
|
function FilterDelete(id) {api.activeRoute.FilterDelete(id);}
|
||||||
|
function FilterChange(id) {api.activeRoute.FilterChange(id);}
|
||||||
|
function Add() {api.activeRoute.Add();}
|
||||||
|
function Delete(id, data = {}) {api.Request("DELETE", data, id);}
|
||||||
|
function Edit(id) {api.activeRoute.Edit(id);}
|
||||||
|
function Cancel(id) {api.activeRoute.Cancel(id);}
|
||||||
|
function Save(id) {api.activeRoute.Save(id);}
|
||||||
|
function SubShowAdd(id, markers) {api.activeRoute.SubShowAdd(id, markers);}
|
||||||
|
function SubShowScan(id, subroute, parsers) {api.activeRoute.SubShowScan(id, subroute, parsers);}
|
||||||
|
function HideScanner() {api.scanner.Stop(); document.getElementById("qrscanner").style.display = "none";}
|
||||||
|
function SwitchCamera() {api.scanner.SwitchCamera();}
|
||||||
|
function SubAdd(id, markers, subroute) {api.activeRoute.SubAdd(id, markers, subroute);}
|
||||||
|
function SubDelete(id, subroute, subid = null) {api.Request("DELETE", {}, id, subroute, subid);}
|
||||||
|
function Request(method, data, id = null, subroute = null, subid = null) {api.Request(method, data, id, subroute, subid);}
|
||||||
|
function Print(printID, printName) {api.Request("GET", {Print: printID, PrintName: printName});}
|
||||||
|
function SelectChange(id, state) {api.activeRoute.SelectChange(id, state);}*/
|
||||||
|
|
||||||
|
document.addEventListener ("DOMContentLoaded", () => {
|
||||||
|
var root = new Root();
|
||||||
|
});
|
||||||
119
client/routes/fahrzeuge.js
Normal file
119
client/routes/fahrzeuge.js
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
class Fahrzeuge extends Route
|
||||||
|
{
|
||||||
|
static route = "Fahrzeuge";
|
||||||
|
static array = {};
|
||||||
|
static groups = {};
|
||||||
|
static subs = {
|
||||||
|
"Eingewiesene": {color: "chocolate", icon: "person.png", entries: []}
|
||||||
|
};
|
||||||
|
static selects = {};
|
||||||
|
static admin = false; // global page admin rights
|
||||||
|
static prints = [];
|
||||||
|
static filter = [];
|
||||||
|
//static selected = [];
|
||||||
|
//static dropzones = {};
|
||||||
|
static visible = false;
|
||||||
|
|
||||||
|
constructor(groupName, json) {
|
||||||
|
super(
|
||||||
|
groupName,
|
||||||
|
json,
|
||||||
|
Fahrzeuge.route,
|
||||||
|
Fahrzeuge.array,
|
||||||
|
Fahrzeuge.groups,
|
||||||
|
Fahrzeuge.subs,
|
||||||
|
Fahrzeuge.selects,
|
||||||
|
Fahrzeuge.admin/*,
|
||||||
|
Fahrzeuge.prints,
|
||||||
|
Fahrzeuge.filter,
|
||||||
|
Fahrzeuge.selected,
|
||||||
|
Fahrzeuge.dropzones*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(json) {
|
||||||
|
this.Kürzel = json.MAIN.KÜRZEL;
|
||||||
|
this.Name = json.MAIN.NAME;
|
||||||
|
this.Bild = json.MAIN.BILD;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEntry(drawEdit) {
|
||||||
|
let html = "<div class='bild'>";
|
||||||
|
if (this.Bild.EXISTIERT) {
|
||||||
|
html+= "<img src='/pic.php/Fahrzeuge/Bild/" + this.Bild.ADRESSE + "?h=260' />";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<div id='" + this.marker("Bild/Delete") + "' class='deleter'><img src='/res/dark/delete.png' /></div>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html+= "<img src='/upl/Fahrzeuge/Bild/Nopic.svg' />";
|
||||||
|
}
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<form action='/" + this.marker("Bilder") + "' class='dropzone' id='dropzone_" + this.ID + "'></form>";
|
||||||
|
}
|
||||||
|
html += "</div>";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<ul>"
|
||||||
|
+ "<li><img src='/res/dark/truck.png' /><input id='" + this.marker("Name") + "' value='" + this.Name + "' style='width: 8cm;'></li>"
|
||||||
|
+ "<li><img src='/res/dark/truck.png' /><input id='" + this.marker("Kürzel") + "' value='" + this.Kürzel + "' style='width: 4cm;'></li>"
|
||||||
|
+ "<li id='" + this.marker("Save") + "' class='button'><img src='/res/dark/save.png' /></li>"
|
||||||
|
+ "<li id='" + this.marker("Delete") + "' class='button'><img src='/res/dark/delete.png' /></li>"
|
||||||
|
+ "<li id='" + this.marker("Reset") + "' class='button'><img src='/res/dark/cancel.png' /></li>"
|
||||||
|
+ "</ul>";
|
||||||
|
} else {
|
||||||
|
html+= "<ul><li><img src='/res/dark/truck.png' />" + this.Name + "</li>"
|
||||||
|
"<li><img src='/res/dark/truck.png' />" + this.Kürzel + "</li>";
|
||||||
|
if (this.admin) {
|
||||||
|
html += "<li id='" + this.marker("Edit") + "' class='button'><img src='/res/dark/edit.png' title='Bearbeiten' /></li>";
|
||||||
|
}
|
||||||
|
html+= "</ul>";
|
||||||
|
}
|
||||||
|
document.getElementById(this.marker("Main")).innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderAdd() {
|
||||||
|
let html = "<h1>Fahrzeug hinzufügen</h1>"
|
||||||
|
+ "<div class='toggleVisibility'><ul><li>Name: <input id='Fahrzeuge/Add/Name' style='width: 8em;' /></li>"
|
||||||
|
+ "<li>Kürzel: <input id='Fahrzeuge/Add/Kürzel' style='width: 4em;' /></li>"
|
||||||
|
+ "<li id='Fahrzeuge/Add/Add' class='button'>Hinzufügen <img src='/res/dark/add.png' /></li></ul></div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroup() {
|
||||||
|
return Fahrzeuge.RenderGroup(this.groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderGroup(groupName) {
|
||||||
|
return {
|
||||||
|
begin: "<article id='Group/" + groupName + "'><div class='group_title'><h1>" + groupName + "</h1></div>",
|
||||||
|
end: "</article>"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static New(groupName, json) {
|
||||||
|
return new Fahrzeuge(groupName, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect() {
|
||||||
|
return Fahrzeuge.Collect(this.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collect(id, contextId = null) {
|
||||||
|
return {
|
||||||
|
ID: id,
|
||||||
|
KÜRZEL: this.Value(id, "Kürzel"),
|
||||||
|
NAME: this.Value(id, "Name")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderSelect(selectId, fahrzeugId, nullable = true) {
|
||||||
|
let html = "<select id='" + selectId + "'>" + (nullable ? "<option value='null'></option>" : "");
|
||||||
|
for (let group in Fahrzeuge.groups) {
|
||||||
|
for (let e in Fahrzeuge.groups[group]) {
|
||||||
|
const entry = Fahrzeuge.groups[group][e];
|
||||||
|
html+= "<option value='" + entry.ID + "'" + (entry.ID==fahrzeugId ? " selected" : "") + ">" + entry.Kürzel + "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</select>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
144
client/routes/personal.js
Normal file
144
client/routes/personal.js
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
class Personal extends Route
|
||||||
|
{
|
||||||
|
static route = "Personal";
|
||||||
|
static array = {};
|
||||||
|
static groups = {};
|
||||||
|
static subs = {
|
||||||
|
"Abteilungen": {color: "purple", icon: "department.png", entries: []},
|
||||||
|
"Gruppen": {color: "olive", icon: "group.png", entries: []},
|
||||||
|
"Lehrgänge": {color: "teal", icon: "skill.png", entries: []},
|
||||||
|
"Einweisungen": {color: "chocolate", icon: "person.png", entries: []}
|
||||||
|
};
|
||||||
|
static selects = {
|
||||||
|
"Kategorie" : [],
|
||||||
|
"Funktion" : []
|
||||||
|
};
|
||||||
|
static admin = true; // global page admin rights
|
||||||
|
static prints = [];
|
||||||
|
static filter = [];
|
||||||
|
//static selected = [];
|
||||||
|
//static dropzones = {};
|
||||||
|
static visible = false;
|
||||||
|
|
||||||
|
constructor(groupName, json) {
|
||||||
|
super(
|
||||||
|
groupName,
|
||||||
|
json,
|
||||||
|
Personal.route,
|
||||||
|
Personal.array,
|
||||||
|
Personal.groups,
|
||||||
|
Personal.subs,
|
||||||
|
Personal.selects,
|
||||||
|
Personal.admin/*,
|
||||||
|
Personal.prints,
|
||||||
|
Personal.filter,
|
||||||
|
Personal.selected,
|
||||||
|
Personal.dropzones*/
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
init(json) {
|
||||||
|
this.OFNr = json.MAIN.OFNR;
|
||||||
|
this.PNr = json.MAIN.PNR;
|
||||||
|
this.Nr = json.MAIN.NR;
|
||||||
|
this.Login = json.MAIN.LOGIN;
|
||||||
|
this.Email = json.MAIN.EMAIL;
|
||||||
|
this.Vornamen = json.MAIN.VORNAMEN;
|
||||||
|
this.Nachnamen = json.MAIN.NACHNAMEN;
|
||||||
|
this.Name = json.MAIN.NAME;
|
||||||
|
this.Bild = json.MAIN.BILD;
|
||||||
|
this.Kategorie = json.MAIN.KATEGORIE;
|
||||||
|
this.Funktion = json.MAIN.FUNKTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderEntry(drawEdit) {
|
||||||
|
let html = "<div class='bild'>";
|
||||||
|
if (this.Bild.EXISTIERT) {
|
||||||
|
html+= "<img src='/pic.php/Personal/Bildadresse/" + this.Bild.ADRESSE + "?h=260' />";
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<div id='" + this.marker("Bild/Delete") + "' class='deleter'><img src='/res/dark/delete.png' /></div>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
html+= "<img src='/upl/Personal/Bildadresse/Nopic.svg' />";
|
||||||
|
}
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= "<form action='/" + this.marker("Bilder") + "' class='dropzone' id='dropzone_" + this.ID + "'></form>";
|
||||||
|
}
|
||||||
|
html += "</div><ul>";
|
||||||
|
|
||||||
|
// TODO: Draw selectbox
|
||||||
|
|
||||||
|
if (drawEdit) {
|
||||||
|
html+= this.renderField("number.png", this.renderInput("OFNr", this.OFNr, 1.5, 2) + "-" + this.renderInput("PNr", this.PNr, 2, 3))
|
||||||
|
+ this.renderField("person.png", this.renderInput("Nachnamen", this.Nachnamen) + ", " + this.renderInput("Vornamen", this.Vornamen))
|
||||||
|
+ this.renderField("type.png", this.renderSelect("Kategorie", this.Kategorie.ID))
|
||||||
|
+ this.renderField("note.png", this.renderInput("Login", this.Login) + "@feuerwehr-bs.net")
|
||||||
|
+ this.renderField("star.png", this.renderSelect("Funktion", this.Funktion.ID, 2.5))
|
||||||
|
+ this.renderButton("save.png", "Save", "Speichern")
|
||||||
|
+ this.renderButton("delete.png", "Delete", "Löschen")
|
||||||
|
+ this.renderButton("cancel.png", "Reset", "Zurücksetzen");
|
||||||
|
} else {
|
||||||
|
html+= this.renderField("number.png", this.Nr)
|
||||||
|
+ this.renderField("person.png", "<b>" + this.Nachnamen + ", " + this.Vornamen + "</b>")
|
||||||
|
+ (this.Login=="" || this.Login===null ? this.renderField("note.png", "<span class='red'>Kein Login!</span>") : "")
|
||||||
|
+ this.renderField("star.png", this.Funktion!==null ? this.Funktion.KÜRZEL : "-");
|
||||||
|
if (this.admin) {
|
||||||
|
html += this.renderButton("edit.png", "Edit", "Bearbeiten");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</ul>";
|
||||||
|
document.getElementById(this.marker("Main")).innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderAdd() {
|
||||||
|
let html = "<h1>Fahrzeug hinzufügen</h1>"
|
||||||
|
+ "<div class='toggleVisibility'><ul><li>Name: <input id='Personal/Add/Name' style='width: 8em;' /></li>"
|
||||||
|
+ "<li>Kürzel: <input id='Personal/Add/Kürzel' style='width: 4em;' /></li>"
|
||||||
|
+ "<li id='Personal/Add/Add' class='button'>Hinzufügen <img src='/res/dark/add.png' /></li></ul></div>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroup() {
|
||||||
|
return Personal.RenderGroup(this.groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderGroup(groupName) {
|
||||||
|
return {
|
||||||
|
begin: "<article id='Group/" + groupName + "'><div class='group_title'><h1>" + groupName + "</h1></div>",
|
||||||
|
end: "</article>"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static New(groupName, json) {
|
||||||
|
return new Personal(groupName, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
collect() {
|
||||||
|
return Personal.Collect(this.ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Collect(id, contextId = null) {
|
||||||
|
return {
|
||||||
|
ID: id,
|
||||||
|
OFNR: this.Value(id, "OFNr"),
|
||||||
|
PNR: this.Value(id, "PNr"),
|
||||||
|
NACHNAMEN: this.Value(id, "Nachnamen"),
|
||||||
|
VORNAMEN: this.Value(id, "Vornamen"),
|
||||||
|
KATEGORIE: this.Value(id, "Kategorie"),
|
||||||
|
LOGIN: this.Value(id, "Login"),
|
||||||
|
FUNKTION: this.Value(id, "Funktion")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static RenderSelect(selectId, fahrzeugId, nullable = true) {
|
||||||
|
let html = "<select id='" + selectId + "'>" + (nullable ? "<option value='null'></option>" : "");
|
||||||
|
for (let group in Personal.groups) {
|
||||||
|
for (let e in Personal.groups[group]) {
|
||||||
|
const entry = Personal.groups[group][e];
|
||||||
|
html+= "<option value='" + entry.ID + "'" + (entry.ID==fahrzeugId ? " selected" : "") + ">" + entry.Kürzel + "</option>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
html+= "</select>";
|
||||||
|
return html;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
client/scanner.js
Normal file
92
client/scanner.js
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
class Scanner
|
||||||
|
{
|
||||||
|
static api;
|
||||||
|
static settings;
|
||||||
|
static scanned;
|
||||||
|
|
||||||
|
constructor(parent) {
|
||||||
|
Scanner.api = parent;
|
||||||
|
this.qr = null;
|
||||||
|
this.cameras = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
Start(id, subroute, parser) {
|
||||||
|
Scanner.settings = {
|
||||||
|
subroute: subroute,
|
||||||
|
parser: parser,
|
||||||
|
id: id
|
||||||
|
};
|
||||||
|
Scanner.scanned = {primary: {}, secondary: {}};
|
||||||
|
for (let i in parser) {
|
||||||
|
if (i!=subroute) {
|
||||||
|
Scanner.scanned.secondary[parser[i]] = null;
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Find already parsed IDs
|
||||||
|
const subData = Scanner.api.activeRoute.resourcesIndex[id].data.SUB[parser[subroute]];
|
||||||
|
for (let i in subData) {
|
||||||
|
Scanner.scanned.primary[subData[i].ID] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.qr = new QrScanner(
|
||||||
|
document.getElementById("qrscanner_video"),
|
||||||
|
Scanner.parse,
|
||||||
|
{returnDetailedScanResult: true, highlightScanRegion: true, highlightCodeOutline: true}
|
||||||
|
);
|
||||||
|
if (QrScanner.hasCamera()) {
|
||||||
|
let _this = this;
|
||||||
|
QrScanner.listCameras(true)
|
||||||
|
.then(result => _this.cameras = result)
|
||||||
|
.catch(error => alert(error || 'Camera detection failed.'));
|
||||||
|
}
|
||||||
|
this.cameraIndex = 0;
|
||||||
|
document.getElementById("qrscanner_text").innerHTML = "";
|
||||||
|
|
||||||
|
this.qr.start();
|
||||||
|
}
|
||||||
|
Stop() {
|
||||||
|
this.qr.stop();
|
||||||
|
this.qr.destroy();
|
||||||
|
this.qr = null;
|
||||||
|
}
|
||||||
|
SwitchCamera() {
|
||||||
|
if (this.qr!==null && QrScanner.hasCamera()) {
|
||||||
|
++this.cameraIndex;
|
||||||
|
if (this.cameraIndex>=this.cameras.length) {
|
||||||
|
this.cameraIndex = 0;
|
||||||
|
}
|
||||||
|
this.qr.setCamera(this.cameras[this.cameraIndex].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static parse(result) {
|
||||||
|
let scan = result.data.split("/");
|
||||||
|
if (scan.length==3) {
|
||||||
|
let route = scan[1];
|
||||||
|
let subid = scan[2];
|
||||||
|
|
||||||
|
// Is it a route we should parse?
|
||||||
|
if (typeof(Scanner.settings.parser[route])!="undefined") {
|
||||||
|
const marker = Scanner.settings.parser[route];
|
||||||
|
|
||||||
|
// Is it the primary route and if yes, has this ID been parsed before?
|
||||||
|
if (route==Scanner.settings.subroute && typeof(Scanner.scanned.primary[subid])=="undefined") {
|
||||||
|
Scanner.scanned.primary[subid] = true;
|
||||||
|
|
||||||
|
let index = -1;
|
||||||
|
|
||||||
|
const options = Scanner.api.activeRoute.options[marker];
|
||||||
|
for (let i in options) {
|
||||||
|
if (options[i].ID==subid) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
// Has the ID been found?
|
||||||
|
if (index>=0) {
|
||||||
|
document.getElementById("qrscanner_text").innerHTML += options[index].NAME + "<br />";
|
||||||
|
Scanner.api.Request("POST", Scanner.scanned.secondary, Scanner.settings.id, Scanner.settings.subroute, subid);
|
||||||
|
}
|
||||||
|
} else if(route!=Scanner.settings.subroute) { // Is it a secondary field?
|
||||||
|
Scanner.scanned.secondary[marker] = subid;
|
||||||
|
} } } }
|
||||||
|
}
|
||||||
|
|
||||||
271
client/template.js
Normal file
271
client/template.js
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
class TemplateEngine
|
||||||
|
{
|
||||||
|
constructor(tpl, adminData) {
|
||||||
|
this.tpl = tpl;
|
||||||
|
this.adminData = adminData;
|
||||||
|
this.modeFallback = {
|
||||||
|
":EDIT": ":ADMIN",
|
||||||
|
":ADMIN": "",
|
||||||
|
"": null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Render(content, marker, mode = "") {
|
||||||
|
return this.render(marker, content, this.extractSection(this.tpl, marker, mode), mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
render(sections, content, tpl, mode = "") {
|
||||||
|
// content has to be an object of `marker` => `data`
|
||||||
|
// data can be a primitive, an array, or an object
|
||||||
|
// Boolean corresponds to a conditional section, call replaceCondition
|
||||||
|
// String and number correspond to a simple marker, call replaceMarker for it
|
||||||
|
// Array and object always correspond to a section
|
||||||
|
// Object can be handed recursively to a new instance of render. Be sure to extract the correct section of tpl though
|
||||||
|
// If data is an array, iterate over it and hand every entry to a new instance like with object. Concatenate the results
|
||||||
|
if (typeof(content)!="object" || tpl=="")
|
||||||
|
return "";
|
||||||
|
var ret = tpl;
|
||||||
|
for (var marker in content) {
|
||||||
|
var sectionMarker = (sections!="" ? sections+"."+marker : marker);
|
||||||
|
var type = typeof(content[marker]);
|
||||||
|
|
||||||
|
if (mode==":EDIT" && sections.indexOf("MAIN")!=-1 && typeof(this.adminData[marker])!="undefined") {
|
||||||
|
var data = this.adminData[marker];
|
||||||
|
var admin_tpl = this.extractSection(ret, sectionMarker, mode);
|
||||||
|
var admin_ret = "";
|
||||||
|
for (var i in data) {
|
||||||
|
if (content[marker]==null) {
|
||||||
|
data[i].SELECTED = data[i].ID=="__NULL__";
|
||||||
|
} else if (Array.isArray(content[marker])) {
|
||||||
|
var selected = false;
|
||||||
|
for (var j in content[marker]) {
|
||||||
|
if (data[i].ID==content[marker][j].ID) {
|
||||||
|
selected = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data[i].SELECTED = selected;
|
||||||
|
} else {
|
||||||
|
data[i].SELECTED = data[i].ID==content[marker].ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
admin_ret+= this.render(sectionMarker, data[i], admin_tpl, "");
|
||||||
|
}
|
||||||
|
ret = this.replaceSection(ret, sectionMarker, admin_ret, "");
|
||||||
|
} else {
|
||||||
|
if (content[marker]==null) {
|
||||||
|
ret = this.replaceSection(ret, sectionMarker, "", mode);
|
||||||
|
ret = this.replaceMarker(ret, sectionMarker, "__NULL__", mode);
|
||||||
|
} if (type=="boolean") {
|
||||||
|
ret = this.replaceCondition(ret, sectionMarker, content[marker], mode);
|
||||||
|
} else if (type=="string" && "__CASE__:"==content[marker].substr(0, 9)) {
|
||||||
|
ret = this.replaceSwitch(ret, sectionMarker, content[marker].split(":"), mode);
|
||||||
|
} else if (type=="string" || type=="number") {
|
||||||
|
ret = this.replaceMarker(ret, sectionMarker, content[marker], mode);
|
||||||
|
} else if (type=="object" && Array.isArray(content[marker])) {
|
||||||
|
var arr_tpl = this.extractSection(ret, sectionMarker, mode);
|
||||||
|
var arr_ret = "";
|
||||||
|
for (var ind in content[marker]) {
|
||||||
|
arr_ret+= this.render(sectionMarker, content[marker][ind], arr_tpl, mode);
|
||||||
|
}
|
||||||
|
ret = this.replaceSection(ret, sectionMarker, arr_ret, mode);
|
||||||
|
} else if (type=="object") { // Hopefully a JSON
|
||||||
|
ret = this.replaceSection(ret, sectionMarker, this.render(sectionMarker, content[marker], this.extractSection(ret, sectionMarker, mode), mode), mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
makeMarker(marker) {
|
||||||
|
return "###" + marker + "###";
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSectionMarker(marker, pos) { //creates START or END marker of section
|
||||||
|
return "<!-- " + marker + " " + pos + " -->";
|
||||||
|
}
|
||||||
|
|
||||||
|
findMarkerModes(tpl, section) {
|
||||||
|
var ret = [];
|
||||||
|
var sectionMarker = this.makeMarker(section+":");
|
||||||
|
var markerLength = 5 + sectionMarker.length - 3; // 5 = "<!-- ", 3 = "###"
|
||||||
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, markerLength);
|
||||||
|
for (var startIndex = 0; startIndex < tpl.length;) {
|
||||||
|
var foundIndex = tpl.indexOf(anyMarker, startIndex);
|
||||||
|
if (foundIndex==-1) {
|
||||||
|
startIndex = tpl.length;
|
||||||
|
} else {
|
||||||
|
var markerEnd = tpl.indexOf("###", foundIndex + markerLength);
|
||||||
|
var mode = tpl.substr(foundIndex + markerLength - 1, markerEnd - foundIndex - markerLength + 1); // Should be e.g. :EDIT
|
||||||
|
var endMarker = this.makeSectionMarker(this.makeMarker(section + mode), "END");
|
||||||
|
var endPos = tpl.indexOf(endMarker, markerEnd);
|
||||||
|
if (endPos==-1) {
|
||||||
|
startIndex = tpl.length;
|
||||||
|
} else {
|
||||||
|
startIndex = endPos + markerLength;
|
||||||
|
ret.push(mode);
|
||||||
|
} } }
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteSection(tpl, section) {
|
||||||
|
var sectionMarker = this.makeMarker(section);
|
||||||
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, 5 + sectionMarker.length); // 5 = "<!-- "
|
||||||
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
||||||
|
var startPos = tpl.indexOf(anyMarker);
|
||||||
|
var endPos = tpl.lastIndexOf(endMarker) + endMarker.length;
|
||||||
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
||||||
|
return tpl;
|
||||||
|
}
|
||||||
|
var ret = (startPos>0 ? tpl.substr(0, startPos) : "");
|
||||||
|
if (endPos<tpl.length) ret+= tpl.substr(endPos);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
findAndDeleteUnneededModes(tpl, section, mode = "") {
|
||||||
|
var ret = tpl;
|
||||||
|
// Find and destroy unneeded mode siblings
|
||||||
|
var modes = this.findMarkerModes(tpl, section);
|
||||||
|
var chosenMode = mode;
|
||||||
|
while (!modes.includes(chosenMode) && this.modeFallback[chosenMode]!=null) {
|
||||||
|
chosenMode = this.modeFallback[chosenMode];
|
||||||
|
}
|
||||||
|
for (var i in modes) {
|
||||||
|
if (modes[i]!=chosenMode) {
|
||||||
|
ret = this.deleteSection(ret, section + modes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chosenMode!="") {
|
||||||
|
ret = this.deleteSection(ret, section);
|
||||||
|
}
|
||||||
|
return {"tpl": ret, "mode": chosenMode};
|
||||||
|
}
|
||||||
|
|
||||||
|
extractSection(tpl, section, mode = "") { //Extracts a section from a template
|
||||||
|
var sectionMarker = this.makeMarker(section + mode);
|
||||||
|
var startMarker = this.makeSectionMarker(sectionMarker, "START");
|
||||||
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
||||||
|
var startPos = tpl.indexOf(startMarker) + startMarker.length;
|
||||||
|
var endPos = tpl.lastIndexOf(endMarker);
|
||||||
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
||||||
|
return (this.modeFallback[mode]!=null ? this.extractSection(tpl, section, this.modeFallback[mode]) : "");
|
||||||
|
}
|
||||||
|
return tpl.substr(startPos,endPos-startPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceSection(tpl, section, value, mode = "") { //replaces a section in a template with a value
|
||||||
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
||||||
|
|
||||||
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
||||||
|
var startMarker = this.makeSectionMarker(sectionMarker, "START");
|
||||||
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
||||||
|
var startPos = modeTpl.tpl.indexOf(startMarker);
|
||||||
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
||||||
|
if ((startPos==-1) || (endPos==-1) || (endPos<=startPos)) {return modeTpl.tpl;}
|
||||||
|
var ret = (startPos>0 ? modeTpl.tpl.substr(0, startPos) : "");
|
||||||
|
ret+= value;
|
||||||
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceCondition(tpl, section, bool, mode = "") { // selects one of two sections in a template
|
||||||
|
// Template looks like this:
|
||||||
|
// <!-- ###SECTION### IF -->
|
||||||
|
// True text
|
||||||
|
// <!-- ###SECTION### ELSE -->
|
||||||
|
// False text
|
||||||
|
// <!-- ###SECTION### END -->
|
||||||
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
||||||
|
|
||||||
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
||||||
|
var ifMarker = this.makeSectionMarker(sectionMarker, "IF");
|
||||||
|
var elseMarker = this.makeSectionMarker(sectionMarker, "ELSE");
|
||||||
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
||||||
|
|
||||||
|
var startPos = modeTpl.tpl.indexOf(ifMarker);
|
||||||
|
var else_startPos = modeTpl.tpl.indexOf(elseMarker);
|
||||||
|
var else_endPos = else_startPos + elseMarker.length;
|
||||||
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
||||||
|
|
||||||
|
if ((startPos==-1) || (else_startPos==-1) || (endPos==-1) || (endPos<=startPos)) {
|
||||||
|
if (bool) {
|
||||||
|
return modeTpl.tpl;
|
||||||
|
} else {
|
||||||
|
return this.replaceSection(modeTpl.tpl, section, "", modeTpl.mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = "";
|
||||||
|
if (bool) {
|
||||||
|
// Show if-part
|
||||||
|
value = modeTpl.tpl.substr(startPos, else_startPos - startPos);
|
||||||
|
} else {
|
||||||
|
// Show else-part
|
||||||
|
value = modeTpl.tpl.substr(else_endPos, endPos - else_endPos - endMarker.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ret = (startPos>0 ? modeTpl.tpl.substr(0, startPos) : "");
|
||||||
|
ret+= value;
|
||||||
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceSwitch(tpl, section, keys, mode = "") { // selects one or more of a variable number of cases from a section in a template
|
||||||
|
// Template looks like this:
|
||||||
|
// <!-- ###SECTION### SWITCH -->
|
||||||
|
// Default text
|
||||||
|
// <!-- ###SECTION### CASE_A -->
|
||||||
|
// Text A
|
||||||
|
// <!-- ###SECTION### CASE_B -->
|
||||||
|
// Text B
|
||||||
|
// <!-- ###SECTION### END -->
|
||||||
|
var modeTpl = this.findAndDeleteUnneededModes(tpl, section, mode);
|
||||||
|
var sectionMarker = this.makeMarker(section + modeTpl.mode);
|
||||||
|
|
||||||
|
var switchMarker = this.makeSectionMarker(sectionMarker, "SWITCH");
|
||||||
|
var endMarker = this.makeSectionMarker(sectionMarker, "END");
|
||||||
|
var anyMarker = this.makeSectionMarker(sectionMarker, "").substr(0, 5 + sectionMarker.length); // 5 = "<!-- "
|
||||||
|
|
||||||
|
var switchPos = modeTpl.tpl.indexOf(switchMarker);
|
||||||
|
var endPos = modeTpl.tpl.lastIndexOf(endMarker) + endMarker.length;
|
||||||
|
if (switchPos==-1 || endPos==-1) {return modeTpl.tpl;}
|
||||||
|
|
||||||
|
var value = "";
|
||||||
|
for (var i = 1; i<keys.length; i++) {
|
||||||
|
var keyMarker = this.makeSectionMarker(sectionMarker, keys[i]);
|
||||||
|
var keyPos = modeTpl.tpl.indexOf(keyMarker);
|
||||||
|
|
||||||
|
if (keyPos==-1) {
|
||||||
|
keyPos = switchPos + switchMarker.length; // Case `key` wasn't found, use default case
|
||||||
|
} else {
|
||||||
|
keyPos+= keyMarker.length;
|
||||||
|
}
|
||||||
|
// Find the start of the next marker.
|
||||||
|
var nextPos = modeTpl.tpl.indexOf(anyMarker, keyPos);
|
||||||
|
if (nextPos==-1) {
|
||||||
|
// Shouldn't happen. If it does, endMarker is before keyMarker
|
||||||
|
nextPos = endPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
value+= modeTpl.tpl.substr(keyPos, nextPos - keyPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ret = (switchPos>0 ? modeTpl.tpl.substr(0, switchPos) : "");
|
||||||
|
ret+= value;
|
||||||
|
if (endPos<modeTpl.tpl.length) ret+= modeTpl.tpl.substr(endPos);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
replaceMarker(tpl, marker, value) { //replaces a marker in a template with a value
|
||||||
|
return tpl.replaceAll(this.makeMarker(marker), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* replaceMarkerArray(tpl, array) { //replaces all markers in a template with values from an array (index defines marker)
|
||||||
|
for (var ind in array)
|
||||||
|
{
|
||||||
|
tpl = tpl.replaceAll(this.makeMarker(ind), array[ind]);
|
||||||
|
}
|
||||||
|
return tpl;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
79
client/toastify.css
Normal file
79
client/toastify.css
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/*!
|
||||||
|
* Toastify js 1.9.3
|
||||||
|
* https://github.com/apvarun/toastify-js
|
||||||
|
* @license MIT licensed
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Varun A P
|
||||||
|
*/
|
||||||
|
|
||||||
|
.toastify {
|
||||||
|
padding: 12px 20px;
|
||||||
|
color: #ffffff;
|
||||||
|
display: inline-block;
|
||||||
|
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.12), 0 10px 36px -4px rgba(77, 96, 232, 0.3);
|
||||||
|
background: -webkit-linear-gradient(315deg, #73a5ff, #5477f5);
|
||||||
|
background: linear-gradient(135deg, #73a5ff, #5477f5);
|
||||||
|
position: fixed;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.4s cubic-bezier(0.215, 0.61, 0.355, 1);
|
||||||
|
border-radius: 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
max-width: calc(50% - 20px);
|
||||||
|
z-index: 2147483647;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify.on {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast-close {
|
||||||
|
opacity: 0.4;
|
||||||
|
padding: 0 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-right {
|
||||||
|
right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-left {
|
||||||
|
left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-top {
|
||||||
|
top: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-bottom {
|
||||||
|
bottom: -150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-rounded {
|
||||||
|
border-radius: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-avatar {
|
||||||
|
width: 1.5em;
|
||||||
|
height: 1.5em;
|
||||||
|
margin: -7px 5px;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toastify-center {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
max-width: fit-content;
|
||||||
|
max-width: -moz-fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 360px) {
|
||||||
|
.toastify-right, .toastify-left {
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
max-width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
394
client/toastify.js
Normal file
394
client/toastify.js
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
/*!
|
||||||
|
* Toastify js 1.9.3
|
||||||
|
* https://github.com/apvarun/toastify-js
|
||||||
|
* @license MIT licensed
|
||||||
|
*
|
||||||
|
* Copyright (C) 2018 Varun A P
|
||||||
|
*/
|
||||||
|
(function(root, factory) {
|
||||||
|
if (typeof module === "object" && module.exports) {
|
||||||
|
module.exports = factory();
|
||||||
|
} else {
|
||||||
|
root.Toastify = factory();
|
||||||
|
}
|
||||||
|
})(this, function(global) {
|
||||||
|
// Object initialization
|
||||||
|
var Toastify = function(options) {
|
||||||
|
// Returning a new init object
|
||||||
|
return new Toastify.lib.init(options);
|
||||||
|
},
|
||||||
|
// Library version
|
||||||
|
version = "1.9.3";
|
||||||
|
|
||||||
|
// Defining the prototype of the object
|
||||||
|
Toastify.lib = Toastify.prototype = {
|
||||||
|
toastify: version,
|
||||||
|
|
||||||
|
constructor: Toastify,
|
||||||
|
|
||||||
|
// Initializing the object with required parameters
|
||||||
|
init: function(options) {
|
||||||
|
// Verifying and validating the input object
|
||||||
|
if (!options) {
|
||||||
|
options = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the options object
|
||||||
|
this.options = {};
|
||||||
|
|
||||||
|
this.toastElement = null;
|
||||||
|
|
||||||
|
// Validating the options
|
||||||
|
this.options.text = options.text || "Hi there!"; // Display message
|
||||||
|
this.options.node = options.node // Display content as node
|
||||||
|
this.options.duration = options.duration === 0 ? 0 : options.duration || 3000; // Display duration
|
||||||
|
this.options.selector = options.selector; // Parent selector
|
||||||
|
this.options.callback = options.callback || function() {}; // Callback after display
|
||||||
|
this.options.destination = options.destination; // On-click destination
|
||||||
|
this.options.newWindow = options.newWindow || false; // Open destination in new window
|
||||||
|
this.options.close = options.close || false; // Show toast close icon
|
||||||
|
this.options.gravity = options.gravity === "bottom" ? "toastify-bottom" : "toastify-top"; // toast position - top or bottom
|
||||||
|
this.options.positionLeft = options.positionLeft || false; // toast position - left or right
|
||||||
|
this.options.position = options.position || ''; // toast position - left or right
|
||||||
|
this.options.backgroundColor = options.backgroundColor; // toast background color
|
||||||
|
this.options.avatar = options.avatar || ""; // img element src - url or a path
|
||||||
|
this.options.className = options.className || ""; // additional class names for the toast
|
||||||
|
this.options.stopOnFocus = options.stopOnFocus === undefined? true: options.stopOnFocus; // stop timeout on focus
|
||||||
|
this.options.onClick = options.onClick; // Callback after click
|
||||||
|
|
||||||
|
this.options.offset = options.offset || { x: 0, y: 0 }; // toast offset
|
||||||
|
|
||||||
|
// Returning the current object for chaining functions
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Building the DOM element
|
||||||
|
buildToast: function() {
|
||||||
|
// Validating if the options are defined
|
||||||
|
if (!this.options) {
|
||||||
|
throw "Toastify is not initialized";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creating the DOM object
|
||||||
|
var divElement = document.createElement("div");
|
||||||
|
divElement.className = "toastify on " + this.options.className;
|
||||||
|
|
||||||
|
// Positioning toast to left or right or center
|
||||||
|
if (!!this.options.position) {
|
||||||
|
divElement.className += " toastify-" + this.options.position;
|
||||||
|
} else {
|
||||||
|
// To be depreciated in further versions
|
||||||
|
if (this.options.positionLeft === true) {
|
||||||
|
divElement.className += " toastify-left";
|
||||||
|
console.warn('Property `positionLeft` will be depreciated in further versions. Please use `position` instead.')
|
||||||
|
} else {
|
||||||
|
// Default position
|
||||||
|
divElement.className += " toastify-right";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assigning gravity of element
|
||||||
|
divElement.className += " " + this.options.gravity;
|
||||||
|
|
||||||
|
if (this.options.backgroundColor) {
|
||||||
|
divElement.style.background = this.options.backgroundColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the toast message/node
|
||||||
|
if (this.options.node && this.options.node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
// If we have a valid node, we insert it
|
||||||
|
divElement.appendChild(this.options.node)
|
||||||
|
} else {
|
||||||
|
divElement.innerHTML = this.options.text;
|
||||||
|
|
||||||
|
if (this.options.avatar !== "") {
|
||||||
|
var avatarElement = document.createElement("img");
|
||||||
|
avatarElement.src = this.options.avatar;
|
||||||
|
|
||||||
|
avatarElement.className = "toastify-avatar";
|
||||||
|
|
||||||
|
if (this.options.position == "left" || this.options.positionLeft === true) {
|
||||||
|
// Adding close icon on the left of content
|
||||||
|
divElement.appendChild(avatarElement);
|
||||||
|
} else {
|
||||||
|
// Adding close icon on the right of content
|
||||||
|
divElement.insertAdjacentElement("afterbegin", avatarElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding a close icon to the toast
|
||||||
|
if (this.options.close === true) {
|
||||||
|
// Create a span for close element
|
||||||
|
var closeElement = document.createElement("span");
|
||||||
|
closeElement.innerHTML = "✖";
|
||||||
|
|
||||||
|
closeElement.className = "toast-close";
|
||||||
|
|
||||||
|
// Triggering the removal of toast from DOM on close click
|
||||||
|
closeElement.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.removeElement(this.toastElement);
|
||||||
|
window.clearTimeout(this.toastElement.timeOutValue);
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Calculating screen width
|
||||||
|
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||||
|
|
||||||
|
// Adding the close icon to the toast element
|
||||||
|
// Display on the right if screen width is less than or equal to 360px
|
||||||
|
if ((this.options.position == "left" || this.options.positionLeft === true) && width > 360) {
|
||||||
|
// Adding close icon on the left of content
|
||||||
|
divElement.insertAdjacentElement("afterbegin", closeElement);
|
||||||
|
} else {
|
||||||
|
// Adding close icon on the right of content
|
||||||
|
divElement.appendChild(closeElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear timeout while toast is focused
|
||||||
|
if (this.options.stopOnFocus && this.options.duration > 0) {
|
||||||
|
var self = this;
|
||||||
|
// stop countdown
|
||||||
|
divElement.addEventListener(
|
||||||
|
"mouseover",
|
||||||
|
function(event) {
|
||||||
|
window.clearTimeout(divElement.timeOutValue);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// add back the timeout
|
||||||
|
divElement.addEventListener(
|
||||||
|
"mouseleave",
|
||||||
|
function() {
|
||||||
|
divElement.timeOutValue = window.setTimeout(
|
||||||
|
function() {
|
||||||
|
// Remove the toast from DOM
|
||||||
|
self.removeElement(divElement);
|
||||||
|
},
|
||||||
|
self.options.duration
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding an on-click destination path
|
||||||
|
if (typeof this.options.destination !== "undefined") {
|
||||||
|
divElement.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (this.options.newWindow === true) {
|
||||||
|
window.open(this.options.destination, "_blank");
|
||||||
|
} else {
|
||||||
|
window.location = this.options.destination;
|
||||||
|
}
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this.options.onClick === "function" && typeof this.options.destination === "undefined") {
|
||||||
|
divElement.addEventListener(
|
||||||
|
"click",
|
||||||
|
function(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
this.options.onClick();
|
||||||
|
}.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding offset
|
||||||
|
if(typeof this.options.offset === "object") {
|
||||||
|
|
||||||
|
var x = getAxisOffsetAValue("x", this.options);
|
||||||
|
var y = getAxisOffsetAValue("y", this.options);
|
||||||
|
|
||||||
|
var xOffset = this.options.position == "left" ? x : "-" + x;
|
||||||
|
var yOffset = this.options.gravity == "toastify-top" ? y : "-" + y;
|
||||||
|
|
||||||
|
divElement.style.transform = "translate(" + xOffset + "," + yOffset + ")";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returning the generated element
|
||||||
|
return divElement;
|
||||||
|
},
|
||||||
|
|
||||||
|
// Displaying the toast
|
||||||
|
showToast: function() {
|
||||||
|
// Creating the DOM object for the toast
|
||||||
|
this.toastElement = this.buildToast();
|
||||||
|
|
||||||
|
// Getting the root element to with the toast needs to be added
|
||||||
|
var rootElement;
|
||||||
|
if (typeof this.options.selector === "undefined") {
|
||||||
|
rootElement = document.body;
|
||||||
|
} else {
|
||||||
|
rootElement = document.getElementById(this.options.selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validating if root element is present in DOM
|
||||||
|
if (!rootElement) {
|
||||||
|
throw "Root element is not defined";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adding the DOM element
|
||||||
|
rootElement.insertBefore(this.toastElement, rootElement.firstChild);
|
||||||
|
|
||||||
|
// Repositioning the toasts in case multiple toasts are present
|
||||||
|
Toastify.reposition();
|
||||||
|
|
||||||
|
if (this.options.duration > 0) {
|
||||||
|
this.toastElement.timeOutValue = window.setTimeout(
|
||||||
|
function() {
|
||||||
|
// Remove the toast from DOM
|
||||||
|
this.removeElement(this.toastElement);
|
||||||
|
}.bind(this),
|
||||||
|
this.options.duration
|
||||||
|
); // Binding `this` for function invocation
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supporting function chaining
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
hideToast: function() {
|
||||||
|
if (this.toastElement.timeOutValue) {
|
||||||
|
clearTimeout(this.toastElement.timeOutValue);
|
||||||
|
}
|
||||||
|
this.removeElement(this.toastElement);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Removing the element from the DOM
|
||||||
|
removeElement: function(toastElement) {
|
||||||
|
// Hiding the element
|
||||||
|
// toastElement.classList.remove("on");
|
||||||
|
toastElement.className = toastElement.className.replace(" on", "");
|
||||||
|
|
||||||
|
// Removing the element from DOM after transition end
|
||||||
|
window.setTimeout(
|
||||||
|
function() {
|
||||||
|
// remove options node if any
|
||||||
|
if (this.options.node && this.options.node.parentNode) {
|
||||||
|
this.options.node.parentNode.removeChild(this.options.node);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the elemenf from the DOM, only when the parent node was not removed before.
|
||||||
|
if (toastElement.parentNode) {
|
||||||
|
toastElement.parentNode.removeChild(toastElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calling the callback function
|
||||||
|
this.options.callback.call(toastElement);
|
||||||
|
|
||||||
|
// Repositioning the toasts again
|
||||||
|
Toastify.reposition();
|
||||||
|
}.bind(this),
|
||||||
|
400
|
||||||
|
); // Binding `this` for function invocation
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Positioning the toasts on the DOM
|
||||||
|
Toastify.reposition = function() {
|
||||||
|
|
||||||
|
// Top margins with gravity
|
||||||
|
var topLeftOffsetSize = {
|
||||||
|
top: 15,
|
||||||
|
bottom: 15,
|
||||||
|
};
|
||||||
|
var topRightOffsetSize = {
|
||||||
|
top: 15,
|
||||||
|
bottom: 15,
|
||||||
|
};
|
||||||
|
var offsetSize = {
|
||||||
|
top: 15,
|
||||||
|
bottom: 15,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get all toast messages on the DOM
|
||||||
|
var allToasts = document.getElementsByClassName("toastify");
|
||||||
|
|
||||||
|
var classUsed;
|
||||||
|
|
||||||
|
// Modifying the position of each toast element
|
||||||
|
for (var i = 0; i < allToasts.length; i++) {
|
||||||
|
// Getting the applied gravity
|
||||||
|
if (containsClass(allToasts[i], "toastify-top") === true) {
|
||||||
|
classUsed = "toastify-top";
|
||||||
|
} else {
|
||||||
|
classUsed = "toastify-bottom";
|
||||||
|
}
|
||||||
|
|
||||||
|
var height = allToasts[i].offsetHeight;
|
||||||
|
classUsed = classUsed.substr(9, classUsed.length-1)
|
||||||
|
// Spacing between toasts
|
||||||
|
var offset = 15;
|
||||||
|
|
||||||
|
var width = window.innerWidth > 0 ? window.innerWidth : screen.width;
|
||||||
|
|
||||||
|
// Show toast in center if screen with less than or qual to 360px
|
||||||
|
if (width <= 360) {
|
||||||
|
// Setting the position
|
||||||
|
allToasts[i].style[classUsed] = offsetSize[classUsed] + "px";
|
||||||
|
|
||||||
|
offsetSize[classUsed] += height + offset;
|
||||||
|
} else {
|
||||||
|
if (containsClass(allToasts[i], "toastify-left") === true) {
|
||||||
|
// Setting the position
|
||||||
|
allToasts[i].style[classUsed] = topLeftOffsetSize[classUsed] + "px";
|
||||||
|
|
||||||
|
topLeftOffsetSize[classUsed] += height + offset;
|
||||||
|
} else {
|
||||||
|
// Setting the position
|
||||||
|
allToasts[i].style[classUsed] = topRightOffsetSize[classUsed] + "px";
|
||||||
|
|
||||||
|
topRightOffsetSize[classUsed] += height + offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Supporting function chaining
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper function to get offset.
|
||||||
|
function getAxisOffsetAValue(axis, options) {
|
||||||
|
|
||||||
|
if(options.offset[axis]) {
|
||||||
|
if(isNaN(options.offset[axis])) {
|
||||||
|
return options.offset[axis];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return options.offset[axis] + 'px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '0px';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsClass(elem, yourClass) {
|
||||||
|
if (!elem || typeof yourClass !== "string") {
|
||||||
|
return false;
|
||||||
|
} else if (
|
||||||
|
elem.className &&
|
||||||
|
elem.className
|
||||||
|
.trim()
|
||||||
|
.split(/\s+/gi)
|
||||||
|
.indexOf(yourClass) > -1
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setting up the prototype for the init object
|
||||||
|
Toastify.lib.init.prototype = Toastify.lib;
|
||||||
|
|
||||||
|
// Returning the Toastify function to be assigned to the window object/module
|
||||||
|
return Toastify;
|
||||||
|
});
|
||||||
7
conf/10_mysql.php.dist
Normal file
7
conf/10_mysql.php.dist
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?php #var/00_mysql.php
|
||||||
|
|
||||||
|
//Mysql login
|
||||||
|
define("DBHOST", "localhost");
|
||||||
|
define("DBUSER", "");
|
||||||
|
define("DBPASS", "");
|
||||||
|
define("DBNAME", "");
|
||||||
4
conf/20_redis.php
Normal file
4
conf/20_redis.php
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?php #var/01_redis.php
|
||||||
|
|
||||||
|
define("REDIS_SOCKET", "/run/redis/redis.sock");
|
||||||
|
|
||||||
12
conf/30_login.php
Normal file
12
conf/30_login.php
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?php #var/30_login.php
|
||||||
|
|
||||||
|
//Login related stuff
|
||||||
|
define("DESIRED_ITERATIONS", 160000);
|
||||||
|
define(
|
||||||
|
"JWT_KEY",
|
||||||
|
"v9upLZkm7uVGH9zqEGnp4sZAqeJX33vygYBTKEGdVqtsKeEKaSGp2KBnZmcVNPDb"
|
||||||
|
."3BkXGwdp9GMDwjaqp6BqfVNRYQf9X5PC8BP74UZPKGPujCeAsayMRpLv2GPBUgXz"
|
||||||
|
."TVY36KcpYy77SPUmZ7zY2q7ZTFEvM73jMBufdUV8fcEDp4eTQDqMpvp8dhkDxZPj"
|
||||||
|
."wVYtkHxLtkTHVa9DMm2pWmXqhsjJckgPMxdRsgngCSv8H8BBjXFkG2dn7ndJeApF"
|
||||||
|
);
|
||||||
|
define("JWT_VALID_TIME", 7 * 24 * 60 * 60);
|
||||||
6
conf/40_time.php
Normal file
6
conf/40_time.php
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?php #conf/40_time.php
|
||||||
|
|
||||||
|
// Date and time locale
|
||||||
|
setlocale(LC_TIME, "de_DE.UTF-8");
|
||||||
|
setlocale(LC_ALL,"de_DE.UTF8");
|
||||||
|
$formatter = new IntlDateFormatter('de_DE', IntlDateFormatter::LONG, IntlDateFormatter::NONE);
|
||||||
188
conf/50_misc.php
Normal file
188
conf/50_misc.php
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
<?php #conf/50_misc.php
|
||||||
|
|
||||||
|
//Additional stuff
|
||||||
|
define ("TITLE", "OF Innenstadt-App");
|
||||||
|
|
||||||
|
define("MIME_MAP", [
|
||||||
|
'video/3gpp2' => '3g2',
|
||||||
|
'video/3gp' => '3gp',
|
||||||
|
'video/3gpp' => '3gp',
|
||||||
|
'application/x-compressed' => '7zip',
|
||||||
|
'audio/x-acc' => 'aac',
|
||||||
|
'audio/ac3' => 'ac3',
|
||||||
|
'application/postscript' => 'ai',
|
||||||
|
'audio/x-aiff' => 'aif',
|
||||||
|
'audio/aiff' => 'aif',
|
||||||
|
'audio/x-au' => 'au',
|
||||||
|
'video/x-msvideo' => 'avi',
|
||||||
|
'video/msvideo' => 'avi',
|
||||||
|
'video/avi' => 'avi',
|
||||||
|
'application/x-troff-msvideo' => 'avi',
|
||||||
|
'application/macbinary' => 'bin',
|
||||||
|
'application/mac-binary' => 'bin',
|
||||||
|
'application/x-binary' => 'bin',
|
||||||
|
'application/x-macbinary' => 'bin',
|
||||||
|
'image/bmp' => 'bmp',
|
||||||
|
'image/x-bmp' => 'bmp',
|
||||||
|
'image/x-bitmap' => 'bmp',
|
||||||
|
'image/x-xbitmap' => 'bmp',
|
||||||
|
'image/x-win-bitmap' => 'bmp',
|
||||||
|
'image/x-windows-bmp' => 'bmp',
|
||||||
|
'image/ms-bmp' => 'bmp',
|
||||||
|
'image/x-ms-bmp' => 'bmp',
|
||||||
|
'application/bmp' => 'bmp',
|
||||||
|
'application/x-bmp' => 'bmp',
|
||||||
|
'application/x-win-bitmap' => 'bmp',
|
||||||
|
'application/cdr' => 'cdr',
|
||||||
|
'application/coreldraw' => 'cdr',
|
||||||
|
'application/x-cdr' => 'cdr',
|
||||||
|
'application/x-coreldraw' => 'cdr',
|
||||||
|
'image/cdr' => 'cdr',
|
||||||
|
'image/x-cdr' => 'cdr',
|
||||||
|
'zz-application/zz-winassoc-cdr' => 'cdr',
|
||||||
|
'application/mac-compactpro' => 'cpt',
|
||||||
|
'application/pkix-crl' => 'crl',
|
||||||
|
'application/pkcs-crl' => 'crl',
|
||||||
|
'application/x-x509-ca-cert' => 'crt',
|
||||||
|
'application/pkix-cert' => 'crt',
|
||||||
|
'text/css' => 'css',
|
||||||
|
'text/x-comma-separated-values' => 'csv',
|
||||||
|
'text/comma-separated-values' => 'csv',
|
||||||
|
'application/vnd.msexcel' => 'csv',
|
||||||
|
'application/x-director' => 'dcr',
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
|
||||||
|
'application/x-dvi' => 'dvi',
|
||||||
|
'message/rfc822' => 'eml',
|
||||||
|
'application/x-msdownload' => 'exe',
|
||||||
|
'video/x-f4v' => 'f4v',
|
||||||
|
'audio/x-flac' => 'flac',
|
||||||
|
'video/x-flv' => 'flv',
|
||||||
|
'image/gif' => 'gif',
|
||||||
|
'application/gpg-keys' => 'gpg',
|
||||||
|
'application/x-gtar' => 'gtar',
|
||||||
|
'application/x-gzip' => 'gzip',
|
||||||
|
'application/mac-binhex40' => 'hqx',
|
||||||
|
'application/mac-binhex' => 'hqx',
|
||||||
|
'application/x-binhex40' => 'hqx',
|
||||||
|
'application/x-mac-binhex40' => 'hqx',
|
||||||
|
'text/html' => 'html',
|
||||||
|
'image/x-icon' => 'ico',
|
||||||
|
'image/x-ico' => 'ico',
|
||||||
|
'image/vnd.microsoft.icon' => 'ico',
|
||||||
|
'text/calendar' => 'ics',
|
||||||
|
'application/java-archive' => 'jar',
|
||||||
|
'application/x-java-application' => 'jar',
|
||||||
|
'application/x-jar' => 'jar',
|
||||||
|
'image/jp2' => 'jp2',
|
||||||
|
'video/mj2' => 'jp2',
|
||||||
|
'image/jpx' => 'jp2',
|
||||||
|
'image/jpm' => 'jp2',
|
||||||
|
'image/jpeg' => 'jpg',
|
||||||
|
'image/pjpeg' => 'jpg',
|
||||||
|
'application/x-javascript' => 'js',
|
||||||
|
'application/json' => 'json',
|
||||||
|
'text/json' => 'json',
|
||||||
|
'application/vnd.google-earth.kml+xml' => 'kml',
|
||||||
|
'application/vnd.google-earth.kmz' => 'kmz',
|
||||||
|
'text/x-log' => 'log',
|
||||||
|
'audio/x-m4a' => 'm4a',
|
||||||
|
'application/vnd.mpegurl' => 'm4u',
|
||||||
|
'audio/midi' => 'mid',
|
||||||
|
'application/vnd.mif' => 'mif',
|
||||||
|
'video/quicktime' => 'mov',
|
||||||
|
'video/x-sgi-movie' => 'movie',
|
||||||
|
'audio/mpeg' => 'mp3',
|
||||||
|
'audio/mpg' => 'mp3',
|
||||||
|
'audio/mpeg3' => 'mp3',
|
||||||
|
'audio/mp3' => 'mp3',
|
||||||
|
'video/mp4' => 'mp4',
|
||||||
|
'video/mpeg' => 'mpeg',
|
||||||
|
'application/oda' => 'oda',
|
||||||
|
'application/vnd.oasis.opendocument.text' => 'odt',
|
||||||
|
'application/vnd.oasis.opendocument.spreadsheet' => 'ods',
|
||||||
|
'application/vnd.oasis.opendocument.presentation' => 'odp',
|
||||||
|
'audio/ogg' => 'ogg',
|
||||||
|
'video/ogg' => 'ogg',
|
||||||
|
'application/ogg' => 'ogg',
|
||||||
|
'application/x-pkcs10' => 'p10',
|
||||||
|
'application/pkcs10' => 'p10',
|
||||||
|
'application/x-pkcs12' => 'p12',
|
||||||
|
'application/x-pkcs7-signature' => 'p7a',
|
||||||
|
'application/pkcs7-mime' => 'p7c',
|
||||||
|
'application/x-pkcs7-mime' => 'p7c',
|
||||||
|
'application/x-pkcs7-certreqresp' => 'p7r',
|
||||||
|
'application/pkcs7-signature' => 'p7s',
|
||||||
|
'application/pdf' => 'pdf',
|
||||||
|
'application/octet-stream' => 'pdf',
|
||||||
|
'application/x-x509-user-cert' => 'pem',
|
||||||
|
'application/x-pem-file' => 'pem',
|
||||||
|
'application/pgp' => 'pgp',
|
||||||
|
'application/x-httpd-php' => 'php',
|
||||||
|
'application/php' => 'php',
|
||||||
|
'application/x-php' => 'php',
|
||||||
|
'text/php' => 'php',
|
||||||
|
'text/x-php' => 'php',
|
||||||
|
'application/x-httpd-php-source' => 'php',
|
||||||
|
'image/png' => 'png',
|
||||||
|
'image/x-png' => 'png',
|
||||||
|
'application/powerpoint' => 'ppt',
|
||||||
|
'application/vnd.ms-powerpoint' => 'ppt',
|
||||||
|
'application/vnd.ms-office' => 'ppt',
|
||||||
|
'application/msword' => 'doc',
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation' => 'pptx',
|
||||||
|
'application/x-photoshop' => 'psd',
|
||||||
|
'image/vnd.adobe.photoshop' => 'psd',
|
||||||
|
'audio/x-realaudio' => 'ra',
|
||||||
|
'audio/x-pn-realaudio' => 'ram',
|
||||||
|
'application/x-rar' => 'rar',
|
||||||
|
'application/rar' => 'rar',
|
||||||
|
'application/x-rar-compressed' => 'rar',
|
||||||
|
'audio/x-pn-realaudio-plugin' => 'rpm',
|
||||||
|
'application/x-pkcs7' => 'rsa',
|
||||||
|
'text/rtf' => 'rtf',
|
||||||
|
'text/richtext' => 'rtx',
|
||||||
|
'video/vnd.rn-realvideo' => 'rv',
|
||||||
|
'application/x-stuffit' => 'sit',
|
||||||
|
'application/smil' => 'smil',
|
||||||
|
'text/srt' => 'srt',
|
||||||
|
'image/svg+xml' => 'svg',
|
||||||
|
'application/x-shockwave-flash' => 'swf',
|
||||||
|
'application/x-tar' => 'tar',
|
||||||
|
'application/x-gzip-compressed' => 'tgz',
|
||||||
|
'image/tiff' => 'tiff',
|
||||||
|
'text/plain' => 'txt',
|
||||||
|
'text/x-vcard' => 'vcf',
|
||||||
|
'application/videolan' => 'vlc',
|
||||||
|
'text/vtt' => 'vtt',
|
||||||
|
'audio/x-wav' => 'wav',
|
||||||
|
'audio/wave' => 'wav',
|
||||||
|
'audio/wav' => 'wav',
|
||||||
|
'application/wbxml' => 'wbxml',
|
||||||
|
'video/webm' => 'webm',
|
||||||
|
'audio/x-ms-wma' => 'wma',
|
||||||
|
'application/wmlc' => 'wmlc',
|
||||||
|
'video/x-ms-wmv' => 'wmv',
|
||||||
|
'video/x-ms-asf' => 'wmv',
|
||||||
|
'application/xhtml+xml' => 'xhtml',
|
||||||
|
'application/excel' => 'xl',
|
||||||
|
'application/msexcel' => 'xls',
|
||||||
|
'application/x-msexcel' => 'xls',
|
||||||
|
'application/x-ms-excel' => 'xls',
|
||||||
|
'application/x-excel' => 'xls',
|
||||||
|
'application/x-dos_ms_excel' => 'xls',
|
||||||
|
'application/xls' => 'xls',
|
||||||
|
'application/x-xls' => 'xls',
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx',
|
||||||
|
'application/vnd.ms-excel' => 'xlsx',
|
||||||
|
'application/xml' => 'xml',
|
||||||
|
'text/xml' => 'xml',
|
||||||
|
'text/xsl' => 'xsl',
|
||||||
|
'application/xspf+xml' => 'xspf',
|
||||||
|
'application/x-compress' => 'z',
|
||||||
|
'application/x-zip' => 'zip',
|
||||||
|
'application/zip' => 'zip',
|
||||||
|
'application/x-zip-compressed' => 'zip',
|
||||||
|
'application/s-compressed' => 'zip',
|
||||||
|
'multipart/x-zip' => 'zip',
|
||||||
|
'text/x-scriptzsh' => 'zsh',
|
||||||
|
]);
|
||||||
BIN
database.png
Executable file
BIN
database.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 294 KiB |
@ -1,858 +0,0 @@
|
|||||||
-- phpMyAdmin SQL Dump
|
|
||||||
-- version 5.0.4
|
|
||||||
-- https://www.phpmyadmin.net/
|
|
||||||
--
|
|
||||||
-- Host: localhost
|
|
||||||
-- Generation Time: Nov 25, 2020 at 05:27 PM
|
|
||||||
-- Server version: 10.5.8-MariaDB
|
|
||||||
-- PHP Version: 7.4.12
|
|
||||||
|
|
||||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
|
||||||
START TRANSACTION;
|
|
||||||
SET time_zone = "+00:00";
|
|
||||||
|
|
||||||
|
|
||||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
|
||||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
|
||||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
|
||||||
/*!40101 SET NAMES utf8mb4 */;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Database: `Feuerwehr_Kameradenverwaltung`
|
|
||||||
--
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Abteilungen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Abteilungen` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Kürzel` varchar(3) NOT NULL,
|
|
||||||
`Name` varchar(255) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":29}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Ausbildungen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Ausbildungen` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Kürzel` varchar(3) DEFAULT NULL,
|
|
||||||
`Name` varchar(255) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":24}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Berechtigungen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Berechtigungen` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Name` varchar(100) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Dienstbuch`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Dienstbuch` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Abteilung` int(11) NOT NULL,
|
|
||||||
`Anfang` datetime NOT NULL,
|
|
||||||
`Ende` datetime NOT NULL,
|
|
||||||
`Leiter` int(11) NOT NULL,
|
|
||||||
`Ort` varchar(50) NOT NULL,
|
|
||||||
`Thema` varchar(50) NOT NULL,
|
|
||||||
`Bemerkungen` varchar(250) NOT NULL,
|
|
||||||
`Kategorie` int(11) NOT NULL,
|
|
||||||
`TM2Thema` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":11}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Dienstpläne`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Dienstpläne` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Abteilung` int(11) NOT NULL,
|
|
||||||
`Jahr` smallint(4) NOT NULL,
|
|
||||||
`Name` varchar(50) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":11,"target":"concat(Jahr, '' '', Name)"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Einsatzarten`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Einsatzarten` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`Einsatzkategorie` int(11) NOT NULL COMMENT '{"target":"name"}',
|
|
||||||
`Name` varchar(255) NOT NULL,
|
|
||||||
`Stichworte` varchar(255) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":31,"order":{"column":"Name","sorting":"asc"}}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Einsatzkategorien`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Einsatzkategorien` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`Name` varchar(255) NOT NULL,
|
|
||||||
`Farbe` varchar(255) NOT NULL DEFAULT '#000000'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":30,"order":{"column":"Name","sorting":"asc"}}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Einsätze`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Einsätze` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Alarmierungszeit` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
||||||
`Einsatzende` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
||||||
`Adresse` varchar(255) NOT NULL,
|
|
||||||
`Einsatzart` int(11) DEFAULT NULL,
|
|
||||||
`Zusammenfassung` varchar(255) NOT NULL,
|
|
||||||
`Einsatzleiter` varchar(255) NOT NULL DEFAULT '',
|
|
||||||
`Überprüft` bit(1) NOT NULL DEFAULT b'0'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":13,"order":{"column":"Alarmierungszeit","sorting":"desc"},"target":"concat(Adresse, date_format(Alarmierungszeit, '' (%d.%m.%Y)''))"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Kommandofunktionen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Kommandofunktionen` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Kürzel` varchar(3) NOT NULL,
|
|
||||||
`Name` varchar(255) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":20}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `link_Abteilungen_Personal`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `link_Abteilungen_Personal` (
|
|
||||||
`Abteilungen` int(11) NOT NULL COMMENT '{"dynitem":true}',
|
|
||||||
`Personal` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `link_Ausbildungen_Personal`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `link_Ausbildungen_Personal` (
|
|
||||||
`Ausbildungen` int(11) NOT NULL COMMENT '{"dynitem":true}',
|
|
||||||
`Personal` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"show":false}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `link_Berechtigungen_Personalgruppen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `link_Berechtigungen_Personalgruppen` (
|
|
||||||
`Berechtigungen` int(11) NOT NULL,
|
|
||||||
`Personalgruppen` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `link_Personal_Personalgruppen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `link_Personal_Personalgruppen` (
|
|
||||||
`Personal` int(11) NOT NULL,
|
|
||||||
`Personalgruppen` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Personal`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Personal` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`OFnr` tinyint(2) UNSIGNED DEFAULT 56 COMMENT '{"group":"nummer","group_title":"P-Nr.","postfix":"-"}',
|
|
||||||
`Pnr` smallint(3) UNSIGNED ZEROFILL DEFAULT NULL COMMENT '{"group":"nummer"}',
|
|
||||||
`Login` varchar(50) NOT NULL,
|
|
||||||
`Nachnamen` varchar(255) NOT NULL COMMENT '{"group":"Name","group_title":"Namen","group_sep":", "}',
|
|
||||||
`Vornamen` varchar(255) NOT NULL COMMENT '{"group":"Name"}',
|
|
||||||
`Bildadresse` varchar(255) DEFAULT NULL COMMENT '{"input":"file"}',
|
|
||||||
`Pool` int(11) DEFAULT NULL COMMENT '{"dynitem":true}',
|
|
||||||
`Kommandofkt` int(11) DEFAULT NULL COMMENT '{"dynitem":true}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":10,"order":{"column":"Name","sorting":"asc"},"target":"concat(left(Vornamen, 1), ''. '', Nachnamen)"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Personalgruppen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Personalgruppen` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Kürzel` varchar(3) NOT NULL,
|
|
||||||
`Name` varchar(255) DEFAULT NULL,
|
|
||||||
`Abteilung` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Pools`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Pools` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Name` varchar(255) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":21}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `print_elements`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `print_elements` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`key_layout_ID` int(11) NOT NULL COMMENT '{"title":"Drucklayout","dynitem":true,"target":"name"}',
|
|
||||||
`cond` varchar(255) NOT NULL COMMENT '{"title":"Bedingung"}',
|
|
||||||
`content` varchar(255) NOT NULL DEFAULT '#%field%#' COMMENT '{"title":"Inhalt"}',
|
|
||||||
`posl` decimal(10,0) NOT NULL COMMENT '{"title":"Pos. von links [mm]"}',
|
|
||||||
`post` decimal(10,0) NOT NULL COMMENT '{"title":"Pos. von oben [mm]"}',
|
|
||||||
`dimw` decimal(10,0) NOT NULL COMMENT '{"title":"Breite [mm]"}',
|
|
||||||
`dimh` decimal(10,0) NOT NULL COMMENT '{"title":"Höhe [mm]"}',
|
|
||||||
`type` varchar(255) NOT NULL DEFAULT 'text' COMMENT '{"title":"Art"}',
|
|
||||||
`params` varchar(255) NOT NULL DEFAULT '{}' COMMENT '{"title":"Parameter"}',
|
|
||||||
`ord` int(11) NOT NULL COMMENT '{"title":"Reihenfolge"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":99,"show":true,"title":"Druckelemente"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `print_layouts`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `print_layouts` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`name` varchar(255) NOT NULL COMMENT '{"title":"Bezeichnung"}',
|
|
||||||
`page` varchar(255) NOT NULL COMMENT '{"title":"Seite","options":"pages"}',
|
|
||||||
`width` int(10) UNSIGNED NOT NULL COMMENT '{"title":"Breite"}',
|
|
||||||
`height` int(10) UNSIGNED NOT NULL COMMENT '{"title":"Höhe"}',
|
|
||||||
`options` varchar(255) NOT NULL COMMENT '{"title":"Optionen"}',
|
|
||||||
`prio` tinyint(1) NOT NULL DEFAULT 0 COMMENT '{"title":"Bevorzugt"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":98,"show":true,"title":"Drucklayouts"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `sys_failedlogins`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `sys_failedlogins` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{"title":"ID", "target":"login"}',
|
|
||||||
`time` int(11) NOT NULL COMMENT '{"title":"Zeitstempel"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"title":"FailedLogin"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `sys_iservhashes`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `sys_iservhashes` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Hash` char(64) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `sys_pass`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `sys_pass` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{"title":"ID", "target":"login"}',
|
|
||||||
`iterations` int(11) NOT NULL COMMENT '{"title":"Iterations"}',
|
|
||||||
`salt` char(32) NOT NULL COMMENT '{"title":"Salt"}',
|
|
||||||
`hash` char(64) NOT NULL COMMENT '{"title":"Hash"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `sys_uaccess`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `sys_uaccess` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`key_sys_user_ID` int(11) NOT NULL COMMENT '{"title":"User", "target":"login"}',
|
|
||||||
`page` varchar(255) NOT NULL COMMENT '{"title":"Seite","options":"pages"}',
|
|
||||||
`access` tinyint(2) NOT NULL DEFAULT 0 COMMENT '{"title":"Seitenzugriff"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `sys_user`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `sys_user` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{"title":"ID"}',
|
|
||||||
`login` varchar(32) NOT NULL COMMENT '{"title":"Login"}',
|
|
||||||
`email` varchar(64) NOT NULL COMMENT '{"title":"eMail"}',
|
|
||||||
`key_personal_ID` int(11) DEFAULT NULL COMMENT '{"title":"Nachnamen","target":"nachnamen"}',
|
|
||||||
`access` tinyint(2) NOT NULL DEFAULT 0 COMMENT '{"title":"Hauptzugriff"}'
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Teilnahmestatus`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Teilnahmestatus` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Name` varchar(10) NOT NULL,
|
|
||||||
`Loswert` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Terminarten`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Terminarten` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`Name` varchar(255) NOT NULL,
|
|
||||||
`Präfix` varchar(50) NOT NULL DEFAULT '',
|
|
||||||
`Farbe` varchar(7) NOT NULL DEFAULT '#000000',
|
|
||||||
`Beschreibung` text NOT NULL,
|
|
||||||
`Verwaltungsrecht` int(11) DEFAULT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":31,"order":{"column":"Name","sorting":"asc"}}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Termine`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Termine` (
|
|
||||||
`ID` int(11) NOT NULL COMMENT '{}',
|
|
||||||
`Beginn` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
||||||
`Ende` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
|
|
||||||
`Thema` varchar(255) CHARACTER SET utf8 NOT NULL,
|
|
||||||
`Ort` varchar(255) CHARACTER SET utf8 NOT NULL,
|
|
||||||
`Verantwortliche` varchar(255) CHARACTER SET utf8 NOT NULL,
|
|
||||||
`Dienstplan` int(11) DEFAULT NULL,
|
|
||||||
`Terminart` int(11) DEFAULT NULL,
|
|
||||||
`Platzvergabe` int(11) DEFAULT NULL,
|
|
||||||
`Ausgelost` tinyint(1) NOT NULL DEFAULT 0,
|
|
||||||
`Personalgruppe` int(11) DEFAULT NULL,
|
|
||||||
`Hash` varchar(32) CHARACTER SET utf8 DEFAULT NULL COMMENT '{"input":"hash"}',
|
|
||||||
`TeilnahmeGeändert` tinyint(1) DEFAULT 1
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='{"sort":12,"order":{"column":"Beginn","sorting":"desc"},"target":"concat(date_format(Beginn, ''%d.%m.''), '' '', Thema)"}';
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Terminplatzvergabe`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Terminplatzvergabe` (
|
|
||||||
`ID` int(11) NOT NULL,
|
|
||||||
`Name` varchar(25) NOT NULL,
|
|
||||||
`Tage` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`LoescheGruppeTage` int(11) DEFAULT NULL,
|
|
||||||
`MaxTeilnehmer` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinZF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinGF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinKFohneF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinKF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinAGTohneF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinAGT` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinTHohneF` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinTH` int(11) NOT NULL DEFAULT 0,
|
|
||||||
`MinNon` int(11) NOT NULL DEFAULT 0
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
-- --------------------------------------------------------
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `Terminteilnahmen`
|
|
||||||
--
|
|
||||||
|
|
||||||
CREATE TABLE `Terminteilnahmen` (
|
|
||||||
`Termin` int(11) NOT NULL,
|
|
||||||
`Personal` int(11) NOT NULL,
|
|
||||||
`Status` int(11) NOT NULL
|
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for dumped tables
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Abteilungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Abteilungen`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Ausbildungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Ausbildungen`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Berechtigungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Berechtigungen`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD UNIQUE KEY `Berechtigungen_UN` (`Name`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Dienstbuch`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Dienstbuch`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_dienstplaene_abteilung` (`Abteilung`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Dienstpläne`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Dienstpläne`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_dienstplaene_abteilung` (`Abteilung`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Einsatzarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsatzarten`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_einsatzarten_einsatzkategorie` (`Einsatzkategorie`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Einsatzkategorien`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsatzkategorien`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Einsätze`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsätze`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_einsatzberichte_einsatzart` (`Einsatzart`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Kommandofunktionen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Kommandofunktionen`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `link_Abteilungen_Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Abteilungen_Personal`
|
|
||||||
ADD PRIMARY KEY (`Abteilungen`,`Personal`),
|
|
||||||
ADD KEY `fk_link_Abteilungen_Personal_2` (`Personal`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `link_Ausbildungen_Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Ausbildungen_Personal`
|
|
||||||
ADD PRIMARY KEY (`Ausbildungen`,`Personal`),
|
|
||||||
ADD KEY `fk_link_personal_ausbildungen_1` (`Personal`),
|
|
||||||
ADD KEY `fk_link_personal_ausbildungen_2` (`Ausbildungen`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `link_Berechtigungen_Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Berechtigungen_Personalgruppen`
|
|
||||||
ADD PRIMARY KEY (`Berechtigungen`,`Personalgruppen`),
|
|
||||||
ADD KEY `fk_link_Berechtigungen_Personalgruppen_2` (`Personalgruppen`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `link_Personal_Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Personal_Personalgruppen`
|
|
||||||
ADD PRIMARY KEY (`Personal`,`Personalgruppen`),
|
|
||||||
ADD KEY `fk_link_Personal_Personalgruppen_2` (`Personalgruppen`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personal`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_personal_1` (`Kommandofkt`),
|
|
||||||
ADD KEY `fk_personal_2` (`Pool`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personalgruppen`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_Personalgruppen_1` (`Abteilung`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Pools`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Pools`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `print_elements`
|
|
||||||
--
|
|
||||||
ALTER TABLE `print_elements`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_print_elements_1` (`key_layout_ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `print_layouts`
|
|
||||||
--
|
|
||||||
ALTER TABLE `print_layouts`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `page` (`page`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `sys_failedlogins`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_failedlogins`
|
|
||||||
ADD PRIMARY KEY (`ID`,`time`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `sys_iservhashes`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_iservhashes`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `sys_pass`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_pass`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `sys_uaccess`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_uaccess`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_sys_access_1` (`key_sys_user_ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `sys_user`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_user`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_sys_users_1` (`key_personal_ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Teilnahmestatus`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Teilnahmestatus`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Terminarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminarten`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `Terminarten_FK` (`Verwaltungsrecht`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Termine`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Termine`
|
|
||||||
ADD PRIMARY KEY (`ID`),
|
|
||||||
ADD KEY `fk_Termine_Dienstplan` (`Dienstplan`),
|
|
||||||
ADD KEY `fk_Termine_Terminart` (`Terminart`),
|
|
||||||
ADD KEY `Termine_FK` (`Platzvergabe`),
|
|
||||||
ADD KEY `fk_Termine_Personalgruppe` (`Personalgruppe`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Terminplatzvergabe`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminplatzvergabe`
|
|
||||||
ADD PRIMARY KEY (`ID`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Indexes for table `Terminteilnahmen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminteilnahmen`
|
|
||||||
ADD PRIMARY KEY (`Termin`,`Personal`),
|
|
||||||
ADD KEY `fk_terminteilnahmen_1` (`Personal`),
|
|
||||||
ADD KEY `fk_terminteilnahmen_3` (`Status`);
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for dumped tables
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Abteilungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Abteilungen`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Ausbildungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Ausbildungen`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Berechtigungen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Berechtigungen`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Dienstbuch`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Dienstbuch`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Dienstpläne`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Dienstpläne`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Einsatzarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsatzarten`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Einsatzkategorien`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsatzkategorien`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Einsätze`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsätze`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Kommandofunktionen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Kommandofunktionen`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personal`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personalgruppen`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Pools`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Pools`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `print_elements`
|
|
||||||
--
|
|
||||||
ALTER TABLE `print_elements`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `print_layouts`
|
|
||||||
--
|
|
||||||
ALTER TABLE `print_layouts`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `sys_uaccess`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_uaccess`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `sys_user`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_user`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{"title":"ID"}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Teilnahmestatus`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Teilnahmestatus`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Terminarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminarten`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Termine`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Termine`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '{}';
|
|
||||||
|
|
||||||
--
|
|
||||||
-- AUTO_INCREMENT for table `Terminplatzvergabe`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminplatzvergabe`
|
|
||||||
MODIFY `ID` int(11) NOT NULL AUTO_INCREMENT;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for dumped tables
|
|
||||||
--
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Dienstpläne`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Dienstpläne`
|
|
||||||
ADD CONSTRAINT `fk_dienstplaene_abteilung` FOREIGN KEY (`Abteilung`) REFERENCES `Abteilungen` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Einsatzarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsatzarten`
|
|
||||||
ADD CONSTRAINT `fk_einsatzarten_einsatzkategorie` FOREIGN KEY (`Einsatzkategorie`) REFERENCES `Einsatzkategorien` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Einsätze`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Einsätze`
|
|
||||||
ADD CONSTRAINT `fk_einsatzberichte_einsatzart` FOREIGN KEY (`Einsatzart`) REFERENCES `Einsatzarten` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `link_Abteilungen_Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Abteilungen_Personal`
|
|
||||||
ADD CONSTRAINT `fk_link_Abteilungen_Personal_1` FOREIGN KEY (`Abteilungen`) REFERENCES `Abteilungen` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_link_Abteilungen_Personal_2` FOREIGN KEY (`Personal`) REFERENCES `Personal` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `link_Ausbildungen_Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Ausbildungen_Personal`
|
|
||||||
ADD CONSTRAINT `fk_link_personal_ausbildungen_1` FOREIGN KEY (`Personal`) REFERENCES `Personal` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_link_personal_ausbildungen_2` FOREIGN KEY (`Ausbildungen`) REFERENCES `Ausbildungen` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `link_Berechtigungen_Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Berechtigungen_Personalgruppen`
|
|
||||||
ADD CONSTRAINT `fk_link_Berechtigungen_Personalgruppen_1` FOREIGN KEY (`Berechtigungen`) REFERENCES `Berechtigungen` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_link_Berechtigungen_Personalgruppen_2` FOREIGN KEY (`Personalgruppen`) REFERENCES `Personalgruppen` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `link_Personal_Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `link_Personal_Personalgruppen`
|
|
||||||
ADD CONSTRAINT `fk_link_Personal_Personalgruppen_1` FOREIGN KEY (`Personal`) REFERENCES `Personal` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_link_Personal_Personalgruppen_2` FOREIGN KEY (`Personalgruppen`) REFERENCES `Personalgruppen` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Personal`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personal`
|
|
||||||
ADD CONSTRAINT `fk_personal_1` FOREIGN KEY (`Kommandofkt`) REFERENCES `Kommandofunktionen` (`ID`) ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_personal_2` FOREIGN KEY (`Pool`) REFERENCES `Pools` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Personalgruppen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Personalgruppen`
|
|
||||||
ADD CONSTRAINT `fk_Personalgruppen_1` FOREIGN KEY (`Abteilung`) REFERENCES `Abteilungen` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `print_elements`
|
|
||||||
--
|
|
||||||
ALTER TABLE `print_elements`
|
|
||||||
ADD CONSTRAINT `fk_print_elements_1` FOREIGN KEY (`key_layout_ID`) REFERENCES `print_layouts` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `sys_failedlogins`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_failedlogins`
|
|
||||||
ADD CONSTRAINT `fk_sys_failedlogins_1` FOREIGN KEY (`ID`) REFERENCES `sys_user` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `sys_iservhashes`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_iservhashes`
|
|
||||||
ADD CONSTRAINT `fk_sys_iservhashes_1` FOREIGN KEY (`ID`) REFERENCES `Personal` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `sys_pass`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_pass`
|
|
||||||
ADD CONSTRAINT `fk_sys_pass_1` FOREIGN KEY (`ID`) REFERENCES `sys_user` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `sys_uaccess`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_uaccess`
|
|
||||||
ADD CONSTRAINT `fk_sys_access_1` FOREIGN KEY (`key_sys_user_ID`) REFERENCES `sys_user` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `sys_user`
|
|
||||||
--
|
|
||||||
ALTER TABLE `sys_user`
|
|
||||||
ADD CONSTRAINT `fk_sys_users_1` FOREIGN KEY (`key_personal_ID`) REFERENCES `Personal` (`ID`) ON DELETE SET NULL ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Terminarten`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminarten`
|
|
||||||
ADD CONSTRAINT `Terminarten_FK` FOREIGN KEY (`Verwaltungsrecht`) REFERENCES `Berechtigungen` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Termine`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Termine`
|
|
||||||
ADD CONSTRAINT `Termine_FK` FOREIGN KEY (`Platzvergabe`) REFERENCES `Terminplatzvergabe` (`ID`) ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_Termine_Dienstplan` FOREIGN KEY (`Dienstplan`) REFERENCES `Dienstpläne` (`ID`) ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_Termine_Personalgruppe` FOREIGN KEY (`Personalgruppe`) REFERENCES `Personalgruppen` (`ID`) ON DELETE SET NULL ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_Termine_Terminart` FOREIGN KEY (`Terminart`) REFERENCES `Terminarten` (`ID`) ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Constraints for table `Terminteilnahmen`
|
|
||||||
--
|
|
||||||
ALTER TABLE `Terminteilnahmen`
|
|
||||||
ADD CONSTRAINT `fk_terminteilnahmen_1` FOREIGN KEY (`Personal`) REFERENCES `Personal` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE,
|
|
||||||
ADD CONSTRAINT `fk_terminteilnahmen_2` FOREIGN KEY (`Termin`) REFERENCES `Termine` (`ID`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
COMMIT;
|
|
||||||
|
|
||||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
|
||||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
|
||||||
6
ext/10_BeforeValidException.php
Executable file
6
ext/10_BeforeValidException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class BeforeValidException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
}
|
||||||
6
ext/11_ExpiredException.php
Executable file
6
ext/11_ExpiredException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class ExpiredException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
}
|
||||||
6
ext/12_SignatureInvalidException.php
Executable file
6
ext/12_SignatureInvalidException.php
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
<?php
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
class SignatureInvalidException extends \UnexpectedValueException
|
||||||
|
{
|
||||||
|
}
|
||||||
512
ext/13_JWT.php
Executable file
512
ext/13_JWT.php
Executable file
@ -0,0 +1,512 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Firebase\JWT;
|
||||||
|
|
||||||
|
use \DomainException;
|
||||||
|
use \InvalidArgumentException;
|
||||||
|
use \UnexpectedValueException;
|
||||||
|
use \DateTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON Web Token implementation, based on this spec:
|
||||||
|
* https://tools.ietf.org/html/rfc7519
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* @category Authentication
|
||||||
|
* @package Authentication_JWT
|
||||||
|
* @author Neuman Vong <neuman@twilio.com>
|
||||||
|
* @author Anant Narayanan <anant@php.net>
|
||||||
|
* @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD
|
||||||
|
* @link https://github.com/firebase/php-jwt
|
||||||
|
*/
|
||||||
|
class JWT
|
||||||
|
{
|
||||||
|
const ASN1_INTEGER = 0x02;
|
||||||
|
const ASN1_SEQUENCE = 0x10;
|
||||||
|
const ASN1_BIT_STRING = 0x03;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When checking nbf, iat or expiration times,
|
||||||
|
* we want to provide some extra leeway time to
|
||||||
|
* account for clock skew.
|
||||||
|
*/
|
||||||
|
public static $leeway = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allow the current timestamp to be specified.
|
||||||
|
* Useful for fixing a value within unit testing.
|
||||||
|
*
|
||||||
|
* Will default to PHP time() value if null.
|
||||||
|
*/
|
||||||
|
public static $timestamp = null;
|
||||||
|
|
||||||
|
public static $supported_algs = array(
|
||||||
|
'ES256' => array('openssl', 'SHA256'),
|
||||||
|
'HS256' => array('hash_hmac', 'SHA256'),
|
||||||
|
'HS384' => array('hash_hmac', 'SHA384'),
|
||||||
|
'HS512' => array('hash_hmac', 'SHA512'),
|
||||||
|
'RS256' => array('openssl', 'SHA256'),
|
||||||
|
'RS384' => array('openssl', 'SHA384'),
|
||||||
|
'RS512' => array('openssl', 'SHA512'),
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decodes a JWT string into a PHP object.
|
||||||
|
*
|
||||||
|
* @param string $jwt The JWT
|
||||||
|
* @param string|array|resource $key The key, or map of keys.
|
||||||
|
* If the algorithm used is asymmetric, this is the public key
|
||||||
|
* @param array $allowed_algs List of supported verification algorithms
|
||||||
|
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
|
*
|
||||||
|
* @return object The JWT's payload as a PHP object
|
||||||
|
*
|
||||||
|
* @throws UnexpectedValueException Provided JWT was invalid
|
||||||
|
* @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed
|
||||||
|
* @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf'
|
||||||
|
* @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat'
|
||||||
|
* @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim
|
||||||
|
*
|
||||||
|
* @uses jsonDecode
|
||||||
|
* @uses urlsafeB64Decode
|
||||||
|
*/
|
||||||
|
public static function decode($jwt, $key, array $allowed_algs = array())
|
||||||
|
{
|
||||||
|
$timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp;
|
||||||
|
|
||||||
|
if (empty($key)) {
|
||||||
|
throw new InvalidArgumentException('Key may not be empty');
|
||||||
|
}
|
||||||
|
$tks = \explode('.', $jwt);
|
||||||
|
if (\count($tks) != 3) {
|
||||||
|
throw new UnexpectedValueException('Wrong number of segments');
|
||||||
|
}
|
||||||
|
list($headb64, $bodyb64, $cryptob64) = $tks;
|
||||||
|
if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) {
|
||||||
|
throw new UnexpectedValueException('Invalid header encoding');
|
||||||
|
}
|
||||||
|
if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) {
|
||||||
|
throw new UnexpectedValueException('Invalid claims encoding');
|
||||||
|
}
|
||||||
|
if (false === ($sig = static::urlsafeB64Decode($cryptob64))) {
|
||||||
|
throw new UnexpectedValueException('Invalid signature encoding');
|
||||||
|
}
|
||||||
|
if (empty($header->alg)) {
|
||||||
|
throw new UnexpectedValueException('Empty algorithm');
|
||||||
|
}
|
||||||
|
if (empty(static::$supported_algs[$header->alg])) {
|
||||||
|
throw new UnexpectedValueException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
if (!\in_array($header->alg, $allowed_algs)) {
|
||||||
|
throw new UnexpectedValueException('Algorithm not allowed');
|
||||||
|
}
|
||||||
|
if ($header->alg === 'ES256') {
|
||||||
|
// OpenSSL expects an ASN.1 DER sequence for ES256 signatures
|
||||||
|
$sig = self::signatureToDER($sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (\is_array($key) || $key instanceof \ArrayAccess) {
|
||||||
|
if (isset($header->kid)) {
|
||||||
|
if (!isset($key[$header->kid])) {
|
||||||
|
throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key');
|
||||||
|
}
|
||||||
|
$key = $key[$header->kid];
|
||||||
|
} else {
|
||||||
|
throw new UnexpectedValueException('"kid" empty, unable to lookup correct key');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the signature
|
||||||
|
if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) {
|
||||||
|
throw new SignatureInvalidException('Signature verification failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the nbf if it is defined. This is the time that the
|
||||||
|
// token can actually be used. If it's not yet that time, abort.
|
||||||
|
if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) {
|
||||||
|
throw new BeforeValidException(
|
||||||
|
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that this token has been created before 'now'. This prevents
|
||||||
|
// using tokens that have been created for later use (and haven't
|
||||||
|
// correctly used the nbf claim).
|
||||||
|
if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) {
|
||||||
|
throw new BeforeValidException(
|
||||||
|
'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if this token has expired.
|
||||||
|
if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) {
|
||||||
|
throw new ExpiredException('Expired token');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts and signs a PHP object or array into a JWT string.
|
||||||
|
*
|
||||||
|
* @param object|array $payload PHP object or array
|
||||||
|
* @param string $key The secret key.
|
||||||
|
* If the algorithm used is asymmetric, this is the private key
|
||||||
|
* @param string $alg The signing algorithm.
|
||||||
|
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
|
* @param mixed $keyId
|
||||||
|
* @param array $head An array with header elements to attach
|
||||||
|
*
|
||||||
|
* @return string A signed JWT
|
||||||
|
*
|
||||||
|
* @uses jsonEncode
|
||||||
|
* @uses urlsafeB64Encode
|
||||||
|
*/
|
||||||
|
public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null)
|
||||||
|
{
|
||||||
|
$header = array('typ' => 'JWT', 'alg' => $alg);
|
||||||
|
if ($keyId !== null) {
|
||||||
|
$header['kid'] = $keyId;
|
||||||
|
}
|
||||||
|
if (isset($head) && \is_array($head)) {
|
||||||
|
$header = \array_merge($head, $header);
|
||||||
|
}
|
||||||
|
$segments = array();
|
||||||
|
$segments[] = static::urlsafeB64Encode(static::jsonEncode($header));
|
||||||
|
$segments[] = static::urlsafeB64Encode(static::jsonEncode($payload));
|
||||||
|
$signing_input = \implode('.', $segments);
|
||||||
|
|
||||||
|
$signature = static::sign($signing_input, $key, $alg);
|
||||||
|
$segments[] = static::urlsafeB64Encode($signature);
|
||||||
|
|
||||||
|
return \implode('.', $segments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sign a string with a given key and algorithm.
|
||||||
|
*
|
||||||
|
* @param string $msg The message to sign
|
||||||
|
* @param string|resource $key The secret key
|
||||||
|
* @param string $alg The signing algorithm.
|
||||||
|
* Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512'
|
||||||
|
*
|
||||||
|
* @return string An encrypted message
|
||||||
|
*
|
||||||
|
* @throws DomainException Unsupported algorithm was specified
|
||||||
|
*/
|
||||||
|
public static function sign($msg, $key, $alg = 'HS256')
|
||||||
|
{
|
||||||
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
|
switch ($function) {
|
||||||
|
case 'hash_hmac':
|
||||||
|
return \hash_hmac($algorithm, $msg, $key, true);
|
||||||
|
case 'openssl':
|
||||||
|
$signature = '';
|
||||||
|
$success = \openssl_sign($msg, $signature, $key, $algorithm);
|
||||||
|
if (!$success) {
|
||||||
|
throw new DomainException("OpenSSL unable to sign data");
|
||||||
|
} else {
|
||||||
|
if ($alg === 'ES256') {
|
||||||
|
$signature = self::signatureFromDER($signature, 256);
|
||||||
|
}
|
||||||
|
return $signature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verify a signature with the message, key and method. Not all methods
|
||||||
|
* are symmetric, so we must have a separate verify and sign method.
|
||||||
|
*
|
||||||
|
* @param string $msg The original message (header and body)
|
||||||
|
* @param string $signature The original signature
|
||||||
|
* @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key
|
||||||
|
* @param string $alg The algorithm
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*
|
||||||
|
* @throws DomainException Invalid Algorithm or OpenSSL failure
|
||||||
|
*/
|
||||||
|
private static function verify($msg, $signature, $key, $alg)
|
||||||
|
{
|
||||||
|
if (empty(static::$supported_algs[$alg])) {
|
||||||
|
throw new DomainException('Algorithm not supported');
|
||||||
|
}
|
||||||
|
|
||||||
|
list($function, $algorithm) = static::$supported_algs[$alg];
|
||||||
|
switch ($function) {
|
||||||
|
case 'openssl':
|
||||||
|
$success = \openssl_verify($msg, $signature, $key, $algorithm);
|
||||||
|
if ($success === 1) {
|
||||||
|
return true;
|
||||||
|
} elseif ($success === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// returns 1 on success, 0 on failure, -1 on error.
|
||||||
|
throw new DomainException(
|
||||||
|
'OpenSSL error: ' . \openssl_error_string()
|
||||||
|
);
|
||||||
|
case 'hash_hmac':
|
||||||
|
default:
|
||||||
|
$hash = \hash_hmac($algorithm, $msg, $key, true);
|
||||||
|
if (\function_exists('hash_equals')) {
|
||||||
|
return \hash_equals($signature, $hash);
|
||||||
|
}
|
||||||
|
$len = \min(static::safeStrlen($signature), static::safeStrlen($hash));
|
||||||
|
|
||||||
|
$status = 0;
|
||||||
|
for ($i = 0; $i < $len; $i++) {
|
||||||
|
$status |= (\ord($signature[$i]) ^ \ord($hash[$i]));
|
||||||
|
}
|
||||||
|
$status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash));
|
||||||
|
|
||||||
|
return ($status === 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a JSON string into a PHP object.
|
||||||
|
*
|
||||||
|
* @param string $input JSON string
|
||||||
|
*
|
||||||
|
* @return object Object representation of JSON string
|
||||||
|
*
|
||||||
|
* @throws DomainException Provided string was invalid JSON
|
||||||
|
*/
|
||||||
|
public static function jsonDecode($input)
|
||||||
|
{
|
||||||
|
if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) {
|
||||||
|
/** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you
|
||||||
|
* to specify that large ints (like Steam Transaction IDs) should be treated as
|
||||||
|
* strings, rather than the PHP default behaviour of converting them to floats.
|
||||||
|
*/
|
||||||
|
$obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING);
|
||||||
|
} else {
|
||||||
|
/** Not all servers will support that, however, so for older versions we must
|
||||||
|
* manually detect large ints in the JSON string and quote them (thus converting
|
||||||
|
*them to strings) before decoding, hence the preg_replace() call.
|
||||||
|
*/
|
||||||
|
$max_int_length = \strlen((string) PHP_INT_MAX) - 1;
|
||||||
|
$json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input);
|
||||||
|
$obj = \json_decode($json_without_bigints);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($errno = \json_last_error()) {
|
||||||
|
static::handleJsonError($errno);
|
||||||
|
} elseif ($obj === null && $input !== 'null') {
|
||||||
|
throw new DomainException('Null result with non-null input');
|
||||||
|
}
|
||||||
|
return $obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a PHP object into a JSON string.
|
||||||
|
*
|
||||||
|
* @param object|array $input A PHP object or array
|
||||||
|
*
|
||||||
|
* @return string JSON representation of the PHP object or array
|
||||||
|
*
|
||||||
|
* @throws DomainException Provided object could not be encoded to valid JSON
|
||||||
|
*/
|
||||||
|
public static function jsonEncode($input)
|
||||||
|
{
|
||||||
|
$json = \json_encode($input);
|
||||||
|
if ($errno = \json_last_error()) {
|
||||||
|
static::handleJsonError($errno);
|
||||||
|
} elseif ($json === 'null' && $input !== null) {
|
||||||
|
throw new DomainException('Null result with non-null input');
|
||||||
|
}
|
||||||
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode a string with URL-safe Base64.
|
||||||
|
*
|
||||||
|
* @param string $input A Base64 encoded string
|
||||||
|
*
|
||||||
|
* @return string A decoded string
|
||||||
|
*/
|
||||||
|
public static function urlsafeB64Decode($input)
|
||||||
|
{
|
||||||
|
$remainder = \strlen($input) % 4;
|
||||||
|
if ($remainder) {
|
||||||
|
$padlen = 4 - $remainder;
|
||||||
|
$input .= \str_repeat('=', $padlen);
|
||||||
|
}
|
||||||
|
return \base64_decode(\strtr($input, '-_', '+/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode a string with URL-safe Base64.
|
||||||
|
*
|
||||||
|
* @param string $input The string you want encoded
|
||||||
|
*
|
||||||
|
* @return string The base64 encode of what you passed in
|
||||||
|
*/
|
||||||
|
public static function urlsafeB64Encode($input)
|
||||||
|
{
|
||||||
|
return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_'));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to create a JSON error.
|
||||||
|
*
|
||||||
|
* @param int $errno An error number from json_last_error()
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function handleJsonError($errno)
|
||||||
|
{
|
||||||
|
$messages = array(
|
||||||
|
JSON_ERROR_DEPTH => 'Maximum stack depth exceeded',
|
||||||
|
JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON',
|
||||||
|
JSON_ERROR_CTRL_CHAR => 'Unexpected control character found',
|
||||||
|
JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON',
|
||||||
|
JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3
|
||||||
|
);
|
||||||
|
throw new DomainException(
|
||||||
|
isset($messages[$errno])
|
||||||
|
? $messages[$errno]
|
||||||
|
: 'Unknown JSON error: ' . $errno
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of bytes in cryptographic strings.
|
||||||
|
*
|
||||||
|
* @param string $str
|
||||||
|
*
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
private static function safeStrlen($str)
|
||||||
|
{
|
||||||
|
if (\function_exists('mb_strlen')) {
|
||||||
|
return \mb_strlen($str, '8bit');
|
||||||
|
}
|
||||||
|
return \strlen($str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert an ECDSA signature to an ASN.1 DER sequence
|
||||||
|
*
|
||||||
|
* @param string $sig The ECDSA signature to convert
|
||||||
|
* @return string The encoded DER object
|
||||||
|
*/
|
||||||
|
private static function signatureToDER($sig)
|
||||||
|
{
|
||||||
|
// Separate the signature into r-value and s-value
|
||||||
|
list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2));
|
||||||
|
|
||||||
|
// Trim leading zeros
|
||||||
|
$r = \ltrim($r, "\x00");
|
||||||
|
$s = \ltrim($s, "\x00");
|
||||||
|
|
||||||
|
// Convert r-value and s-value from unsigned big-endian integers to
|
||||||
|
// signed two's complement
|
||||||
|
if (\ord($r[0]) > 0x7f) {
|
||||||
|
$r = "\x00" . $r;
|
||||||
|
}
|
||||||
|
if (\ord($s[0]) > 0x7f) {
|
||||||
|
$s = "\x00" . $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::encodeDER(
|
||||||
|
self::ASN1_SEQUENCE,
|
||||||
|
self::encodeDER(self::ASN1_INTEGER, $r) .
|
||||||
|
self::encodeDER(self::ASN1_INTEGER, $s)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes a value into a DER object.
|
||||||
|
*
|
||||||
|
* @param int $type DER tag
|
||||||
|
* @param string $value the value to encode
|
||||||
|
* @return string the encoded object
|
||||||
|
*/
|
||||||
|
private static function encodeDER($type, $value)
|
||||||
|
{
|
||||||
|
$tag_header = 0;
|
||||||
|
if ($type === self::ASN1_SEQUENCE) {
|
||||||
|
$tag_header |= 0x20;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
$der = \chr($tag_header | $type);
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$der .= \chr(\strlen($value));
|
||||||
|
|
||||||
|
return $der . $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes signature from a DER object.
|
||||||
|
*
|
||||||
|
* @param string $der binary signature in DER format
|
||||||
|
* @param int $keySize the number of bits in the key
|
||||||
|
* @return string the signature
|
||||||
|
*/
|
||||||
|
private static function signatureFromDER($der, $keySize)
|
||||||
|
{
|
||||||
|
// OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE
|
||||||
|
list($offset, $_) = self::readDER($der);
|
||||||
|
list($offset, $r) = self::readDER($der, $offset);
|
||||||
|
list($offset, $s) = self::readDER($der, $offset);
|
||||||
|
|
||||||
|
// Convert r-value and s-value from signed two's compliment to unsigned
|
||||||
|
// big-endian integers
|
||||||
|
$r = \ltrim($r, "\x00");
|
||||||
|
$s = \ltrim($s, "\x00");
|
||||||
|
|
||||||
|
// Pad out r and s so that they are $keySize bits long
|
||||||
|
$r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||||
|
$s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT);
|
||||||
|
|
||||||
|
return $r . $s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads binary DER-encoded data and decodes into a single object
|
||||||
|
*
|
||||||
|
* @param string $der the binary data in DER format
|
||||||
|
* @param int $offset the offset of the data stream containing the object
|
||||||
|
* to decode
|
||||||
|
* @return array [$offset, $data] the new offset and the decoded object
|
||||||
|
*/
|
||||||
|
private static function readDER($der, $offset = 0)
|
||||||
|
{
|
||||||
|
$pos = $offset;
|
||||||
|
$size = \strlen($der);
|
||||||
|
$constructed = (\ord($der[$pos]) >> 5) & 0x01;
|
||||||
|
$type = \ord($der[$pos++]) & 0x1f;
|
||||||
|
|
||||||
|
// Length
|
||||||
|
$len = \ord($der[$pos++]);
|
||||||
|
if ($len & 0x80) {
|
||||||
|
$n = $len & 0x1f;
|
||||||
|
$len = 0;
|
||||||
|
while ($n-- && $pos < $size) {
|
||||||
|
$len = ($len << 8) | \ord($der[$pos++]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value
|
||||||
|
if ($type == self::ASN1_BIT_STRING) {
|
||||||
|
$pos++; // Skip the first contents octet (padding indicator)
|
||||||
|
$data = \substr($der, $pos, $len - 1);
|
||||||
|
$pos += $len - 1;
|
||||||
|
} elseif (!$constructed) {
|
||||||
|
$data = \substr($der, $pos, $len);
|
||||||
|
$pos += $len;
|
||||||
|
} else {
|
||||||
|
$data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($pos, $data);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
ext/20_PHPMailer.php
Normal file
5
ext/20_PHPMailer.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php // ext/XX_PHPMailer.php
|
||||||
|
|
||||||
|
require_once("ext/PHPMailer/src/Exception.php");
|
||||||
|
require_once("ext/PHPMailer/src/PHPMailer.php");
|
||||||
|
require_once("ext/PHPMailer/src/SMTP.php");
|
||||||
1
ext/PHPMailer
Submodule
1
ext/PHPMailer
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 48392076504a2ee4b091d46fec2c3089b71f804a
|
||||||
1
ext/php-jwt
Submodule
1
ext/php-jwt
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 474047dbd8442a730ab03810f2835839b107cd29
|
||||||
1
ext/php-qrcode
Submodule
1
ext/php-qrcode
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit d650fe73067c2559519f09949e9b71b466ab2bee
|
||||||
1
ext/php-settings-container
Submodule
1
ext/php-settings-container
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 98ccc1b31b31a53bcb563465c4961879b2b93096
|
||||||
46
index.php
Normal file → Executable file
46
index.php
Normal file → Executable file
@ -1,35 +1,27 @@
|
|||||||
<?php #/index.php
|
<?php #/index.php
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set("display_errors", 1);
|
||||||
|
|
||||||
require_once "bin/init.php";
|
require_once "bin/init.php";
|
||||||
|
|
||||||
//Parse page(s)
|
if ("JSON"==$man->RequestedContentType()) { // User wants JSON. So tell him what he wants. What he really really wants.
|
||||||
//array $page = (int main, string sub, array css = [strings], array js = [strings])
|
http_response_code($man->ResponseCode());
|
||||||
//array $output = (string stat, string main, string navi)
|
header('Content-type: application/json');
|
||||||
if ($userID = lgnCheckLogin($mysqli)) {
|
echo trim(json_encode($man->Output()));
|
||||||
$pages = pgsLoadPages($mysqli, $userID);
|
} elseif ("PRINT"==$man->RequestedContentType()) { // User wants a PRINTOUT. Oh my.
|
||||||
if (sizeof($pages)==0) {
|
header('Content-type: text/html');
|
||||||
$output["main"] = "No pages to show";
|
if (200 <= $man->ResponseCode() && 300 > $man->ResponseCode()) {
|
||||||
|
$man->printer->CreateHtml($man->input["print"]);
|
||||||
|
http_response_code($man->printer->ResponseCode());
|
||||||
|
echo trim($man->printer->GetHtml());
|
||||||
} else {
|
} else {
|
||||||
$page["main"] = (isset($input["main"]) && isset($pages[$input["main"]])) ? $input["main"] : key($pages);
|
http_response_code($man->ResponseCode());
|
||||||
$page["sub"] = isset($input["sub"]) ? $input["sub"] : "main";
|
|
||||||
|
|
||||||
include "bin/navi.php";
|
|
||||||
|
|
||||||
$files["pgs"] = pgsIncPage($pages[$page["main"]]["path"]);
|
|
||||||
if (file_exists("pgs/".$pages[$page["main"]]["path"]."/lib.php")) {
|
|
||||||
include "pgs/".$pages[$page["main"]]["path"]."/lib.php";
|
|
||||||
}
|
|
||||||
if ($files["pgs"]) {
|
|
||||||
include $files["pgs"];
|
|
||||||
}
|
|
||||||
|
|
||||||
$output["showlogin"] = "off";
|
|
||||||
}
|
}
|
||||||
|
} else { // Say hello to new connection. And then crush him with our very bad HTML-JS-Client. It will automatically try to authenticate if jwt is found in cookies
|
||||||
|
http_response_code(200);
|
||||||
|
header('Content-type: text/html');
|
||||||
|
include "res/main.html";
|
||||||
}
|
}
|
||||||
|
|
||||||
//Include error-handling, print page and quit
|
include "bin/exit.php"; // Closing connections
|
||||||
include "bin/errors.php"; //Parse error reporting
|
|
||||||
include "bin/print.php"; //Print page
|
|
||||||
include "bin/exit.php"; //Conclusions
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|||||||
15
index_new.php
Normal file
15
index_new.php
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?php #/index.php
|
||||||
|
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
ini_set("display_errors", 1);
|
||||||
|
|
||||||
|
require_once "bin/init_new.php";
|
||||||
|
|
||||||
|
Request::Read();
|
||||||
|
if (Request::IsServeClient()) {
|
||||||
|
http_response_code(200);
|
||||||
|
header('Content-type: text/html');
|
||||||
|
include "client/index.html";
|
||||||
|
} else {
|
||||||
|
Manager::Answer();
|
||||||
|
}
|
||||||
55
lib/00_main.php
Normal file → Executable file
55
lib/00_main.php
Normal file → Executable file
@ -7,71 +7,26 @@
|
|||||||
* Version: alpha (incomplete, tested, commented) *
|
* Version: alpha (incomplete, tested, commented) *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
#### INCOMPLETE! TBD: ##############
|
function myDate($time = 0) { //Formats timestamp to standard date format
|
||||||
# Mehr Datums- und Zeitformate #
|
|
||||||
####################################
|
|
||||||
|
|
||||||
function SS($param)
|
|
||||||
{ //Prevents injections from strings
|
|
||||||
global $mysqli;
|
|
||||||
return $mysqli->escape_string($param);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SI($param)
|
|
||||||
{ //Prevents injections from integers
|
|
||||||
return intval($param);
|
|
||||||
}
|
|
||||||
|
|
||||||
function myDate($time = 0)
|
|
||||||
{ //Formats timestamp to standard date format
|
|
||||||
if ($time==0) $time = time();
|
if ($time==0) $time = time();
|
||||||
return date(FORMATDATE, $time);
|
return date(FORMATDATE, $time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function myTime($time = 0)
|
function myTime($time = 0) { //Formats timestamp to standard time format
|
||||||
{ //Formats timestamp to standard time format
|
|
||||||
if ($time==0) $time = time();
|
if ($time==0) $time = time();
|
||||||
return date(FORMATTIME, $time);
|
return date(FORMATTIME, $time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function myDateTime($time = 0)
|
function myDateTime($time = 0) { //Formats timestamp to standard date and time format
|
||||||
{ //Formats timestamp to standard date and time format
|
|
||||||
return myDate($time)." ".myTime($time);
|
return myDate($time)." ".myTime($time);
|
||||||
}
|
}
|
||||||
function fmtTime($format, $time = 0)
|
function fmtTime($format, $time = 0) { //Formats timestamp to a preset value
|
||||||
{ //Formats timestamp to a preset value
|
|
||||||
global $times;
|
global $times;
|
||||||
return (isset($times[$format]) ? date($times[$format], $time) : myDateTime);
|
return (isset($times[$format]) ? date($times[$format], $time) : myDateTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addError($ident, $add)
|
function addError($ident, $add) { //Lists an error
|
||||||
{ //Lists an error
|
|
||||||
global $err;
|
global $err;
|
||||||
if (isset($err[$ident])) $err["out"][] = $err[$ident].$add;
|
if (isset($err[$ident])) $err["out"][] = $err[$ident].$add;
|
||||||
else $err["out"][] = $err["unknown"].$ident.$add;
|
else $err["out"][] = $err["unknown"].$ident.$add;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLibrary($lib, $notify=true)
|
|
||||||
{ //Includes a php-library
|
|
||||||
global $err;
|
|
||||||
if (!(substr($lib,0,4)=="lib/")) $lib = "lib/".$lib;
|
|
||||||
if (!(substr($lib,-4,4)==".php")) $lib.= ".php";
|
|
||||||
if (file_exists($lib)) include_once($lib);
|
|
||||||
elseif ($notify) addError("noLib", $lib);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addStyle($css, $notify=true)
|
|
||||||
{ //adds a stylesheet to $output-array
|
|
||||||
global $output, $err;
|
|
||||||
if (file_exists($css) && ! in_array($css,$output["css"])) $output["css"][] = $css;
|
|
||||||
elseif ($notify) addError("noCss", $css);
|
|
||||||
}
|
|
||||||
|
|
||||||
function addJscript($js, $notify=true)
|
|
||||||
{ //adds a javascript to $output-array
|
|
||||||
global $output, $err;
|
|
||||||
if (file_exists($js) && ! in_array($js,$output["js"])) $output["js"][] = $js;
|
|
||||||
elseif ($notify) addError("noJs", $js);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
483
lib/10_login.php
483
lib/10_login.php
@ -1,254 +1,323 @@
|
|||||||
<?php #lib/10_login.php
|
<?php #lib/10_login.php
|
||||||
|
|
||||||
/********************************************************************************
|
/********************************************************************************
|
||||||
* Content: Login related functions *
|
* Content: Login class *
|
||||||
* Author: Nils Otterpohl *
|
* Author: Nils Otterpohl *
|
||||||
* Last modification: 24.06.2019 *
|
* Last modification: 13.12.2020 *
|
||||||
* Version: alpha (incomplete, tested, commented) *
|
* Version: alpha (object, incomplete, uncommented, untested) *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
function lgnGenHash($password, $salt, $iterations, $length = 32, $algo = "sha256") {
|
class Login
|
||||||
|
{
|
||||||
|
public static $instance = null;
|
||||||
|
public static $id = ""; // ID of authenticated user
|
||||||
|
public static $loggedIn = false;
|
||||||
|
|
||||||
|
private $key; // JWT Server private key
|
||||||
|
private $man; // Owning API manager
|
||||||
|
|
||||||
|
private $jwt = null; // JSON Web Token (JWT), which was submitted or generated
|
||||||
|
private $jwt_expires = 0; // Expiration date of JWT
|
||||||
|
private $jwt_renewed = false;//
|
||||||
|
private $status = [ // Status array to be issued to client
|
||||||
|
"loggedIn" => false, // Status of login
|
||||||
|
"newjwt" => null, // If not null, client needs to use this new jwt from now on
|
||||||
|
"secToken" => null // Client needs to use this secToken to accompany the next input
|
||||||
|
];
|
||||||
|
|
||||||
|
private $login = ""; // Login name of authenticated user
|
||||||
|
private $vornamen = ""; // Prenames of authenticated user
|
||||||
|
private $nachnamen = ""; // Surnames of authenticated user
|
||||||
|
private $permissions = null;// Buffer to hold Personalgruppen, Abteilungen and Rechte
|
||||||
|
|
||||||
|
private $secTokenUse = null; // (One time) Security token to be issued to client to be used with incoming data
|
||||||
|
private $secTokenVerify = null; // Last Security token issued. Incoming data will be verified against this
|
||||||
|
|
||||||
|
/***** Static functions *****/
|
||||||
|
|
||||||
|
// Generates a salted password hash of desired length and with the desired algorithm
|
||||||
|
public static function GenHash($password, $salt, $iterations, $length = 32, $algo = "sha256") {
|
||||||
return hash_pbkdf2($algo, $password, $salt, $iterations, $length*2);
|
return hash_pbkdf2($algo, $password, $salt, $iterations, $length*2);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lgnGenSalt($length = 16) {
|
// Generates a random salt (or also password) of desired length
|
||||||
|
public static function GenSalt($length = 16) {
|
||||||
return bin2hex(random_bytes($length));
|
return bin2hex(random_bytes($length));
|
||||||
}
|
|
||||||
function lgnTransformPassword($password = "") {
|
|
||||||
$salt = lgnGenSalt();
|
|
||||||
if ($password=="")
|
|
||||||
$password = lgnGenSalt(8);
|
|
||||||
return array(
|
|
||||||
"password" => $password,
|
|
||||||
"iterations" => DESIRED_ITERATIONS,
|
|
||||||
"salt" => $salt,
|
|
||||||
"hash" => lgnGenHash($password, $salt, DESIRED_ITERATIONS)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnSecSessionStart() {
|
|
||||||
// Copied and adjusted after https://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen
|
|
||||||
|
|
||||||
// Zwingt die Sessions nur Cookies zu benutzen.
|
|
||||||
if (ini_set("session.use_only_cookies", 1) === FALSE) {
|
|
||||||
exit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Holt Cookie-Parameter.
|
/***** Getter functions and Constructor *****/
|
||||||
$cookieParams = session_get_cookie_params();
|
|
||||||
// Erstes true = Nur https Zugriff, Zweites true = Blockt JavaScript Zugriff
|
|
||||||
session_set_cookie_params($cookieParams["lifetime"], $cookieParams["path"], $cookieParams["domain"], true, true);
|
|
||||||
$sessionName = "SessionOfHigherSecureness";
|
|
||||||
session_name($sessionName);
|
|
||||||
session_start(); // Startet die PHP-Sitzung
|
|
||||||
session_regenerate_id(); // Erneuert die Session, löscht die alte.
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnRegenerateToken() {
|
public function JWT() {return $this->jwt;}
|
||||||
if (isset($_SESSION["secTokenUse"])) {
|
public function SecToken() {return $this->secTokenUse;}
|
||||||
$_SESSION["secTokenVerify"] = $_SESSION["secTokenUse"];
|
public function Status() {return $this->status;}
|
||||||
|
public function LoggedIn() {return self::$loggedIn;}
|
||||||
|
public function ID() {return self::$id;}
|
||||||
|
public function Login() {return $this->login;}
|
||||||
|
public function Vornamen() {return $this->vornamen;}
|
||||||
|
public function Nachnamen() {return $this->nachnamen;}
|
||||||
|
|
||||||
|
public function __construct($database, $keyvaluestorage, $jwtKey, $manager, $jwt, $input, $issueNewSecToken = true, $allowJwtRenewal = false) {
|
||||||
|
self::$instance = $this;
|
||||||
|
$this->key = $jwtKey;
|
||||||
|
$this->man = $manager;
|
||||||
|
$this->jwt = $jwt;
|
||||||
|
|
||||||
|
// Try to login via JWT, if it was submitted
|
||||||
|
if ($this->jwt!==null) {
|
||||||
|
$this->authenticateJWT($allowJwtRenewal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to login via password, if not yet logged in
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
if (isset($input["login"], $input["password"])) {
|
||||||
|
$this->authenticatePWD($input["login"], $input["password"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$loggedIn) {
|
||||||
|
// Erzeuge neues Security-Token
|
||||||
|
$this->secTokenUse = $this->secTokenVerify;
|
||||||
|
if ($issueNewSecToken || $this->secTokenUse==null) {
|
||||||
|
$this->secTokenUse = Login::GenSalt(16);
|
||||||
|
}
|
||||||
|
Manager::$kv->set("secToken:".self::$id, $this->secTokenUse);
|
||||||
|
Manager::$kv->expire("secToken:".self::$id, 60*60); // secToken ist für 60 min * 60 s/min gültig
|
||||||
|
$this->status["secToken"] = $this->secTokenUse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Public functions *****/
|
||||||
|
|
||||||
|
// Blacklists the issued JWT and sets logged in status to false
|
||||||
|
public function Logout() {
|
||||||
|
Manager::$kv->set("JWT:Blacklist:".self::$id, $this->jwt);
|
||||||
|
Manager::$kv->expireAt("JWT:Blacklist:".self::$id, $this->jwt_expires);
|
||||||
|
$this->jwt = null;
|
||||||
|
$this->status["loggedIn"] = false; // DEPRECATED
|
||||||
|
self::$loggedIn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function AppendJwtAndSecToken(&$json) {
|
||||||
|
if (self::$instance->secTokenVerify!=$this->secTokenUse) {
|
||||||
|
$json["secToken"] = $this->secTokenUse;
|
||||||
|
}
|
||||||
|
if ($this->jwt_renewed) {
|
||||||
|
$json["jwt"] = $this->jwt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function VerifySecToken($secToken) {
|
||||||
|
return ($this->secTokenVerify==null || $secToken==$this->secTokenVerify);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if logged in user has the (or, if array, one of the) named right(s)
|
||||||
|
// Fetched rights will be buffered, so recurring queries are no performance problem
|
||||||
|
static function HasRight($rightNames) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($rightNames==null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isset(self::$instance->permissions["rights"])) {
|
||||||
|
self::$instance->permissions["rights"] = []; // TODO: private member not accessible DÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖÖT
|
||||||
|
$qry = "SELECT r.Name FROM Personal_Verwalter pv "
|
||||||
|
."LEFT JOIN Rechte_Verwalter rv ON rv.Verwalter = pv.Verwalter "
|
||||||
|
."LEFT JOIN Rechte r ON r.ID = rv.Rechte "
|
||||||
|
."WHERE pv.Personal = ".self::$instance->ID();
|
||||||
|
$res = Manager::$db->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
self::$instance->permissions["rights"][] = $row["Name"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($rightNames)) {
|
||||||
|
foreach ($rightNames as $r) {
|
||||||
|
if (in_array($r, self::$instance->permissions["rights"])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
} else {
|
} else {
|
||||||
$_SESSION["secTokenVerify"] = "";
|
return in_array($rightNames, self::$instance->permissions["rights"]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$_SESSION["secTokenUse"] = lgnGenSalt(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnLogin($mysqli, $login, $password) {
|
// Returns true if logged in user is in the (or, if array, one of the) named department(s) ("Abteilungen")
|
||||||
|
// Fetched departments will be buffered, so recurring queries are no performance problem
|
||||||
|
public function InAbteilung($abteilungIDs) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($abteilungIDs==null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isset($this->permissions["abteilungenIDs"])) {
|
||||||
|
$this->permissions["abteilungenIDs"] = [];
|
||||||
|
$qry = "SELECT Abteilungen FROM Personal_Abteilungen WHERE Personal = ".self::$id;
|
||||||
|
$res = Manager::$db->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$this->permissions["abteilungenIDs"][] = $row["Abteilungen"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($abteilungIDs)) {
|
||||||
|
foreach ($abteilungIDs as $aID) {
|
||||||
|
if (in_array($aID, $this->permissions["abteilungenIDs"])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return in_array($abteilungIDs, $this->permissions["abteilungenIDs"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if logged in user is in the (or, if array, one of the) named group(s) ("Gruppen")
|
||||||
|
// Fetched groups will be buffered, so recurring queries are no performance problem
|
||||||
|
public function InGruppe($gruppenIDs) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($gruppenIDs==null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isset($this->permissions["gruppenIDs"])) {
|
||||||
|
$this->permissions["gruppenIDs"] = [];
|
||||||
|
$qry = "SELECT Gruppen FROM Personal_Gruppen WHERE Personal = ".self::$id;
|
||||||
|
$res = Manager::$db->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$this->permissions["gruppenIDs"][] = $row["Gruppen"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_array($gruppenIDs)) {
|
||||||
|
foreach ($gruppenIDs as $gID) {
|
||||||
|
if (in_array($gID, $this->permissions["gruppenIDs"])) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return in_array($gruppenIDs, $this->permissions["gruppenIDs"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Private functions *****/
|
||||||
|
|
||||||
|
// Tries to decode the submitted JWT, checks blacklisting of JWT and sets logged in status accordingly
|
||||||
|
private function authenticateJWT($allowJwtRenewal) {
|
||||||
|
try {
|
||||||
|
$decoded = \Firebase\JWT\JWT::decode($this->jwt, $this->key, ['HS256']);
|
||||||
|
// What has been an array before encoding is now an object
|
||||||
|
// Read member variables from decoded data
|
||||||
|
self::$id = $decoded->data->userID==58 && isset($_GET["emulate"]) ? $_GET["emulate"] : $decoded->data->userID;
|
||||||
|
$this->login = $decoded->data->userLogin;
|
||||||
|
$this->vornamen = $decoded->data->userVornamen;
|
||||||
|
$this->nachnamen = $decoded->data->userNachnamen;
|
||||||
|
$this->jwt_expires = $decoded->exp;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// JWT will throw exceptions if it cannot decode the JWT. In this case - don't login
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Manager::$kv->get("JWT:Blacklist:".self::$id)!=$this->jwt) {
|
||||||
|
// Not blacklisted, can log in
|
||||||
|
$this->status["loggedIn"] = true; // DEPRECATED
|
||||||
|
self::$loggedIn = true;
|
||||||
|
$this->secTokenVerify = Manager::$kv->get("secToken:".self::$id);
|
||||||
|
$now = time();
|
||||||
|
if ($this->jwt_expires-$now <= JWT_VALID_TIME*0.5 && $allowJwtRenewal) {
|
||||||
|
$this->man->AddMessage("Extending JWT");
|
||||||
|
$this->encodeJWT();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->jwt = null;
|
||||||
|
self::$id = -99;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function encodeJWT() {
|
||||||
|
$time = time();
|
||||||
|
$this->jwt_expires = $time + JWT_VALID_TIME;
|
||||||
|
$issuer = "http://fw-innenstadt.de/";
|
||||||
|
$token = [
|
||||||
|
"iat" => $time,
|
||||||
|
"exp" => $this->jwt_expires,
|
||||||
|
"iss" => "fw-innenstadt.de",
|
||||||
|
"data" => [
|
||||||
|
"userID" => self::$id,
|
||||||
|
"userLogin" => $this->login,
|
||||||
|
"userVornamen" => $this->vornamen,
|
||||||
|
"userNachnamen" => $this->nachnamen
|
||||||
|
]
|
||||||
|
];
|
||||||
|
$this->jwt = \Firebase\JWT\JWT::encode($token, $this->key);
|
||||||
|
$this->jwt_renewed = true;
|
||||||
|
$this->status["newjwt"] = $this->jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function authenticatePWD($login, $password) {
|
||||||
// Copied and adjusted after https://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen
|
// Copied and adjusted after https://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen
|
||||||
// and https://github.com/nextcloud/user_external/blob/master/lib/webdavauth.php
|
// and https://github.com/nextcloud/user_external/blob/master/lib/webdavauth.php
|
||||||
|
// and https://codeofaninja.com/2018/09/rest-api-authentication-example-php-jwt-tutorial.html
|
||||||
|
|
||||||
// Das Benutzen vorbereiteter Statements verhindert SQL-Injektion.
|
// Login zum gewünschten Format zurechtbiegen
|
||||||
$login = strtolower($login);
|
$login = strtolower($login);
|
||||||
$login = str_replace("@feuerwehr-bs.net", "", $login);
|
$login = str_replace("@feuerwehr-bs.net", "", $login);
|
||||||
|
|
||||||
if ($stmt = $mysqli->prepare("SELECT p.ID, p.login FROM Personal p WHERE p.login = ? LIMIT 1")) {
|
// Das Benutzen vorbereiteter Statements verhindert SQL-Injektion.
|
||||||
|
if ($stmt = Manager::$db->prepare("SELECT p.ID, p.login, p.Vornamen, p.Nachnamen FROM Personal p WHERE p.login = ? LIMIT 1")) {
|
||||||
$stmt->bind_param("s", $login); // Bind "$login" to parameter.
|
$stmt->bind_param("s", $login); // Bind "$login" to parameter.
|
||||||
$stmt->execute(); // Führe die vorbereitete Anfrage aus.
|
$stmt->execute(); // Führe die vorbereitete Anfrage aus.
|
||||||
$stmt->store_result();
|
$stmt->store_result();
|
||||||
|
|
||||||
// hole Variablen von result.
|
// hole Variablen von result.
|
||||||
$stmt->bind_result($userID, $userLogin);
|
$stmt->bind_result(self::$id, $this->login, $this->vornamen, $this->nachnamen);
|
||||||
$stmt->fetch();
|
$stmt->fetch();
|
||||||
|
|
||||||
if ($stmt->num_rows == 1) {
|
if ($stmt->num_rows == 1) {
|
||||||
$url= 'https://'.urlencode($userLogin).':'.urlencode($password).'@feuerwehr-bs.net/webdav';
|
$url= 'https://'.urlencode($this->login).':'.urlencode($password).'@feuerwehr-bs.net/webdav';
|
||||||
$headers = get_headers($url);
|
$headers = get_headers($url);
|
||||||
if($headers === false) {
|
if($headers === false) {
|
||||||
addError("loginFailed", 'ERROR: Not possible to connect to WebDAV Url: "https://feuerwehr-bs.net/webdav"');
|
//addError("loginFailed", 'ERROR: Not possible to connect to WebDAV Url: "https://feuerwehr-bs.net/webdav"');
|
||||||
return false;
|
// THROW
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
$returnCode= substr($headers[0], 9, 3);
|
$returnCode= substr($headers[0], 9, 3);
|
||||||
|
|
||||||
if(substr($returnCode, 0, 1) === '2') {
|
if (substr($returnCode, 0, 1) === '2') {
|
||||||
// Passwort ist korrekt!
|
// Passwort ist korrekt!
|
||||||
|
|
||||||
// Hole den user-agent string des Benutzers.
|
|
||||||
$userBrowser = $_SERVER["HTTP_USER_AGENT"];
|
|
||||||
// XSS-Schutz, denn eventuell wird der Wert gedruckt
|
// XSS-Schutz, denn eventuell wird der Wert gedruckt
|
||||||
$userID = preg_replace("/[^0-9]+/", "", $userID);
|
self::$id = preg_replace("/[^0-9]+/", "", self::$id);
|
||||||
$_SESSION["userID"] = $userID;
|
$this->login = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $this->login);
|
||||||
// XSS-Schutz, denn eventuell wird der Wert gedruckt
|
|
||||||
$userLogin = preg_replace("/[^a-zA-Z0-9_\-]+/", "", $userLogin);
|
|
||||||
$_SESSION["userLogin"] = $userLogin;
|
|
||||||
|
|
||||||
// Generiere einen Hash aus dem Passwort mit zufälligem Salt und speichere ihn in der Datenbank
|
// Generiere einen Hash aus dem Passwort mit zufälligem Salt und speichere ihn in der Datenbank
|
||||||
$hash = lgnGenHash($password, lgnGenSalt(), DESIRED_ITERATIONS);
|
// Question: Why?
|
||||||
$_SESSION["loginString"] = lgnGenHash($hash, $userBrowser, 10, 64, "sha512");
|
$hash = Login::GenHash($password, Login::GenSalt(), DESIRED_ITERATIONS);
|
||||||
$mysqli->query("REPLACE INTO sys_iservhashes(ID, Hash) VALUES ('".$userID."', '".$hash."')");
|
Manager::$db->query("REPLACE INTO sys_iservhashes(ID, Hash) VALUES ('".self::$id."', '".$hash."')");
|
||||||
|
|
||||||
// Login erfolgreich.
|
// Login erfolgreich.
|
||||||
return true;
|
$this->encodeJWT();
|
||||||
} else {
|
$this->status["loggedIn"] = true; // DEPRECATED
|
||||||
|
self::$loggedIn = true;
|
||||||
|
$this->man->AddMessage("Login erfolgreich!");
|
||||||
|
return;
|
||||||
|
} else if ($returnCode === "401") {
|
||||||
// Passwort ist nicht korrekt
|
// Passwort ist nicht korrekt
|
||||||
addError("loginFailed", " Passwort nicht korrekt. Fehlercode (bitte an Nils senden): ".$returnCode);
|
$this->man->AddMessage("Das Passwort ist vermutlich nicht korrekt. Bitte erneut probieren oder an Nils wenden.");
|
||||||
|
} else if ($returnCode === "503") {
|
||||||
|
// Passwort ist nicht korrekt
|
||||||
|
$this->man->AddMessage("IServ verweigert im Moment die Anmeldung. Vermutlich gab es temporär zu viele falsche Passworteingaben "
|
||||||
|
."(auch von anderen Nutzern). Bitte erst in 10 Minuten neu probieren!");
|
||||||
|
} else {
|
||||||
|
// Unbekannter Fehler
|
||||||
|
$this->man->AddMessage("Anmeldung konnte aus unbekannten Gründen nicht durchgeführt werden. Fehlercode (bitte an Nils senden): ".$returnCode);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addError("loginFailed", "Benutzername inkorrekt ".$login);
|
$this->man->AddMessage("Benutzername inkorrekt: ".$login);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($mysqli->error!="") {
|
if (Manager::$db->error!="") {
|
||||||
addError("mysql", $mysqli->error);
|
$this->man->AddMessage("Mysql error: ".Manager::$db->error);
|
||||||
}
|
}
|
||||||
return false;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function lgnLogout() {
|
|
||||||
// Setze alle Session-Werte zurück
|
|
||||||
$_SESSION = array();
|
|
||||||
|
|
||||||
// hole Session-Parameter
|
|
||||||
$params = session_get_cookie_params();
|
|
||||||
|
|
||||||
// Lösche das aktuelle Cookie.
|
|
||||||
setcookie(
|
|
||||||
session_name(),
|
|
||||||
'',
|
|
||||||
time() - 42000,
|
|
||||||
$params["path"],
|
|
||||||
$params["domain"],
|
|
||||||
$params["secure"],
|
|
||||||
$params["httponly"]
|
|
||||||
);
|
|
||||||
|
|
||||||
// Vernichte die Session
|
|
||||||
session_destroy();
|
|
||||||
header('Location: index.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnChangePass($mysqli, $userID, $passOld, $passNew, $passRepeat) {
|
|
||||||
$ret = false;
|
|
||||||
if ($passOld==$passNew) {
|
|
||||||
addError("passChangeFail", "Neues und altes Passwort sind nicht unterschiedlich?");
|
|
||||||
} else if (strlen($passNew)<12) {
|
|
||||||
addError("passChangeFail", "Passwort muss mindestens 12 Zeichen haben!");
|
|
||||||
} else if ($passNew!=$passRepeat) {
|
|
||||||
addError("passChangeFail", "Passwortwiederholung falsch!");
|
|
||||||
} else {
|
|
||||||
if ($stmt = $mysqli->prepare("SELECT iterations, salt, hash FROM users WHERE ID = ? LIMIT 1")) {
|
|
||||||
$stmt->bind_param("i", $_SESSION["userID"]);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->store_result();
|
|
||||||
$stmt->bind_result($passIterations, $passSalt, $passHash);
|
|
||||||
$stmt->fetch();
|
|
||||||
|
|
||||||
if ($stmt->num_rows == 1) {
|
|
||||||
if (lgnGenHash($passOld, $passSalt, $passIterations)!=$passHash) {
|
|
||||||
addError("passChangeFail", "Altes Passwort inkorrekt!");
|
|
||||||
} else {
|
|
||||||
$newSalt = lgnGenSalt();
|
|
||||||
$newHash = lgnGenHash($passNew, $newSalt, DESIRED_ITERATIONS);
|
|
||||||
$mysqli->query("UPDATE users SET iterations='".DESIRED_ITERATIONS."', salt='".$newSalt."', hash='".$newHash."' "
|
|
||||||
."WHERE ID='".$_SESSION["userID"]."'");
|
|
||||||
$userBrowser = $_SERVER["HTTP_USER_AGENT"];
|
|
||||||
$_SESSION["loginString"] = lgnGenHash($newHash, $userBrowser, 10, 64, "sha512");
|
|
||||||
$ret = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addError("passChangeFailed", $mysqli->error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnCheckBrute($mysqli, $userID) {
|
|
||||||
// Copied and adjusted after https://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen
|
|
||||||
// Hole den aktuellen Zeitstempel
|
|
||||||
|
|
||||||
// Alle Login-Versuche der letzten zwei Stunden werden gezählt.
|
|
||||||
if ($stmt = $mysqli->prepare("SELECT time FROM failedlogins WHERE ID = ? AND time > DATE_SUB(NOW(), INTERVAL 2 HOUR)")) {
|
|
||||||
$stmt->bind_param("i", $userID);
|
|
||||||
|
|
||||||
// Führe die vorbereitet Abfrage aus.
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->store_result();
|
|
||||||
|
|
||||||
// Wenn es mehr als 5 fehlgeschlagene Versuche gab
|
|
||||||
if ($stmt->num_rows > 5) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnCheckLogin($mysqli) {
|
|
||||||
// Überprüfe, ob alle Session-Variablen gesetzt sind
|
|
||||||
if (isset($_SESSION["userID"], $_SESSION["userLogin"], $_SESSION["loginString"])) {
|
|
||||||
$userID = $_SESSION["userID"];
|
|
||||||
$loginString = $_SESSION["loginString"];
|
|
||||||
$userLogin = $_SESSION["userLogin"];
|
|
||||||
|
|
||||||
// Hole den user-agent string des Benutzers.
|
|
||||||
$userBrowser = $_SERVER["HTTP_USER_AGENT"];
|
|
||||||
|
|
||||||
// Die UserID wird hier nochmal abgerufen, damit dem Rückgabewert der Funktion vertraut werden kann
|
|
||||||
if ($stmt = $mysqli->prepare("SELECT ID, Hash FROM sys_iservhashes WHERE ID = ? LIMIT 1")) {
|
|
||||||
$stmt->bind_param("i", $userID);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->store_result();
|
|
||||||
|
|
||||||
if ($stmt->num_rows == 1) {
|
|
||||||
// Wenn es den Benutzer gibt, hole die Variablen von result.
|
|
||||||
$stmt->bind_result($uID, $uHash);
|
|
||||||
$stmt->fetch();
|
|
||||||
|
|
||||||
if (lgnGenHash($uHash, $userBrowser, 10, 64, "sha512") == $loginString) {
|
|
||||||
// Eingeloggt!!!!
|
|
||||||
return $uID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Nicht eingeloggt
|
|
||||||
if ($mysqli->error!="") {
|
|
||||||
addError("mysql", $mysqli->error);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function lgnCheckRight($mysqli, $right, $userID = null) {
|
|
||||||
if ($right==null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if ($userID==null) {
|
|
||||||
$userID = lgnCheckLogin($mysqli);
|
|
||||||
}
|
|
||||||
$query = "SELECT b.name FROM Personal p "
|
|
||||||
."LEFT JOIN link_Personal_Personalgruppen lpp ON p.ID=lpp.Personal "
|
|
||||||
."LEFT JOIN link_Berechtigungen_Personalgruppen lbp ON lbp.Personalgruppen = lpp.Personalgruppen "
|
|
||||||
."LEFT JOIN Berechtigungen b ON b.ID = lbp.Berechtigungen "
|
|
||||||
."WHERE p.ID = ".$userID." and b.Name ";
|
|
||||||
if (is_array($right)) {
|
|
||||||
$query.= "IN ('".implode("', '", $right)."')";
|
|
||||||
} else {
|
|
||||||
$query.= "= '".$right."'";
|
|
||||||
}
|
|
||||||
if ($res = $mysqli->query($query)) {
|
|
||||||
if ($res->num_rows>0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
31
lib/20_template.php
Normal file → Executable file
31
lib/20_template.php
Normal file → Executable file
@ -56,6 +56,35 @@ function tplReplSection($tpl, $section, $value)
|
|||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function tplReplCondition($tpl, $section, $bool)
|
||||||
|
{ // selects one of two sections in a template
|
||||||
|
tplCheckMarker($section);
|
||||||
|
$mif = tplMakeSectionMarker($section, "IF");
|
||||||
|
$melse = tplMakeSectionMarker($section, "ELSE");
|
||||||
|
$mend = tplMakeSectionMarker($section, "END");
|
||||||
|
|
||||||
|
$start = strpos($tpl, $mif);
|
||||||
|
$else_start = strpos($tpl, $melse);
|
||||||
|
$else_end = $else_start + strlen($melse);
|
||||||
|
$end = strrpos($tpl, $mend)+strlen($mend);
|
||||||
|
|
||||||
|
if (($start===false) || ($else_start == false) || ($end===false) || ($end<=$start)) return $tpl;
|
||||||
|
|
||||||
|
$value = "";
|
||||||
|
if ($bool) {
|
||||||
|
// Show if-part
|
||||||
|
$value = substr($tpl, $start,$else_end-$start);
|
||||||
|
} else {
|
||||||
|
// Show else-part
|
||||||
|
$value = substr($tpl, $else_start,$end-$else_start);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ret = ($start>0 ? substr($tpl, 0, $start) : "");
|
||||||
|
$ret.= $value;
|
||||||
|
if ($end<strlen($tpl)) $ret.= substr($tpl, $end);
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
function tplReplMarker($tpl, $marker, $value)
|
function tplReplMarker($tpl, $marker, $value)
|
||||||
{ //replaces a marker in a template with a value
|
{ //replaces a marker in a template with a value
|
||||||
tplCheckMarker($marker);
|
tplCheckMarker($marker);
|
||||||
@ -71,5 +100,3 @@ function tplReplMarkerArray($tpl, $array)
|
|||||||
}
|
}
|
||||||
return $tpl;
|
return $tpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
292
lib/30_pages.php
Normal file → Executable file
292
lib/30_pages.php
Normal file → Executable file
@ -7,61 +7,259 @@
|
|||||||
* Version: stable (complete, tested, uncommented) *
|
* Version: stable (complete, tested, uncommented) *
|
||||||
********************************************************************************/
|
********************************************************************************/
|
||||||
|
|
||||||
function pgsCreatePage($label, $link, $class)
|
/**************************************************/
|
||||||
{
|
/******************** BaseApp *********************/
|
||||||
#Creates array with markers required for navigation
|
/**************************************************/
|
||||||
return array(
|
|
||||||
"###NAVLABEL###" => $label,
|
abstract class BaseApp {
|
||||||
"###NAVHREF###" => $link,
|
protected $db; // Database connection
|
||||||
"###NAVCLASS###" => $class
|
protected $kv; // Redis connection
|
||||||
);
|
protected $man; // Manager link
|
||||||
|
protected $output = []; // Output
|
||||||
|
protected $meta = [];
|
||||||
|
protected $useRight = null;
|
||||||
|
protected $adminRight = null;
|
||||||
|
protected $table = "";
|
||||||
|
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info) {
|
||||||
|
$this->db = $database;
|
||||||
|
$this->kv = $keyvaluestore;
|
||||||
|
$this->man = $manager;
|
||||||
|
$this->useRight = $info["useRight"];
|
||||||
|
$this->adminRight = $info["adminRight"];
|
||||||
|
$this->table = $info["table"];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function canEdit() {
|
||||||
|
return $this->man->user->HasRight($this->adminRight);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pgsLoadPages($mysqli, $userID) {
|
/**************************************************/
|
||||||
/* userID unused for now, maybe will add page access control if neccessary */
|
/******************** BaseLink ********************/
|
||||||
$ret = array();
|
/**************************************************/
|
||||||
|
|
||||||
$dir = scandir("pgs");
|
abstract class BaseLink extends BaseApp
|
||||||
|
{
|
||||||
|
private $page;
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info);
|
||||||
|
$this->page = $page;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($dir as $val) {
|
public function DoRequest($method, $ids) {
|
||||||
if ($val!="." && $val!="..") {
|
switch ($method) {
|
||||||
if (file_exists("pgs/".$val."/module.json")) {
|
case "GET":
|
||||||
$json = json_decode(file_get_contents("pgs/".$val."/module.json"), true);
|
return $this->get($ids);
|
||||||
if (lgnCheckRight($mysqli, $json["useRight"], $userID)) {
|
case "POST":
|
||||||
$key = $json["moduleName"];
|
return $this->canEdit() ? $this->insert($ids) : 403;
|
||||||
$ret[$key]["ID"] = $json["moduleName"];
|
case "PATCH":
|
||||||
$ret[$key]["name"] = $json["title"];
|
return $this->canEdit() ? $this->update($ids) : 403;
|
||||||
$ret[$key]["path"] = $val;
|
case "DELETE":
|
||||||
$ret[$key]["tpl"] = pgsCreatePage($val, "", "");
|
return $this->canEdit() ? $this->remove($ids) : 403;
|
||||||
|
default:
|
||||||
|
return 501;
|
||||||
|
} }
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get($ids) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update($ids) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************************************************/
|
||||||
|
/******************** BasePage ********************/
|
||||||
|
/**************************************************/
|
||||||
|
|
||||||
|
abstract class BasePage extends BaseApp
|
||||||
|
{
|
||||||
|
protected $title; // Title of Hyperlink
|
||||||
|
protected $route; // Module name, i.e. the route to open this page (/index.php/page/filter/%ROUTE%/filter)
|
||||||
|
protected $path; // Internal path to files (/pgs/PATH/)
|
||||||
|
protected $links = []; // List of existing submodules
|
||||||
|
protected $output = []; // Output
|
||||||
|
protected $groupIndex = [];
|
||||||
|
protected $options = [];
|
||||||
|
protected $optionsIndex = [];
|
||||||
|
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info);
|
||||||
|
$this->title = $info["title"];
|
||||||
|
$this->route = $info["route"];
|
||||||
|
$this->path = $info["path"];
|
||||||
|
$this->links = $info["links"];
|
||||||
|
$this->options = $this->fillOptions($this->canEdit());
|
||||||
|
foreach ($this->options as $field => $rows) {
|
||||||
|
foreach ($rows as $index => $row) {
|
||||||
|
$this->optionsIndex[$field][$row["ID"]] = &$this->options[$field][$index];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function GetLink($link) {
|
||||||
|
if (isset($this->links[$link])) {
|
||||||
|
return $this->links[$link];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function GetTemplate() {
|
||||||
|
if (file_exists($this->FullPath()."template.html")) {
|
||||||
|
return $this->FullPath()."template.html";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function FullPath() {
|
||||||
|
return "pgs/".$this->path."/";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function DoRequest($method) {
|
||||||
|
$id = $this->man->Main();
|
||||||
|
switch ($method) {
|
||||||
|
case "HEAD":
|
||||||
|
return $this->head();
|
||||||
|
case "GET":
|
||||||
|
return $this->get($id);
|
||||||
|
case "OPTIONS":
|
||||||
|
return $this->info();
|
||||||
|
case "POST":
|
||||||
|
return $this->canEdit() ? $this->insert() : 403;
|
||||||
|
case "PATCH":
|
||||||
|
case "DELETE":
|
||||||
|
if ($id===null) {
|
||||||
|
$this->man->AddMessage("Keine ID spezifiziert!");
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->canEdit()) {
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
return $method=="PATCH" ? $this->update($id) : $this->remove($id);
|
||||||
|
}
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert() {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function head() {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function get($id) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function info() {
|
||||||
|
$this->man->output["options"] = $this->options;
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update($id) {
|
||||||
|
return 501;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($id) {
|
||||||
|
if ($stmt = $this->db->prepare("DELETE FROM ".$this->table." WHERE ID = ?")) {
|
||||||
|
$stmt->bind_param("i", $id);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Erfolgreich entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else if (0==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Fehler: Es wurde nichts entfernt!");
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Fehler: Es wurden mehrere Einträge entfernt!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetResult() {
|
||||||
|
return $this->output;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetMeta() {
|
||||||
|
return $this->meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetOptions($admin = false) {
|
||||||
|
return $this->options;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** Protected functions ********************/
|
||||||
|
|
||||||
|
protected function getFilterString($op, $allowedOps, $table, $field, $value) {
|
||||||
|
$tblString = ""!=$table ? $table."." : "";
|
||||||
|
if (in_array($op, $allowedOps)) {
|
||||||
|
switch ($op) {
|
||||||
|
case "=":
|
||||||
|
return $value===null ? $tblString."`".$field."` IS NULL " : $tblString."`".$field."` = '".$value."' ";
|
||||||
|
case "<>":
|
||||||
|
return $value===null ? $tblString."`".$field."` IS NOT NULL " : $tblString."`".$field."` <> '".$value."' ";
|
||||||
|
case "<":
|
||||||
|
return $tblString."`".$field."` < '".$value."' ";
|
||||||
|
case ">":
|
||||||
|
return $tblString."`".$field."` > '".$value."' ";
|
||||||
|
case "<=":
|
||||||
|
return $tblString."`".$field."` <= '".$value."' ";
|
||||||
|
case ">=":
|
||||||
|
return $tblString."`".$field."` >= '".$value."' ";
|
||||||
|
case "like":
|
||||||
|
return $tblString."`".$field."` LIKE '%".$value."%' ";
|
||||||
|
case "not like":
|
||||||
|
return $tblString."`".$field."` NOT LIKE '%".$value."%' ";
|
||||||
|
case "in":
|
||||||
|
return "'".$value."' IN ".$tblString.$field."' ";
|
||||||
|
case "not in":
|
||||||
|
return "'".$value."' NOT IN ".$tblString.$field."' ";
|
||||||
|
} }
|
||||||
|
return "1 ";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function registerGroup($group) {
|
||||||
|
if (!array_key_exists($group, $this->groupIndex)) {
|
||||||
|
$len = array_push($this->output, ["ID" => $group, "ENTRIES" => []]);
|
||||||
|
$this->groupIndex[$group] = &$this->output[$len-1];
|
||||||
|
} }
|
||||||
|
|
||||||
|
protected function addEntryToOutput($group, $entry, $single) {
|
||||||
|
if ($single) {
|
||||||
|
$entry["GROUP"] = $group;
|
||||||
|
$this->output = $entry;
|
||||||
|
} else {
|
||||||
|
$this->registerGroup($group);
|
||||||
|
$this->groupIndex[$group]["ENTRIES"][] = $entry;
|
||||||
|
} }
|
||||||
|
|
||||||
|
protected function fillOptions($admin = false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSub($field, $string) {
|
||||||
|
$ret = [];
|
||||||
|
$entries = explode(",", $string ?? "");
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
if (""!=$entry && isset($this->optionsIndex[$field][$entry])) {
|
||||||
|
$ret[] = $this->optionsIndex[$field][$entry];
|
||||||
|
} }
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
|
||||||
|
|
||||||
function pgsIncPage($name)
|
|
||||||
{
|
|
||||||
#Checks if a mainpage exists, completes filename and returns valid page or default
|
|
||||||
$path = "pgs/".$name."/";
|
|
||||||
$possiblefiles = array("index.php", $name.".php");
|
|
||||||
foreach ($possiblefiles as $val)
|
|
||||||
{
|
|
||||||
if (file_exists($path.$val)) return $path.$val;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function pgsInclSub($name)
|
|
||||||
{ //
|
|
||||||
global $pages,$page;
|
|
||||||
$possibleexts = array("", ".php", ".htm", ".html", ".txt");
|
|
||||||
$file = "pgs/".$pages[$page["main"]]["path"]."/".$name;
|
|
||||||
foreach ($possibleexts as $val)
|
|
||||||
{
|
|
||||||
if (file_exists($file.$val)) return $file.$val;
|
|
||||||
}
|
|
||||||
addError("pgsInc", $name);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
4623
lib/40_PHPMailer.php
4623
lib/40_PHPMailer.php
File diff suppressed because it is too large
Load Diff
271
lib/40_email.php
Executable file
271
lib/40_email.php
Executable file
@ -0,0 +1,271 @@
|
|||||||
|
<?php #lib/40_email.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: email related functions *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
function emlSendEmail($to, $subject, $text) {
|
||||||
|
global $mail;
|
||||||
|
|
||||||
|
$mail->setFrom(MAILNOREPLY, "Webserver OF Innenstadt");
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$mail->Body = $text;
|
||||||
|
$mail->addAddress($to);
|
||||||
|
$mail->send();
|
||||||
|
$mail->clearAddresses();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aus pgs/xx_termine/lib.php
|
||||||
|
|
||||||
|
function printTeilnehmer($val) {
|
||||||
|
$ret = "<tr><td>".$val["Nachnamen"].", ".$val["Vornamen"]."</td><td>";
|
||||||
|
$traits = array();
|
||||||
|
|
||||||
|
if ($val["ZF"]) $traits[] = "ZF";
|
||||||
|
if ($val["GF"]) $traits[] = "GF";
|
||||||
|
if ($val["KF"]) $traits[] = "KF";
|
||||||
|
if ($val["AGT"]) $traits[] = "AGT";
|
||||||
|
if ($val["TH"]) $traits[] = "TH";
|
||||||
|
if (!$val["TM"]) $traits[] = "Kein Grundlehrgang";
|
||||||
|
if (count($traits)) $ret.= implode(", ", $traits);
|
||||||
|
|
||||||
|
$ret.= "</td><td>Corona-Unterweisung: ".($val["CoronaUnt"] ? "Ja" : "Nein")."</td>"
|
||||||
|
."<td>".($val["Teilnehmer"] ? "Dabei" : "Will")."</td></tr>\n";
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
function informParticipant($name, $mail, $can, $dienst, $verantwortliche) {
|
||||||
|
$text = "Hallo ".$name."!<br/><br/>\nDu hast dich für ".$dienst." angemeldet.<br/><br/>";
|
||||||
|
if ($can) {
|
||||||
|
$text.= "Du wurdest zu diesem Dienst als Teilnehmer ausgelost und <b>DARFST KOMMEN</b>. Sei bitte pünktlich und bring eine Maske mit!<br/>"
|
||||||
|
."Wenn Du dich am Tag des Dienstes nicht gesund fühlst, bleib zuhause! Wenn Du aus welchem Grund auch immer doch nicht kommen kannst, "
|
||||||
|
."teile dieses bitte unverzüglich deinen Diensthabenden mit: ".$verantwortliche."<br/><br/>\n";
|
||||||
|
} else {
|
||||||
|
$text.= "Leider hattest Du diesmal Pech und <u>DARFST N I C H T KOMMEN</u>. Dafür wirst Du bei den nächsten Verlosungen bevorzugt.<br/><br/>\n";
|
||||||
|
}
|
||||||
|
$text.= "Viele Grüße vom Webserver der OF Innenstadt!";
|
||||||
|
emlSendEmail($mail, "Ergebnis der Platzvergabe für ".$dienst, $text);
|
||||||
|
}
|
||||||
|
|
||||||
|
function informParticipantFromID($mysqli, $terminID, $personalID, $can) {
|
||||||
|
$qry = "SELECT Beginn,Thema,Verantwortliche,a.Name FROM Termine t LEFT JOIN Termine_Kategorien a ON a.ID = t.Kategorie WHERE t.ID = '".$terminID."'";
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
$termin = $res->fetch_assoc();
|
||||||
|
$qry = "SELECT Vornamen,Login FROM Personal p WHERE p.ID = '".$personalID."'";
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
if ($personal = $res->fetch_assoc())
|
||||||
|
informParticipant($personal["Vornamen"], $personal["Login"]."@feuerwehr-bs.net", $can, $termin["Name"]." am ".date("d.m.Y H:i", strtotime($termin["Beginn"])), $termin["Verantwortliche"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendParticipantsListToResponsibles($mysqli, $terminID) {
|
||||||
|
$qry = "SELECT t.Beginn,IF(t.Beginn>=NOW(),1,0) nichtvorbei,t.Thema,t.Verantwortliche,a.Name as aName FROM Termine t "
|
||||||
|
."LEFT JOIN Termine_Kategorien a ON a.ID=t.Kategorie "
|
||||||
|
."WHERE t.ID='".$terminID."'";
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
$tmrow = $res->fetch_assoc();
|
||||||
|
|
||||||
|
if ("1" == $tmrow["nichtvorbei"]) {
|
||||||
|
// Generate participants list
|
||||||
|
$qry = "SELECT p.ID,p.Login,p.Nachnamen,p.Vornamen,pk.Name pkName,pf.Kürzel pfKürzel,GROUP_CONCAT(l.Kürzel) lKürzel, tt.Status "
|
||||||
|
."FROM Termine_Teilnahmen tt "
|
||||||
|
."LEFT JOIN Personal p ON p.ID=tt.Personal "
|
||||||
|
."LEFT JOIN Personal_Kategorien pk ON pk.ID=p.Kategorie "
|
||||||
|
."LEFT JOIN Personal_Funktionen pf ON pf.ID=p.Funktion "
|
||||||
|
."LEFT JOIN Personal_Lehrgänge pl ON pl.Personal=p.ID "
|
||||||
|
."LEFT JOIN Lehrgänge l ON l.ID=pl.Lehrgänge "
|
||||||
|
."WHERE tt.Termine='".$terminID."' "
|
||||||
|
."GROUP BY p.ID ORDER BY tt.Status DESC, p.Kategorie DESC";
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
|
||||||
|
$status = "";
|
||||||
|
$recipients = array();
|
||||||
|
$liste = "<h3>Teilnahmeliste für ".$tmrow["aName"]." am ".date("d.m.Y H:i", strtotime($tmrow["Beginn"])).": ".$tmrow["Thema"]."</h3>\n<table border=1>\n";
|
||||||
|
$markedCauser = false;
|
||||||
|
$liste.= "<tr><th>Name</th><th>Funktionen</th><th>Corona-Unterweisung</th></tr>\n";
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
if ($status!=$row["Status"]) {
|
||||||
|
$status = $row["Status"];
|
||||||
|
if ("2" == $status) {
|
||||||
|
$liste.= "<tr><th colspan=3>Diensthabende</th></tr>\n";
|
||||||
|
} else if ("1" == $status) {
|
||||||
|
$liste.= "<tr><th colspan=3>Erlaubte Teilnehmer</th></tr>\n";
|
||||||
|
} else if ("0" == $status) {
|
||||||
|
$liste.= "<tr><th colspan=3>Weitere Interessenten</th></tr>\n";
|
||||||
|
} }
|
||||||
|
$liste.= "<tr><td>".$row["Nachnamen"].", ".$row["Vornamen"]."</td><td>";
|
||||||
|
$ausb = explode(",", $row["lKürzel"]);
|
||||||
|
|
||||||
|
$traits = array();
|
||||||
|
if ($row["pfKürzel"]!=null) $traits[] = $row["pfKürzel"];
|
||||||
|
if (in_array("KF", $ausb)) $traits[] = "KF";
|
||||||
|
if (in_array("MA", $ausb)) $traits[] = "MA";
|
||||||
|
if (in_array("AGT", $ausb)) $traits[] = "AGT";
|
||||||
|
if (in_array("TH", $ausb)) $traits[] = "TH";
|
||||||
|
if (!in_array("TM1", $ausb)) $traits[] = "Ohne TM1";
|
||||||
|
if (count($traits)) $liste.= implode(", ", $traits);
|
||||||
|
|
||||||
|
$liste.= "</td><td>".(in_array("CU", $ausb) ? "Ja" : "Nein")."</td></tr>\n";
|
||||||
|
|
||||||
|
if ("2" == $row["Status"]) {
|
||||||
|
$recipients[] = $row["Login"]."@feuerwehr-bs.net";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$liste.= "</table>\n";
|
||||||
|
|
||||||
|
if (!in_array("nils.otterpohl@feuerwehr-bs.net", $recipients))
|
||||||
|
$recipients[] = "nils.otterpohl@feuerwehr-bs.net";
|
||||||
|
foreach ($recipients as $recipient) {
|
||||||
|
emlSendEmail($recipient, "Teilnahmeliste - Dienst ".date("D, den d.m.Y um H:i", strtotime($tmrow["Beginn"])), $liste);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $liste;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// mysql_con mysqli
|
||||||
|
// string subject
|
||||||
|
// string text
|
||||||
|
// int notificationLvl : {1: Neuer Thread, 2: Neue Nachricht}
|
||||||
|
// string neededRecipientRight
|
||||||
|
/*function emlSendNotification($mysqli, $sendUser, $subject, $text, $notificationLvl, $neededRecipientRight = null) {
|
||||||
|
global $mail;
|
||||||
|
|
||||||
|
$mail->setFrom(MAILNOREPLY, "Sophia und Nils Hochzeitswebsite");
|
||||||
|
$mail->isHTML(false);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$to_tpl = tplExtrSection($text, "###TO###");
|
||||||
|
|
||||||
|
$qry = "SELECT DISTINCT u.email, u.groupID FROM users u "
|
||||||
|
.($neededRecipientRight!==null ? "LEFT JOIN rolerights rr ON rr.roleID=u.roleID LEFT JOIN rights r ON r.ID=rr.rightID " : "")
|
||||||
|
."WHERE u.ID!=".$sendUser." AND u.notifications>=".$notificationLvl." ".($neededRecipientRight!==null ? "AND r.name='".$neededRecipientRight."' " : "");
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$nameres = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
||||||
|
."FROM guests WHERE companion=0 AND groupID=".$row["groupID"]);
|
||||||
|
$to = "";
|
||||||
|
while ($namerow = $nameres->fetch_assoc()) {
|
||||||
|
$to.= tplReplMarkerArray($to_tpl, $namerow);
|
||||||
|
}
|
||||||
|
$mail->Body = tplReplSection($text, "###TO###", $to);
|
||||||
|
$mail->addAddress($row["email"]);
|
||||||
|
$mail->send();
|
||||||
|
$mail->clearAddresses();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Sendet eine Mail an ALLE User (die nicht abgesagt haben)
|
||||||
|
// mysql_con mysqli
|
||||||
|
// int sendUser
|
||||||
|
// string from
|
||||||
|
// string subject
|
||||||
|
// string text
|
||||||
|
// string neededRecipientRight
|
||||||
|
/*function emlSendMassMail($mysqli, $sendUser, $from, $subject, $text, $neededRecipientRight = null) {
|
||||||
|
global $mail;
|
||||||
|
|
||||||
|
if (!$res = $mysqli->query("SELECT DISTINCT IFNULL(u.eMailFrom, u.email) email FROM users u WHERE u.ID=".$sendUser))
|
||||||
|
addError("Mysql", $mysqli->error);
|
||||||
|
$from_mail = $res->fetch_assoc()["email"];
|
||||||
|
$mail->setFrom($from_mail, $from);
|
||||||
|
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$to_tpl = tplExtrSection($text, "###TO###");
|
||||||
|
|
||||||
|
$qry = "SELECT DISTINCT u.email, u.groupID FROM guests g LEFT JOIN users u ON u.groupID=g.groupID "
|
||||||
|
.($neededRecipientRight!==null ? "LEFT JOIN rolerights rr ON rr.roleID=u.roleID LEFT JOIN rights r ON r.ID=rr.rightID " : "")
|
||||||
|
."WHERE u.ID!=".$sendUser." AND IFNULL(g.comes, 99)!=0 ".($neededRecipientRight!==null ? "AND r.name='".$neededRecipientRight."' " : "");
|
||||||
|
$res = $mysqli->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$tores = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
||||||
|
."FROM guests WHERE companion=0 AND groupID=".$row["groupID"]);
|
||||||
|
$to = "";
|
||||||
|
while ($torow = $tores->fetch_assoc()) {
|
||||||
|
$to.= tplReplMarkerArray($to_tpl, $torow);
|
||||||
|
}
|
||||||
|
$mail->Body = tplReplSection($text, "###TO###", $to);
|
||||||
|
$mail->addAddress($row["email"]);
|
||||||
|
$mail->send();
|
||||||
|
$mail->clearAddresses();
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Sendet eine Mail an EINEN User
|
||||||
|
// mysql_con mysqli
|
||||||
|
// int targetUser
|
||||||
|
// string subject
|
||||||
|
// string text
|
||||||
|
/*function emlSendSingleMail($mysqli, $targetUser, $subject, $text) {
|
||||||
|
global $mail;
|
||||||
|
$ret = false;
|
||||||
|
|
||||||
|
$targetMail = null;
|
||||||
|
$groupID = null;
|
||||||
|
if ($stmt = $mysqli->prepare("SELECT email, groupID FROM users u WHERE u.ID = ?")) {
|
||||||
|
$stmt->bind_param("i", $targetUser);
|
||||||
|
$stmt->execute();
|
||||||
|
$stmt->bind_result($targetMail, $groupID);
|
||||||
|
$stmt->fetch();
|
||||||
|
$stmt->close();
|
||||||
|
} else {
|
||||||
|
addError("Mysql", $mysqli->error);
|
||||||
|
}
|
||||||
|
if ($targetMail!=null && $groupID!=null) {
|
||||||
|
$mail->setFrom(MAILNOREPLY, "Sophia und Nils Hochzeitswebsite");
|
||||||
|
|
||||||
|
$mail->isHTML(true);
|
||||||
|
$mail->Subject = $subject;
|
||||||
|
$to_tpl = tplExtrSection($text, "###TO###");
|
||||||
|
|
||||||
|
$tores = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
||||||
|
."FROM guests WHERE companion=0 AND groupID=".$groupID);
|
||||||
|
$to = "";
|
||||||
|
while ($torow = $tores->fetch_assoc()) {
|
||||||
|
$to.= tplReplMarkerArray($to_tpl, $torow);
|
||||||
|
}
|
||||||
|
$mail->Body = tplReplSection($text, "###TO###", $to);
|
||||||
|
$mail->addAddress($targetMail);
|
||||||
|
$mail->send();
|
||||||
|
$mail->clearAddresses();
|
||||||
|
$ret = true;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}*/
|
||||||
1347
lib/41_SMTP.php
1347
lib/41_SMTP.php
File diff suppressed because it is too large
Load Diff
@ -1,39 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PHPMailer Exception class.
|
|
||||||
* PHP Version 5.5.
|
|
||||||
*
|
|
||||||
* @see https://github.com/PHPMailer/PHPMailer/ The PHPMailer GitHub project
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon (Synchro/coolbru) <phpmailer@synchromedia.co.uk>
|
|
||||||
* @author Jim Jagielski (jimjag) <jimjag@gmail.com>
|
|
||||||
* @author Andy Prevost (codeworxtech) <codeworxtech@users.sourceforge.net>
|
|
||||||
* @author Brent R. Matzelle (original founder)
|
|
||||||
* @copyright 2012 - 2017 Marcus Bointon
|
|
||||||
* @copyright 2010 - 2012 Jim Jagielski
|
|
||||||
* @copyright 2004 - 2009 Andy Prevost
|
|
||||||
* @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
|
|
||||||
* @note This program is distributed in the hope that it will be useful - WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PHPMailer\PHPMailer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PHPMailer exception handler.
|
|
||||||
*
|
|
||||||
* @author Marcus Bointon <phpmailer@synchromedia.co.uk>
|
|
||||||
*/
|
|
||||||
class Exception extends \Exception
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Prettify error message output.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function errorMessage()
|
|
||||||
{
|
|
||||||
return '<strong>' . htmlspecialchars($this->getMessage()) . "</strong><br />\n";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
192
lib/43_email.php
192
lib/43_email.php
@ -1,192 +0,0 @@
|
|||||||
<?php #lib/main.php
|
|
||||||
|
|
||||||
/********************************************************************************
|
|
||||||
* Content: email related functions *
|
|
||||||
* Author: Nils Otterpohl *
|
|
||||||
********************************************************************************/
|
|
||||||
|
|
||||||
function emlSendEmail($to, $subject, $text) {
|
|
||||||
global $mail;
|
|
||||||
|
|
||||||
$mail->setFrom(MAILNOREPLY, "Webserver OF Innenstadt");
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$mail->Body = $text;
|
|
||||||
$mail->addAddress($to);
|
|
||||||
$mail->send();
|
|
||||||
$mail->clearAddresses();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// string $email
|
|
||||||
// array $prenames
|
|
||||||
// array $surnames
|
|
||||||
// string $password
|
|
||||||
function emlSendInvitation($email, $invites, $password) {
|
|
||||||
global $mail;
|
|
||||||
|
|
||||||
$mail->setFrom(MAILNOREPLY, "Sophia und Nils Hochzeitswebsite");
|
|
||||||
$mail->addAddress($email);
|
|
||||||
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = "Einladung zur Hochzeit von Sophia und Nils";
|
|
||||||
|
|
||||||
$tpl = tplLoadFile("res/invitation.html");
|
|
||||||
$text = tplExtrSection($tpl, "###INVITATION###");
|
|
||||||
$to_tpl = tplExtrSection($text, "###TO###");
|
|
||||||
$to = "";
|
|
||||||
$count = 0;
|
|
||||||
foreach ($invites as $names) {
|
|
||||||
if ($names["companion"]==0) {
|
|
||||||
$count++;
|
|
||||||
$replace = array(
|
|
||||||
"###ADDRESSING###" => $names["addressing"],
|
|
||||||
"###PRENAMES###" => $names["prenames"],
|
|
||||||
"###SURNAMES###" => $names["surnames"],
|
|
||||||
"###NICKNAME###" => $names["nickname"]=="" ? $names["prenames"] : $names["nickname"]
|
|
||||||
);
|
|
||||||
$to.= tplReplMarkerArray($to_tpl, $replace);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$text = tplReplSection($text, "###TO###", $to);
|
|
||||||
$text = tplReplSection($text, "###INTRO###", $count>1 ? tplExtrSection($tpl, "###INTRO.MULTI###") : tplExtrSection($tpl, "###INTRO.SINGLE###"));
|
|
||||||
$text = tplReplSection($text, "###EXPLANATION###", $count>1 ? tplExtrSection($tpl, "###EXPLANATION.MULTI###") : tplExtrSection($tpl, "###EXPLANATION.SINGLE###"));
|
|
||||||
$text = tplReplSection($text, "###MULTIPLEASE###", $count>1 ? tplExtrSection($tpl, "###MULTIPLEASE###") : "");
|
|
||||||
$url = parse_url("https://".$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']);
|
|
||||||
$text = tplReplMarker($text, "###PAGEURL###", "https://".$url["host"].$url["path"]);
|
|
||||||
$text = tplReplMarker($text, "###EMAIL###", $email);
|
|
||||||
$text = tplReplMarker($text, "###PASSWORD###", $password);
|
|
||||||
|
|
||||||
$mail->Body = $text;
|
|
||||||
|
|
||||||
|
|
||||||
$attachments = explode(";", tplExtrSection($tpl, "###ATTACHMENTS###"));
|
|
||||||
foreach ($attachments as $attachment) {
|
|
||||||
if ($attachment!="") {
|
|
||||||
$attributes = explode("|", $attachment);
|
|
||||||
if (count($attributes)>1 && $attributes[0]=="embedded") {
|
|
||||||
$mail->addEmbeddedImage($attributes[1].$attributes[2], $attributes[3], $attributes[2]);
|
|
||||||
} else {
|
|
||||||
$mail->addAttachment($attachment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//$mail->addEmbeddedImage("res/Einladung.png", "cid_einladung", "Einladung.png");
|
|
||||||
|
|
||||||
$mail->send();
|
|
||||||
$mail->clearAddresses();
|
|
||||||
$mail->clearAttachments();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mysql_con mysqli
|
|
||||||
// string subject
|
|
||||||
// string text
|
|
||||||
// int notificationLvl : {1: Neuer Thread, 2: Neue Nachricht}
|
|
||||||
// string neededRecipientRight
|
|
||||||
function emlSendNotification($mysqli, $sendUser, $subject, $text, $notificationLvl, $neededRecipientRight = null) {
|
|
||||||
global $mail;
|
|
||||||
|
|
||||||
$mail->setFrom(MAILNOREPLY, "Sophia und Nils Hochzeitswebsite");
|
|
||||||
$mail->isHTML(false);
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$to_tpl = tplExtrSection($text, "###TO###");
|
|
||||||
|
|
||||||
$qry = "SELECT DISTINCT u.email, u.groupID FROM users u "
|
|
||||||
.($neededRecipientRight!==null ? "LEFT JOIN rolerights rr ON rr.roleID=u.roleID LEFT JOIN rights r ON r.ID=rr.rightID " : "")
|
|
||||||
."WHERE u.ID!=".$sendUser." AND u.notifications>=".$notificationLvl." ".($neededRecipientRight!==null ? "AND r.name='".$neededRecipientRight."' " : "");
|
|
||||||
$res = $mysqli->query($qry);
|
|
||||||
while ($row = $res->fetch_assoc()) {
|
|
||||||
$nameres = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
|
||||||
."FROM guests WHERE companion=0 AND groupID=".$row["groupID"]);
|
|
||||||
$to = "";
|
|
||||||
while ($namerow = $nameres->fetch_assoc()) {
|
|
||||||
$to.= tplReplMarkerArray($to_tpl, $namerow);
|
|
||||||
}
|
|
||||||
$mail->Body = tplReplSection($text, "###TO###", $to);
|
|
||||||
$mail->addAddress($row["email"]);
|
|
||||||
$mail->send();
|
|
||||||
$mail->clearAddresses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sendet eine Mail an ALLE User (die nicht abgesagt haben)
|
|
||||||
// mysql_con mysqli
|
|
||||||
// int sendUser
|
|
||||||
// string from
|
|
||||||
// string subject
|
|
||||||
// string text
|
|
||||||
// string neededRecipientRight
|
|
||||||
function emlSendMassMail($mysqli, $sendUser, $from, $subject, $text, $neededRecipientRight = null) {
|
|
||||||
global $mail;
|
|
||||||
|
|
||||||
if (!$res = $mysqli->query("SELECT DISTINCT IFNULL(u.eMailFrom, u.email) email FROM users u WHERE u.ID=".$sendUser))
|
|
||||||
addError("Mysql", $mysqli->error);
|
|
||||||
$from_mail = $res->fetch_assoc()["email"];
|
|
||||||
$mail->setFrom($from_mail, $from);
|
|
||||||
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$to_tpl = tplExtrSection($text, "###TO###");
|
|
||||||
|
|
||||||
$qry = "SELECT DISTINCT u.email, u.groupID FROM guests g LEFT JOIN users u ON u.groupID=g.groupID "
|
|
||||||
.($neededRecipientRight!==null ? "LEFT JOIN rolerights rr ON rr.roleID=u.roleID LEFT JOIN rights r ON r.ID=rr.rightID " : "")
|
|
||||||
."WHERE u.ID!=".$sendUser." AND IFNULL(g.comes, 99)!=0 ".($neededRecipientRight!==null ? "AND r.name='".$neededRecipientRight."' " : "");
|
|
||||||
$res = $mysqli->query($qry);
|
|
||||||
while ($row = $res->fetch_assoc()) {
|
|
||||||
$tores = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
|
||||||
."FROM guests WHERE companion=0 AND groupID=".$row["groupID"]);
|
|
||||||
$to = "";
|
|
||||||
while ($torow = $tores->fetch_assoc()) {
|
|
||||||
$to.= tplReplMarkerArray($to_tpl, $torow);
|
|
||||||
}
|
|
||||||
$mail->Body = tplReplSection($text, "###TO###", $to);
|
|
||||||
$mail->addAddress($row["email"]);
|
|
||||||
$mail->send();
|
|
||||||
$mail->clearAddresses();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sendet eine Mail an EINEN User
|
|
||||||
// mysql_con mysqli
|
|
||||||
// int targetUser
|
|
||||||
// string subject
|
|
||||||
// string text
|
|
||||||
function emlSendSingleMail($mysqli, $targetUser, $subject, $text) {
|
|
||||||
global $mail;
|
|
||||||
$ret = false;
|
|
||||||
|
|
||||||
$targetMail = null;
|
|
||||||
$groupID = null;
|
|
||||||
if ($stmt = $mysqli->prepare("SELECT email, groupID FROM users u WHERE u.ID = ?")) {
|
|
||||||
$stmt->bind_param("i", $targetUser);
|
|
||||||
$stmt->execute();
|
|
||||||
$stmt->bind_result($targetMail, $groupID);
|
|
||||||
$stmt->fetch();
|
|
||||||
$stmt->close();
|
|
||||||
} else {
|
|
||||||
addError("Mysql", $mysqli->error);
|
|
||||||
}
|
|
||||||
if ($targetMail!=null && $groupID!=null) {
|
|
||||||
$mail->setFrom(MAILNOREPLY, "Sophia und Nils Hochzeitswebsite");
|
|
||||||
|
|
||||||
$mail->isHTML(true);
|
|
||||||
$mail->Subject = $subject;
|
|
||||||
$to_tpl = tplExtrSection($text, "###TO###");
|
|
||||||
|
|
||||||
$tores = $mysqli->query("SELECT prenames `###PRENAMES###`, surnames `###SURNAMES###`, addressing `###ADDRESSING###`, IFNULL(nickname, prenames) `###NICKNAME###` "
|
|
||||||
."FROM guests WHERE companion=0 AND groupID=".$groupID);
|
|
||||||
$to = "";
|
|
||||||
while ($torow = $tores->fetch_assoc()) {
|
|
||||||
$to.= tplReplMarkerArray($to_tpl, $torow);
|
|
||||||
}
|
|
||||||
$mail->Body = tplReplSection($text, "###TO###", $to);
|
|
||||||
$mail->addAddress($targetMail);
|
|
||||||
$mail->send();
|
|
||||||
$mail->clearAddresses();
|
|
||||||
$ret = true;
|
|
||||||
}
|
|
||||||
return $ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
6
lib/50_content.php
Normal file → Executable file
6
lib/50_content.php
Normal file → Executable file
@ -14,12 +14,8 @@ function cntCipherTextSym($text, $cipher)
|
|||||||
{
|
{
|
||||||
for ($j=0; $j<strlen($cipher) && $i<strlen($text); $j++, $i++)
|
for ($j=0; $j<strlen($cipher) && $i<strlen($text); $j++, $i++)
|
||||||
{
|
{
|
||||||
$outText .= $text{$i} ^ $cipher{$j};
|
$outText .= $text[$i] ^ $cipher[$j];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $outText;
|
return $outText;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
444
lib/60_printer.php
Normal file
444
lib/60_printer.php
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
<?php #ajax/print.php
|
||||||
|
|
||||||
|
class Printer
|
||||||
|
{
|
||||||
|
private $db;
|
||||||
|
private $man;
|
||||||
|
private $page;
|
||||||
|
private $responseCode = 500;
|
||||||
|
private $prints = [];
|
||||||
|
private $html = "";
|
||||||
|
|
||||||
|
public function __construct($db, $man) {
|
||||||
|
$this->db = $db;
|
||||||
|
$this->man = $man;
|
||||||
|
$this->page = $this->man->Route();
|
||||||
|
|
||||||
|
$qry = "SELECT l.ID,l.Bezeichnung,l.Hauptblock,b.Css,b.Breite,b.Höhe FROM sys_print_layouts l "
|
||||||
|
."LEFT JOIN sys_print_blocks b ON l.Hauptblock=b.ID WHERE Seite=? ORDER BY l.Bevorzugt DESC, l.Bezeichnung ASC";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("s", $this->page);
|
||||||
|
$stmt->execute();
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$this->prints[] = [
|
||||||
|
"ID" => strval($row["ID"]),
|
||||||
|
"Bezeichnung" => $row["Bezeichnung"],
|
||||||
|
"Hauptblock" => $row["Hauptblock"],
|
||||||
|
"Css" => $row["Css"],
|
||||||
|
"Breite" => $row["Breite"],
|
||||||
|
"Höhe" => $row["Höhe"],
|
||||||
|
];
|
||||||
|
} } }
|
||||||
|
|
||||||
|
public function GetPrints() {
|
||||||
|
return $this->prints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CreateHtml($printID) {
|
||||||
|
// Find requested layout
|
||||||
|
$layout = null;
|
||||||
|
foreach ($this->prints as $key => $print) {
|
||||||
|
if (strval($printID)==$print["ID"]) {
|
||||||
|
$layout = $print;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Breakoff if layout doesn't exist
|
||||||
|
if (null == $layout) {
|
||||||
|
$this->responseCode = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get content
|
||||||
|
$content = $this->man->Output()["content"];
|
||||||
|
|
||||||
|
$block = [
|
||||||
|
"ID" => $layout["Hauptblock"],
|
||||||
|
"Bezeichnung" => $layout["Bezeichnung"],
|
||||||
|
"Css" => $layout["Css"],
|
||||||
|
"Breite" => $layout["Breite"],
|
||||||
|
"Höhe" => $layout["Höhe"],
|
||||||
|
"Links" => 0,
|
||||||
|
"Oben" => 0
|
||||||
|
];
|
||||||
|
$entries = $this->prepareEntries($content);
|
||||||
|
$this->printBlock($block, $entries);
|
||||||
|
$this->html.= $this->joinHtml($entries);
|
||||||
|
|
||||||
|
$this->responseCode = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetHtml() {
|
||||||
|
return $this->html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ResponseCode() {
|
||||||
|
return $this->responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** Private functions *************************/
|
||||||
|
|
||||||
|
private function prepareEntries($content) {
|
||||||
|
$ret = [];
|
||||||
|
$selectionExists = sizeof($this->man->selected)>0;
|
||||||
|
foreach ($content as $group) {
|
||||||
|
foreach ($group["ENTRIES"] as $entry) {
|
||||||
|
if (!$selectionExists || in_array($entry["ID"], $this->man->selected)) {
|
||||||
|
$ret[$entry["ID"]] = [
|
||||||
|
"html" => "",
|
||||||
|
"floating" => [],
|
||||||
|
"content" => $entry,
|
||||||
|
];
|
||||||
|
} } }
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function joinHtml($entries) {
|
||||||
|
$ret = "";
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$ret.= $entry["html"];
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printBlock($block, &$entries) {
|
||||||
|
// Initialize block
|
||||||
|
$div = "<div name='".$block["Bezeichnung"]."' style='".$block["Css"]
|
||||||
|
."width:".($block["Breite"]/10)."mm;height:".($block["Höhe"]/10)."mm;left:".($block["Links"]/10)."mm;top:".($block["Oben"]/10)."mm;'>";
|
||||||
|
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$entry["html"].= $div;
|
||||||
|
}
|
||||||
|
unset($entry); // $entry was used as a reference, so it needs to be unset
|
||||||
|
|
||||||
|
// Fetch sub-blocks
|
||||||
|
$qry = "SELECT spb.*,sph.Links,sph.Oben FROM sys_print_hierarchy sph LEFT JOIN sys_print_blocks spb ON spb.ID=sph.Kindblock WHERE sph.Block=".$block["ID"];
|
||||||
|
$res = $this->db->query($qry);
|
||||||
|
while ($subblock = $res->fetch_assoc()) {
|
||||||
|
$this->printBlock($subblock, $entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch block elements
|
||||||
|
$elements = [];
|
||||||
|
if ($res = $this->db->query("SELECT * FROM sys_print_elements WHERE Block=".$block["ID"]." ORDER BY Reihenfolge ASC")) {
|
||||||
|
while ($element = $res->fetch_assoc()) {
|
||||||
|
$this->printEntryElement($element, $entries);
|
||||||
|
//$elements[] = $row;
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Finalize block
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$entry["html"].= "</div>";
|
||||||
|
}
|
||||||
|
unset($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printEntryElement($element, &$entries) {
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$do_print = false;
|
||||||
|
$cond = explode("|", $element["Bedingung"]);
|
||||||
|
if ($element["Parameter"]=="") {
|
||||||
|
$element["Parameter"] = "{}";
|
||||||
|
}
|
||||||
|
$params = json_decode($element["Parameter"], true);
|
||||||
|
switch ($cond[0]) {
|
||||||
|
case null:
|
||||||
|
case "":
|
||||||
|
case "0": //Print always
|
||||||
|
$do_print = true;
|
||||||
|
//echo " - Print always";
|
||||||
|
break;
|
||||||
|
case "1": //Field has to be NOT null, not false and not empty text
|
||||||
|
//echo " - Print if NOT null/false/empty";
|
||||||
|
if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])!==null && (
|
||||||
|
$this->getContent($entry["content"], $cond[1])!==false || $this->getContent($entry["content"], $cond[1], true)!=""
|
||||||
|
) ) ) {
|
||||||
|
$do_print = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "2": //Field has to be null, false or empty text
|
||||||
|
//echo " - Print if null/false/empty";
|
||||||
|
if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])===null || $this->getContent($entry["content"], $cond[1])===false || $this->getContent($entry["content"], $cond[1], true)==""))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "3": //Field has to be value (second parameter) or include value if is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes value ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1]);
|
||||||
|
$arr = is_array($result);
|
||||||
|
$do_print = ($arr && in_array($cond[2], $result)) || (!$arr && $cond[2]==$result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "4": //Field has to be NOT value (second parameter) or DONT include value if is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes NOT value ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1]);
|
||||||
|
$arr = is_array($result);
|
||||||
|
$do_print = ($arr && !in_array($cond[2], $result)) || (!$arr && $cond[2]!=$result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "5": //Field has to be text (second parameter) or include text if it is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
//echo " - Print if has/includes text ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1], 1);
|
||||||
|
$testtexts = explode(",", $cond[2]);
|
||||||
|
if (is_array($result)) {
|
||||||
|
$do_print = true;
|
||||||
|
foreach ($testtexts as $testtext) {
|
||||||
|
if (!in_array($testtext, $result)) {
|
||||||
|
$do_print = false;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
} else {
|
||||||
|
$do_print = $cond[2]==$result;
|
||||||
|
} }
|
||||||
|
break;
|
||||||
|
case "6": //Field has to be NOT text (second parameter) or DONT include text if it is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes NOT text ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1], 1);
|
||||||
|
$testtexts = explode(",", $cond[2]);
|
||||||
|
if (is_array($result)) {
|
||||||
|
$do_print = true;
|
||||||
|
foreach ($testtexts as $testtext) {
|
||||||
|
if (in_array($testtext, $result)) {
|
||||||
|
$do_print = false;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
} else {
|
||||||
|
$do_print = $cond[2]!=$result;
|
||||||
|
} }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$print_count = 0;
|
||||||
|
if ($do_print) {
|
||||||
|
$print_count = $params["repeat"] ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($print_count>0) {
|
||||||
|
//echo " - Printing";
|
||||||
|
$print_count--;
|
||||||
|
$border = array();
|
||||||
|
$border["top"] = isset($params["border-top"]) ? $params["border-top"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["bottom"] = isset($params["border-bottom"]) ? $params["border-bottom"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["left"] = isset($params["border-left"]) ? $params["border-left"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["right"] = isset($params["border-right"]) ? $params["border-right"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["color"] = isset($params["border-color"]) ? $params["border-color"] : "#000000";
|
||||||
|
$border["string"] = "";
|
||||||
|
if ($border["left"]!="0") {
|
||||||
|
$border["string"].= "border-left: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["right"]!="0") {
|
||||||
|
$border["string"].= "border-right: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["top"]!="0") {
|
||||||
|
$border["string"].= "border-top: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["bottom"]!="0") {
|
||||||
|
$border["string"].= "border-bottom: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
/*if (isset($params["border"])) {
|
||||||
|
$border["string"] = "border: 1px solid ".$border["color"].";";
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$cnt = $element["Inhalt"];
|
||||||
|
$fields = array();
|
||||||
|
if (preg_match_all("/#([^#\[\]]+)(?:\[(\d*):(\d*)\])?#/", $cnt, $matches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
$replace = "";
|
||||||
|
$fields[] = $match[1];
|
||||||
|
$replace = $this->getContent($entry["content"], $match[1], 1);
|
||||||
|
if (sizeof($match)>3) {
|
||||||
|
if ($match[3]!="") {
|
||||||
|
$replace = substr($replace, 0, intval($match[3]));
|
||||||
|
}
|
||||||
|
if ($match[2]!="") {
|
||||||
|
$replace = substr($replace, intval($match[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cnt = str_replace($match[0], $replace, $cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$left = $element["Links"]/10;
|
||||||
|
$top = $element["Oben"]/10;
|
||||||
|
$width = $element["Breite"]/10;
|
||||||
|
if ($width == 0) {
|
||||||
|
$width = 10;
|
||||||
|
}
|
||||||
|
$height = $element["Höhe"]/10;
|
||||||
|
if ($height == 0) {
|
||||||
|
$height = 10;
|
||||||
|
}
|
||||||
|
$this->moveIfOverlap($entry["floating"], $left, $top, $width, $height, isset($params["float"]) ? $params["float"] : "none");
|
||||||
|
$position = "position:absolute;";
|
||||||
|
if (isset($params["position"])) {
|
||||||
|
$position = "position:".$params["position"].";";
|
||||||
|
}
|
||||||
|
$position.= "z-index:".$element["Reihenfolge"].";";
|
||||||
|
|
||||||
|
switch ($element["Art"]) {
|
||||||
|
case "img":
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="keep") {
|
||||||
|
$imgsize = "max-width: ".$width."mm; max-height: ".$height."mm; width: auto; height: auto; ";
|
||||||
|
}
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="height") {
|
||||||
|
$imgsize = "height: ".$height."mm; width: auto; ";
|
||||||
|
} else {
|
||||||
|
$imgsize = "width: ".$width."mm; height: ".$height."mm; ";
|
||||||
|
}
|
||||||
|
$path = $cnt;
|
||||||
|
if (count($fields)) {
|
||||||
|
$path = $cnt;
|
||||||
|
}
|
||||||
|
//if (file_exists($path)) {
|
||||||
|
$entry["html"].= "<img src='".$path."' style='".$position." left: ".$left."mm; top: ".$top."mm; ".$imgsize."' />";
|
||||||
|
//}
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
$rotate = "";
|
||||||
|
if (isset($params["rotate"]) && $params["rotate"]=="1") {
|
||||||
|
$rotate = "transform: rotate(-90deg); ";
|
||||||
|
$left = "calc(".($left + ($height - $width)/2)."mm - ".$border["left"]."px); ";
|
||||||
|
$top = "calc(".($top + ($width - $height)/2)."mm - ".$border["top"]."px); ";
|
||||||
|
} else {
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
}
|
||||||
|
$font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
|
||||||
|
$font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
|
||||||
|
if (isset($params["weight"])) {
|
||||||
|
$font.= "font-weight: ".$params["weight"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["align"])) {
|
||||||
|
$font.= "text-align: ".$params["align"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["color"])) {
|
||||||
|
$font.= "color: ".$params["color"]."; ";
|
||||||
|
}
|
||||||
|
$entry["html"].= "<span style='".$position."overflow:hidden;white-space:nowrap;left:".$left."top:".$top."width:".$width."mm;height:"
|
||||||
|
.$height."mm;".$font.$border["string"].$rotate."'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
case "circle":
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
$display = "display: inline-flex; align-items: center; justify-content: space-around; ";
|
||||||
|
$font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
|
||||||
|
$font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
|
||||||
|
if (isset($params["weight"])) {
|
||||||
|
$font.= "font-weight: ".$params["weight"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["color"])) {
|
||||||
|
$font.= "color: ".$params["color"]."; ";
|
||||||
|
}
|
||||||
|
$font.= "text-align: center; ";
|
||||||
|
$background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
|
||||||
|
$entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;".$display.$background.$font
|
||||||
|
.$border["string"]."border-radius:50%;'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
case "quartercircle":
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
$background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
|
||||||
|
$border_radius = isset($params["corner"]) ? "border-".$params["corner"]."-radius:100%;" : "border-top-left-radius:100%;";
|
||||||
|
$entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;"/*.$display*/.$background/*.$font*/
|
||||||
|
.$border_radius."'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
} } }
|
||||||
|
unset($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTextFromReference($ref, $getText) {
|
||||||
|
if (1==$getText && array_key_exists("KÜRZEL", $ref)) {
|
||||||
|
return $ref["KÜRZEL"];
|
||||||
|
} elseif (1<=$getText && array_key_exists("NAME", $ref)) {
|
||||||
|
return $ref["NAME"];
|
||||||
|
} elseif (array_key_exists("ID", $ref)) {
|
||||||
|
return $ref["ID"];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// $entry Content-array of entry ([ID, MAIN, SUB])
|
||||||
|
// $key Requested field
|
||||||
|
// $getText 0 = ID is sufficient, 1 = Kürzel (prio.) or Name desired, 2 = Name desired
|
||||||
|
private function getContent($entry, $key, $getText = 0) {
|
||||||
|
// Check if requested field are actually nested keys separated by dots
|
||||||
|
$dotPos = strpos($key, ".");
|
||||||
|
if ($dotPos!==false) {
|
||||||
|
$subkey = substr($key, 0, $dotPos);
|
||||||
|
if (array_key_exists($subkey, $entry)) {
|
||||||
|
return $this->getContent($entry[$subkey], substr($key, $dotPos+1, strlen($key)-$dotPos-1));
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Check if requested field exists
|
||||||
|
if (array_key_exists($key, $entry)) {
|
||||||
|
// Check if requested field is an array (which means it was selected through a foreign reference or link table)
|
||||||
|
if (is_array($entry[$key])) {
|
||||||
|
// Check if array is size 0 -> This means an unset reference, i.e. NULL
|
||||||
|
if (0==sizeof($entry[$key])) {
|
||||||
|
return null;
|
||||||
|
} elseif (array_key_exists("ID", $entry[$key])) { // Check if requested field is a normal foreign reference -> It will have the ID field then.
|
||||||
|
// Foreign reference with ID, NAME and maybe KÜRZEL
|
||||||
|
return $this->getTextFromReference($entry[$key], $getText);
|
||||||
|
} else { // If not, we assume to have an sequential array which means a n:m-link-table
|
||||||
|
$ret = [];
|
||||||
|
foreach ($entry[$key] as $i => $ref) {
|
||||||
|
$ret[] = $this->getTextFromReference($ref, $getText);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
} else { // Requested field is a normal value
|
||||||
|
return $entry[$key];
|
||||||
|
}
|
||||||
|
} elseif (array_key_exists("MAIN", $entry)) { // If we are in the root structure of a content object, we will have sections MAIN and SUB and check those recursively
|
||||||
|
// Search the MAIN-array for field
|
||||||
|
$ret = $this->getContent($entry["MAIN"], $key, $getText);
|
||||||
|
// If only the field name was returned check SUB-array if exists
|
||||||
|
if ($key==$ret && array_key_exists("SUB", $entry)) {
|
||||||
|
$ret = $this->getContent($entry["SUB"], $key, $getText);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
} else {
|
||||||
|
// Nothing was found, we return the field name
|
||||||
|
return $key;
|
||||||
|
} }
|
||||||
|
|
||||||
|
private function moveIfOverlap(&$floating, &$left, &$top, $width, $height, $dir = "none", $lastdir = "none") {
|
||||||
|
$moved = false;
|
||||||
|
foreach ($floating as $key => $float) {
|
||||||
|
if (
|
||||||
|
$left < $float["right"] &&
|
||||||
|
$left+$width > $float["left"] &&
|
||||||
|
$top < $float["bottom"] &&
|
||||||
|
$top+$height > $float["top"]
|
||||||
|
) {
|
||||||
|
if ($float["dir"]=="left" && $lastdir!="right") {
|
||||||
|
$left = $float["right"];
|
||||||
|
} else if ($float["dir"]=="right" && $lastdir!="left") {
|
||||||
|
$left = $float["left"] - $width;
|
||||||
|
} else if ($float["dir"]=="up" && $lastdir!="down") {
|
||||||
|
$top = $float["bottom"];
|
||||||
|
} else if ($float["dir"]=="down" && $lastdir!="up") {
|
||||||
|
$top = $float["top"] - $height;
|
||||||
|
}
|
||||||
|
$lastdir = $float["dir"];
|
||||||
|
$moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($moved) {
|
||||||
|
$this->moveIfOverlap($floating, $left, $top, $width, $height, $dir, $lastdir);
|
||||||
|
} else if ($dir!="none") {
|
||||||
|
$floating[] = array(
|
||||||
|
"left" => $left,
|
||||||
|
"right" => $left + $width,
|
||||||
|
"top" => $top,
|
||||||
|
"bottom" => $top + $height,
|
||||||
|
"dir" => $dir
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
}
|
||||||
332
lib/99_manager.php
Executable file
332
lib/99_manager.php
Executable file
@ -0,0 +1,332 @@
|
|||||||
|
<?php #lib/99_manager.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: API Manager class *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
* Last modification: 13.12.2020 *
|
||||||
|
* Version: alpha (object, incomplete, untested, uncommented) *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
// Missing:
|
||||||
|
// - Error handling
|
||||||
|
// - Proper response codes
|
||||||
|
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
public static $db; // Database, i.e. Mysqli
|
||||||
|
public static $kv; // Key-Value-Storage, i.e. Redis
|
||||||
|
|
||||||
|
public $user; // Object that handles login and logout
|
||||||
|
public $printer;
|
||||||
|
|
||||||
|
private $method = "GET"; // HTTP method used in connection
|
||||||
|
private $requestedContentType = "HTML"; // Desired content type of request
|
||||||
|
|
||||||
|
// Path used after index.php: index.php/MAIN/FILTER/SUB/SUBFILTER
|
||||||
|
private $route = [
|
||||||
|
"route" => "", // Route of page module. Has to be set, at least to "/" [GET(all), POST]
|
||||||
|
"mainID" => null, // Filter of main resource. [GET(one), PUT, PATCH, DELETE] with ID, [GET(all)] with condition
|
||||||
|
"sub" => null, // Route of sub module. Requests are specific to a main resource, so only valid if filter is an ID. [GET(all), POST]
|
||||||
|
"subID" => null // Filter of sub resource. [GET(one), PUT, PATCH, DELETE] with ID, [GET(all)] with condition
|
||||||
|
];
|
||||||
|
|
||||||
|
private $availablePages = []; // Array of scanned and accessible page modules
|
||||||
|
private $availableLinks = []; // Array of scanned and accessible link modules
|
||||||
|
private $availableObjects = []; // Array of scanned and accessible object modules
|
||||||
|
private $responseCode = 401; // HTTP code to be returned
|
||||||
|
|
||||||
|
public $objects = [];
|
||||||
|
public $input = []; // Submitted JSON data after sanitation. ONLY SET IF Security token was valid.
|
||||||
|
public $filter = [];
|
||||||
|
public $selected = [];
|
||||||
|
private $output = [
|
||||||
|
"messages" => [],
|
||||||
|
"admin" => false // TODO DEPRECATED
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct($db, $kv, $key) {
|
||||||
|
self::$db = $db;
|
||||||
|
self::$kv = $kv;
|
||||||
|
|
||||||
|
$jwt = $this->readRequest();
|
||||||
|
|
||||||
|
if (isset($_SERVER["CONTENT_TYPE"]) && "application/json"==$_SERVER["CONTENT_TYPE"]) {
|
||||||
|
$input = $this->cleanInput(json_decode(file_get_contents("php://input"), true));
|
||||||
|
} else {
|
||||||
|
$input = [];
|
||||||
|
if (isset($_GET["filter"])) {
|
||||||
|
$input["filter"] = $this->cleanInput(json_decode($_GET["filter"], true));
|
||||||
|
}
|
||||||
|
if (isset($_GET["selected"])) {
|
||||||
|
$input["selected"] = $this->cleanInput(json_decode($_GET["selected"], true));
|
||||||
|
} else {
|
||||||
|
$input["selected"] = [];
|
||||||
|
}
|
||||||
|
if (isset($_GET["print"])) {
|
||||||
|
$input["print"] = $_GET["print"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("HTML" == $this->requestedContentType && isset($input["print"])) {
|
||||||
|
$this->requestedContentType = "PRINT";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->user = new Login($db, $kv, $key, $this, $jwt, $input, "GET"!=$this->method);
|
||||||
|
if (in_array("Logout", $this->route)) {
|
||||||
|
$this->user->Logout();
|
||||||
|
$this->AddMessage("Logout erkannt!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output status of user login to client
|
||||||
|
$this->output["status"] = $this->user->Status();
|
||||||
|
|
||||||
|
// Login was successful?
|
||||||
|
if ($this->user->LoggedIn()) {
|
||||||
|
// Überprüfe gesendetes Security-Token und veröffentliche ggfs. $input
|
||||||
|
if ("GET"==$this->method) {
|
||||||
|
// Maximal Filter oder Print als Input erlaubt
|
||||||
|
if (isset($input["print"])) {
|
||||||
|
$this->input["print"] = $input["print"];
|
||||||
|
}
|
||||||
|
if (isset($input["filter"])) {
|
||||||
|
$this->filter = $input["filter"];
|
||||||
|
}
|
||||||
|
if (isset($input["selected"])) {
|
||||||
|
$this->selected = $input["selected"];
|
||||||
|
}
|
||||||
|
} else if ("OPTIONS"==$this->method) {
|
||||||
|
// No filter or input at the moment
|
||||||
|
|
||||||
|
} else if (!empty($input)) {
|
||||||
|
if (isset($input["secToken"]) && $this->user->VerifySecToken($input["secToken"])) {
|
||||||
|
$this->input = $input;
|
||||||
|
} else {
|
||||||
|
$this->addMessage("Input wird ignoriert. Senden Sie den secToken mit!");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->detectObjects();
|
||||||
|
$this->detectLinks();
|
||||||
|
$this->detectPages();
|
||||||
|
if ("OPTIONS"==$this->method) {
|
||||||
|
$pages = $this->output["pages"];
|
||||||
|
$this->output["pages"] = [];
|
||||||
|
foreach ($pages as $page) {
|
||||||
|
$this->output["pages"][] = $page["route"];
|
||||||
|
}
|
||||||
|
$this->output["pagesChecksum"] = sha1(implode(",", $this->output["pages"]));
|
||||||
|
$this->responseCode = 200;
|
||||||
|
} else {
|
||||||
|
$this->loadPage();
|
||||||
|
$this->detectPrints();
|
||||||
|
$this->output["filter"] = $this->filter;
|
||||||
|
} } }
|
||||||
|
|
||||||
|
public function JWT() {return $this->jwt;}
|
||||||
|
public function Method() {return $this->method;}
|
||||||
|
public function RequestedContentType() {return $this->requestedContentType;}
|
||||||
|
public function Output() {return $this->output;}
|
||||||
|
public function Route() {return $this->route["route"];}
|
||||||
|
public function Main() {return $this->route["mainID"];}
|
||||||
|
public function Sub() {return $this->route["sub"];}
|
||||||
|
public function SubID() {return $this->route["subID"];}
|
||||||
|
public function ResponseCode() {return $this->responseCode;}
|
||||||
|
public function Filter() {return $this->filter;}
|
||||||
|
|
||||||
|
public function AddMessage($message, $ident = null) {$this->output["messages"][] = $message;}
|
||||||
|
public function ArrayToString($a) {
|
||||||
|
$ret = "";
|
||||||
|
foreach ($a as $k => $v) {
|
||||||
|
$ret.=$k." => ".(is_array($v) ? "[".$this->ArrayToString($v)."]" : $v).", ";
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** PRIVATE FUNCTIONS ********************/
|
||||||
|
|
||||||
|
private function readRequest() {
|
||||||
|
$jwt = null;
|
||||||
|
// Copied from https://stackoverflow.com/a/40582472
|
||||||
|
$headers = null;
|
||||||
|
if (isset($_SERVER['Authorization'])) {
|
||||||
|
$headers = trim($_SERVER["Authorization"]);
|
||||||
|
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
|
||||||
|
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
|
||||||
|
} elseif (function_exists('apache_request_headers')) {
|
||||||
|
$requestHeaders = apache_request_headers();
|
||||||
|
// Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
|
||||||
|
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
|
||||||
|
//print_r($requestHeaders);
|
||||||
|
if (isset($requestHeaders['Authorization'])) {
|
||||||
|
$headers = trim($requestHeaders['Authorization']);
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Get the access token from the header
|
||||||
|
if (!empty($headers)) {
|
||||||
|
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||||
|
$jwt = $matches[1];
|
||||||
|
} }
|
||||||
|
// End of copied
|
||||||
|
|
||||||
|
// Read accepted/requested content type
|
||||||
|
if (isset($_SERVER["HTTP_ACCEPT"]) && "application/json"==$_SERVER["HTTP_ACCEPT"]) {
|
||||||
|
$this->requestedContentType = "JSON";
|
||||||
|
} elseif (isset($_SERVER["HTTP_ACCEPT"]) && "application/pdf"==$_SERVER["HTTP_ACCEPT"]) {
|
||||||
|
$this->requestedContentType = "PDF";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the requested route (resource) and method
|
||||||
|
$this->method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
if (preg_match('$/([^/]+)/?([^/]+)?/?([^/]+)?/?([^/]+)?$', $_SERVER['PATH_INFO'] ?? "", $matches, PREG_UNMATCHED_AS_NULL)) {
|
||||||
|
$this->route["route"] = $matches[1];
|
||||||
|
$this->route["mainID"] = $matches[2];
|
||||||
|
$this->route["sub"] = $matches[3];
|
||||||
|
$this->route["subID"] = $matches[4];
|
||||||
|
}
|
||||||
|
return $jwt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cleanInput($array) { // Cleans input
|
||||||
|
$ret = [];
|
||||||
|
if ($array!==null) {
|
||||||
|
foreach ($array as $key => $value) {
|
||||||
|
$ret[$key] = is_null($value) ? null : (is_array($value) ? $this->cleanInput($value) : (is_string($value) ? self::$db->escape_string($value) : intval($value)));
|
||||||
|
} }
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function detectObjects() {
|
||||||
|
$dir = scandir("obj");
|
||||||
|
foreach ($dir as $val) {
|
||||||
|
if ($val!="." && $val!="..") {
|
||||||
|
if (file_exists("obj/".$val."/module.json")) {
|
||||||
|
$json = json_decode(file_get_contents("obj/".$val."/module.json"), true);
|
||||||
|
$this->availableObjects[$json["name"]] = [
|
||||||
|
"name" => $json["name"],
|
||||||
|
"class" => $json["class"],
|
||||||
|
"path" => $val,
|
||||||
|
"useRight" => $json["useRight"] ?? null,
|
||||||
|
"adminRight" => $json["adminRight"] ?? null
|
||||||
|
];
|
||||||
|
} } } }
|
||||||
|
|
||||||
|
private function detectLinks() {
|
||||||
|
$dir = scandir("lnk");
|
||||||
|
foreach ($dir as $val) {
|
||||||
|
if ($val!="." && $val!="..") {
|
||||||
|
if (file_exists("lnk/".$val."/module.json")) {
|
||||||
|
$json = json_decode(file_get_contents("lnk/".$val."/module.json"), true);
|
||||||
|
if ($this->user->HasRight($json["useRight"])) {
|
||||||
|
$this->availableLinks[$json["route"]] = [
|
||||||
|
"route" => $json["route"],
|
||||||
|
"table" => $json["table"] ?? "",
|
||||||
|
"path" => $val,
|
||||||
|
"dependencies" => $json["dependencies"] ?? [],
|
||||||
|
"useRight" => $json["useRight"] ?? null,
|
||||||
|
"adminRight" => $json["adminRight"] ?? null,
|
||||||
|
];
|
||||||
|
} } } } }
|
||||||
|
|
||||||
|
private function detectPages() {
|
||||||
|
$dir = scandir("pgs");
|
||||||
|
foreach ($dir as $val) {
|
||||||
|
if ($val!="." && $val!="..") {
|
||||||
|
if (file_exists("pgs/".$val."/module.json")) {
|
||||||
|
$json = json_decode(file_get_contents("pgs/".$val."/module.json"), true);
|
||||||
|
if ($this->user->HasRight($json["useRight"])) {
|
||||||
|
$this->availablePages[$json["route"]] = [
|
||||||
|
"title" => $json["title"] ?? "",
|
||||||
|
"route" => $json["route"],
|
||||||
|
"table" => $json["table"] ?? "",
|
||||||
|
"path" => $val,
|
||||||
|
"links" => $json["links"] ?? [],
|
||||||
|
"dependencies" => $json["dependencies"] ?? [],
|
||||||
|
"useRight" => $json["useRight"] ?? null,
|
||||||
|
"adminRight" => $json["adminRight"] ?? null,
|
||||||
|
];
|
||||||
|
$page = [
|
||||||
|
"link" => "/index.php/".$json["route"],
|
||||||
|
"route" => $json["route"],
|
||||||
|
"isPage" => $json["isPage"] ?? false,
|
||||||
|
];
|
||||||
|
if ($page["isPage"]) {
|
||||||
|
$page["name"] = $json["title"];
|
||||||
|
$page["template"] = isset($json["template"]) ? "/pgs/".$val."/".$json["template"] : null;
|
||||||
|
$page["marker"] = $json["marker"];
|
||||||
|
$page["mainFields"] = $json["mainFields"];
|
||||||
|
}
|
||||||
|
$this->output["pages"][] = $page;
|
||||||
|
} } } } }
|
||||||
|
|
||||||
|
private function loadPage() {
|
||||||
|
if (isset($this->availablePages[$this->Route()])) {
|
||||||
|
$info = $this->availablePages[$this->Route()];
|
||||||
|
|
||||||
|
foreach ($info["dependencies"] as $dep) {
|
||||||
|
if (isset($this->availableObjects[$dep]) && !isset($this->objects[$dep])) {
|
||||||
|
include "obj/".$this->availableObjects[$dep]["path"]."/object.php";
|
||||||
|
$className = "\\".$this->availableObjects[$dep]["name"]."\\".$this->availableObjects[$dep]["class"];
|
||||||
|
$this->objects[$dep] = new $className(self::$db, $this, $this->Main());
|
||||||
|
} }
|
||||||
|
|
||||||
|
$file = "pgs/".$info["path"]."/page.php";
|
||||||
|
if (file_exists($file)) {
|
||||||
|
include $file;
|
||||||
|
$className = $info["route"]."Page";
|
||||||
|
$page = new $className(self::$db, self::$kv, $this, $info);
|
||||||
|
|
||||||
|
if (isset($this->route["sub"])) {
|
||||||
|
if ($this->Main()!==null && $this->SubID()!==null) {
|
||||||
|
$sub = $page->GetLink($this->route["sub"]);
|
||||||
|
if (isset($sub, $this->availableLinks[$sub["route"]])) {
|
||||||
|
$link = $this->availableLinks[$sub["route"]];
|
||||||
|
include "lnk/".$link["path"]."/link.php";
|
||||||
|
$subClassName = $link["route"]."Link";
|
||||||
|
$subClass = new $subClassName(self::$db, self::$kv, $this, $link, $page);
|
||||||
|
$ids[$sub["mainName"]] = $this->Main();
|
||||||
|
$ids[$sub["subName"]] = $this->SubID();
|
||||||
|
|
||||||
|
$this->responseCode = $subClass->DoRequest($this->method, $ids);
|
||||||
|
$page->DoRequest("GET");
|
||||||
|
} else {
|
||||||
|
$this->responseCode = 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->responseCode = 400;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->responseCode = $page->DoRequest($this->method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->responseCode>=200 && $this->responseCode<300) {
|
||||||
|
$this->output["meta"] = $page->GetMeta();
|
||||||
|
$this->output["content"] = $page->GetResult();
|
||||||
|
$this->output["template"] = $page->GetTemplate();
|
||||||
|
if (isset($info["adminRight"]) && $this->user->HasRight($info["adminRight"])) {
|
||||||
|
$this->output["admin"] = true;
|
||||||
|
}
|
||||||
|
$this->output["options"] = $page->GetOptions($this->output["admin"]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->responseCode = 500; // Missing file means misconfiguration
|
||||||
|
$this->AddMessage($this->Route()." seems to be misconfigured. A required file is missing!");
|
||||||
|
}
|
||||||
|
} else if ($this->Route()==null) {
|
||||||
|
// Dashboard with Stubs (hopefully)?
|
||||||
|
$this->responseCode = 200;
|
||||||
|
$this->output["content"] = [];
|
||||||
|
$this->output["template"] = "";
|
||||||
|
} else {
|
||||||
|
// Load nothing, because an unknown page was requested
|
||||||
|
$this->responseCode = 404;
|
||||||
|
$this->AddMessage($this->Route()." doesn't exist");
|
||||||
|
$this->output["content"] = [];
|
||||||
|
$this->output["template"] = "";
|
||||||
|
} }
|
||||||
|
|
||||||
|
private function detectPrints() {
|
||||||
|
$this->printer = new Printer(self::$db, $this);
|
||||||
|
$this->output["prints"] = $this->printer->GetPrints();
|
||||||
|
}
|
||||||
|
}
|
||||||
74
lib_new/10_response.php
Normal file
74
lib_new/10_response.php
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<?php #lib/90_response.php
|
||||||
|
|
||||||
|
class Response
|
||||||
|
{
|
||||||
|
public $code = 500;
|
||||||
|
private $body = ["messages" => [], "content" => []];
|
||||||
|
private $etag = null;
|
||||||
|
private $html = null;
|
||||||
|
private $hasBody = true;
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/***** Static functions *****/
|
||||||
|
|
||||||
|
public static function &Get() {
|
||||||
|
if (is_null(self::$instance)) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Con-/Destructor *****/
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
public function __destruct() {
|
||||||
|
http_response_code($this->code);
|
||||||
|
if (is_null($this->html)) {
|
||||||
|
header("Content-type: application/json");
|
||||||
|
} else {
|
||||||
|
header("Content-type: text/html");
|
||||||
|
}
|
||||||
|
if (!is_null($this->etag)) {
|
||||||
|
header("ETag: ".$this->etag);
|
||||||
|
}
|
||||||
|
if ($this->hasBody) {
|
||||||
|
if ($this->IsOk() && Request::IsPrint()) {
|
||||||
|
echo trim(Printer::Print(Request::PrintID, $this->body["content"]));
|
||||||
|
} else {
|
||||||
|
echo trim(json_encode($this->body));
|
||||||
|
} } }
|
||||||
|
|
||||||
|
/***** Public functions *****/
|
||||||
|
|
||||||
|
public function IsOk() {return 200==$this->code;}
|
||||||
|
|
||||||
|
public function &Message($message) {$this->body["messages"][] = $message; return $this;}
|
||||||
|
public function &Code($code) {$this->code = $code; return $this;}
|
||||||
|
public function &Json($name, $json) {$this->body[$name] = $json; return $this;}
|
||||||
|
public function &Content($json) {$this->body["content"] = $json; return $this;}
|
||||||
|
public function &Etag($etag) {$this->etag = $etag; return $this;}
|
||||||
|
public function &Html($html) {$this->html = $html; return $this;}
|
||||||
|
public function &DisableBody() {$this->hasBody = false; return $this;}
|
||||||
|
|
||||||
|
public function &Good() {$this->Code(200); return $this;}
|
||||||
|
public function &Inserted($name) {$this->Code(201)->Message($name." hinzugefügt"); return $this;}
|
||||||
|
public function &Deleted($name) {$this->Code(200)->Message($name." entfernt"); return $this;}
|
||||||
|
public function &Updated($name) {$this->Code(200)->Message($name." aktualisiert"); return $this;}
|
||||||
|
|
||||||
|
public function &SubInserted() {$this->Code(201)->Message("Verknüpfung hinzugefügt"); return $this;}
|
||||||
|
public function &SubDeleted() {$this->Code(200)->Message("Verknüpfung entfernt"); return $this;}
|
||||||
|
public function &SubUpdated() {$this->Code(200)->Message("Verknüpfung aktualisiert"); return $this;}
|
||||||
|
|
||||||
|
public function &FileUploaded() {$this->Code(201)->Message("Datei hochgeladen"); return $this;}
|
||||||
|
public function &FileErased() {$this->Code(200)->Message("Datei gelöscht"); return $this;}
|
||||||
|
|
||||||
|
public function &Forbidden() {$this->Code(403); return $this;}
|
||||||
|
public function &NotFound() {$this->Code(404)->Message("Resource not found"); return $this;}
|
||||||
|
public function &MissingID() {$this->Code(400)->Message("Required ID was not specified"); return $this;}
|
||||||
|
public function &IllegalInput() {$this->Code(400)->Message("You are forbidden to change at least something in the provided dataset."); return $this;}
|
||||||
|
public function &IllegalQuery() {$this->Code(400)->Message("You are forbidden to request at least something in the queried dataset."); return $this;}
|
||||||
|
public function &MissingContent() {$this->Code(400)->Message("Required input 'content' is missing"); return $this;}
|
||||||
|
public function &NotImplemented() {$this->Code(405)->Message("This method is not (yet) supported by this resource!"); return $this;}
|
||||||
|
public function &SubRouteNotExisting($name) {$this->Code(404)->Message("Subroute `".$name."` not existant for this resource"); return $this;}
|
||||||
|
public function &DbError() {$this->Code(500)->Message("Mysql error: ".Db::Get()->error); return $this;}
|
||||||
|
}
|
||||||
127
lib_new/20_request.php
Normal file
127
lib_new/20_request.php
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?php #lib/90_response.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: Request class *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
* Last modification: 03.04.2023 *
|
||||||
|
* Version: alpha (object, incomplete, uncommented, untested) *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
class Request
|
||||||
|
{
|
||||||
|
private static $jwt = null;
|
||||||
|
private static $accept = "HTML";
|
||||||
|
private static $method = "GET";
|
||||||
|
|
||||||
|
private static $route = "/";
|
||||||
|
private static $id = null;
|
||||||
|
private static $subroute = null;
|
||||||
|
private static $subid = null;
|
||||||
|
|
||||||
|
private static $input = [];
|
||||||
|
private static $filter = [];
|
||||||
|
private static $selected = [];
|
||||||
|
private static $printID = null;
|
||||||
|
private static $detailDepth = 0;
|
||||||
|
|
||||||
|
/***** Public Static functions *****/
|
||||||
|
|
||||||
|
public static function JWT() {return self::$jwt;}
|
||||||
|
public static function Accept() {return self::$accept;}
|
||||||
|
public static function Method() {return self::$method;}
|
||||||
|
|
||||||
|
public static function Route() {return self::$route;}
|
||||||
|
public static function ID() {return self::$id;}
|
||||||
|
public static function Subroute() {return self::$subroute;}
|
||||||
|
public static function SubID() {return self::$subid;}
|
||||||
|
|
||||||
|
public static function Input($name) {return isset(self::$input[$name]) ? self::$input[$name] : null;}
|
||||||
|
public static function Filter() {return self::$filter;}
|
||||||
|
public static function Selected() {return self::$selected;}
|
||||||
|
public static function PrintID() {return self::$printID;}
|
||||||
|
|
||||||
|
public static function Read() {
|
||||||
|
// Copied from https://stackoverflow.com/a/40582472
|
||||||
|
$headers = null;
|
||||||
|
if (isset($_SERVER['Authorization'])) {
|
||||||
|
$headers = trim($_SERVER["Authorization"]);
|
||||||
|
} else if (isset($_SERVER['HTTP_AUTHORIZATION'])) { //Nginx or fast CGI
|
||||||
|
$headers = trim($_SERVER["HTTP_AUTHORIZATION"]);
|
||||||
|
} elseif (function_exists('apache_request_headers')) {
|
||||||
|
$requestHeaders = apache_request_headers();
|
||||||
|
// Server-side fix for bug in old Android versions (a nice side-effect of this fix means we don't care about capitalization for Authorization)
|
||||||
|
$requestHeaders = array_combine(array_map('ucwords', array_keys($requestHeaders)), array_values($requestHeaders));
|
||||||
|
if (isset($requestHeaders['Authorization'])) {
|
||||||
|
$headers = trim($requestHeaders['Authorization']);
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Get the access token from the header
|
||||||
|
if (!empty($headers)) {
|
||||||
|
if (preg_match('/Bearer\s(\S+)/', $headers, $matches)) {
|
||||||
|
self::$jwt = $matches[1];
|
||||||
|
} }
|
||||||
|
// End of copied
|
||||||
|
|
||||||
|
// Read accepted/requested content type
|
||||||
|
if (isset($_SERVER["HTTP_ACCEPT"]) && "application/json"==$_SERVER["HTTP_ACCEPT"]) {
|
||||||
|
self::$accept = "JSON";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the requested route (resource) and method
|
||||||
|
self::$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
if ("HEAD"==self::$method) {
|
||||||
|
Response::Get()->DisableBody();
|
||||||
|
}
|
||||||
|
if (preg_match('$/([^/]+)/?([^/]+)?/?([^/]+)?/?([^/]+)?$', $_SERVER['PATH_INFO'] ?? "", $matches, PREG_UNMATCHED_AS_NULL)) {
|
||||||
|
self::$route = $matches[1];
|
||||||
|
self::$id = $matches[2];
|
||||||
|
self::$subroute = $matches[3];
|
||||||
|
self::$subid = $matches[4];
|
||||||
|
} }
|
||||||
|
|
||||||
|
public static function ParseInput() {
|
||||||
|
// Clean input
|
||||||
|
if (isset($_SERVER["CONTENT_TYPE"]) && "application/json"==$_SERVER["CONTENT_TYPE"]) {
|
||||||
|
$input = self::cleanInput(json_decode(file_get_contents("php://input"), true));
|
||||||
|
if (!empty($input)) {
|
||||||
|
self::$input = $input;
|
||||||
|
}
|
||||||
|
} else if (in_array(self::$method, ["HEAD", "GET"])) {
|
||||||
|
self::$filter = isset($_GET["filter"]) ? self::cleanInput(json_decode($_GET["filter"], true)) : [];
|
||||||
|
self::$selected = isset($_GET["selected"]) ? self::cleanInput(json_decode($_GET["selected"], true)) : [];
|
||||||
|
self::$printID = isset($_GET["print"]) ? self::cleanInput($_GET["print"]) : null;
|
||||||
|
self::$detailDepth = \Login::HasRight("ADMIN") && isset($_GET["depth"]) ? self::cleanInput($_GET["depth"]) : null;
|
||||||
|
} }
|
||||||
|
|
||||||
|
public static function VerifyInputSecToken($verifyToken) {
|
||||||
|
if (!empty(self::$input) && (!isset(self::$input["secToken"]) || self::$input["secToken"]!=$verifyToken)) {
|
||||||
|
//Response::Get()->Message("Wrong or Missing SecToken! Ignoring input.");
|
||||||
|
//self::$input = [];
|
||||||
|
} }
|
||||||
|
|
||||||
|
public static function IsRoot() {return "/"==self::$route;}
|
||||||
|
public static function IsServeClient() {return self::IsRoot() && "GET"==self::$method/* && "HTML"==self::$accept*/;}
|
||||||
|
public static function IsLogout() {return self::IsRoot() && "DELETE"==self::$method;}
|
||||||
|
public static function IsPrint() {return "HTML"==self::$accept && !is_null(self::$printID);}
|
||||||
|
public static function DetailDepth() {return self::$detailDepth;}
|
||||||
|
public static function IssueNewSecToken() {return in_array(self::$method, ["POST", "PATCH", "DELETE"]);}
|
||||||
|
public static function AllowJwtRenewal() {return "HEAD"!=self::$method && "HTML"!=self::$accept;}
|
||||||
|
|
||||||
|
/***** Private Static Functions *****/
|
||||||
|
|
||||||
|
private static function cleanInput($value) { // Cleans input
|
||||||
|
if (is_null($value)) {
|
||||||
|
return null;
|
||||||
|
} else if (is_array($value)) {
|
||||||
|
$ret = [];
|
||||||
|
foreach ($value as $key => $element) {
|
||||||
|
$ret[$key] = self::cleanInput($element);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
} else if (is_string($value)) {
|
||||||
|
return DB::Get()->escape_string($value);
|
||||||
|
} else {
|
||||||
|
return intval($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
111
lib_new/21_filter.php
Normal file
111
lib_new/21_filter.php
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Filter;
|
||||||
|
|
||||||
|
class Condition
|
||||||
|
{
|
||||||
|
private $table;
|
||||||
|
private $field;
|
||||||
|
private $op;
|
||||||
|
private $value;
|
||||||
|
|
||||||
|
public function Get() {
|
||||||
|
$tblString = !is_null($this->table) ? $this->table."." : "";
|
||||||
|
switch ($this->op) {
|
||||||
|
case "=":
|
||||||
|
return is_null($this->value) ? $tblString."`".$this->field."` IS NULL " : $tblString."`".$this->field."` = '".$this->value."'";
|
||||||
|
case "<>":
|
||||||
|
return is_null($this->value) ? $tblString."`".$this->field."` IS NOT NULL " : $tblString."`".$this->field."` <> '".$this->value."'";
|
||||||
|
case "<":
|
||||||
|
return $tblString."`".$this->field."` < '".$this->value."'";
|
||||||
|
case ">":
|
||||||
|
return $tblString."`".$this->field."` > '".$this->value."'";
|
||||||
|
case "<=":
|
||||||
|
return $tblString."`".$this->field."` <= '".$this->value."'";
|
||||||
|
case ">=":
|
||||||
|
return $tblString."`".$this->field."` >= '".$this->value."'";
|
||||||
|
case "like":
|
||||||
|
return $tblString."`".$this->field."` LIKE '%".$this->value."%'";
|
||||||
|
case "not like":
|
||||||
|
return $tblString."`".$this->field."` NOT LIKE '%".$this->value."%'";
|
||||||
|
case "in":
|
||||||
|
return "'".$this->value."' IN ".$tblString.$this->field."'";
|
||||||
|
case "not in":
|
||||||
|
return "'".$this->value."' NOT IN ".$tblString.$this->field."'";
|
||||||
|
}
|
||||||
|
return "1 ";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($field, $op, $value, $table = null) {
|
||||||
|
$this->field = $field;
|
||||||
|
$this->op = $op;
|
||||||
|
$this->value = $value;
|
||||||
|
$this->table = $table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ConditionList
|
||||||
|
{
|
||||||
|
private $mode = "or";
|
||||||
|
private $list = [];
|
||||||
|
|
||||||
|
public function Get() {
|
||||||
|
if (count($this->list)==0) {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
|
||||||
|
$strings = [];
|
||||||
|
foreach ($this->list as $entry) {
|
||||||
|
$strings[] = $entry->Get();
|
||||||
|
}
|
||||||
|
$strings = array_unique($strings);
|
||||||
|
$string = join(" ".$this->mode." ", $strings);
|
||||||
|
return count($strings)>1 ? "(".$string.")" : $string;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __construct($mode) {
|
||||||
|
$this->mode = $mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Add($condition) {
|
||||||
|
$this->list[] = $condition;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Filter
|
||||||
|
{
|
||||||
|
private static $fields = [];
|
||||||
|
private static $requested = null;
|
||||||
|
private static $restricted = null;
|
||||||
|
|
||||||
|
public static function Where() {
|
||||||
|
if (is_null(self::$requested) && is_null(self::$restricted)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
$ret = " WHERE ";
|
||||||
|
if (!is_null(self::$requested)) {
|
||||||
|
$ret.= self::$requested->Get();
|
||||||
|
}
|
||||||
|
if (!is_null(self::$restricted)) {
|
||||||
|
$ret.= (is_null(self::$requested) ? "" : "AND ").self::$restricted->Get();
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Selected($field) {
|
||||||
|
return empty(self::$fields) || in_array($field, self::$fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Limit($fields) {
|
||||||
|
self::$fields = $fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Request($condition) {
|
||||||
|
self::$requested = $condition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Restrict($condition) {
|
||||||
|
self::$restricted = $condition;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
lib_new/30_database.php
Normal file
41
lib_new/30_database.php
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
<?php #lib/90_response.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: Database class *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
* Last modification: 03.04.2023 *
|
||||||
|
* Version: alpha (object, incomplete, uncommented, untested) *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
class DB
|
||||||
|
{
|
||||||
|
private $mysqli = null;
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/***** Static functions *****/
|
||||||
|
|
||||||
|
public static function Get() {
|
||||||
|
if (is_null(self::$instance)) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance->Connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function LeftJoin($target, $targetShort, $targetField, $sourceShort, $sourceField) {
|
||||||
|
return "LEFT JOIN ".$target." ".$targetShort." ON ".$targetShort.".".$targetField."=".$sourceShort.".".$sourceField." ";
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Con-/Destructor *****/
|
||||||
|
|
||||||
|
private function __construct() {
|
||||||
|
$this->mysqli = new mysqli(DBHOST, DBUSER, DBPASS, DBNAME);
|
||||||
|
$this->mysqli->set_charset("utf8mb4");
|
||||||
|
}
|
||||||
|
public function __destruct() {
|
||||||
|
$this->mysqli->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Public functions *****/
|
||||||
|
|
||||||
|
public function Connection() {return $this->mysqli;}
|
||||||
|
}
|
||||||
38
lib_new/40_keyvalue.php
Normal file
38
lib_new/40_keyvalue.php
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<?php #lib/90_response.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: Database class *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
* Last modification: 03.04.2023 *
|
||||||
|
* Version: alpha (object, incomplete, uncommented, untested) *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
class KV
|
||||||
|
{
|
||||||
|
private $redis = null;
|
||||||
|
private static $instance = null;
|
||||||
|
|
||||||
|
/***** Static functions *****/
|
||||||
|
|
||||||
|
public static function Get() {
|
||||||
|
if (is_null(self::$instance)) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
return self::$instance->Connection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Con-/Destructor *****/
|
||||||
|
|
||||||
|
private function __construct() {
|
||||||
|
$this->redis = new Redis();
|
||||||
|
$this->redis->connect(REDIS_SOCKET);
|
||||||
|
$this->redis->select(1);;
|
||||||
|
}
|
||||||
|
public function __destruct() {
|
||||||
|
$this->redis->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/***** Public functions *****/
|
||||||
|
|
||||||
|
public function Connection() {return $this->redis;}
|
||||||
|
}
|
||||||
276
lib_new/50_login.php
Normal file
276
lib_new/50_login.php
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
<?php #lib/10_login.php
|
||||||
|
|
||||||
|
/********************************************************************************
|
||||||
|
* Content: Login class *
|
||||||
|
* Author: Nils Otterpohl *
|
||||||
|
* Last modification: 13.12.2020 *
|
||||||
|
* Version: alpha (object, incomplete, uncommented, untested) *
|
||||||
|
********************************************************************************/
|
||||||
|
|
||||||
|
class Login
|
||||||
|
{
|
||||||
|
private static $id = ""; // ID of authenticated user
|
||||||
|
private static $loggedIn = false;
|
||||||
|
|
||||||
|
private static $user = ""; // Login name of authenticated user
|
||||||
|
private static $vornamen = ""; // Prenames of authenticated user
|
||||||
|
private static $nachnamen = ""; // Surnames of authenticated user
|
||||||
|
|
||||||
|
private static $groups = null;
|
||||||
|
private static $departments = null;
|
||||||
|
private static $rights = null;
|
||||||
|
|
||||||
|
private static $jwt_expires = 0;
|
||||||
|
private static $secTokenUse = null; // (One time) Security token to be issued to client to be used with incoming data
|
||||||
|
private static $secTokenVerify = null; // Last Security token issued. Incoming data will be verified against this
|
||||||
|
|
||||||
|
/***** Public Static functions *****/
|
||||||
|
|
||||||
|
// Generates a salted password hash of desired length and with the desired algorithm
|
||||||
|
public static function GenHash($password, $salt, $iterations, $length = 32, $algo = "sha256") {
|
||||||
|
return hash_pbkdf2($algo, $password, $salt, $iterations, $length*2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generates a random salt (or also password) of desired length
|
||||||
|
public static function GenSalt($length = 16) {
|
||||||
|
return bin2hex(random_bytes($length));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function LoggedIn() {return self::$loggedIn;}
|
||||||
|
public static function ID() {return self::$id;}
|
||||||
|
public static function User() {return self::$user;}
|
||||||
|
public static function Vornamen() {return self::$vornamen;}
|
||||||
|
public static function Nachnamen() {return self::$nachnamen;}
|
||||||
|
|
||||||
|
public static function Login() {
|
||||||
|
// Try to login via JWT, if it was submitted
|
||||||
|
if (!is_null(Request::JWT())) {
|
||||||
|
self::authenticateJWT();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to login via password, if not yet logged in
|
||||||
|
if (!self::$loggedIn && !is_null(Request::Input("login")) && !is_null(Request::Input("password"))) {
|
||||||
|
self::authenticatePWD(Request::Input("login"), Request::Input("password"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$loggedIn) {
|
||||||
|
// Erzeuge neues Security-Token
|
||||||
|
self::$secTokenUse = self::$secTokenVerify;
|
||||||
|
if (Request::IssueNewSecToken() || self::$secTokenUse==null) {
|
||||||
|
self::$secTokenUse = self::GenSalt(16);
|
||||||
|
Response::Get()->Json("secToken", self::$secTokenUse);
|
||||||
|
}
|
||||||
|
KV::Get()->set("secToken:".self::$id, self::$secTokenUse);
|
||||||
|
KV::Get()->expire("secToken:".self::$id, 60*60); // secToken ist für 60 min * 60 s/min gültig
|
||||||
|
if (!is_null(self::$secTokenVerify)) {
|
||||||
|
Request::VerifyInputSecToken(self::$secTokenVerify);
|
||||||
|
} } }
|
||||||
|
|
||||||
|
// Blacklists the issued JWT and sets logged in status to false
|
||||||
|
public static function Logout() {
|
||||||
|
KV::Get()->set("JWT:Blacklist:".self::$id, Request::JWT());
|
||||||
|
KV::Get()->expireAt("JWT:Blacklist:".self::$id, self::$jwt_expires);
|
||||||
|
self::$loggedIn = false; // DEPRECATED
|
||||||
|
self::$loggedIn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function VerifySecToken($secToken) {
|
||||||
|
Response::Get()->Message("sTV: ".self::$secTokenVerify);
|
||||||
|
return (self::$secTokenVerify==null || $secToken==self::$secTokenVerify);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if logged in user has the (or, if array, one of the) named right(s)
|
||||||
|
// If null or empty array is passed, no rights shall be required and return will be true (if logged in).
|
||||||
|
// Fetched rights will be buffered, so recurring queries are no performance problem
|
||||||
|
static function HasRight($rightNames) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((is_array($rightNames) && empty($rightNames)) || is_null($rightNames)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (is_null(self::$rights)) {
|
||||||
|
self::$rights = [];
|
||||||
|
$qry = "SELECT r.Name FROM Personal_Verwalter pv "
|
||||||
|
."LEFT JOIN Rechte_Verwalter rv ON rv.Verwalter = pv.Verwalter "
|
||||||
|
."LEFT JOIN Rechte r ON r.ID = rv.Rechte "
|
||||||
|
."WHERE pv.Personal = ".self::$id;
|
||||||
|
$res = Db::Get()->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
self::$rights[] = $row["Name"];
|
||||||
|
} }
|
||||||
|
if (is_array($rightNames)) {
|
||||||
|
foreach ($rightNames as $r) {
|
||||||
|
if (in_array($r, self::$rights)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return in_array($rightNames, self::$rights);
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Returns true if logged in user is in the (or, if array, one of the) named department(s) ("Abteilungen")
|
||||||
|
// Fetched departments will be buffered, so recurring queries are no performance problem
|
||||||
|
public static function InDepartment($departmentIDs) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ((is_array($departmentIDs) && empty($departmentIDs)) || is_null($departmentIDs)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isset(self::$departments)) {
|
||||||
|
self::$departments = [];
|
||||||
|
$qry = "SELECT Abteilungen FROM Personal_Abteilungen WHERE Personal = ".self::$id;
|
||||||
|
$res = Db::Get()->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
self::$departments[] = $row["Abteilungen"];
|
||||||
|
} }
|
||||||
|
if (is_array($departmentIDs)) {
|
||||||
|
foreach ($departmentIDs as $dID) {
|
||||||
|
if (in_array($dID, self::$departments)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return in_array($departmentIDs, self::$departments);
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Returns true if logged in user is in the (or, if array, one of the) named group(s) ("Gruppen")
|
||||||
|
// Fetched groups will be buffered, so recurring queries are no performance problem
|
||||||
|
public static function InGroup($groupIDs) {
|
||||||
|
if (!self::$loggedIn) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if ($groupIDs==null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!isset(self::$groups)) {
|
||||||
|
self::$groups = [];
|
||||||
|
$qry = "SELECT Gruppen FROM Personal_Gruppen WHERE Personal = ".self::$id;
|
||||||
|
$res = Db::Get()->query($qry);
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
self::$groups[] = $row["Gruppen"];
|
||||||
|
} }
|
||||||
|
if (is_array($groupIDs)) {
|
||||||
|
foreach ($groupIDs as $gID) {
|
||||||
|
if (in_array($gID, self::$groups)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return in_array($groupIDs, self::$groups);
|
||||||
|
} }
|
||||||
|
|
||||||
|
/***** Private Static functions *****/
|
||||||
|
|
||||||
|
// Tries to decode the submitted JWT, checks blacklisting of JWT and sets logged in status accordingly
|
||||||
|
private static function authenticateJWT() {
|
||||||
|
try {
|
||||||
|
$decoded = \Firebase\JWT\JWT::decode(Request::JWT(), JWT_KEY, ['HS256']);
|
||||||
|
// What has been an array before encoding is now an object
|
||||||
|
// Read member variables from decoded data
|
||||||
|
self::$id = $decoded->data->userID==58 && isset($_GET["emulate"]) ? $_GET["emulate"] : $decoded->data->userID;
|
||||||
|
self::$user = $decoded->data->userLogin;
|
||||||
|
self::$vornamen = $decoded->data->userVornamen;
|
||||||
|
self::$nachnamen = $decoded->data->userNachnamen;
|
||||||
|
self::$jwt_expires = $decoded->exp;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// JWT will throw exceptions if it cannot decode the JWT. In this case - don't login
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (KV::Get()->get("JWT:Blacklist:".self::$id)!=Request::JWT()) {
|
||||||
|
// Not blacklisted, can log in
|
||||||
|
self::$loggedIn = true;
|
||||||
|
self::$secTokenVerify = KV::Get()->get("secToken:".self::$id);
|
||||||
|
$now = time();
|
||||||
|
if (self::$jwt_expires-$now <= JWT_VALID_TIME*0.5 && Request::AllowJwtRenewal()) {
|
||||||
|
Response::Get()->Message("Extending JWT");
|
||||||
|
self::encodeJWT();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self::$id = -99;
|
||||||
|
} }
|
||||||
|
|
||||||
|
private static function encodeJWT() {
|
||||||
|
$time = time();
|
||||||
|
self::$jwt_expires = $time + JWT_VALID_TIME;
|
||||||
|
$issuer = "http://fw-innenstadt.de/";
|
||||||
|
$token = [
|
||||||
|
"iat" => $time,
|
||||||
|
"exp" => self::$jwt_expires,
|
||||||
|
"iss" => "fw-innenstadt.de",
|
||||||
|
"data" => [
|
||||||
|
"userID" => self::$id,
|
||||||
|
"userLogin" => self::$user,
|
||||||
|
"userVornamen" => self::$vornamen,
|
||||||
|
"userNachnamen" => self::$nachnamen
|
||||||
|
]
|
||||||
|
];
|
||||||
|
Response::Get()->Json("jwt", \Firebase\JWT\JWT::encode($token, JWT_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function authenticatePWD($user, $password) {
|
||||||
|
// Copied and adjusted after https://de.wikihow.com/Ein-sicheres-Login-Skript-mit-PHP-und-MySQL-erstellen
|
||||||
|
// and https://github.com/nextcloud/user_external/blob/master/lib/webdavauth.php
|
||||||
|
// and https://codeofaninja.com/2018/09/rest-api-authentication-example-php-jwt-tutorial.html
|
||||||
|
|
||||||
|
// Login zum gewünschten Format zurechtbiegen
|
||||||
|
$user = strtolower($user);
|
||||||
|
$user = str_replace("@feuerwehr-bs.net", "", $user);
|
||||||
|
|
||||||
|
// Das Benutzen vorbereiteter Statements verhindert SQL-Injektion.
|
||||||
|
if ($stmt = Db::Get()->prepare("SELECT p.ID, p.login, p.Vornamen, p.Nachnamen FROM Personal p WHERE p.login = ? LIMIT 1")) {
|
||||||
|
$stmt->bind_param("s", $user); // Bind "$user" to parameter.
|
||||||
|
$stmt->execute(); // Führe die vorbereitete Anfrage aus.
|
||||||
|
$stmt->store_result();
|
||||||
|
|
||||||
|
// hole Variablen von result.
|
||||||
|
$stmt->bind_result(self::$id, self::$user, self::$vornamen, self::$nachnamen);
|
||||||
|
$stmt->fetch();
|
||||||
|
|
||||||
|
if ($stmt->num_rows == 1) {
|
||||||
|
$url= 'https://'.urlencode(self::$user).':'.urlencode($password).'@feuerwehr-bs.net/webdav';
|
||||||
|
$headers = get_headers($url);
|
||||||
|
if($headers === false) {
|
||||||
|
Response::Get()->Message("ERROR: Not possible to connect to WebDAV Url: https://feuerwehr-bs.net/webdav");
|
||||||
|
// THROW
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$returnCode= substr($headers[0], 9, 3);
|
||||||
|
|
||||||
|
if (substr($returnCode, 0, 1) === '2') {
|
||||||
|
// Passwort ist korrekt!
|
||||||
|
// XSS-Schutz, denn eventuell wird der Wert gedruckt
|
||||||
|
self::$id = preg_replace("/[^0-9]+/", "", self::$id);
|
||||||
|
self::$user = preg_replace("/[^a-zA-Z0-9_\-]+/", "", self::$user);
|
||||||
|
|
||||||
|
// Generiere einen Hash aus dem Passwort mit zufälligem Salt und speichere ihn in der Datenbank
|
||||||
|
// Question: Why?
|
||||||
|
$hash = Login::GenHash($password, Login::GenSalt(), DESIRED_ITERATIONS);
|
||||||
|
Db::Get()->query("REPLACE INTO sys_iservhashes(ID, Hash) VALUES ('".self::$id."', '".$hash."')");
|
||||||
|
|
||||||
|
// Login erfolgreich.
|
||||||
|
self::encodeJWT();
|
||||||
|
self::$loggedIn = true;
|
||||||
|
Response::Get()->Message("Login erfolgreich!");
|
||||||
|
return;
|
||||||
|
} else if ($returnCode === "401") {
|
||||||
|
// Passwort ist nicht korrekt
|
||||||
|
Response::Get()->Message("Das Passwort ist vermutlich nicht korrekt. Bitte erneut probieren oder an Nils wenden.");
|
||||||
|
} else if ($returnCode === "503") {
|
||||||
|
// Passwort ist nicht korrekt
|
||||||
|
Response::Get()->Message("IServ verweigert im Moment die Anmeldung. Vermutlich gab es temporär zu viele falsche Passworteingaben "
|
||||||
|
."(auch von anderen Nutzern). Bitte erst in 10 Minuten neu probieren!");
|
||||||
|
} else {
|
||||||
|
// Unbekannter Fehler
|
||||||
|
Response::Get()->Code(500)->Message("Anmeldung konnte aus unbekannten Gründen nicht durchgeführt werden. Fehlercode (bitte an Nils senden): ".$returnCode);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Response::Get()->Message("Benutzername inkorrekt: ".$user);
|
||||||
|
} }
|
||||||
|
if (Db::Get()->error!="") {
|
||||||
|
Response::Get()->Message("Mysql error: ".Db::Get()->error);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
411
lib_new/60_resources.php
Normal file
411
lib_new/60_resources.php
Normal file
@ -0,0 +1,411 @@
|
|||||||
|
<?php #lib/20_routes.php
|
||||||
|
|
||||||
|
namespace Resources;
|
||||||
|
|
||||||
|
class Element
|
||||||
|
{
|
||||||
|
protected $parent;
|
||||||
|
protected $filled = false;
|
||||||
|
protected $callerList;
|
||||||
|
protected $id;
|
||||||
|
protected $chksum = "";
|
||||||
|
|
||||||
|
protected $fields = [];
|
||||||
|
protected $keys = [];
|
||||||
|
protected $links = [];
|
||||||
|
protected $files = [];
|
||||||
|
|
||||||
|
public function __construct($parent, $id, $callerList) {
|
||||||
|
$this->parent = $parent;
|
||||||
|
$this->callerList = $callerList;
|
||||||
|
$this->callerList[] = $this->parent->Table();
|
||||||
|
$this->id = $id;
|
||||||
|
|
||||||
|
$definitions = $parent->Definitions();
|
||||||
|
$this->fields = array_combine(array_keys($definitions["fields"]), array_column($definitions["fields"], "default"));
|
||||||
|
$this->keys = array_fill_keys(array_keys($definitions["keys"]), null);
|
||||||
|
$this->files = array_keys($definitions["files"]);
|
||||||
|
foreach ($definitions["links"] as $link => $def) {
|
||||||
|
$resClass = "\\Resources\\".$def["resourceClass"];
|
||||||
|
if (!in_array($resClass::Get()->Table(), $callerList)) {
|
||||||
|
$this->links[$link] = [];
|
||||||
|
} } }
|
||||||
|
|
||||||
|
public function ID() {return $this->id;}
|
||||||
|
public function Checksum() {return $this->filled ? $this->chksum : "";}
|
||||||
|
|
||||||
|
public function Patch($data) {
|
||||||
|
// Overwrite field values if data exists
|
||||||
|
foreach ($this->fields as $field => &$value) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$value = $data[$field];
|
||||||
|
} }
|
||||||
|
unset($value); // If removed, $value would still point to last element of fields
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
foreach ($this->keys as $key => &$reference) {
|
||||||
|
if (isset($data[$key])) {
|
||||||
|
$class = "\\Resources\\".$definitions["keys"][$key]["resourceClass"];
|
||||||
|
$reference = is_null($data[$key]) ? null : $class::Get()->Ref($data[$key], $this->callerList);
|
||||||
|
} }
|
||||||
|
unset($reference); // If removed, $reference would still point to last element of keys
|
||||||
|
|
||||||
|
foreach ($this->links as $link => &$refList) {
|
||||||
|
$class = "\\Links\\".$definitions["links"][$link]["linkClass"];
|
||||||
|
$refList = $class::Get()->Refs($this->id, $this->parent->Ident(), $this->callerList);
|
||||||
|
}
|
||||||
|
unset($refList); // If removed, $reference would still point to last element of keys
|
||||||
|
|
||||||
|
$this->filled = true;
|
||||||
|
|
||||||
|
if (isset($data["CHKSUM"]) && !is_null($data["CHKSUM"])) {
|
||||||
|
$this->chksum = $data["CHKSUM"];
|
||||||
|
} else {
|
||||||
|
$this->updateChecksum();
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function Load() {
|
||||||
|
$qry = "SELECT * FROM ".$this->parent->Table()." WHERE ID = ?";
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param("s", $this->id);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
if ($row = $res->fetch_assoc()) {
|
||||||
|
$this->Patch($row);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Not found
|
||||||
|
return false;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Store() {
|
||||||
|
if ($this->filled) {
|
||||||
|
$fields = [];
|
||||||
|
$values = [];
|
||||||
|
$types = "";
|
||||||
|
|
||||||
|
if ($this->parent->HasChksum()) {
|
||||||
|
$fields[] = "CHKSUM";
|
||||||
|
$values[] = $this->chksum;
|
||||||
|
$types.= "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
foreach ($definitions["fields"] as $field => $def) {
|
||||||
|
$fields[] = $field;
|
||||||
|
$values[] = $this->fields[$field];
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
foreach ($definitions["keys"] as $key => $def) {
|
||||||
|
$fields[] = $key;
|
||||||
|
$values[] = is_null($this->keys[$key]) ? null : $this->keys[$key]->ID();
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "UPDATE ".$this->parent->Table()." SET ".implode(" = ?, ", $fields)." = ? WHERE ID = ?";
|
||||||
|
$values[] = $this->id;
|
||||||
|
$types.= "s";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param($types, ...$values);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$this->parent->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function updateChecksum() {
|
||||||
|
$this->chksum = $this->parent->CalcChecksum($this->Json(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Json($depth = null) {
|
||||||
|
if (is_null($depth)) {
|
||||||
|
$depth = \Request::DetailDepth();
|
||||||
|
}
|
||||||
|
if (!$this->filled && !$this->Load()) {
|
||||||
|
return $this->id;
|
||||||
|
} else {
|
||||||
|
$ret = [
|
||||||
|
"ID" => $this->id,
|
||||||
|
"CHKSUM" => $this->chksum
|
||||||
|
];
|
||||||
|
foreach ($this->fields as $field => $value) {
|
||||||
|
if (\Filter\Filter::Selected($field)) {
|
||||||
|
$ret[$field] = $value;
|
||||||
|
} }
|
||||||
|
foreach ($this->files as $field) {
|
||||||
|
if (\Filter\Filter::Selected($field)) {
|
||||||
|
$ret[$field] = $this->parent->FileGet($field, $this->id);
|
||||||
|
} }
|
||||||
|
foreach ($this->keys as $key => $reference) {
|
||||||
|
if (\Filter\Filter::Selected($key)) {
|
||||||
|
$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
|
||||||
|
} }
|
||||||
|
foreach ($this->links as $link => $referenceList) {
|
||||||
|
if (\Filter\Filter::Selected($link)) {
|
||||||
|
$ids = [];
|
||||||
|
foreach ($referenceList as $reference) {
|
||||||
|
if (!is_null($reference)) {
|
||||||
|
$ids[] = $depth>0 ? $reference->Json($depth-1) : $reference->ID();
|
||||||
|
} }
|
||||||
|
$ret[$link] = $ids;
|
||||||
|
} }
|
||||||
|
return $ret;
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function FileUpload($field) {
|
||||||
|
if (isset($_FILES["file"])) {
|
||||||
|
$source = $_FILES ["file"]["tmp_name"];
|
||||||
|
|
||||||
|
// Delete old files
|
||||||
|
$this->FileErase($field);
|
||||||
|
|
||||||
|
// Ensure path exists
|
||||||
|
$dir = $this->parent->FileDirectory($field);
|
||||||
|
if (!file_exists($dir)) {
|
||||||
|
mkdir($dir, 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get file type for proper extension
|
||||||
|
$mime = mime_content_type($source);
|
||||||
|
if (array_key_exists($mime, MIME_MAP)) {
|
||||||
|
if (move_uploaded_file($source, $dir.$this->id.".".MIME_MAP[$mime])) {
|
||||||
|
return true;
|
||||||
|
} } }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FileErase($field) {
|
||||||
|
$dir = $this->parent->FileDirectory($field);
|
||||||
|
|
||||||
|
$filelist = glob($dir.$this->id.".*");
|
||||||
|
foreach ($filelist as $existingfile) {
|
||||||
|
unlink($existingfile);
|
||||||
|
}
|
||||||
|
return sizeof($filelist)>0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Handler {
|
||||||
|
protected static $instances = [];
|
||||||
|
protected $index = [];
|
||||||
|
public static function Get() {
|
||||||
|
$class = get_called_class();
|
||||||
|
if (!isset($instances[$class])) {
|
||||||
|
self::$instances[$class] = new $class();
|
||||||
|
}
|
||||||
|
return self::$instances[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct() {
|
||||||
|
foreach (["fields", "keys", "files", "links"] as $type) {
|
||||||
|
if (!array_key_exists($type, $this->definitions)) {
|
||||||
|
$this->definitions[$type] = [];
|
||||||
|
} }
|
||||||
|
|
||||||
|
foreach ($this->definitions["links"] as &$link) {
|
||||||
|
$linkClass = "\\Links\\".$link["linkClass"];
|
||||||
|
$link["resourceClass"] = $linkClass::Get()->OtherResourceClass(get_called_class());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Table() {return $this->names["table"];}
|
||||||
|
public function Ident() {return $this->names["ident"];}
|
||||||
|
public function Short() {return $this->names["short"];}
|
||||||
|
public function TableWithShort() {return $this->names["table"]." ".$this->names["short"];}
|
||||||
|
public function HasUuid() {return $this->has["uuid"];}
|
||||||
|
public function HasChksum() {return $this->has["sha256"];}
|
||||||
|
public function Definitions() {return $this->definitions;}
|
||||||
|
|
||||||
|
public function Ref($id, $callerList = []) {
|
||||||
|
if (!isset($this->index[$id]) ) {
|
||||||
|
$this->index[$id] = new Element($this, $id, $callerList); // Might be null, but we will buffer the result anyway
|
||||||
|
}
|
||||||
|
return $this->index[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RefAll($callerList = []) {
|
||||||
|
$this->loadAll($callerList);
|
||||||
|
return $this->index;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadChecksum($id = null) {
|
||||||
|
$field = "CHKSUM";
|
||||||
|
if (!$this->has["sha256"]) {
|
||||||
|
// There is no checksum column to use. Smasch all columns together
|
||||||
|
$fields = [];
|
||||||
|
$arr = array_merge(array_keys($this->definitions["fields"]), array_keys($this->definitions["keys"]));
|
||||||
|
$field = "CONCAT(IFNULL(".implode(", '__NULL__'), IFNULL(", $arr).", '__NULL__'))";
|
||||||
|
}
|
||||||
|
if (is_null($id)) {
|
||||||
|
// We need to concat all the rows
|
||||||
|
$field = "GROUP_CONCAT(".$field.")";
|
||||||
|
}
|
||||||
|
if (!$this->has["sha256"] || is_null($id)) {
|
||||||
|
// Call the hash function, because $field is not a pregenerated checksum
|
||||||
|
$field = "SHA2(".$field.", 256)";
|
||||||
|
}
|
||||||
|
$qry = "SELECT ".$field." cs FROM ".$this->Table();
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$qry.= " WHERE ID = ?";
|
||||||
|
}
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
if ($row = $res->fetch_assoc()) {
|
||||||
|
$chksum = $row["cs"];
|
||||||
|
return $chksum;
|
||||||
|
}
|
||||||
|
// Does not exist
|
||||||
|
return null;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CalcChecksum($data) {
|
||||||
|
$concat = "";
|
||||||
|
foreach ($this->definitions as $type => $definition) {
|
||||||
|
if ($type!="links") {
|
||||||
|
foreach ($definition as $field => $value) {
|
||||||
|
$concat.= is_array($data[$field]) ? implode("", $data[$field]) : ($data[$field] ?? "__NULL__");
|
||||||
|
} } }
|
||||||
|
return hash("sha256", $concat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Checksum($id = null) {
|
||||||
|
if (is_null($id)) {
|
||||||
|
return \KV::Get()->get("Routes:".get_called_class().":Checksum");
|
||||||
|
} else if (isset($this->index[$id])) {
|
||||||
|
return $this->index[$id]->Checksum();
|
||||||
|
} else {
|
||||||
|
return $this->loadChecksum($id);
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function UpdateChecksum() {
|
||||||
|
$checksum = $this->loadChecksum();
|
||||||
|
\KV::Get()->set("Routes:".get_called_class().":Checksum", $checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Remove($id) {
|
||||||
|
if ($stmt = \DB::Get()->prepare("DELETE FROM ".$this->Table()." WHERE ID = ?")) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} else if (0==$stmt->affected_rows) {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurde nichts entfernt!");
|
||||||
|
} else {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurden mehrere Einträge entfernt!");
|
||||||
|
} } }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Insert($data, &$newid) {
|
||||||
|
$fields = [];
|
||||||
|
$values = [];
|
||||||
|
$types = "";
|
||||||
|
$placeholders = [];
|
||||||
|
|
||||||
|
if ($this->has["uuid"]) {
|
||||||
|
$res = \DB::Get()->query("SELECT UUID_SHORT() uuid");
|
||||||
|
$fields[] = "ID";
|
||||||
|
$values[] = $res->fetch_assoc()["uuid"];
|
||||||
|
$types.= "s";
|
||||||
|
$placeholders[] = "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set `files` elements in data for initial checksum calculation
|
||||||
|
foreach ($this->definitions["files"] as $field => $fallback) {
|
||||||
|
$data[$field] = $this->FileGet($field, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->has["sha256"]) {
|
||||||
|
$fields[] = "CHKSUM";
|
||||||
|
$values[] = $this->CalcChecksum($data);
|
||||||
|
$types.= "s";
|
||||||
|
$placeholders[] = "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->definitions["fields"] as $field => $def) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$fields[] = $field;
|
||||||
|
$values[] = $data[$field];
|
||||||
|
$types.= $def["type"];
|
||||||
|
$placeholders[] = "?";
|
||||||
|
} }
|
||||||
|
|
||||||
|
foreach ($this->definitions["keys"] as $key => $def) {
|
||||||
|
if (isset($data[$key])) {
|
||||||
|
$fields[] = $key;
|
||||||
|
$values[] = $data[$key]=="__NULL__" ? null : $data[$key];
|
||||||
|
$types.= $def["type"];
|
||||||
|
$placeholders[] = "?";
|
||||||
|
} }
|
||||||
|
|
||||||
|
$qry = "INSERT INTO ".$this->Table()." (".implode(", ", $fields).") VALUES (".implode(", ", $placeholders).")";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param($types, ...$values);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$newid = $this->HasUuid() ? $values[0] : \DB::Get()->insert_id;
|
||||||
|
$this->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function joins() {
|
||||||
|
$ret = "";
|
||||||
|
foreach ($this->definitions["links"] as $field => $definition) {
|
||||||
|
$ret.= " LEFT JOIN ".("\\Links\\".$definition["linkClass"])::Get()->TableWithShort()
|
||||||
|
." ON ".("\\Links\\".$definition["linkClass"])::Get()->Short().".`".$this->Ident()."`=".$this->Short().".ID";
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadAll($callerList) {
|
||||||
|
$qry = "SELECT ID FROM ".$this->TableWithShort().$this->joins().\Filter\Filter::Where();
|
||||||
|
|
||||||
|
if (($stmt = \DB::Get()->prepare($qry)) && $stmt->execute()) {
|
||||||
|
$stmt->bind_result($id);
|
||||||
|
while ($stmt->fetch()) {
|
||||||
|
$this->Ref($id, $callerList);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function HasFile($field) {
|
||||||
|
return array_key_exists($field, $this->definitions["files"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FileDirectory($field) {
|
||||||
|
return "upl/".$this->Table()."/".$field."/";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FileGet($field, $id = null) {
|
||||||
|
$dir = $this->FileDirectory($field);
|
||||||
|
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$filelist = glob($dir.$id.".*");
|
||||||
|
if (sizeof($filelist)>0) {
|
||||||
|
return $filelist[0];
|
||||||
|
} }
|
||||||
|
|
||||||
|
$fallback = $this->definitions["files"][$field];
|
||||||
|
return is_null($fallback) ? null : $dir.$fallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
92
lib_new/61_links.php
Normal file
92
lib_new/61_links.php
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<?php #lib/20_routes.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
abstract class Link {
|
||||||
|
protected static $instances = [];
|
||||||
|
protected $leftIndex = [];
|
||||||
|
protected $rightIndex = [];
|
||||||
|
|
||||||
|
public static function Get() {
|
||||||
|
$class = get_called_class();
|
||||||
|
if (!isset($instances[$class])) {
|
||||||
|
self::$instances[$class] = new $class();
|
||||||
|
}
|
||||||
|
return self::$instances[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public function Table() {return $this->names["table"];}
|
||||||
|
public function Short() {return $this->names["short"];}
|
||||||
|
public function TableWithShort() {return $this->names["table"]." ".$this->names["short"];}
|
||||||
|
public function OtherResourceClass($callerClass) {
|
||||||
|
$callFromRight = $callerClass=="Resources\\".$this->definitions["right"]["resourceClass"];
|
||||||
|
return $this->definitions[$callFromRight ? "left" : "right"]["resourceClass"];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Refs($id, $from, $callerList) {
|
||||||
|
if ($from==$this->definitions["right"]["ident"]) {
|
||||||
|
// We come from right
|
||||||
|
if (!isset($this->rightIndex[$id])) {
|
||||||
|
$this->load("left", $id, $this->rightIndex, $callerList);
|
||||||
|
}
|
||||||
|
return $this->rightIndex[$id];
|
||||||
|
} else {
|
||||||
|
// We come from left
|
||||||
|
if (!isset($this->leftIndex[$id])) {
|
||||||
|
$this->load("right", $id, $this->leftIndex, $callerList);
|
||||||
|
}
|
||||||
|
return $this->leftIndex[$id];
|
||||||
|
} }
|
||||||
|
|
||||||
|
protected function load($to, $id, &$index, $callerList) {
|
||||||
|
$index[$id] = [];
|
||||||
|
$from = "left" == $to ? "right" : "left";
|
||||||
|
$fromField = $this->definitions[$from]["ident"];
|
||||||
|
$toField = $this->definitions[$to]["ident"];
|
||||||
|
$toClass = "\\Resources\\".$this->definitions[$to]["resourceClass"];
|
||||||
|
|
||||||
|
$qry = "SELECT ".$toField." FROM ".$this->Table()." WHERE ".$fromField." = ?";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$index[$id][] = $toClass::Get()->Ref($row[$toField], $callerList);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Unset($leftid, $rightid) {
|
||||||
|
$qry = "DELETE FROM ".$this->Table()." WHERE ".$this->definitions["left"]["ident"]." = ? AND ".$this->definitions["right"]["ident"]." = ?";
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ss", $leftid, $rightid);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
return true;
|
||||||
|
} else if (0==$stmt->affected_rows) {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurde nichts entfernt!");
|
||||||
|
} else {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurden mehrere Einträge entfernt!");
|
||||||
|
} } }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Set($leftid, $rightid) {
|
||||||
|
$qry = "INSERT INTO ".$this->Table()." (".$this->definitions["left"]["ident"].",".$this->definitions["right"]["ident"].") VALUES (?, ?)";
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ss", $leftid, $rightid);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
161
lib_new/70_routes.php
Normal file
161
lib_new/70_routes.php
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Routes;
|
||||||
|
|
||||||
|
/**************************************************/
|
||||||
|
/********************* Route **********************/
|
||||||
|
/**************************************************/
|
||||||
|
|
||||||
|
abstract class Route {
|
||||||
|
public function __construct() {}
|
||||||
|
|
||||||
|
public function Answer() {
|
||||||
|
$subroute = \Request::Subroute();
|
||||||
|
if (is_null($subroute)) {
|
||||||
|
switch (\Request::Method()) {
|
||||||
|
case "OPTIONS":
|
||||||
|
return $this->info();
|
||||||
|
case "HEAD":
|
||||||
|
$id = \Request::ID();
|
||||||
|
return $this->restrictFilter($id) && $this->head($id);
|
||||||
|
case "GET":
|
||||||
|
$id = \Request::ID();
|
||||||
|
if (is_null($id)) {
|
||||||
|
return $this->restrictFilter() && $this->head() && $this->getAll();
|
||||||
|
} else {
|
||||||
|
$json = [];
|
||||||
|
return $this->head($id) && $this->getOne($id, $json) && $this->checkOutput($id, $json);
|
||||||
|
}
|
||||||
|
case "POST":
|
||||||
|
$json = \Request::Input("content");
|
||||||
|
return $this->checkInput("insert", null, $json) && $this->insert($json) && $this->head();
|
||||||
|
case "PATCH":
|
||||||
|
$json = \Request::Input("content");
|
||||||
|
$id = \Request::ID();
|
||||||
|
return $this->checkInput("update", $id, $json) && $this->update($id, $json) && $this->head();
|
||||||
|
case "DELETE":
|
||||||
|
$id = \Request::ID();
|
||||||
|
return $this->checkInput("remove", $id) && $this->remove($id) && $this->head();
|
||||||
|
default:
|
||||||
|
\Response::Get()->NotImplemented();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$id = \Request::ID();
|
||||||
|
return $this->answerSub($subroute, $id) && $this->head($id) && $this->get($id) && $this->checkOutput($id, );
|
||||||
|
} }
|
||||||
|
|
||||||
|
abstract public static function Rights();
|
||||||
|
|
||||||
|
protected function info() {
|
||||||
|
$class = get_called_class();
|
||||||
|
\Response::Get()->Good()->Json("rights", $class::Rights()->Export());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function answerSub($subroute, $id) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
$answerFnc = "answer_".$class::Get()->Table()."_".$subroute;
|
||||||
|
if (method_exists($this, $answerFnc)) {
|
||||||
|
$subid = \Request::SubID();
|
||||||
|
return $this->$answerFnc($id, $subid);
|
||||||
|
} else if (method_exists($this, "answer_Files") && $class::Get()->HasFile($subroute)) {
|
||||||
|
return $this->answer_Files($this->resource, $id, $subroute);
|
||||||
|
}
|
||||||
|
\Response::Get()->SubRouteNotExisting($subroute);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function head($id = null) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
\Response::Get()->Good()->Etag($class::Get()->Checksum($id));
|
||||||
|
return true; // Needed for &&-chain-call
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getOne($id, &$json) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
$resource = $class::Get()->Ref($id);
|
||||||
|
if ($resource->Load()) {
|
||||||
|
$json = $resource->Json();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
\Response::Get()->NotFound();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getAll() {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
$list = $class::Get()->RefAll();
|
||||||
|
$json = [];
|
||||||
|
foreach ($list as $resource) {
|
||||||
|
$json[] = $resource->Json();
|
||||||
|
}
|
||||||
|
\Response::Get()->Good()->Content($json);
|
||||||
|
return true;
|
||||||
|
\Response::Get()->NotFound();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($json) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
$newid = null;
|
||||||
|
if ($class::Get()->Insert($json, $newid)) {
|
||||||
|
\Response::Get()->Inserted($this->resource)->Content($class::Get()->Ref($newid)->Json());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function update($id, $json) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
$resource = $class::Get()->Ref($id);
|
||||||
|
if ($resource->Load()) {
|
||||||
|
$resource->Patch($json);
|
||||||
|
if ($resource->Store()) {
|
||||||
|
\Response::Get()->Updated($this->resource)->Content($resource->Json());
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($id) {
|
||||||
|
$class = "\\Resources\\".$this->resource;
|
||||||
|
if ($class::Get()->Remove($id)) {
|
||||||
|
\Response::Get()->Deleted($this->resource);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function restrictFilter() {
|
||||||
|
if (get_called_class()::Rights()->RestrictFilter()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
\Response::Get()->IllegalQuery();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkOutput($id, $json) {
|
||||||
|
if (get_called_class()::Rights()->CheckInput("get", $id, $json)) {
|
||||||
|
\Response::Get()->Good()->Content($json);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
\Response::Get()->IllegalQuery();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function checkInput($action, $id = null, $json = []) {
|
||||||
|
if (is_null($id) && in_array($action, ["update", "remove"])) {
|
||||||
|
\Response::Get()->MissingID();
|
||||||
|
return false;
|
||||||
|
} else if ((is_null($json) || empty($json)) && in_array($action, ["insert", "update"])) {
|
||||||
|
\Response::Get()->MissingContent();
|
||||||
|
return false;
|
||||||
|
} else if (!get_called_class()::Rights()->CheckInput($action, $id, $json)) {
|
||||||
|
\Response::Get()->IllegalInput();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
303
lib_new/71_rights.php
Normal file
303
lib_new/71_rights.php
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
<?php #lib/20_routes.php
|
||||||
|
|
||||||
|
/**************************************************/
|
||||||
|
/********************* Route **********************/
|
||||||
|
/**************************************************/
|
||||||
|
|
||||||
|
class Condition
|
||||||
|
{
|
||||||
|
protected $mode = "and";
|
||||||
|
protected $list = [];
|
||||||
|
protected $subs = [];
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct($mode = "and") {$this->mode = $mode;}
|
||||||
|
|
||||||
|
public function Add($field, $value, $table = null) {
|
||||||
|
$this->list[] = ["field" => $field, "value" => $value, "table" => $table];
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Sub(Condition $condition) {
|
||||||
|
$this->subs[] = $condition;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkValue($is, $should) {
|
||||||
|
if (is_array($is)) {
|
||||||
|
if (array_key_exists("ID", $is)) {
|
||||||
|
return $should==$is["ID"];
|
||||||
|
} else {
|
||||||
|
foreach ($is as $entry) {
|
||||||
|
if ($this->checkValue($entry, $should)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return in_array($should, $is);
|
||||||
|
} else {
|
||||||
|
return $should==$is;
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function CheckInput($json) {
|
||||||
|
if ($this->mode=="and") {
|
||||||
|
foreach ($this->list as $cond) {
|
||||||
|
if (!$this->checkValue($json[$cond["field"]], $cond["value"])) {
|
||||||
|
return false;
|
||||||
|
} }
|
||||||
|
foreach ($this->subs as $sub) {
|
||||||
|
if (!$sub->CheckInput($json)) {
|
||||||
|
return false;
|
||||||
|
} }
|
||||||
|
return true;
|
||||||
|
} else if ($this->mode=="or") {
|
||||||
|
foreach ($this->list as $cond) {
|
||||||
|
if ($this->checkValue($json[$cond["field"]], $cond["value"])) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
foreach ($this->subs as $sub) {
|
||||||
|
if ($sub->CheckInput($json)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return false; // Failsafe if mode was misconfigured
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetFilterCondition() {
|
||||||
|
$conditionList = new \Filter\ConditionList($this->mode);
|
||||||
|
foreach ($this->list as $cond) {
|
||||||
|
$conditionList->Add(new \Filter\Condition($cond["field"], "=", $cond["value"], $cond["table"]));
|
||||||
|
}
|
||||||
|
foreach ($this->subs as $sub) {
|
||||||
|
$conditionList->Add($sub->GetFilterCondition());
|
||||||
|
}
|
||||||
|
return $conditionList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Export() {
|
||||||
|
if (sizeof($this->list) + sizeof($this->subs) == 1) {
|
||||||
|
return empty($this->list) ? $this->subs[0]->Export() : $this->list[0];
|
||||||
|
}
|
||||||
|
$ret = [
|
||||||
|
"mode" => $this->mode,
|
||||||
|
"conditions" => $this->list
|
||||||
|
];
|
||||||
|
foreach ($this->subs as &$condition) {
|
||||||
|
$ret["conditions"][] = $condition->Export();
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Make($mode = "and") {
|
||||||
|
return (new self($mode));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Restriction
|
||||||
|
{
|
||||||
|
protected $condition = null;
|
||||||
|
protected $limits = [];
|
||||||
|
|
||||||
|
public function AddCondition(\Condition $condition) {
|
||||||
|
if (is_null($this->condition)) {
|
||||||
|
$this->condition = \Condition::Make("or");
|
||||||
|
}
|
||||||
|
$this->condition->Sub($condition);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function AddLimits($fields) {
|
||||||
|
if (is_array($fields)) {
|
||||||
|
foreach ($fields as $field) {
|
||||||
|
$this->limits[] = $field;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->limits[] = $fields;
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CheckInput($id = null, $json = []) {
|
||||||
|
// Check limits
|
||||||
|
if (!empty($json) && !empty($this->limits)) {
|
||||||
|
foreach ($json as $field => $value) {
|
||||||
|
if (!in_array($field, $this->limits)) {
|
||||||
|
return false;
|
||||||
|
} } }
|
||||||
|
|
||||||
|
// Check condition
|
||||||
|
return is_null($this->condition) || $this->condition->CheckInput(array_merge(["ID" => $id], $json));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetFilterCondition() {
|
||||||
|
return is_null($this->condition) ? null : $this->condition->GetFilterCondition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Export() {
|
||||||
|
return [
|
||||||
|
"limits" => $this->limits,
|
||||||
|
"condition" => is_null($this->condition) ? null : $this->condition->Export()
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Right
|
||||||
|
{
|
||||||
|
protected $requiredRight = null;
|
||||||
|
protected $restriction = null;
|
||||||
|
protected $allow = [];
|
||||||
|
|
||||||
|
public function __construct($requiredRight = null) {
|
||||||
|
$this->requiredRight = $requiredRight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function IsApplicable() {
|
||||||
|
return is_null($this->requiredRight) || \Login::HasRight($this->requiredRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function IsAdmin() {
|
||||||
|
return sizeof($this->allow)==6 && is_null($this->restriction);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Allow($allow = "readonly") {
|
||||||
|
switch ($allow) {
|
||||||
|
case "admin":
|
||||||
|
$this->allow = ["get", "update", "insert", "remove", "upload", "erase"];
|
||||||
|
break;
|
||||||
|
case "nodelete":
|
||||||
|
$this->allow = ["get", "update", "insert", "upload"];
|
||||||
|
break;
|
||||||
|
case "justdata":
|
||||||
|
$this->allow = ["get", "update", "insert", "remove"];
|
||||||
|
break;
|
||||||
|
case "update":
|
||||||
|
$this->allow = ["get", "update"];
|
||||||
|
break;
|
||||||
|
case "readonly":
|
||||||
|
$this->allow = ["get"];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$this->allow = [];
|
||||||
|
}
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Require(\Condition $condition) {
|
||||||
|
if (is_null($this->restriction)) {
|
||||||
|
$this->restriction = new \Restriction();
|
||||||
|
}
|
||||||
|
$this->restriction->AddCondition($condition);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Limit($fields) {
|
||||||
|
if (is_null($this->restriction)) {
|
||||||
|
$this->restriction = new \Restriction();
|
||||||
|
}
|
||||||
|
$this->restriction->AddLimits($fields);
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetAllowed() {
|
||||||
|
return $this->allow;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetRestriction() {
|
||||||
|
return $this->restriction;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Make($requiredRight = null) {
|
||||||
|
return (new self($requiredRight));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AllowSelf() {
|
||||||
|
return self::Make()->Allow("readonly")->Require(\Condition::Make()->Add("ID", \Login::ID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function AllowUpdateSelf() {
|
||||||
|
return self::Make()->Allow("update")->Require(\Condition::Make()->Add("ID", \Login::ID()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Rights {
|
||||||
|
// false == not allowed
|
||||||
|
// array with refs to restrictions [a ... n] == restrictions apply
|
||||||
|
// true == allowed without restrictions
|
||||||
|
protected $actions = [
|
||||||
|
"get" => false,
|
||||||
|
"update" => false,
|
||||||
|
"insert" => false,
|
||||||
|
"remove" => false,
|
||||||
|
"upload" => false,
|
||||||
|
"erase" => false
|
||||||
|
];
|
||||||
|
|
||||||
|
public function __construct() {}
|
||||||
|
|
||||||
|
public function Add($right) {
|
||||||
|
// If user already has admin rights or does not posess this right, skip
|
||||||
|
if ($right->IsApplicable()) {
|
||||||
|
$restriction = $right->GetRestriction();
|
||||||
|
// Parse the right for every action
|
||||||
|
foreach ($right->GetAllowed() as $action) {
|
||||||
|
if (is_null($restriction)) {
|
||||||
|
// No restriction in the added right, so just overwrite current restrictions
|
||||||
|
$this->actions[$action] = true;
|
||||||
|
} else if (is_array($this->actions[$action])) {
|
||||||
|
// Restrictions applied before (or nothing was allowed) and in the new one, so add new restrictions
|
||||||
|
$this->actions[$action][] = $restriction;
|
||||||
|
} else if ($this->actions[$action]===false) {
|
||||||
|
// Not allowed before, but now restrictions apply
|
||||||
|
$this->actions[$action] = [$restriction];
|
||||||
|
} } }
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CheckInput($action, $id = null, $json = []) {
|
||||||
|
if (!is_array($this->actions[$action])) {
|
||||||
|
return $this->actions[$action];
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point, some but not everything is allowed for this user and this action, so check more carefully
|
||||||
|
foreach ($this->actions[$action] as $restriction) {
|
||||||
|
if ($restriction->CheckInput($id, $json)) {
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RestrictFilter() {
|
||||||
|
if (is_array($this->actions["get"])) {
|
||||||
|
$restrictions = new \Filter\ConditionList("or");
|
||||||
|
foreach ($this->actions["get"] as $restriction) {
|
||||||
|
$filterCondition = $restriction->GetFilterCondition();
|
||||||
|
if (!is_null($filterCondition)) {
|
||||||
|
$restrictions->Add($filterCondition);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
\Filter\Filter::Restrict($restrictions);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return $this->actions["get"];
|
||||||
|
} }
|
||||||
|
|
||||||
|
public function Export() {
|
||||||
|
$ret = [];
|
||||||
|
foreach ($this->actions as $action => $restrictions) {
|
||||||
|
if (is_array($restrictions)) {
|
||||||
|
$ret[$action] = [];
|
||||||
|
foreach ($restrictions as $restriction) {
|
||||||
|
$ret[$action][] = $restriction->Export();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$ret[$action] = $restrictions;
|
||||||
|
} }
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Make() {
|
||||||
|
return (new self())->Add(\Right::Make("ADMIN")->Allow("admin"));
|
||||||
|
}
|
||||||
|
}
|
||||||
444
lib_new/80_printer.php
Normal file
444
lib_new/80_printer.php
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
<?php #ajax/print.php
|
||||||
|
|
||||||
|
class Printer
|
||||||
|
{
|
||||||
|
private $page;
|
||||||
|
private $responseCode = 500;
|
||||||
|
private $prints = [];
|
||||||
|
private $html = "";
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
$this->page = $this->man->Route();
|
||||||
|
|
||||||
|
$qry = "SELECT l.ID,l.Bezeichnung,l.Hauptblock,b.Css,b.Breite,b.Höhe FROM sys_print_layouts l "
|
||||||
|
."LEFT JOIN sys_print_blocks b ON l.Hauptblock=b.ID WHERE Seite=? ORDER BY l.Bevorzugt DESC, l.Bezeichnung ASC";
|
||||||
|
if ($stmt = Db::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param("s", $this->page);
|
||||||
|
$stmt->execute();
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$this->prints[] = [
|
||||||
|
"ID" => strval($row["ID"]),
|
||||||
|
"Bezeichnung" => $row["Bezeichnung"],
|
||||||
|
"Hauptblock" => $row["Hauptblock"],
|
||||||
|
"Css" => $row["Css"],
|
||||||
|
"Breite" => $row["Breite"],
|
||||||
|
"Höhe" => $row["Höhe"],
|
||||||
|
];
|
||||||
|
} } }
|
||||||
|
|
||||||
|
public function GetPrints() {
|
||||||
|
return $this->prints;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function Print($printID, $json) {
|
||||||
|
return "<div>Not yet implemented</div>";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CreateHtml($printID) {
|
||||||
|
// Find requested layout
|
||||||
|
$layout = null;
|
||||||
|
foreach ($this->prints as $key => $print) {
|
||||||
|
if (strval($printID)==$print["ID"]) {
|
||||||
|
$layout = $print;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Breakoff if layout doesn't exist
|
||||||
|
if (null == $layout) {
|
||||||
|
$this->responseCode = 400;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get content
|
||||||
|
$content = $this->man->Output()["content"];
|
||||||
|
|
||||||
|
$block = [
|
||||||
|
"ID" => $layout["Hauptblock"],
|
||||||
|
"Bezeichnung" => $layout["Bezeichnung"],
|
||||||
|
"Css" => $layout["Css"],
|
||||||
|
"Breite" => $layout["Breite"],
|
||||||
|
"Höhe" => $layout["Höhe"],
|
||||||
|
"Links" => 0,
|
||||||
|
"Oben" => 0
|
||||||
|
];
|
||||||
|
$entries = $this->prepareEntries($content);
|
||||||
|
$this->printBlock($block, $entries);
|
||||||
|
$this->html.= $this->joinHtml($entries);
|
||||||
|
|
||||||
|
$this->responseCode = 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function GetHtml() {
|
||||||
|
return $this->html;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ResponseCode() {
|
||||||
|
return $this->responseCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************** Private functions *************************/
|
||||||
|
|
||||||
|
private function prepareEntries($content) {
|
||||||
|
$ret = [];
|
||||||
|
$selectionExists = sizeof($this->man->selected)>0;
|
||||||
|
foreach ($content as $group) {
|
||||||
|
foreach ($group["ENTRIES"] as $entry) {
|
||||||
|
if (!$selectionExists || in_array($entry["ID"], $this->man->selected)) {
|
||||||
|
$ret[$entry["ID"]] = [
|
||||||
|
"html" => "",
|
||||||
|
"floating" => [],
|
||||||
|
"content" => $entry,
|
||||||
|
];
|
||||||
|
} } }
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function joinHtml($entries) {
|
||||||
|
$ret = "";
|
||||||
|
foreach ($entries as $entry) {
|
||||||
|
$ret.= $entry["html"];
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printBlock($block, &$entries) {
|
||||||
|
// Initialize block
|
||||||
|
$div = "<div name='".$block["Bezeichnung"]."' style='".$block["Css"]
|
||||||
|
."width:".($block["Breite"]/10)."mm;height:".($block["Höhe"]/10)."mm;left:".($block["Links"]/10)."mm;top:".($block["Oben"]/10)."mm;'>";
|
||||||
|
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$entry["html"].= $div;
|
||||||
|
}
|
||||||
|
unset($entry); // $entry was used as a reference, so it needs to be unset
|
||||||
|
|
||||||
|
// Fetch sub-blocks
|
||||||
|
$qry = "SELECT spb.*,sph.Links,sph.Oben FROM sys_print_hierarchy sph LEFT JOIN sys_print_blocks spb ON spb.ID=sph.Kindblock WHERE sph.Block=".$block["ID"];
|
||||||
|
$res = Db::Get()->query($qry);
|
||||||
|
while ($subblock = $res->fetch_assoc()) {
|
||||||
|
$this->printBlock($subblock, $entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch block elements
|
||||||
|
$elements = [];
|
||||||
|
if ($res = Db::Get()->query("SELECT * FROM sys_print_elements WHERE Block=".$block["ID"]." ORDER BY Reihenfolge ASC")) {
|
||||||
|
while ($element = $res->fetch_assoc()) {
|
||||||
|
$this->printEntryElement($element, $entries);
|
||||||
|
//$elements[] = $row;
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Finalize block
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$entry["html"].= "</div>";
|
||||||
|
}
|
||||||
|
unset($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function printEntryElement($element, &$entries) {
|
||||||
|
foreach ($entries as &$entry) {
|
||||||
|
$do_print = false;
|
||||||
|
$cond = explode("|", $element["Bedingung"]);
|
||||||
|
if ($element["Parameter"]=="") {
|
||||||
|
$element["Parameter"] = "{}";
|
||||||
|
}
|
||||||
|
$params = json_decode($element["Parameter"], true);
|
||||||
|
switch ($cond[0]) {
|
||||||
|
case null:
|
||||||
|
case "":
|
||||||
|
case "0": //Print always
|
||||||
|
$do_print = true;
|
||||||
|
//echo " - Print always";
|
||||||
|
break;
|
||||||
|
case "1": //Field has to be NOT null, not false and not empty text
|
||||||
|
//echo " - Print if NOT null/false/empty";
|
||||||
|
if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])!==null && (
|
||||||
|
$this->getContent($entry["content"], $cond[1])!==false || $this->getContent($entry["content"], $cond[1], true)!=""
|
||||||
|
) ) ) {
|
||||||
|
$do_print = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "2": //Field has to be null, false or empty text
|
||||||
|
//echo " - Print if null/false/empty";
|
||||||
|
if (sizeof($cond)>1 && ($this->getContent($entry["content"], $cond[1])===null || $this->getContent($entry["content"], $cond[1])===false || $this->getContent($entry["content"], $cond[1], true)==""))
|
||||||
|
$do_print = true;
|
||||||
|
break;
|
||||||
|
case "3": //Field has to be value (second parameter) or include value if is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes value ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1]);
|
||||||
|
$arr = is_array($result);
|
||||||
|
$do_print = ($arr && in_array($cond[2], $result)) || (!$arr && $cond[2]==$result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "4": //Field has to be NOT value (second parameter) or DONT include value if is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes NOT value ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1]);
|
||||||
|
$arr = is_array($result);
|
||||||
|
$do_print = ($arr && !in_array($cond[2], $result)) || (!$arr && $cond[2]!=$result);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "5": //Field has to be text (second parameter) or include text if it is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
//echo " - Print if has/includes text ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1], 1);
|
||||||
|
$testtexts = explode(",", $cond[2]);
|
||||||
|
if (is_array($result)) {
|
||||||
|
$do_print = true;
|
||||||
|
foreach ($testtexts as $testtext) {
|
||||||
|
if (!in_array($testtext, $result)) {
|
||||||
|
$do_print = false;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
} else {
|
||||||
|
$do_print = $cond[2]==$result;
|
||||||
|
} }
|
||||||
|
break;
|
||||||
|
case "6": //Field has to be NOT text (second parameter) or DONT include text if it is link
|
||||||
|
if (sizeof($cond)>2) {
|
||||||
|
// UNTESTED
|
||||||
|
//echo " - Print if has/includes NOT text ".$cond[2];
|
||||||
|
$result = $this->getContent($entry["content"], $cond[1], 1);
|
||||||
|
$testtexts = explode(",", $cond[2]);
|
||||||
|
if (is_array($result)) {
|
||||||
|
$do_print = true;
|
||||||
|
foreach ($testtexts as $testtext) {
|
||||||
|
if (in_array($testtext, $result)) {
|
||||||
|
$do_print = false;
|
||||||
|
break;
|
||||||
|
} }
|
||||||
|
} else {
|
||||||
|
$do_print = $cond[2]!=$result;
|
||||||
|
} }
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$print_count = 0;
|
||||||
|
if ($do_print) {
|
||||||
|
$print_count = $params["repeat"] ?? 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ($print_count>0) {
|
||||||
|
//echo " - Printing";
|
||||||
|
$print_count--;
|
||||||
|
$border = array();
|
||||||
|
$border["top"] = isset($params["border-top"]) ? $params["border-top"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["bottom"] = isset($params["border-bottom"]) ? $params["border-bottom"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["left"] = isset($params["border-left"]) ? $params["border-left"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["right"] = isset($params["border-right"]) ? $params["border-right"] : (isset($params["border"]) ? $params["border"] : "0");
|
||||||
|
$border["color"] = isset($params["border-color"]) ? $params["border-color"] : "#000000";
|
||||||
|
$border["string"] = "";
|
||||||
|
if ($border["left"]!="0") {
|
||||||
|
$border["string"].= "border-left: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["right"]!="0") {
|
||||||
|
$border["string"].= "border-right: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["top"]!="0") {
|
||||||
|
$border["string"].= "border-top: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
if ($border["bottom"]!="0") {
|
||||||
|
$border["string"].= "border-bottom: 1px solid ".$border["color"]."; ";
|
||||||
|
}
|
||||||
|
/*if (isset($params["border"])) {
|
||||||
|
$border["string"] = "border: 1px solid ".$border["color"].";";
|
||||||
|
}*/
|
||||||
|
|
||||||
|
$cnt = $element["Inhalt"];
|
||||||
|
$fields = array();
|
||||||
|
if (preg_match_all("/#([^#\[\]]+)(?:\[(\d*):(\d*)\])?#/", $cnt, $matches, PREG_SET_ORDER)) {
|
||||||
|
foreach ($matches as $match) {
|
||||||
|
$replace = "";
|
||||||
|
$fields[] = $match[1];
|
||||||
|
$replace = $this->getContent($entry["content"], $match[1], 1);
|
||||||
|
if (sizeof($match)>3) {
|
||||||
|
if ($match[3]!="") {
|
||||||
|
$replace = substr($replace, 0, intval($match[3]));
|
||||||
|
}
|
||||||
|
if ($match[2]!="") {
|
||||||
|
$replace = substr($replace, intval($match[2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cnt = str_replace($match[0], $replace, $cnt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$left = $element["Links"]/10;
|
||||||
|
$top = $element["Oben"]/10;
|
||||||
|
$width = $element["Breite"]/10;
|
||||||
|
if ($width == 0) {
|
||||||
|
$width = 10;
|
||||||
|
}
|
||||||
|
$height = $element["Höhe"]/10;
|
||||||
|
if ($height == 0) {
|
||||||
|
$height = 10;
|
||||||
|
}
|
||||||
|
$this->moveIfOverlap($entry["floating"], $left, $top, $width, $height, isset($params["float"]) ? $params["float"] : "none");
|
||||||
|
$position = "position:absolute;";
|
||||||
|
if (isset($params["position"])) {
|
||||||
|
$position = "position:".$params["position"].";";
|
||||||
|
}
|
||||||
|
$position.= "z-index:".$element["Reihenfolge"].";";
|
||||||
|
|
||||||
|
switch ($element["Art"]) {
|
||||||
|
case "img":
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="keep") {
|
||||||
|
$imgsize = "max-width: ".$width."mm; max-height: ".$height."mm; width: auto; height: auto; ";
|
||||||
|
}
|
||||||
|
if (isset($params["aspect"]) && $params["aspect"]=="height") {
|
||||||
|
$imgsize = "height: ".$height."mm; width: auto; ";
|
||||||
|
} else {
|
||||||
|
$imgsize = "width: ".$width."mm; height: ".$height."mm; ";
|
||||||
|
}
|
||||||
|
$path = $cnt;
|
||||||
|
if (count($fields)) {
|
||||||
|
$path = $cnt;
|
||||||
|
}
|
||||||
|
//if (file_exists($path)) {
|
||||||
|
$entry["html"].= "<img src='".$path."' style='".$position." left: ".$left."mm; top: ".$top."mm; ".$imgsize."' />";
|
||||||
|
//}
|
||||||
|
break;
|
||||||
|
case "text":
|
||||||
|
$rotate = "";
|
||||||
|
if (isset($params["rotate"]) && $params["rotate"]=="1") {
|
||||||
|
$rotate = "transform: rotate(-90deg); ";
|
||||||
|
$left = "calc(".($left + ($height - $width)/2)."mm - ".$border["left"]."px); ";
|
||||||
|
$top = "calc(".($top + ($width - $height)/2)."mm - ".$border["top"]."px); ";
|
||||||
|
} else {
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
}
|
||||||
|
$font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
|
||||||
|
$font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
|
||||||
|
if (isset($params["weight"])) {
|
||||||
|
$font.= "font-weight: ".$params["weight"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["align"])) {
|
||||||
|
$font.= "text-align: ".$params["align"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["color"])) {
|
||||||
|
$font.= "color: ".$params["color"]."; ";
|
||||||
|
}
|
||||||
|
$entry["html"].= "<span style='".$position."overflow:hidden;white-space:nowrap;left:".$left."top:".$top."width:".$width."mm;height:"
|
||||||
|
.$height."mm;".$font.$border["string"].$rotate."'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
case "circle":
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
$display = "display: inline-flex; align-items: center; justify-content: space-around; ";
|
||||||
|
$font = "font-family: ".(isset($params["font"]) && $params["font"]=="narrow" ? "Calibri, Carlito, Arial Narrow, Liberation Sans Narrow, Arial, Liberation Sans; " : "Arial, Liberation Sans; ");
|
||||||
|
$font.= "font-size: ".(isset($params["size"]) ? $params["size"] : $height)."mm; ";
|
||||||
|
if (isset($params["weight"])) {
|
||||||
|
$font.= "font-weight: ".$params["weight"]."; ";
|
||||||
|
}
|
||||||
|
if (isset($params["color"])) {
|
||||||
|
$font.= "color: ".$params["color"]."; ";
|
||||||
|
}
|
||||||
|
$font.= "text-align: center; ";
|
||||||
|
$background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
|
||||||
|
$entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;".$display.$background.$font
|
||||||
|
.$border["string"]."border-radius:50%;'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
case "quartercircle":
|
||||||
|
$left.= "mm; ";
|
||||||
|
$top.= "mm; ";
|
||||||
|
$background = isset($params["background"]) ? "background-color: ".$params["background"].";" : "";
|
||||||
|
$border_radius = isset($params["corner"]) ? "border-".$params["corner"]."-radius:100%;" : "border-top-left-radius:100%;";
|
||||||
|
$entry["html"].= "<span style='".$position."left:".$left."top:".$top."width:".$width."mm;height:".$height."mm;"/*.$display*/.$background/*.$font*/
|
||||||
|
.$border_radius."'>".$cnt."</span>";
|
||||||
|
break;
|
||||||
|
} } }
|
||||||
|
unset($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getTextFromReference($ref, $getText) {
|
||||||
|
if (1==$getText && array_key_exists("KÜRZEL", $ref)) {
|
||||||
|
return $ref["KÜRZEL"];
|
||||||
|
} elseif (1<=$getText && array_key_exists("NAME", $ref)) {
|
||||||
|
return $ref["NAME"];
|
||||||
|
} elseif (array_key_exists("ID", $ref)) {
|
||||||
|
return $ref["ID"];
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// $entry Content-array of entry ([ID, MAIN, SUB])
|
||||||
|
// $key Requested field
|
||||||
|
// $getText 0 = ID is sufficient, 1 = Kürzel (prio.) or Name desired, 2 = Name desired
|
||||||
|
private function getContent($entry, $key, $getText = 0) {
|
||||||
|
// Check if requested field are actually nested keys separated by dots
|
||||||
|
$dotPos = strpos($key, ".");
|
||||||
|
if ($dotPos!==false) {
|
||||||
|
$subkey = substr($key, 0, $dotPos);
|
||||||
|
if (array_key_exists($subkey, $entry)) {
|
||||||
|
return $this->getContent($entry[$subkey], substr($key, $dotPos+1, strlen($key)-$dotPos-1));
|
||||||
|
} }
|
||||||
|
|
||||||
|
// Check if requested field exists
|
||||||
|
if (array_key_exists($key, $entry)) {
|
||||||
|
// Check if requested field is an array (which means it was selected through a foreign reference or link table)
|
||||||
|
if (is_array($entry[$key])) {
|
||||||
|
// Check if array is size 0 -> This means an unset reference, i.e. NULL
|
||||||
|
if (0==sizeof($entry[$key])) {
|
||||||
|
return null;
|
||||||
|
} elseif (array_key_exists("ID", $entry[$key])) { // Check if requested field is a normal foreign reference -> It will have the ID field then.
|
||||||
|
// Foreign reference with ID, NAME and maybe KÜRZEL
|
||||||
|
return $this->getTextFromReference($entry[$key], $getText);
|
||||||
|
} else { // If not, we assume to have an sequential array which means a n:m-link-table
|
||||||
|
$ret = [];
|
||||||
|
foreach ($entry[$key] as $i => $ref) {
|
||||||
|
$ret[] = $this->getTextFromReference($ref, $getText);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
} else { // Requested field is a normal value
|
||||||
|
return $entry[$key];
|
||||||
|
}
|
||||||
|
} elseif (array_key_exists("MAIN", $entry)) { // If we are in the root structure of a content object, we will have sections MAIN and SUB and check those recursively
|
||||||
|
// Search the MAIN-array for field
|
||||||
|
$ret = $this->getContent($entry["MAIN"], $key, $getText);
|
||||||
|
// If only the field name was returned check SUB-array if exists
|
||||||
|
if ($key==$ret && array_key_exists("SUB", $entry)) {
|
||||||
|
$ret = $this->getContent($entry["SUB"], $key, $getText);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
} else {
|
||||||
|
// Nothing was found, we return the field name
|
||||||
|
return $key;
|
||||||
|
} }
|
||||||
|
|
||||||
|
private function moveIfOverlap(&$floating, &$left, &$top, $width, $height, $dir = "none", $lastdir = "none") {
|
||||||
|
$moved = false;
|
||||||
|
foreach ($floating as $key => $float) {
|
||||||
|
if (
|
||||||
|
$left < $float["right"] &&
|
||||||
|
$left+$width > $float["left"] &&
|
||||||
|
$top < $float["bottom"] &&
|
||||||
|
$top+$height > $float["top"]
|
||||||
|
) {
|
||||||
|
if ($float["dir"]=="left" && $lastdir!="right") {
|
||||||
|
$left = $float["right"];
|
||||||
|
} else if ($float["dir"]=="right" && $lastdir!="left") {
|
||||||
|
$left = $float["left"] - $width;
|
||||||
|
} else if ($float["dir"]=="up" && $lastdir!="down") {
|
||||||
|
$top = $float["bottom"];
|
||||||
|
} else if ($float["dir"]=="down" && $lastdir!="up") {
|
||||||
|
$top = $float["top"] - $height;
|
||||||
|
}
|
||||||
|
$lastdir = $float["dir"];
|
||||||
|
$moved = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($moved) {
|
||||||
|
$this->moveIfOverlap($floating, $left, $top, $width, $height, $dir, $lastdir);
|
||||||
|
} else if ($dir!="none") {
|
||||||
|
$floating[] = array(
|
||||||
|
"left" => $left,
|
||||||
|
"right" => $left + $width,
|
||||||
|
"top" => $top,
|
||||||
|
"bottom" => $top + $height,
|
||||||
|
"dir" => $dir
|
||||||
|
);
|
||||||
|
} }
|
||||||
|
}
|
||||||
62
lib_new/99_manager.php
Normal file
62
lib_new/99_manager.php
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
<?php #lib/99_manager.php
|
||||||
|
|
||||||
|
// Missing:
|
||||||
|
// - Error handling
|
||||||
|
// - Proper response codes
|
||||||
|
|
||||||
|
class Manager
|
||||||
|
{
|
||||||
|
private static $routes = [
|
||||||
|
//"Termine",
|
||||||
|
//"Dienstpläne",
|
||||||
|
"Personal",
|
||||||
|
//"Prozesse",
|
||||||
|
//"Spinde",
|
||||||
|
//"Einsätze",
|
||||||
|
//"Anwesenheiten",
|
||||||
|
//"Fahrzeuge"
|
||||||
|
];
|
||||||
|
private function __construct() {}
|
||||||
|
private function __destruct() {}
|
||||||
|
|
||||||
|
public static function Answer() {
|
||||||
|
try {
|
||||||
|
Request::ParseInput();
|
||||||
|
Login::Login();
|
||||||
|
|
||||||
|
if (Request::IsLogout()) {
|
||||||
|
// Logout detected
|
||||||
|
Login::Logout();
|
||||||
|
Response::Get()->Code(200)->Message("Logout erkannt!");
|
||||||
|
} else if (!Login::LoggedIn()) {
|
||||||
|
// Login is necessary
|
||||||
|
Response::Get()->Code(401);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Include routes. Needed also for /-Request
|
||||||
|
foreach (self::$routes as $route => $filename) {
|
||||||
|
$file = "routes/".str_replace(["ä", "ö", "ü", "ß"], ["ae", "oe", "ue", "ss"], $filename).".php";
|
||||||
|
if (file_exists($file)) {
|
||||||
|
include $file;
|
||||||
|
} }
|
||||||
|
|
||||||
|
if (Request::IsRoot()) {
|
||||||
|
// POST (Login) or GET/OPTIONS... i dont care, all will result in the same
|
||||||
|
$access = [];
|
||||||
|
foreach (self::$routes as $route => $filename) {
|
||||||
|
$class = "\\Routes\\".$route;
|
||||||
|
$access[$route] = $class::Rights();
|
||||||
|
}
|
||||||
|
Response::Get()->Code(200)->Content($access)->ETag(hash('sha256', json_encode($access)));
|
||||||
|
} else if (!in_array(Request::Route(), self::$routes)) {
|
||||||
|
Response::Get()->Code(404)->Message("Route not found");
|
||||||
|
} else {
|
||||||
|
// This is the good path. We can try to answer the request
|
||||||
|
$class = "\\Routes\\".Request::Route();
|
||||||
|
(new $class)->Answer();
|
||||||
|
} }
|
||||||
|
} catch (\Throwable $e) { // For PHP 7
|
||||||
|
Response::Get()->Code(500)->Message("<h5>Caught throwable:</h5>".$e);
|
||||||
|
} catch (\Exception $e) { // For PHP 5
|
||||||
|
Response::Get()->Code(500)->Message("<h5>Caught exception:</h5>".$e);
|
||||||
|
} } }
|
||||||
337
lib_new_bak/60_resources.php
Normal file
337
lib_new_bak/60_resources.php
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
<?php #lib/20_routes.php
|
||||||
|
|
||||||
|
namespace Resources;
|
||||||
|
|
||||||
|
class Element {
|
||||||
|
protected $parent;
|
||||||
|
protected $id;
|
||||||
|
protected $chksum = "";
|
||||||
|
|
||||||
|
protected $fields = [];
|
||||||
|
protected $keys = [];
|
||||||
|
protected $links = [];
|
||||||
|
|
||||||
|
public function __construct($parent, $data) {
|
||||||
|
$this->parent = $parent;
|
||||||
|
$this->id = $data["ID"];
|
||||||
|
|
||||||
|
$definitions = $parent->Definitions();
|
||||||
|
|
||||||
|
$this->fields = array_combine(array_keys($definitions["fields"]), array_column($definitions["fields"], "value"));
|
||||||
|
$this->keys = array_combine(array_keys($definitions["keys"]), array_column($definitions["keys"], "reference"));
|
||||||
|
$this->links = array_combine(array_keys($definitions["links"]), array_column($definitions["links"], "referenceList"));
|
||||||
|
|
||||||
|
$this->Patch($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ID() {return $this->id;}
|
||||||
|
public function Checksum() {return $this->chksum;}
|
||||||
|
|
||||||
|
public function Patch($data) {
|
||||||
|
// Overwrite field values if data exists
|
||||||
|
foreach ($this->fields as $field => &$value) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$value = $data[$field];
|
||||||
|
} }
|
||||||
|
unset($value); // If removed, $value would still point to last element of fields
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
foreach ($this->keys as $key => &$reference) {
|
||||||
|
if (isset($data[$key])) {
|
||||||
|
$class = "\\Resources\\".$definitions["keys"][$key]["resourceClass"];
|
||||||
|
$reference = is_array($data[$key]) ? $class::Get()->RefByData($data[$key]) : $class::Get()->RefById($data[$key]);
|
||||||
|
} }
|
||||||
|
unset($reference); // If removed, $reference would still point to last element of keys
|
||||||
|
|
||||||
|
foreach ($this->links as $link => &$refList) {
|
||||||
|
if (isset($data[$link])) {
|
||||||
|
$class = "\\Links\\".$definitions["links"][$link]["linkClass"];
|
||||||
|
$refList = $class::Get()->RefList($this->id, $link, $data[$link]);
|
||||||
|
} }
|
||||||
|
unset($refList); // If removed, $reference would still point to last element of keys
|
||||||
|
|
||||||
|
if (isset($data["CHKSUM"]) && !is_null($data["CHKSUM"])) {
|
||||||
|
$this->chksum = $data["CHKSUM"];
|
||||||
|
} else {
|
||||||
|
$this->updateChecksum();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Store() {
|
||||||
|
$fields = [];
|
||||||
|
$values = [];
|
||||||
|
$types = "";
|
||||||
|
|
||||||
|
if ($this->parent->HasChksum()) {
|
||||||
|
$fields[] = "CHKSUM";
|
||||||
|
$values[] = $this->chksum;
|
||||||
|
$types.= "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
foreach ($definitions["fields"] as $field => $def) {
|
||||||
|
$fields[] = $field;
|
||||||
|
$values[] = $this->fields[$field];
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
foreach ($definitions["keys"] as $key => $def) {
|
||||||
|
$fields[] = $key;
|
||||||
|
$values[] = is_null($this->keys[$key]) ? null : $this->keys[$key]->ID();
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "UPDATE ".$this->parent->Table()." SET ".implode(" = ?, ", $fields)." = ? WHERE ID = ?";
|
||||||
|
$values[] = $this->id;
|
||||||
|
$types.= "s";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param($types, ...$values);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$this->parent->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function updateChecksum() {
|
||||||
|
$this->chksum = $this->parent->CalcChecksum($this->Json(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Json($depth = null) {
|
||||||
|
if (is_null($depth)) {
|
||||||
|
$depth = \Request::DetailDepth();
|
||||||
|
}
|
||||||
|
$ret = [
|
||||||
|
"ID" => $this->id,
|
||||||
|
"CHKSUM" => $this->chksum
|
||||||
|
];
|
||||||
|
foreach ($this->fields as $field => $value) {
|
||||||
|
$ret[$field] = $value;
|
||||||
|
}
|
||||||
|
foreach ($this->keys as $key => $reference) {
|
||||||
|
$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
|
||||||
|
}
|
||||||
|
foreach ($this->links as $link => $referenceList) {
|
||||||
|
$ids = [];
|
||||||
|
foreach ($referenceList as $reference) {
|
||||||
|
if (!is_null($reference)) {
|
||||||
|
$ids[] = $depth>0 ? $reference->Json($depth-1) : $reference->ID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$ret[$link] = $ids;
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Handler {
|
||||||
|
protected static $instances = [];
|
||||||
|
protected $liste = [];
|
||||||
|
public static function Get() {
|
||||||
|
$class = get_called_class();
|
||||||
|
if (!isset($instances[$class])) {
|
||||||
|
self::$instances[$class] = new $class();
|
||||||
|
}
|
||||||
|
return self::$instances[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public function Table() {return $this->names["table"];}
|
||||||
|
public function Ident() {return $this->names["ident"];}
|
||||||
|
public function Short() {return $this->names["short"];}
|
||||||
|
public function TableWithShort() {return $this->names["table"]." ".$this->names["short"];}
|
||||||
|
public function HasUuid() {return $this->has["uuid"];}
|
||||||
|
public function HasChksum() {return $this->has["sha256"];}
|
||||||
|
public function Definitions() {return $this->definitions;}
|
||||||
|
|
||||||
|
public function RefById($id) {
|
||||||
|
if (!isset($this->liste[$id]) ) {
|
||||||
|
$this->liste[$id] = $this->load($id); // Might be null, but we will buffer the result anyway
|
||||||
|
}
|
||||||
|
return $this->liste[$id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RefByData($data) {
|
||||||
|
if (!isset($data["ID"])) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!isset($this->liste[$data["ID"]])) {
|
||||||
|
$this->liste[$data["ID"]] = new Element($this, $data);
|
||||||
|
}
|
||||||
|
return $this->liste[$data["ID"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function RefAll() {
|
||||||
|
$this->liste = $this->load();
|
||||||
|
return $this->liste;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function loadChecksum($id = null) {
|
||||||
|
$field = "CHKSUM";
|
||||||
|
if (!$this->has["sha256"]) {
|
||||||
|
// There is no checksum column to use. Smasch all columns together
|
||||||
|
$fields = [];
|
||||||
|
$arr = array_merge(array_keys($this->definitions["fields"]), array_keys($this->definitions["keys"]));
|
||||||
|
$field = "CONCAT(IFNULL(".implode(", '__NULL__'), IFNULL(", $arr).", '__NULL__'))";
|
||||||
|
}
|
||||||
|
if (is_null($id)) {
|
||||||
|
// We need to concat all the rows
|
||||||
|
$field = "GROUP_CONCAT(".$field.")";
|
||||||
|
}
|
||||||
|
if (!$this->has["sha256"] || is_null($id)) {
|
||||||
|
// Call the hash function, because $field is not a pregenerated checksum
|
||||||
|
$field = "SHA2(".$field.", 256)";
|
||||||
|
}
|
||||||
|
$qry = "SELECT ".$field." cs FROM ".$this->Table();
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$qry.= " WHERE ID = ?";
|
||||||
|
}
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$chksum = $stmt->get_result()->fetch_assoc()["cs"];
|
||||||
|
return $chksum;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function CalcChecksum($data) {
|
||||||
|
$concat = "";
|
||||||
|
foreach ($this->definitions as $definition) {
|
||||||
|
foreach ($definition as $field => $value) {
|
||||||
|
$concat.= is_array($data[$field]) ? implode("", $data[$field]) : ($data[$field] ?? "__NULL__");
|
||||||
|
} }
|
||||||
|
return hash("sha256", $concat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Checksum($id = null) {
|
||||||
|
if (is_null($id)) {
|
||||||
|
return \KV::Get()->get("Routes:".get_called_class().":Checksum");
|
||||||
|
} else if (isset($this->liste[$id])) {
|
||||||
|
return $this->liste[$id]->Checksum();
|
||||||
|
} else {
|
||||||
|
return $this->loadChecksum($id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function UpdateChecksum() {
|
||||||
|
$checksum = $this->loadChecksum();
|
||||||
|
\KV::Get()->set("Routes:".get_called_class().":Checksum", $checksum);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Remove($id) {
|
||||||
|
if ($stmt = \DB::Get()->prepare("DELETE FROM ".$this->Table()." WHERE ID = ?")) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} else if (0==$stmt->affected_rows) {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurde nichts entfernt!");
|
||||||
|
} else {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurden mehrere Einträge entfernt!");
|
||||||
|
} } }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Insert($data, &$newid) {
|
||||||
|
$fields = [];
|
||||||
|
$values = [];
|
||||||
|
$types = "";
|
||||||
|
$placeholders = [];
|
||||||
|
|
||||||
|
if ($this->has["uuid"]) {
|
||||||
|
$res = \DB::Get()->query("SELECT UUID_SHORT() uuid");
|
||||||
|
$fields[] = "ID";
|
||||||
|
$values[] = $res->fetch_assoc()["uuid"];
|
||||||
|
$types.= "s";
|
||||||
|
$placeholders[] = "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->has["sha256"]) {
|
||||||
|
$fields[] = "CHKSUM";
|
||||||
|
$values[] = $this->CalcChecksum($data);
|
||||||
|
$types.= "s";
|
||||||
|
$placeholders[] = "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->definitions["fields"] as $field => $def) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$fields[] = $field;
|
||||||
|
$values[] = $data[$field];
|
||||||
|
$types.= $def["type"];
|
||||||
|
$placeholders[] = "?";
|
||||||
|
} }
|
||||||
|
foreach ($this->definitions["keys"] as $key => $def) {
|
||||||
|
if (isset($data[$key])) {
|
||||||
|
$fields[] = $key;
|
||||||
|
$values[] = $data[$key]=="__NULL__" ? null : $data[$key];
|
||||||
|
$types.= $def["type"];
|
||||||
|
$placeholders[] = "?";
|
||||||
|
} }
|
||||||
|
|
||||||
|
$qry = "INSERT INTO ".$this->Table()." (".implode(", ", $fields).") VALUES (".implode(", ", $placeholders).")";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param($types, ...$values);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$newid = $this->HasUuid() ? $values[0] : \DB::Get()->insert_id;
|
||||||
|
$this->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function load($id = null) {
|
||||||
|
$fields = [$this->Short().".*"];
|
||||||
|
$joins = [];
|
||||||
|
foreach ($this->definitions["keys"] as $key => $definition) {
|
||||||
|
$class = "\\Resources\\".$definition["resourceClass"];
|
||||||
|
$joins[] = \DB::LeftJoin($class::Get()->Table(), $class::Get()->Short(), "ID", $this->Short(), $key);
|
||||||
|
$fields[] = $key;
|
||||||
|
}
|
||||||
|
foreach ($this->definitions["links"] as $link => $definition) {
|
||||||
|
$resClass = "\\Resources\\".$definition["resourceClass"];
|
||||||
|
$lnkClass = "\\Links\\".$definition["linkClass"];
|
||||||
|
|
||||||
|
$joins[] = \DB::LeftJoin($lnkClass::Get()->Table(), $lnkClass::Get()->Short(), $this->Ident(), $this->Short(), "ID");
|
||||||
|
//$joins[] = \DB::LeftJoin($resClass::Get()->Table(), $resClass::Get()->Short(), "ID", $lnkClass::Get()->Short(), $link);
|
||||||
|
$fields[] = "CONCAT(',', GROUP_CONCAT(DISTINCT ".$lnkClass::Get()->Short().".".$link." SEPARATOR ','), ',') ".$link;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "SELECT ".implode(", ", $fields)." FROM ".$this->TableWithShort()." ".implode(" ", $joins);
|
||||||
|
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$qry.= "WHERE ".$this->Short().".ID = ?";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
if (is_null($id)) {
|
||||||
|
$ret = [];
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$ret[] = new Element($this, $row);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
} else {
|
||||||
|
if ($row = $res->fetch_assoc()) {
|
||||||
|
return new Element($this, $row);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
226
lib_new_bak/61_links.php
Normal file
226
lib_new_bak/61_links.php
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
<?php #lib/20_routes.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
/*class Element {
|
||||||
|
protected $parent;
|
||||||
|
|
||||||
|
protected $left;
|
||||||
|
protected $right;
|
||||||
|
protected $fields = [];
|
||||||
|
protected $keys = [];
|
||||||
|
|
||||||
|
public function __construct($parent, $data) {
|
||||||
|
$this->parent = $parent;
|
||||||
|
$definitions = $parent->Definitions();
|
||||||
|
|
||||||
|
$classLeft = "\\Resources\\".$definitions["left"]["resourceClass"];
|
||||||
|
$this->left = $classLeft::;
|
||||||
|
foreach ($definitions["ids"] as $id => $className) {
|
||||||
|
$class = "\\Resources\\".$className;
|
||||||
|
$this->ids[$id] = isset($data[$id]) ? $class::Get()->RefByData($data[$id]) : null;
|
||||||
|
}
|
||||||
|
$this->fields = array_combine(array_keys($definitions["fields"]), array_column($definitions["fields"], "value"));
|
||||||
|
$this->keys = array_combine(array_keys($definitions["keys"]), array_column($definitions["keys"], "reference"));
|
||||||
|
|
||||||
|
$this->Patch($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function ID($key) {return $this->ids[$key];}
|
||||||
|
|
||||||
|
public function Patch($data) {
|
||||||
|
// Overwrite field values if data exists
|
||||||
|
foreach ($this->fields as $field => &$value) {
|
||||||
|
if (isset($data[$field])) {
|
||||||
|
$value = $data[$field];
|
||||||
|
} }
|
||||||
|
unset($value); // If removed, $value would still point to last element of fields
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
foreach ($this->keys as $key => &$reference) {
|
||||||
|
if (isset($data[$key])) {
|
||||||
|
$class = "\\Resources\\".$definitions["keys"][$key]["resourceClass"];
|
||||||
|
$reference = is_array($data[$key]) ? $class::Get()->RefByData($data[$key]) : $class::Get()->RefById($data[$key]);
|
||||||
|
} }
|
||||||
|
unset($reference); // If removed, $reference would still point to last element of keys
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Store() {
|
||||||
|
$ids = [];
|
||||||
|
$fields = [];
|
||||||
|
$values = [];
|
||||||
|
$types = "";
|
||||||
|
|
||||||
|
$definitions = $this->parent->Definitions();
|
||||||
|
if (!empty($definitions["fields"]) || !empty($definitions["keys"])) {
|
||||||
|
|
||||||
|
foreach ($definitions["fields"] as $field => $def) {
|
||||||
|
$fields[] = $field." = ?";
|
||||||
|
$values[] = $this->fields[$field];
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
foreach ($definitions["keys"] as $key => $def) {
|
||||||
|
$fields[] = $key;
|
||||||
|
$values[] = is_null($this->keys[$key]) ? null : $this->keys[$key]->ID();
|
||||||
|
$types.= $def["type"];
|
||||||
|
}
|
||||||
|
foreach ($this->ids as $id => $reference) {
|
||||||
|
$ids[] = $id." = ?";
|
||||||
|
$values[] = $reference->ID();
|
||||||
|
$types.= "s";
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "UPDATE ".$this->parent->Table()." SET ".implode(", ", $fields)." WHERE ".implode(" AND ", $ids);
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
$stmt->bind_param($types, ...$values);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$this->parent->UpdateChecksum();
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Json($depth = null) {
|
||||||
|
if (is_null($depth)) {
|
||||||
|
$depth = \Request::DetailDepth();
|
||||||
|
}
|
||||||
|
$ret = [];
|
||||||
|
foreach ($this->ids as $id => $reference) {
|
||||||
|
$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
|
||||||
|
}
|
||||||
|
foreach ($this->fields as $field => $value) {
|
||||||
|
$ret[$field] = $value;
|
||||||
|
}
|
||||||
|
foreach ($this->keys as $key => $reference) {
|
||||||
|
$ret[$key] = is_null($reference) ? null : ($depth>0 ? $reference->Json($depth-1) : $reference->ID());
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
abstract class Link {
|
||||||
|
protected static $instances = [];
|
||||||
|
protected $leftIndex = [];
|
||||||
|
protected $rightIndex = [];
|
||||||
|
|
||||||
|
public static function Get() {
|
||||||
|
$class = get_called_class();
|
||||||
|
if (!isset($instances[$class])) {
|
||||||
|
self::$instances[$class] = new $class();
|
||||||
|
}
|
||||||
|
return self::$instances[$class];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function __construct() {}
|
||||||
|
|
||||||
|
public function Table() {return $this->names["table"];}
|
||||||
|
public function Short() {return $this->names["short"];}
|
||||||
|
public function TableWithShort() {return $this->names["table"]." ".$this->names["short"];}
|
||||||
|
|
||||||
|
public function RefList($id, $target, $dataOrIds = null) {
|
||||||
|
if ($target==$this->definitions["left"]["ident"]) {
|
||||||
|
// We come from right
|
||||||
|
if (!isset($this->rightIndex[$id])) {
|
||||||
|
$this->rightIndex[$id] = is_array($dataOrIds) ? $this->connect("left", $dataOrIds) : $this->load("left", $id);
|
||||||
|
}
|
||||||
|
return $this->rightIndex[$id];
|
||||||
|
} else {
|
||||||
|
// We come from left
|
||||||
|
if (!isset($this->leftIndex[$id])) {
|
||||||
|
$this->rightIndex[$id] = is_array($dataOrIds) ? $this->connect("right", $dataOrIds) : $this->load("right", $id);
|
||||||
|
}
|
||||||
|
return $this->leftIndex[$id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Unset($id, $target, $targetId) {
|
||||||
|
$idIsRight = $target==$this->definitions["left"]["ident"];
|
||||||
|
|
||||||
|
$qry = "DELETE FROM ".$this->Table()." WHERE ".$this->definitions["left"]["ident"]." = ? AND ".$this->definitions["right"]["ident"]." = ?";
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if ($idIsRight) {
|
||||||
|
$stmt->bind_param("ss", $targetId, $id);
|
||||||
|
} else {
|
||||||
|
$stmt->bind_param("ss", $id, $targetId);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
if ($idIsRight) {
|
||||||
|
$this->indexRight[$id] = $this->load("left", $id);
|
||||||
|
} else {
|
||||||
|
$this->indexLeft[$id] = $this->load("right", $id);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (0==$stmt->affected_rows) {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurde nichts entfernt!");
|
||||||
|
} else {
|
||||||
|
\Response::Get()->Message("Fehler: Es wurden mehrere Einträge entfernt!");
|
||||||
|
} } }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Set($id, $target, $targetId) {
|
||||||
|
$idIsRight = $target==$this->definitions["left"]["ident"];
|
||||||
|
|
||||||
|
$qry = "INSERT INTO ".$this->Table()." (".$this->definitions["left"]["ident"].",".$this->definitions["right"]["ident"].") VALUES (?, ?)";
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if ($idIsRight) {
|
||||||
|
$stmt->bind_param("ss", $targetId, $id);
|
||||||
|
} else {
|
||||||
|
$stmt->bind_param("ss", $id, $targetId);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
if ($idIsRight) {
|
||||||
|
$this->indexRight[$id] = $this->load("left", $id);
|
||||||
|
} else {
|
||||||
|
$this->indexLeft[$id] = $this->load("right", $id);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function connect($target, $dataOrIds) {
|
||||||
|
$targetClass = "\\Resources\\".$this->definitions[$target]["resourceClass"];
|
||||||
|
|
||||||
|
$ret = [];
|
||||||
|
foreach ($dataOrIds as $dataOrId) {
|
||||||
|
if (is_array($dataOrId)) {
|
||||||
|
$ret[] = $targetClass::Get()->RefByData($dataOrId);
|
||||||
|
} else {
|
||||||
|
$ret[] = $targetClass::Get()->RefById($dataOrId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function load($target, $id) {
|
||||||
|
$source = "left" == $target ? "right" : "left";
|
||||||
|
$sourceField = $this->definitions[$source]["ident"];
|
||||||
|
$targetField = $this->definitions[$target]["ident"];
|
||||||
|
$targetClass = "\\Resources\\".$this->definitions[$target]["resourceClass"];
|
||||||
|
|
||||||
|
$qry = "SELECT ".$targetField." FROM ".$this->Table()." WHERE ".$sourceField." = ?";
|
||||||
|
|
||||||
|
if ($stmt = \DB::Get()->prepare($qry)) {
|
||||||
|
if (!is_null($id)) {
|
||||||
|
$stmt->bind_param("s", $id);
|
||||||
|
}
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$res = $stmt->get_result();
|
||||||
|
$ret = [];
|
||||||
|
while ($row = $res->fetch_assoc()) {
|
||||||
|
$ret[] = $targetClass::Get()->RefById($row[$targetField]);
|
||||||
|
}
|
||||||
|
return $ret;
|
||||||
|
} }
|
||||||
|
\Response::Get()->DbError();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
17
links/Anwesenheiten_Einsaetze.php
Normal file
17
links/Anwesenheiten_Einsaetze.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Anwesenheiten/Anwesenheit.php");
|
||||||
|
require_once("resources/Einsätze/Einsatz.php");
|
||||||
|
|
||||||
|
class Anwesenheiten_Einsätze extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Anwesenheiten_Einsätze", "short" => "ae"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Anwesenheiten", "resourceClass" => "Anwesenheiten\\Anwesenheit"],
|
||||||
|
"right" => ["ident" => "Einsätze", "resourceClass" => "Einsätze\\Einsatz"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
20
links/Anwesenheiten_Personal.php
Normal file
20
links/Anwesenheiten_Personal.php
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Anwesenheiten/Anwesenheit.php");
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Fahrzeuge/Fahrzeug.php");
|
||||||
|
|
||||||
|
class Anwesenheiten_Personal extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Anwesenheiten_Personal", "short" => "ap"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Anwesenheiten", "resourceClass" => "Anwesenheiten\\Anwesenheit"],
|
||||||
|
"right" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => [
|
||||||
|
"Fahrzeuge" => ["resourceClass" => "Fahrzeuge\\Fahrzeug", "nullable" => true, "type" => "s"]
|
||||||
|
]
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Anwesenheiten_Termine.php
Normal file
17
links/Anwesenheiten_Termine.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Anwesenheiten/Anwesenheit.php");
|
||||||
|
require_once("resources/Einsätze/Einsatz.php");
|
||||||
|
|
||||||
|
class Anwesenheiten_Termine extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Anwesenheiten_Termine", "short" => "at"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Anwesenheiten", "resourceClass" => "Anwesenheiten\\Anwesenheit"],
|
||||||
|
"right" => ["ident" => "Termine", "resourceClass" => "Termine\\Termin"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Fahrzeuge_Einweisungen.php
Normal file
17
links/Fahrzeuge_Einweisungen.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Fahrzeuge/Fahrzeug.php");
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
|
||||||
|
class Fahrzeuge_Einweisungen extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Fahrzeuge_Einweisungen", "short" => "fe"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Fahrzeuge", "resourceClass" => "Fahrzeuge\\Fahrzeug"],
|
||||||
|
"right" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Personal_Abteilungen.php
Normal file
17
links/Personal_Abteilungen.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Struktur/Abteilung.php");
|
||||||
|
|
||||||
|
class Personal_Abteilungen extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Personal_Abteilungen", "short" => "pa"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"right" => ["ident" => "Abteilungen", "resourceClass" => "Struktur\\Abteilung"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Personal_Gruppen.php
Normal file
17
links/Personal_Gruppen.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Struktur/Gruppe.php");
|
||||||
|
|
||||||
|
class Personal_Gruppen extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Personal_Gruppen", "short" => "pg"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"right" => ["ident" => "Gruppen", "resourceClass" => "Struktur\\Gruppe"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Personal_Lehrgaenge.php
Normal file
17
links/Personal_Lehrgaenge.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Lehrgaenge/Lehrgang.php");
|
||||||
|
|
||||||
|
class Personal_Lehrgänge extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Personal_Lehrgänge", "short" => "pl"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"right" => ["ident" => "Lehrgänge", "resourceClass" => "Lehrgänge\\Lehrgang"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Personal_Verwalter.php
Normal file
17
links/Personal_Verwalter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Struktur/Verwalter.php");
|
||||||
|
|
||||||
|
class Personal_Verwalter extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Personal_Verwalter", "short" => "pv"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"right" => ["ident" => "Verwalter", "resourceClass" => "Struktur\\Verwalter"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Rechte_Verwalter.php
Normal file
17
links/Rechte_Verwalter.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Rechte/Recht.php");
|
||||||
|
require_once("resources/Struktur/Verwalter.php");
|
||||||
|
|
||||||
|
class Rechte_Verwalter extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Rechte_Verwalter", "short" => "rv"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Rechte", "resourceClass" => "Rechte\\Recht"],
|
||||||
|
"right" => ["ident" => "Verwalter", "resourceClass" => "Struktur\\Verwalter"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/Spinde_Zuordnung.php
Normal file
17
links/Spinde_Zuordnung.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Spinde/Spind.php");
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
|
||||||
|
class Spinde_Zuordnung extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Spinde_Zuordnung", "short" => "sz"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Spinde", "resourceClass" => "Spinde\\Spind"],
|
||||||
|
"right" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
17
links/__Termine_Teilnahmen.php
Normal file
17
links/__Termine_Teilnahmen.php
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
namespace Links;
|
||||||
|
|
||||||
|
require_once("resources/Personal/Personal.php");
|
||||||
|
require_once("resources/Struktur/Verwalter.php");
|
||||||
|
|
||||||
|
class Personal_Verwalter extends \Links\Link
|
||||||
|
{
|
||||||
|
protected $names = ["table" => "Personal_Verwalter", "short" => "pv"];
|
||||||
|
protected $definitions = [
|
||||||
|
"left" => ["ident" => "Personal", "resourceClass" => "Personal\\Personal"],
|
||||||
|
"right" => ["ident" => "Verwalter", "resourceClass" => "Struktur\\Verwalter"],
|
||||||
|
"fields" => [],
|
||||||
|
"keys" => []
|
||||||
|
];
|
||||||
|
}
|
||||||
49
lnk/anwesenheiten_einstze/link.php
Normal file
49
lnk/anwesenheiten_einstze/link.php
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<?php #lnk/anwesenheiten_einsätze/class.php
|
||||||
|
|
||||||
|
class Anwesenheiten_EinsätzeLink extends BaseLink
|
||||||
|
{
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Einsätze"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Anwesenheiten_Einsätze (Anwesenheiten, Einsätze) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Einsätze"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Einsatz zu Anwesenheitsliste hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Einsätze"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Anwesenheiten_Einsätze WHERE Anwesenheiten = ? AND Einsätze = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Einsätze"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Einsatz aus Anwesenheitsliste entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/anwesenheiten_einstze/module.json
Normal file
7
lnk/anwesenheiten_einstze/module.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Anwesenheiten_Einsätze",
|
||||||
|
"table" : "Anwesenheiten_Einsätze",
|
||||||
|
"useRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"adminRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
52
lnk/anwesenheiten_personal/link.php
Executable file
52
lnk/anwesenheiten_personal/link.php
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
<?php #lnk/anwesenheiten_personal/class.php
|
||||||
|
|
||||||
|
class Anwesenheiten_PersonalLink extends BaseLink
|
||||||
|
{
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Anwesenheiten_Personal (Anwesenheiten, Personal) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Personal"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal zu Anwesenheitsliste hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Anwesenheiten_Personal WHERE Anwesenheiten = ? AND Personal = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Personal"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal aus Anwesenheitsliste entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/anwesenheiten_personal/module.json
Executable file
7
lnk/anwesenheiten_personal/module.json
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Anwesenheiten_Personal",
|
||||||
|
"table": "Anwesenheiten_Personal",
|
||||||
|
"useRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"adminRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
52
lnk/anwesenheiten_termine/link.php
Normal file
52
lnk/anwesenheiten_termine/link.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php #lnk/anwesenheiten_termine/class.php
|
||||||
|
|
||||||
|
class Anwesenheiten_TermineLink extends BaseLink
|
||||||
|
{
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Termine"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Anwesenheiten_Termine (Anwesenheiten, Termine) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Termine"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Termin zu Anwesenheitsliste hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Anwesenheiten"]==null || $ids["Termine"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Anwesenheiten_Termine WHERE Anwesenheiten = ? AND Termine = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Anwesenheiten"], $ids["Termine"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Termin aus Anwesenheitsliste entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/anwesenheiten_termine/module.json
Normal file
7
lnk/anwesenheiten_termine/module.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Anwesenheiten_Termine",
|
||||||
|
"table": "Anwesenheiten_Termine",
|
||||||
|
"useRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"adminRight": ["DARF_PERSONAL_VERWALTEN", "DARF_EINSÄTZE_VERWALTEN"],
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
88
lnk/dateien/link.php
Executable file
88
lnk/dateien/link.php
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
<?php #lnk/xx_dateien/class.php
|
||||||
|
|
||||||
|
class DateienLink extends BaseLink
|
||||||
|
{
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
if (isset($_FILES["file"])) {
|
||||||
|
$source = $_FILES ["file"]["tmp_name"];
|
||||||
|
$dest_dir = "upl/";
|
||||||
|
$keys = [];
|
||||||
|
$vals = [];
|
||||||
|
// ids should be [%TABLENAME => %ENTRYID, %FIELDNAME => NULL]
|
||||||
|
foreach ($ids as $path => $id) {
|
||||||
|
$dest_dir.= $path."/";
|
||||||
|
$keys[] = $path;
|
||||||
|
$vals[] = $id;
|
||||||
|
}
|
||||||
|
if (!file_exists($dest_dir)) {
|
||||||
|
mkdir($dest_dir, 0770, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$mime = mime_content_type($source);
|
||||||
|
switch ($mime) {
|
||||||
|
case "image/gif": $filename = $vals[0].".gif"; break;
|
||||||
|
case "image/jpeg": $filename = $vals[0].".jpg"; break;
|
||||||
|
case "image/png": $filename = $vals[0].".png"; break;
|
||||||
|
case "image/svg": $filename = $vals[0].".svg"; break;
|
||||||
|
default: $this->man->AddMessage("Unbekannter Dateityp"); return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete existing files
|
||||||
|
foreach (glob($dest_dir.$vals[0].".*") as $existingfile) {
|
||||||
|
unlink($existingfile);
|
||||||
|
}
|
||||||
|
if (move_uploaded_file($source, $dest_dir.$filename)) {
|
||||||
|
if ($stmt = $this->db->prepare("UPDATE `".$keys[0]."` SET `".$keys[1]."` = ? WHERE ID = ?")) {
|
||||||
|
$stmt->bind_param("si", $filename, $vals[0]);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
return 201;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("File could not be moved");
|
||||||
|
return 403;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
$dest_dir = "upl/";
|
||||||
|
$keys = [];
|
||||||
|
$vals = [];
|
||||||
|
// ids should be [%TABLENAME => %ENTRYID, %FIELDNAME => NULL]
|
||||||
|
foreach ($ids as $path => $id) {
|
||||||
|
$dest_dir.= $path."/";
|
||||||
|
$keys[] = $path;
|
||||||
|
$vals[] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete existing files
|
||||||
|
$files = glob($dest_dir.$vals[0].".*");
|
||||||
|
if (empty($files)) {
|
||||||
|
$this->man->AddMessage("No files were found!");
|
||||||
|
}
|
||||||
|
foreach ($files as $existingfile) {
|
||||||
|
unlink($existingfile);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($stmt = $this->db->prepare("UPDATE `".$keys[0]."` SET `".$keys[1]."` = NULL WHERE ID = ?")) {
|
||||||
|
$stmt->bind_param("i", $vals[0]);
|
||||||
|
if ($stmt->execute()) {
|
||||||
|
$this->man->AddMessage("Datei erfolgreich gelöscht!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
}
|
||||||
6
lnk/dateien/module.json
Executable file
6
lnk/dateien/module.json
Executable file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"route": "Dateien",
|
||||||
|
"useRight": "DARF_DATEIEN_VERWALTEN",
|
||||||
|
"adminRight": "DARF_DATEIEN_VERWALTEN",
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
56
lnk/fahrzeuge_einweisungen/link.php
Normal file
56
lnk/fahrzeuge_einweisungen/link.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php #lnk/xx_fahrzeuge_einweisungen/class.php
|
||||||
|
|
||||||
|
class Fahrzeuge_EinweisungenLink extends BaseLink
|
||||||
|
{
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Fahrzeuge"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Fahrzeuge_Einweisungen (Personal, Fahrzeuge) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Personal"], $ids["Fahrzeuge"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Fahrzeugeinweisung hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Fahrzeuge"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Fahrzeuge_Einweisungen WHERE Fahrzeuge = ? AND Personal = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Fahrzeuge"], $ids["Personal"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Fahrzeugeinweisung entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/fahrzeuge_einweisungen/module.json
Normal file
7
lnk/fahrzeuge_einweisungen/module.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Fahrzeuge_Einweisungen",
|
||||||
|
"table": "Fahrzeuge_Einweisungen",
|
||||||
|
"useRight": null,
|
||||||
|
"adminRight": "DARF_FAHRZEUG_EINWEISUNGEN_VERWALTEN",
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
56
lnk/personal_abteilungen/link.php
Normal file
56
lnk/personal_abteilungen/link.php
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
<?php #lnk/xx_personal_abteilungen/class.php
|
||||||
|
|
||||||
|
class Personal_AbteilungenLink extends BaseLink
|
||||||
|
{
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Abteilungen"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Personal_Abteilungen (Personal, Abteilungen) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Personal"], $ids["Abteilungen"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal zu Abteilung hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Abteilungen"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Personal_Abteilungen WHERE Abteilungen = ? AND Personal = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Abteilungen"], $ids["Personal"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal aus Abteilung entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/personal_abteilungen/module.json
Normal file
7
lnk/personal_abteilungen/module.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Personal_Abteilungen",
|
||||||
|
"table": "Personal_Abteilungen",
|
||||||
|
"useRight": null,
|
||||||
|
"adminRight": "DARF_PERSONAL_VERWALTEN",
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
56
lnk/personal_gruppen/link.php
Executable file
56
lnk/personal_gruppen/link.php
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
<?php #lnk/xx_personal_gruppen/class.php
|
||||||
|
|
||||||
|
class Personal_GruppenLink extends BaseLink
|
||||||
|
{
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Gruppen"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Personal_Gruppen (Personal, Gruppen) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Personal"], $ids["Gruppen"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal zu Gruppe hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Gruppen"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Personal_Gruppen WHERE Personal = ? AND Gruppen = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Personal"], $ids["Gruppen"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personal aus Gruppe entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/personal_gruppen/module.json
Executable file
7
lnk/personal_gruppen/module.json
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Personal_Gruppen",
|
||||||
|
"table": "Personal_Gruppen",
|
||||||
|
"useRight": null,
|
||||||
|
"adminRight": "DARF_PERSONAL_VERWALTEN",
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
56
lnk/personal_lehrgaenge/link.php
Executable file
56
lnk/personal_lehrgaenge/link.php
Executable file
@ -0,0 +1,56 @@
|
|||||||
|
<?php #lnk/xx_personal_lehrgänge/class.php
|
||||||
|
|
||||||
|
class Personal_LehrgängeLink extends BaseLink
|
||||||
|
{
|
||||||
|
public function __construct($database, $keyvaluestore, $manager, $info, $page) {
|
||||||
|
parent::__construct($database, $keyvaluestore, $manager, $info, $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function insert($ids) {
|
||||||
|
if ($ids["Lehrgänge"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "INSERT INTO Personal_Lehrgänge (Personal, Lehrgänge) VALUES (?, ?)";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Personal"], $ids["Lehrgänge"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personalausbildung hinzugefügt!");
|
||||||
|
return 201;
|
||||||
|
} else {
|
||||||
|
return 200;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
}
|
||||||
|
return 500; // Should not reach this stage
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function remove($ids) {
|
||||||
|
if ($ids["Lehrgänge"]==null || $ids["Personal"]==null) {
|
||||||
|
return 400;
|
||||||
|
}
|
||||||
|
if (!$this->man->user->HasRight($this->adminRight)) {
|
||||||
|
return 403; // You shall not pass!
|
||||||
|
}
|
||||||
|
|
||||||
|
$qry = "DELETE FROM Personal_Lehrgänge WHERE Lehrgänge = ? AND Personal = ?";
|
||||||
|
if ($stmt = $this->db->prepare($qry)) {
|
||||||
|
$stmt->bind_param("ii", $ids["Lehrgänge"], $ids["Personal"]);
|
||||||
|
$stmt->execute();
|
||||||
|
if (1==$stmt->affected_rows) {
|
||||||
|
$this->man->AddMessage("Personalausbildung entfernt!");
|
||||||
|
return 200;
|
||||||
|
} else {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$this->man->AddMessage("Mysql error: ".$this->db->error);
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
lnk/personal_lehrgaenge/module.json
Executable file
7
lnk/personal_lehrgaenge/module.json
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"route": "Personal_Lehrgänge",
|
||||||
|
"table": "Personal_Lehrgänge",
|
||||||
|
"useRight": null,
|
||||||
|
"adminRight": "DARF_PERSONAL_VERWALTEN",
|
||||||
|
"isPage": false
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user