Merge BusinessAppTemplate
This commit is contained in:
commit
d8613f0baa
41
action.php
41
action.php
@ -8,7 +8,6 @@
|
||||
* Make things happen when buttons are pressed and forms submitted.
|
||||
*/
|
||||
require_once __DIR__ . "/required.php";
|
||||
require_once __DIR__ . "/lib/userinfo.php";
|
||||
|
||||
if ($VARS['action'] !== "signout") {
|
||||
dieifnotloggedin();
|
||||
@ -39,7 +38,7 @@ switch ($VARS['action']) {
|
||||
global $VARS, $binstack, $error, $oktx;
|
||||
|
||||
if (empty($VARS['items'])) {
|
||||
$error = lang("no items", false);
|
||||
$error = $Strings->get("no items", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -56,7 +55,7 @@ switch ($VARS['action']) {
|
||||
$txid = $VARS['txid'];
|
||||
$cashid = $database->get('transactions', 'cashid', ['txid' => $txid]);
|
||||
if (!$database->has('cash_drawer', ['AND' => ['cashid' => $cashid, 'close' => null]])) {
|
||||
$error = lang("cash already closed", false);
|
||||
$error = $Strings->get("cash already closed", false);
|
||||
return false;
|
||||
}
|
||||
// Nuke the payments to make room for their replacements
|
||||
@ -72,15 +71,15 @@ switch ($VARS['action']) {
|
||||
}
|
||||
|
||||
if ($customer != "" && !$database->has('customers', ['customerid' => $customer])) {
|
||||
$error = lang("invalid customer", false);
|
||||
$error = $Strings->get("invalid customer", false);
|
||||
return false;
|
||||
}
|
||||
if ($register != "" && !$database->has('registers', ['registerid' => $register])) {
|
||||
$error = lang("invalid register", false);
|
||||
$error = $Strings->get("invalid register", false);
|
||||
return false;
|
||||
}
|
||||
if ($register != "" && !$database->has('cash_drawer', ['AND' => ['registerid' => $register, 'close' => null]])) {
|
||||
$error = lang("cash not open", false);
|
||||
$error = $Strings->get("cash not open", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -94,19 +93,19 @@ switch ($VARS['action']) {
|
||||
foreach ($items as $i) {
|
||||
$totalcharge += $i['each'] * $i['qty'];
|
||||
if (!$binstack->has('items', ['itemid' => $i['id']])) {
|
||||
$error = lang("invalid item", false);
|
||||
$error = $Strings->get("invalid item", false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach ($payments as $p) {
|
||||
if (!$database->has('payment_types', ['typename' => $p['type']])) {
|
||||
$error = lang("invalid payment type", false);
|
||||
$error = $Strings->get("invalid payment type", false);
|
||||
return false;
|
||||
}
|
||||
$totalpaid += $p['amount'];
|
||||
if ($p['type'] == "giftcard") {
|
||||
if (!$database->has('certificates', ['AND' => ['amount[>=]' => $p['amount'], 'deleted[!]' => 1, 'certcode' => $p['code']]])) {
|
||||
$error = lang("invalid giftcard", false);
|
||||
$error = $Strings->get("invalid giftcard", false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -120,7 +119,7 @@ switch ($VARS['action']) {
|
||||
}
|
||||
|
||||
if ($totalcharge > $totalpaid) {
|
||||
$error = lang("insufficient payment", false);
|
||||
$error = $Strings->get("insufficient payment", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -225,15 +224,15 @@ switch ($VARS['action']) {
|
||||
$cashid = null;
|
||||
|
||||
if ($customer != "" && !$database->has('customers', ['customerid' => $customer])) {
|
||||
$error = lang("invalid customer", false);
|
||||
$error = $Strings->get("invalid customer", false);
|
||||
return false;
|
||||
}
|
||||
if ($register != "" && !$database->has('registers', ['registerid' => $register])) {
|
||||
$error = lang("invalid register", false);
|
||||
$error = $Strings->get("invalid register", false);
|
||||
return false;
|
||||
}
|
||||
if ($register != "" && !$database->has('cash_drawer', ['AND' => ['registerid' => $register, 'close' => null]])) {
|
||||
$error = lang("cash not open", false);
|
||||
$error = $Strings->get("cash not open", false);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -246,19 +245,19 @@ switch ($VARS['action']) {
|
||||
foreach ($items as $i) {
|
||||
$totaldue += $i['each'] * $i['qty'];
|
||||
if (!$binstack->has('items', ['itemid' => $i['id']])) {
|
||||
$error = lang("invalid item", false);
|
||||
$error = $Strings->get("invalid item", false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach ($payments as $p) {
|
||||
if (!$database->has('payment_types', ['typename' => $p['type']])) {
|
||||
$error = lang("invalid payment type", false);
|
||||
$error = $Strings->get("invalid payment type", false);
|
||||
return false;
|
||||
}
|
||||
$totalrefund += $p['amount'];
|
||||
if ($p['type'] == "giftcard") {
|
||||
if (!$database->has('certificates', ['AND' => ['amount[>=]' => $p['amount'], 'deleted[!]' => 1, 'certcode' => $p['code']]])) {
|
||||
$error = lang("invalid giftcard", false);
|
||||
$error = $Strings->get("invalid giftcard", false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -319,7 +318,7 @@ switch ($VARS['action']) {
|
||||
$txid = $VARS['txid'];
|
||||
$cashid = $database->get('transactions', 'cashid', ['txid' => $txid]);
|
||||
if (!$database->has('cash_drawer', ['AND' => ['cashid' => $cashid, 'close' => null]])) {
|
||||
$error = lang("cash already closed", false);
|
||||
$error = $Strings->get("cash already closed", false);
|
||||
}
|
||||
|
||||
$database->action(function ($database) {
|
||||
@ -350,7 +349,7 @@ switch ($VARS['action']) {
|
||||
$database->delete('transactions', ['txid' => $txid, 'LIMIT' => 1]);
|
||||
});
|
||||
} else {
|
||||
$error = lang("invalid parameters", false);
|
||||
$error = $Strings->get("invalid parameters", false);
|
||||
}
|
||||
if (!is_null($error)) {
|
||||
exit(json_encode(["status" => "ERROR", "message" => $error]));
|
||||
@ -429,10 +428,10 @@ switch ($VARS['action']) {
|
||||
$transactions[$i]['editable'] = false;
|
||||
}
|
||||
if (!is_null($transactions[$i]['cashierid'])) {
|
||||
$cashier = getUserByID($transactions[$i]['cashierid']);
|
||||
$cashier = new User($transactions[$i]['cashierid']);
|
||||
$transactions[$i]['cashier'] = [
|
||||
"name" => $cashier['name'],
|
||||
"username" => $cashier['username']
|
||||
"name" => $cashier->getName(),
|
||||
"username" => $cashier->getUsername()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
6
api.php
6
api.php
@ -12,17 +12,15 @@
|
||||
* user passwords.
|
||||
*/
|
||||
require __DIR__ . '/required.php';
|
||||
require_once __DIR__ . '/lib/login.php';
|
||||
require_once __DIR__ . '/lib/userinfo.php';
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$username = $VARS['username'];
|
||||
$password = $VARS['password'];
|
||||
if (user_exists($username) !== true || authenticate_user($username, $password, $errmsg) !== true) {
|
||||
$user = User::byUsername($username);
|
||||
if ($user->exists() !== true || Login::auth($username, $password) !== Login::LOGIN_OK) {
|
||||
header("HTTP/1.1 403 Unauthorized");
|
||||
die("\"403 Unauthorized\"");
|
||||
}
|
||||
$userinfo = getUserByUsername($username);
|
||||
|
||||
// query max results
|
||||
$max = 20;
|
||||
|
16
app.php
16
app.php
@ -28,10 +28,10 @@ header("Link: <static/fonts/Roboto.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/app.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/fa-svg-with-js.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/svg-with-js.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/js/fontawesome-all.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", false);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -47,7 +47,7 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="static/css/material-color/material-color.min.css" rel="stylesheet">
|
||||
<link href="static/css/app.css" rel="stylesheet">
|
||||
<link href="static/css/fa-svg-with-js.css" rel="stylesheet">
|
||||
<link href="static/css/svg-with-js.min.css" rel="stylesheet">
|
||||
<script nonce="<?php echo $SECURE_NONCE; ?>">
|
||||
FontAwesomeConfig = {autoAddCss: false}
|
||||
</script>
|
||||
@ -69,9 +69,9 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
if (isset($_GET['msg']) && !is_empty($_GET['msg']) && array_key_exists($_GET['msg'], MESSAGES)) {
|
||||
// optional string generation argument
|
||||
if (!isset($_GET['arg']) || is_empty($_GET['arg'])) {
|
||||
$alertmsg = lang(MESSAGES[$_GET['msg']]['string'], false);
|
||||
$alertmsg = $Strings->get(MESSAGES[$_GET['msg']]['string'], false);
|
||||
} else {
|
||||
$alertmsg = lang2(MESSAGES[$_GET['msg']]['string'], ["arg" => strip_tags($_GET['arg'])], false);
|
||||
$alertmsg = $Strings->build(MESSAGES[$_GET['msg']]['string'], ["arg" => strip_tags($_GET['arg'])], false);
|
||||
}
|
||||
$alerttype = MESSAGES[$_GET['msg']]['type'];
|
||||
$alerticon = "square-o";
|
||||
@ -146,7 +146,7 @@ END;
|
||||
if (isset($pg['icon'])) {
|
||||
?><i class="<?php echo $pg['icon']; ?> fa-fw"></i> <?php
|
||||
}
|
||||
lang($pg['title']);
|
||||
$Strings->get($pg['title']);
|
||||
?>
|
||||
</a>
|
||||
</span>
|
||||
@ -163,7 +163,7 @@ END;
|
||||
</span>
|
||||
<span class="nav-item mr-auto py-<?php echo $navbar_breakpoint; ?>-0">
|
||||
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="action.php?action=signout">
|
||||
<i class="fas fa-sign-out-alt fa-fw"></i><span> <?php lang("sign out") ?></span>
|
||||
<i class="fas fa-sign-out-alt fa-fw"></i><span> <?php $Strings->get("sign out") ?></span>
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
@ -182,7 +182,7 @@ END;
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="static/js/bootstrap.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="static/js/app.js"></script>
|
||||
<?php
|
||||
// custom page scripts
|
||||
|
419
composer.lock
generated
419
composer.lock
generated
@ -1,419 +0,0 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "0e5db12408080dd084cad072b8cfd599",
|
||||
"content-hash": "348006dfc1d25121fcc3b4cb32bc3369",
|
||||
"packages": [
|
||||
{
|
||||
"name": "catfan/medoo",
|
||||
"version": "v1.5.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/catfan/Medoo.git",
|
||||
"reference": "1aa25a4001e0cfb739ba2996f00f4a3d2a7fdf07"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/catfan/Medoo/zipball/1aa25a4001e0cfb739ba2996f00f4a3d2a7fdf07",
|
||||
"reference": "1aa25a4001e0cfb739ba2996f00f4a3d2a7fdf07",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=5.4"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-pdo_dblib": "For MSSQL or Sybase database on Linux/UNIX platform",
|
||||
"ext-pdo_mysql": "For MySQL or MariaDB database",
|
||||
"ext-pdo_oci": "For Oracle database",
|
||||
"ext-pdo_oci8": "For Oracle version 8 database",
|
||||
"ext-pdo_pqsql": "For PostgreSQL database",
|
||||
"ext-pdo_sqlite": "For SQLite database",
|
||||
"ext-pdo_sqlsrv": "For MSSQL database"
|
||||
},
|
||||
"type": "framework",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Medoo\\": "/src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Angel Lai",
|
||||
"email": "angel@catfan.me"
|
||||
}
|
||||
],
|
||||
"description": "The lightest PHP database framework to accelerate development",
|
||||
"homepage": "https://medoo.in",
|
||||
"keywords": [
|
||||
"database",
|
||||
"lightweight",
|
||||
"mariadb",
|
||||
"mssql",
|
||||
"mysql",
|
||||
"oracle",
|
||||
"php framework",
|
||||
"postgresql",
|
||||
"sql",
|
||||
"sqlite"
|
||||
],
|
||||
"time": "2017-12-25 17:02:41"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "6.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/f4db5a78a5ea468d4831de7f0bf9d9415e348699",
|
||||
"reference": "f4db5a78a5ea468d4831de7f0bf9d9415e348699",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"guzzlehttp/promises": "^1.0",
|
||||
"guzzlehttp/psr7": "^1.4",
|
||||
"php": ">=5.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"phpunit/phpunit": "^4.0 || ^5.0",
|
||||
"psr/log": "^1.0"
|
||||
},
|
||||
"suggest": {
|
||||
"psr/log": "Required for using the Log middleware"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "6.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle is a PHP HTTP client library",
|
||||
"homepage": "http://guzzlephp.org/",
|
||||
"keywords": [
|
||||
"client",
|
||||
"curl",
|
||||
"framework",
|
||||
"http",
|
||||
"http client",
|
||||
"rest",
|
||||
"web service"
|
||||
],
|
||||
"time": "2017-06-22 18:50:49"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
"version": "v1.3.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/promises.git",
|
||||
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/promises/zipball/a59da6cf61d80060647ff4d3eb2c03a2bc694646",
|
||||
"reference": "a59da6cf61d80060647ff4d3eb2c03a2bc694646",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Promise\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
}
|
||||
],
|
||||
"description": "Guzzle promises library",
|
||||
"keywords": [
|
||||
"promise"
|
||||
],
|
||||
"time": "2016-12-20 10:07:11"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "1.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||
"reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"psr/http-message": "~1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/http-message-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.4-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"GuzzleHttp\\Psr7\\": "src/"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Michael Dowling",
|
||||
"email": "mtdowling@gmail.com",
|
||||
"homepage": "https://github.com/mtdowling"
|
||||
},
|
||||
{
|
||||
"name": "Tobias Schultze",
|
||||
"homepage": "https://github.com/Tobion"
|
||||
}
|
||||
],
|
||||
"description": "PSR-7 message implementation that also provides common utility methods",
|
||||
"keywords": [
|
||||
"http",
|
||||
"message",
|
||||
"request",
|
||||
"response",
|
||||
"stream",
|
||||
"uri",
|
||||
"url"
|
||||
],
|
||||
"time": "2017-03-20 17:10:46"
|
||||
},
|
||||
{
|
||||
"name": "lapinator/ods-php-generator",
|
||||
"version": "v0.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/Lapinator/odsPhpGenerator.git",
|
||||
"reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/Lapinator/odsPhpGenerator/zipball/575314c003c2ec3032813bedcc1d27032b7b7ab2",
|
||||
"reference": "575314c003c2ec3032813bedcc1d27032b7b7ab2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"src/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"LGPL-3.0"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Laurent VUIBERT",
|
||||
"email": "lapinator@gmx.fr",
|
||||
"homepage": "http://lapinator.net",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Open Document Spreadsheet (.ods) generator ",
|
||||
"homepage": "https://odsphpgenerator.lapinator.net/",
|
||||
"keywords": [
|
||||
"LibreOffice",
|
||||
"ods"
|
||||
],
|
||||
"time": "2016-04-14 21:51:27"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
"version": "9.1.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/csv.git",
|
||||
"reference": "9c8ad06fb5d747c149875beb6133566c00eaa481"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/9c8ad06fb5d747c149875beb6133566c00eaa481",
|
||||
"reference": "9c8ad06fb5d747c149875beb6133566c00eaa481",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=7.0.10"
|
||||
},
|
||||
"require-dev": {
|
||||
"ext-curl": "*",
|
||||
"friendsofphp/php-cs-fixer": "^2.0",
|
||||
"phpstan/phpstan": "^0.9.2",
|
||||
"phpstan/phpstan-phpunit": "^0.9.4",
|
||||
"phpstan/phpstan-strict-rules": "^0.9.0",
|
||||
"phpunit/phpunit": "^6.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "9.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Csv\\": "src"
|
||||
},
|
||||
"files": [
|
||||
"src/functions_include.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Ignace Nyamagana Butera",
|
||||
"email": "nyamsprod@gmail.com",
|
||||
"homepage": "https://github.com/nyamsprod/",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Csv data manipulation made easy in PHP",
|
||||
"homepage": "http://csv.thephpleague.com",
|
||||
"keywords": [
|
||||
"csv",
|
||||
"export",
|
||||
"filter",
|
||||
"import",
|
||||
"read",
|
||||
"write"
|
||||
],
|
||||
"time": "2018-05-01 18:32:48"
|
||||
},
|
||||
{
|
||||
"name": "psr/http-message",
|
||||
"version": "1.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/php-fig/http-message.git",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"reference": "f6561bf28d520154e4b0ec72be95418abe6d9363",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.0.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Psr\\Http\\Message\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "PHP-FIG",
|
||||
"homepage": "http://www.php-fig.org/"
|
||||
}
|
||||
],
|
||||
"description": "Common interface for HTTP messages",
|
||||
"homepage": "https://github.com/php-fig/http-message",
|
||||
"keywords": [
|
||||
"http",
|
||||
"http-message",
|
||||
"psr",
|
||||
"psr-7",
|
||||
"request",
|
||||
"response"
|
||||
],
|
||||
"time": "2016-08-06 14:39:51"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": [],
|
||||
"platform-dev": []
|
||||
}
|
95
index.php
95
index.php
@ -5,87 +5,98 @@
|
||||
|
||||
require_once __DIR__ . "/required.php";
|
||||
|
||||
require_once __DIR__ . "/lib/login.php";
|
||||
|
||||
// if we're logged in, we don't need to be here.
|
||||
if (!empty($_SESSION['loggedin']) && $_SESSION['loggedin'] === true && !isset($_GET['permissionerror'])) {
|
||||
header('Location: app.php');
|
||||
}
|
||||
|
||||
if (isset($_GET['permissionerror'])) {
|
||||
$alert = lang("no access permission", false);
|
||||
$alert = $Strings->get("no access permission", false);
|
||||
}
|
||||
|
||||
/* Authenticate user */
|
||||
$userpass_ok = false;
|
||||
$multiauth = false;
|
||||
if (checkLoginServer()) {
|
||||
if (!empty($VARS['progress']) && $VARS['progress'] == "1") {
|
||||
if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && verifyCaptcheck($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
|
||||
$errmsg = "";
|
||||
if (authenticate_user($VARS['username'], $VARS['password'], $errmsg)) {
|
||||
switch (get_account_status($VARS['username'])) {
|
||||
if (Login::checkLoginServer()) {
|
||||
if (empty($VARS['progress'])) {
|
||||
// Easy way to remove "undefined" warnings.
|
||||
} else if ($VARS['progress'] == "1") {
|
||||
if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
|
||||
$autherror = "";
|
||||
$user = User::byUsername($VARS['username']);
|
||||
if ($user->exists()) {
|
||||
$status = $user->getStatus()->getString();
|
||||
switch ($status) {
|
||||
case "LOCKED_OR_DISABLED":
|
||||
$alert = lang("account locked", false);
|
||||
$alert = $Strings->get("account locked", false);
|
||||
break;
|
||||
case "TERMINATED":
|
||||
$alert = lang("account terminated", false);
|
||||
$alert = $Strings->get("account terminated", false);
|
||||
break;
|
||||
case "CHANGE_PASSWORD":
|
||||
$alert = lang("password expired", false);
|
||||
$alert = $Strings->get("password expired", false);
|
||||
break;
|
||||
case "NORMAL":
|
||||
$userpass_ok = true;
|
||||
$username_ok = true;
|
||||
break;
|
||||
case "ALERT_ON_ACCESS":
|
||||
sendLoginAlertEmail($VARS['username']);
|
||||
$userpass_ok = true;
|
||||
$mail_resp = $user->sendAlertEmail();
|
||||
if (DEBUG) {
|
||||
var_dump($mail_resp);
|
||||
}
|
||||
$username_ok = true;
|
||||
break;
|
||||
default:
|
||||
if (!is_empty($error)) {
|
||||
$alert = $error;
|
||||
} else {
|
||||
$alert = $Strings->get("login error", false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($userpass_ok) {
|
||||
if ($username_ok) {
|
||||
if ($user->checkPassword($VARS['password'])) {
|
||||
$_SESSION['passok'] = true; // stop logins using only username and authcode
|
||||
if (userHasTOTP($VARS['username'])) {
|
||||
if ($user->has2fa()) {
|
||||
$multiauth = true;
|
||||
} else {
|
||||
doLoginUser($VARS['username'], $VARS['password']);
|
||||
Session::start($user);
|
||||
header('Location: app.php');
|
||||
die("Logged in, go to app.php");
|
||||
}
|
||||
} else {
|
||||
$alert = $Strings->get("login incorrect", false);
|
||||
}
|
||||
}
|
||||
} else { // User does not exist anywhere
|
||||
$alert = $Strings->get("login incorrect", false);
|
||||
}
|
||||
} else {
|
||||
if (!is_empty($errmsg)) {
|
||||
$alert = lang2("login server error", ['arg' => $errmsg], false);
|
||||
} else {
|
||||
$alert = lang("login incorrect", false);
|
||||
$alert = $Strings->get("captcha error", false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$alert = lang("captcha error", false);
|
||||
}
|
||||
} else if (!empty($VARS['progress']) && $VARS['progress'] == "2") {
|
||||
} else if ($VARS['progress'] == "2") {
|
||||
$user = User::byUsername($VARS['username']);
|
||||
if ($_SESSION['passok'] !== true) {
|
||||
// stop logins using only username and authcode
|
||||
sendError("Password integrity check failed!");
|
||||
}
|
||||
if (verifyTOTP($VARS['username'], $VARS['authcode'])) {
|
||||
if (doLoginUser($VARS['username'])) {
|
||||
if ($user->check2fa($VARS['authcode'])) {
|
||||
Session::start($user);
|
||||
header('Location: app.php');
|
||||
die("Logged in, go to app.php");
|
||||
} else {
|
||||
$alert = lang("login server user data error", false);
|
||||
}
|
||||
} else {
|
||||
$alert = lang("2fa incorrect", false);
|
||||
$alert = $Strings->get("2fa incorrect", false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$alert = lang("login server unavailable", false);
|
||||
$alert = $Strings->get("login server unavailable", false);
|
||||
}
|
||||
header("Link: <static/fonts/Roboto.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/index.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", false);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@ -114,7 +125,7 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-11 col-xs-11 col-sm-8 col-md-6 col-lg-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php lang("sign in"); ?></h5>
|
||||
<h5 class="card-title"><?php $Strings->get("sign in"); ?></h5>
|
||||
<form action="" method="POST">
|
||||
<?php
|
||||
if (!empty($alert)) {
|
||||
@ -127,8 +138,8 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
|
||||
if ($multiauth != true) {
|
||||
?>
|
||||
<input type="text" class="form-control" name="username" placeholder="<?php lang("username"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="password" class="form-control" name="password" placeholder="<?php lang("password"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /><br />
|
||||
<input type="text" class="form-control" name="username" placeholder="<?php $Strings->get("username"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="password" class="form-control" name="password" placeholder="<?php $Strings->get("password"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /><br />
|
||||
<?php if (CAPTCHA_ENABLED) { ?>
|
||||
<div class="captcheck_container" data-stylenonce="<?php echo $SECURE_NONCE; ?>"></div>
|
||||
<br />
|
||||
@ -138,16 +149,16 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
} else if ($multiauth) {
|
||||
?>
|
||||
<div class="alert alert-info">
|
||||
<?php lang("2fa prompt"); ?>
|
||||
<?php $Strings->get("2fa prompt"); ?>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="authcode" placeholder="<?php lang("authcode"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="text" class="form-control" name="authcode" placeholder="<?php $Strings->get("authcode"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="hidden" name="progress" value="2" />
|
||||
<input type="hidden" name="username" value="<?php echo $VARS['username']; ?>" />
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<?php lang("continue"); ?>
|
||||
<?php $Strings->get("continue"); ?>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
@ -159,6 +170,6 @@ header("Link: <static/js/bootstrap.min.js>; rel=preload; as=script", false);
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="static/js/bootstrap.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
144
lang/en_us.php
144
lang/en_us.php
@ -1,144 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
define("STRINGS", [
|
||||
"sign in" => "Sign In",
|
||||
"username" => "Username",
|
||||
"password" => "Password",
|
||||
"continue" => "Continue",
|
||||
"authcode" => "Authentication code",
|
||||
"2fa prompt" => "Enter the six-digit code from your mobile authenticator app.",
|
||||
"2fa incorrect" => "Authentication code incorrect.",
|
||||
"login incorrect" => "Login incorrect.",
|
||||
"login server unavailable" => "Login server unavailable. Try again later or contact technical support.",
|
||||
"account locked" => "This account has been disabled. Contact technical support.",
|
||||
"password expired" => "You must change your password before continuing.",
|
||||
"account terminated" => "Account terminated. Access denied.",
|
||||
"account state error" => "Your account state is not stable. Log out, restart your browser, and try again.",
|
||||
"welcome user" => "Welcome, {user}!",
|
||||
"sign out" => "Sign out",
|
||||
"settings" => "Settings",
|
||||
"options" => "Options",
|
||||
"404 error" => "404 Error",
|
||||
"page not found" => "Page not found.",
|
||||
"invalid parameters" => "Invalid request parameters.",
|
||||
"login server error" => "The login server returned an error: {arg}",
|
||||
"login server user data error" => "The login server refused to provide account information. Try again or contact technical support.",
|
||||
"captcha error" => "There was a problem with the CAPTCHA (robot test). Try again.",
|
||||
"no access permission" => "You do not have permission to access this system.",
|
||||
"home" => "Home",
|
||||
"point of sale" => "Point of Sale",
|
||||
"barcode" => "Barcode",
|
||||
"barcode or search" => "Barcode or Search",
|
||||
"cash" => "Cash",
|
||||
"check" => "Check",
|
||||
"card" => "Card",
|
||||
"crypto" => "Crypto",
|
||||
"gift card" => "Gift Card",
|
||||
"free" => "Free",
|
||||
"paid" => "Paid",
|
||||
"owed" => "Owed",
|
||||
"change" => "Change",
|
||||
"enter payment" => "Enter Payment",
|
||||
"receipt" => "Receipt",
|
||||
"close" => "Close",
|
||||
"print" => "Print",
|
||||
"customer" => "Customer",
|
||||
"customer search" => "Search customers",
|
||||
"new sale" => "New Sale",
|
||||
"customers" => "Customers",
|
||||
"actions" => "Actions",
|
||||
"name" => "Name",
|
||||
"phone" => "Phone",
|
||||
"email" => "Email",
|
||||
"address" => "Address",
|
||||
"notes" => "Notes",
|
||||
"edit" => "Edit",
|
||||
"new customer" => "New Customer",
|
||||
"adding customer" => "Adding Customer",
|
||||
"editing customer" => "Editing {name}",
|
||||
"save" => "Save",
|
||||
"customer saved" => "Customer saved.",
|
||||
"invalid customer id" => "Invalid customer ID",
|
||||
"customer pricing" => "Customer Pricing",
|
||||
"item" => "Item",
|
||||
"cost" => "Cost",
|
||||
"normal price" => "Normal Price",
|
||||
"customer price" => "Customer Price",
|
||||
"add price" => "Add Price",
|
||||
"add customer price" => "Add Customer Price",
|
||||
"delete" => "Delete",
|
||||
"cancel" => "Cancel",
|
||||
"price" => "Price",
|
||||
"finish" => "Finish",
|
||||
"registers" => "Registers",
|
||||
"add register" => "Add Register",
|
||||
"balance" => "Balance",
|
||||
"opened" => "Opened",
|
||||
"closed" => "Closed",
|
||||
"never" => "Never",
|
||||
"last opened" => "Last Opened",
|
||||
"still open" => "Still Open",
|
||||
"open" => "Open",
|
||||
"no cash" => "No cash",
|
||||
"choose register" => "Choose a cash register",
|
||||
"cash not open" => "Cash not open. Go to Registers to open it.",
|
||||
"cash opened" => "Cash opened.",
|
||||
"cash closed" => "Cash closed.",
|
||||
"register set" => "Register set.",
|
||||
"change register" => "Change register",
|
||||
"reports" => "Reports",
|
||||
"report type" => "Report Type",
|
||||
"format" => "Format",
|
||||
"filter" => "Filter",
|
||||
"generate report" => "Generate Report",
|
||||
"cashflow" => "Cash Flow",
|
||||
"z report" => "Z Report",
|
||||
"csv file" => "CSV text file",
|
||||
"ods file" => "ODS spreadsheet",
|
||||
"html file" => "HTML web page",
|
||||
"register" => "Register",
|
||||
"all" => "All",
|
||||
"date range" => "Date Range",
|
||||
"start" => "Start",
|
||||
"end" => "End",
|
||||
"grid view" => "Grid view",
|
||||
"edit register" => "Edit Register",
|
||||
"editing register" => "Editing register {name}",
|
||||
"adding register" => "Adding register",
|
||||
"register saved" => "Register saved.",
|
||||
"register name taken" => "Register name already taken. Use a different name.",
|
||||
"no open registers" => "No open cash registers. Go to the Registers page to open one.",
|
||||
"register management" => "Register Management",
|
||||
"manage register" => "Manage register",
|
||||
"manage" => "Manage",
|
||||
"x report" => "X Report",
|
||||
"z report" => "Z Report",
|
||||
"pick cash" => "Choose",
|
||||
"cash already closed" => "Cash already closed, cannot edit this transaction. Process a return instead.",
|
||||
"update" => "Update",
|
||||
"transaction search" => "Search transactions",
|
||||
"return" => "Return",
|
||||
"enter refund" => "Enter Refund",
|
||||
"refund" => "Refund",
|
||||
"cannot edit return transaction" => "Cannot edit a return transaction.",
|
||||
"gift cards" => "Gift Cards",
|
||||
"add card" => "Add Card",
|
||||
"card number" => "Card Number",
|
||||
"start balance" => "Starting Balance",
|
||||
"issued" => "Issued",
|
||||
"editing card x" => "Editing card {code}",
|
||||
"adding card" => "Adding card",
|
||||
"card added" => "Gift card added.",
|
||||
"card saved" => "Gift card updated.",
|
||||
"card x added" => "Gift card #{arg} added.",
|
||||
"card x saved" => "Gift card #{arg} updated.",
|
||||
"open drawer" => "Open Drawer",
|
||||
"no items" => "No items in transaction.",
|
||||
"delete transaction" => "Delete transaction",
|
||||
"transaction discount" => "Transaction discount",
|
||||
"Online Sales" => "Online Sales",
|
||||
]);
|
26
langs/en/core.json
Normal file
26
langs/en/core.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"sign in": "Sign In",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"continue": "Continue",
|
||||
"authcode": "Authentication code",
|
||||
"2fa prompt": "Enter the six-digit code from your mobile authenticator app.",
|
||||
"2fa incorrect": "Authentication code incorrect.",
|
||||
"login incorrect": "Login incorrect.",
|
||||
"login server unavailable": "Login server unavailable. Try again later or contact technical support.",
|
||||
"account locked": "This account has been disabled. Contact technical support.",
|
||||
"password expired": "You must change your password before continuing.",
|
||||
"account terminated": "Account terminated. Access denied.",
|
||||
"account state error": "Your account state is not stable. Log out, restart your browser, and try again.",
|
||||
"welcome user": "Welcome, {user}!",
|
||||
"sign out": "Sign out",
|
||||
"settings": "Settings",
|
||||
"options": "Options",
|
||||
"404 error": "404 Error",
|
||||
"page not found": "Page not found.",
|
||||
"invalid parameters": "Invalid request parameters.",
|
||||
"login server error": "The login server returned an error: {arg}",
|
||||
"login server user data error": "The login server refused to provide account information. Try again or contact technical support.",
|
||||
"captcha error": "There was a problem with the CAPTCHA (robot test). Try again.",
|
||||
"no access permission": "You do not have permission to access this system."
|
||||
}
|
112
langs/en/strings.json
Normal file
112
langs/en/strings.json
Normal file
@ -0,0 +1,112 @@
|
||||
{
|
||||
"point of sale": "Point of Sale",
|
||||
"barcode": "Barcode",
|
||||
"barcode or search": "Barcode or Search",
|
||||
"cash": "Cash",
|
||||
"check": "Check",
|
||||
"card": "Card",
|
||||
"crypto": "Crypto",
|
||||
"gift card": "Gift Card",
|
||||
"free": "Free",
|
||||
"paid": "Paid",
|
||||
"owed": "Owed",
|
||||
"change": "Change",
|
||||
"enter payment": "Enter Payment",
|
||||
"receipt": "Receipt",
|
||||
"close": "Close",
|
||||
"print": "Print",
|
||||
"customer": "Customer",
|
||||
"customer search": "Search customers",
|
||||
"new sale": "New Sale",
|
||||
"customers": "Customers",
|
||||
"actions": "Actions",
|
||||
"name": "Name",
|
||||
"phone": "Phone",
|
||||
"email": "Email",
|
||||
"address": "Address",
|
||||
"notes": "Notes",
|
||||
"edit": "Edit",
|
||||
"new customer": "New Customer",
|
||||
"adding customer": "Adding Customer",
|
||||
"editing customer": "Editing {name}",
|
||||
"save": "Save",
|
||||
"customer saved": "Customer saved.",
|
||||
"invalid customer id": "Invalid customer ID",
|
||||
"customer pricing": "Customer Pricing",
|
||||
"item": "Item",
|
||||
"cost": "Cost",
|
||||
"normal price": "Normal Price",
|
||||
"customer price": "Customer Price",
|
||||
"add price": "Add Price",
|
||||
"add customer price": "Add Customer Price",
|
||||
"delete": "Delete",
|
||||
"cancel": "Cancel",
|
||||
"price": "Price",
|
||||
"finish": "Finish",
|
||||
"registers": "Registers",
|
||||
"add register": "Add Register",
|
||||
"balance": "Balance",
|
||||
"opened": "Opened",
|
||||
"closed": "Closed",
|
||||
"never": "Never",
|
||||
"last opened": "Last Opened",
|
||||
"still open": "Still Open",
|
||||
"open": "Open",
|
||||
"no cash": "No cash",
|
||||
"choose register": "Choose a cash register",
|
||||
"cash not open": "Cash not open. Go to Registers to open it.",
|
||||
"cash opened": "Cash opened.",
|
||||
"cash closed": "Cash closed.",
|
||||
"register set": "Register set.",
|
||||
"change register": "Change register",
|
||||
"reports": "Reports",
|
||||
"report type": "Report Type",
|
||||
"format": "Format",
|
||||
"filter": "Filter",
|
||||
"generate report": "Generate Report",
|
||||
"cashflow": "Cash Flow",
|
||||
"z report": "Z Report",
|
||||
"csv file": "CSV text file",
|
||||
"ods file": "ODS spreadsheet",
|
||||
"html file": "HTML web page",
|
||||
"register": "Register",
|
||||
"all": "All",
|
||||
"date range": "Date Range",
|
||||
"start": "Start",
|
||||
"end": "End",
|
||||
"grid view": "Grid view",
|
||||
"edit register": "Edit Register",
|
||||
"editing register": "Editing register {name}",
|
||||
"adding register": "Adding register",
|
||||
"register saved": "Register saved.",
|
||||
"register name taken": "Register name already taken. Use a different name.",
|
||||
"no open registers": "No open cash registers. Go to the Registers page to open one.",
|
||||
"register management": "Register Management",
|
||||
"manage register": "Manage register",
|
||||
"manage": "Manage",
|
||||
"x report": "X Report",
|
||||
"pick cash": "Choose",
|
||||
"cash already closed": "Cash already closed, cannot edit this transaction. Process a return instead.",
|
||||
"update": "Update",
|
||||
"transaction search": "Search transactions",
|
||||
"return": "Return",
|
||||
"enter refund": "Enter Refund",
|
||||
"refund": "Refund",
|
||||
"cannot edit return transaction": "Cannot edit a return transaction.",
|
||||
"gift cards": "Gift Cards",
|
||||
"add card": "Add Card",
|
||||
"card number": "Card Number",
|
||||
"start balance": "Starting Balance",
|
||||
"issued": "Issued",
|
||||
"editing card x": "Editing card {code}",
|
||||
"adding card": "Adding card",
|
||||
"card added": "Gift card added.",
|
||||
"card saved": "Gift card updated.",
|
||||
"card x added": "Gift card #{arg} added.",
|
||||
"card x saved": "Gift card #{arg} updated.",
|
||||
"open drawer": "Open Drawer",
|
||||
"no items": "No items in transaction.",
|
||||
"delete transaction": "Delete transaction",
|
||||
"transaction discount": "Transaction discount",
|
||||
"Online Sales": "Online Sales"
|
||||
}
|
4
langs/en/titles.json
Normal file
4
langs/en/titles.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"home": "Home",
|
||||
"test": "Test"
|
||||
}
|
13
lib/Exceptions.lib.php
Normal file
13
lib/Exceptions.lib.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class IncorrectPasswordException extends Exception {
|
||||
public function __construct(string $message = "Incorrect password.", int $code = 0, \Throwable $previous = null) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
}
|
@ -6,8 +6,6 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . "/receipts.php";
|
||||
|
||||
class GenerateReceipt {
|
||||
|
||||
const RECEIPT_TYPE_TRANSACTION = 1;
|
||||
@ -51,7 +49,7 @@ class GenerateReceipt {
|
||||
if ($p['amount'] < 0) {
|
||||
continue;
|
||||
}
|
||||
$paymentlines[] = new ReceiptLine(lang($p['text'], false), "", '$' . number_format($p['amount'] * 1.0, 2));
|
||||
$paymentlines[] = new ReceiptLine($Strings->get($p['text'], false), "", '$' . number_format($p['amount'] * 1.0, 2));
|
||||
$paid += $p['amount'] * 1.0;
|
||||
}
|
||||
$change = $paid - $total;
|
||||
@ -124,7 +122,7 @@ class GenerateReceipt {
|
||||
'txid' => $txid
|
||||
]);
|
||||
foreach ($payments as $p) {
|
||||
$paymentlines[] = new ReceiptLine(lang($p['text'], false), "", '$' . number_format($p['amount'] * -1.0, 2));
|
||||
$paymentlines[] = new ReceiptLine($Strings->get($p['text'], false), "", '$' . number_format($p['amount'] * -1.0, 2));
|
||||
$paid += $p['amount'] * 1.0;
|
||||
}
|
||||
|
||||
@ -191,7 +189,7 @@ class GenerateReceipt {
|
||||
$balance[$p['type']] += $p['amount'];
|
||||
}
|
||||
|
||||
$receipt->appendHeader(new ReceiptLine(lang("x report", false), "", "", ReceiptLine::LINEFORMAT_BOLD | ReceiptLine::LINEFORMAT_CENTER));
|
||||
$receipt->appendHeader(new ReceiptLine($Strings->get("x report", false), "", "", ReceiptLine::LINEFORMAT_BOLD | ReceiptLine::LINEFORMAT_CENTER));
|
||||
|
||||
$receipt->appendLine(new ReceiptLine("Printed:", "", date(DATETIME_FORMAT)));
|
||||
$receipt->appendLine(new ReceiptLine("Register:", "", $registername));
|
||||
@ -209,7 +207,7 @@ class GenerateReceipt {
|
||||
$receipt->appendLine(new ReceiptLine("Sales", "", "", ReceiptLine::LINEFORMAT_CENTER));
|
||||
$receipt->appendBreak();
|
||||
foreach ($paymenttypes as $t) {
|
||||
$receipt->appendLine(new ReceiptLine(lang($t['text'], false) . ":", "", '$' . number_format($balance[$t['type']], 2)));
|
||||
$receipt->appendLine(new ReceiptLine($Strings->get($t['text'], false) . ":", "", '$' . number_format($balance[$t['type']], 2)));
|
||||
}
|
||||
|
||||
$receipt->appendBlank();
|
||||
@ -246,7 +244,7 @@ class GenerateReceipt {
|
||||
$balance[$p['type']] += $p['amount'];
|
||||
}
|
||||
|
||||
$receipt->appendHeader(new ReceiptLine(lang("z report", false), "", "", ReceiptLine::LINEFORMAT_BOLD | ReceiptLine::LINEFORMAT_CENTER));
|
||||
$receipt->appendHeader(new ReceiptLine($Strings->get("z report", false), "", "", ReceiptLine::LINEFORMAT_BOLD | ReceiptLine::LINEFORMAT_CENTER));
|
||||
|
||||
$receipt->appendLine(new ReceiptLine("Printed:", "", date(DATETIME_FORMAT)));
|
||||
$receipt->appendLine(new ReceiptLine("Register:", "", $registername));
|
||||
@ -271,7 +269,7 @@ class GenerateReceipt {
|
||||
$receipt->appendLine(new ReceiptLine("Sales", "", "", ReceiptLine::LINEFORMAT_CENTER));
|
||||
$receipt->appendBreak();
|
||||
foreach ($paymenttypes as $t) {
|
||||
$receipt->appendLine(new ReceiptLine(lang($t['text'], false) . ":", "", '$' . number_format($balance[$t['type']], 2)));
|
||||
$receipt->appendLine(new ReceiptLine($Strings->get($t['text'], false) . ":", "", '$' . number_format($balance[$t['type']], 2)));
|
||||
}
|
||||
|
||||
return $receipt;
|
135
lib/IPUtils.lib.php
Normal file
135
lib/IPUtils.lib.php
Normal file
@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
class IPUtils {
|
||||
|
||||
/**
|
||||
* Check if a given ipv4 address is in a given cidr
|
||||
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
|
||||
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
|
||||
* @return boolean true if the ip is in this range / false if not.
|
||||
* @author Thorsten Ott <https://gist.github.com/tott/7684443>
|
||||
*/
|
||||
public static function ip4_in_cidr($ip, $cidr) {
|
||||
if (strpos($cidr, '/') == false) {
|
||||
$cidr .= '/32';
|
||||
}
|
||||
// $range is in IP/CIDR format eg 127.0.0.1/24
|
||||
list( $cidr, $netmask ) = explode('/', $cidr, 2);
|
||||
$range_decimal = ip2long($cidr);
|
||||
$ip_decimal = ip2long($ip);
|
||||
$wildcard_decimal = pow(2, ( 32 - $netmask)) - 1;
|
||||
$netmask_decimal = ~ $wildcard_decimal;
|
||||
return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given ipv6 address is in a given cidr
|
||||
* @param string $ip IP to check in IPV6 format
|
||||
* @param string $cidr CIDR netmask
|
||||
* @return boolean true if the IP is in this range, false otherwise.
|
||||
* @author MW. <https://stackoverflow.com/a/7952169>
|
||||
*/
|
||||
public static function ip6_in_cidr($ip, $cidr) {
|
||||
$address = inet_pton($ip);
|
||||
$subnetAddress = inet_pton(explode("/", $cidr)[0]);
|
||||
$subnetMask = explode("/", $cidr)[1];
|
||||
|
||||
$addr = str_repeat("f", $subnetMask / 4);
|
||||
switch ($subnetMask % 4) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
$addr .= "8";
|
||||
break;
|
||||
case 2:
|
||||
$addr .= "c";
|
||||
break;
|
||||
case 3:
|
||||
$addr .= "e";
|
||||
break;
|
||||
}
|
||||
$addr = str_pad($addr, 32, '0');
|
||||
$addr = pack("H*", $addr);
|
||||
|
||||
$binMask = $addr;
|
||||
return ($address & $binMask) == $subnetAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the REMOTE_ADDR is on Cloudflare's network.
|
||||
* @return boolean true if it is, otherwise false
|
||||
*/
|
||||
public static function validateCloudflare() {
|
||||
if (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
// Using IPv6
|
||||
$cloudflare_ips_v6 = [
|
||||
"2400:cb00::/32",
|
||||
"2405:8100::/32",
|
||||
"2405:b500::/32",
|
||||
"2606:4700::/32",
|
||||
"2803:f800::/32",
|
||||
"2c0f:f248::/32",
|
||||
"2a06:98c0::/29"
|
||||
];
|
||||
$valid = false;
|
||||
foreach ($cloudflare_ips_v6 as $cidr) {
|
||||
if (ip6_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Using IPv4
|
||||
$cloudflare_ips_v4 = [
|
||||
"103.21.244.0/22",
|
||||
"103.22.200.0/22",
|
||||
"103.31.4.0/22",
|
||||
"104.16.0.0/12",
|
||||
"108.162.192.0/18",
|
||||
"131.0.72.0/22",
|
||||
"141.101.64.0/18",
|
||||
"162.158.0.0/15",
|
||||
"172.64.0.0/13",
|
||||
"173.245.48.0/20",
|
||||
"188.114.96.0/20",
|
||||
"190.93.240.0/20",
|
||||
"197.234.240.0/22",
|
||||
"198.41.128.0/17"
|
||||
];
|
||||
$valid = false;
|
||||
foreach ($cloudflare_ips_v4 as $cidr) {
|
||||
if (ip4_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a good guess at the client's real IP address.
|
||||
*
|
||||
* @return string Client IP or `0.0.0.0` if we can't find anything
|
||||
*/
|
||||
public static function getClientIP() {
|
||||
// If CloudFlare is in the mix, we should use it.
|
||||
// Check if the request is actually from CloudFlare before trusting it.
|
||||
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
|
||||
if (validateCloudflare()) {
|
||||
return $_SERVER["HTTP_CF_CONNECTING_IP"];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER["REMOTE_ADDR"])) {
|
||||
return $_SERVER["REMOTE_ADDR"];
|
||||
}
|
||||
|
||||
return "0.0.0.0"; // This will not happen unless we aren't a web server
|
||||
}
|
||||
|
||||
}
|
129
lib/Login.lib.php
Normal file
129
lib/Login.lib.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Login {
|
||||
|
||||
const BAD_USERPASS = 1;
|
||||
const BAD_2FA = 2;
|
||||
const ACCOUNT_DISABLED = 3;
|
||||
const LOGIN_OK = 4;
|
||||
|
||||
public static function auth(string $username, string $password, string $twofa = ""): int {
|
||||
global $database;
|
||||
$username = strtolower($username);
|
||||
|
||||
$user = User::byUsername($username);
|
||||
|
||||
if (!$user->exists()) {
|
||||
return Login::BAD_USERPASS;
|
||||
}
|
||||
if (!$user->checkPassword($password)) {
|
||||
return Login::BAD_USERPASS;
|
||||
}
|
||||
|
||||
if ($user->has2fa()) {
|
||||
if (!$user->check2fa($twofa)) {
|
||||
return Login::BAD_2FA;
|
||||
}
|
||||
}
|
||||
|
||||
switch ($user->getStatus()->get()) {
|
||||
case AccountStatus::TERMINATED:
|
||||
return Login::BAD_USERPASS;
|
||||
case AccountStatus::LOCKED_OR_DISABLED:
|
||||
return Login::ACCOUNT_DISABLED;
|
||||
case AccountStatus::NORMAL:
|
||||
default:
|
||||
return Login::LOGIN_OK;
|
||||
}
|
||||
|
||||
return Login::LOGIN_OK;
|
||||
}
|
||||
|
||||
public static function verifyCaptcha(string $session, string $answer, string $url): bool {
|
||||
$data = [
|
||||
'session_id' => $session,
|
||||
'answer_id' => $answer,
|
||||
'action' => "verify"
|
||||
];
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data)
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
$resp = json_decode($result, TRUE);
|
||||
if (!$resp['result']) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the login server API for sanity
|
||||
* @return boolean true if OK, else false
|
||||
*/
|
||||
public static function checkLoginServer() {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given AccountHub API key is valid by attempting to
|
||||
* access the API with it.
|
||||
* @param String $key The API key to check
|
||||
* @return boolean TRUE if the key is valid, FALSE if invalid or something went wrong
|
||||
*/
|
||||
function checkAPIKey($key) {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => $key,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
65
lib/Notifications.lib.php
Normal file
65
lib/Notifications.lib.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Notifications {
|
||||
|
||||
/**
|
||||
* Add a new notification.
|
||||
* @global $database
|
||||
* @param User $user
|
||||
* @param string $title
|
||||
* @param string $content
|
||||
* @param string $timestamp If left empty, the current date and time will be used.
|
||||
* @param string $url
|
||||
* @param bool $sensitive If true, the notification is marked as containing sensitive content, and the $content might be hidden on lockscreens and other non-secure places.
|
||||
* @return int The newly-created notification ID.
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function add(User $user, string $title, string $content, string $timestamp = "", string $url = "", bool $sensitive = false): int {
|
||||
global $Strings;
|
||||
if ($user->exists()) {
|
||||
if (empty($title) || empty($content)) {
|
||||
throw new Exception($Strings->get("invalid parameters", false));
|
||||
}
|
||||
|
||||
$timestamp = date("Y-m-d H:i:s");
|
||||
if (!empty($timestamp)) {
|
||||
$timestamp = date("Y-m-d H:i:s", strtotime($timestamp));
|
||||
}
|
||||
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "addnotification",
|
||||
'uid' => $user->getUID(),
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'timestamp' => $timestamp,
|
||||
'url' => $url,
|
||||
'sensitive' => $sensitive
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['id'] * 1;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
throw new Exception($Strings->get("user does not exist", false));
|
||||
}
|
||||
|
||||
}
|
124
lib/Receipt.lib.php
Normal file
124
lib/Receipt.lib.php
Normal file
@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Receipt {
|
||||
|
||||
private $lines = [];
|
||||
private $header = [];
|
||||
private $footer = [];
|
||||
|
||||
function __construct() {
|
||||
|
||||
}
|
||||
|
||||
function appendLine(ReceiptLine $line) {
|
||||
$this->lines[] = $line;
|
||||
}
|
||||
|
||||
function appendLines($lines) {
|
||||
foreach ($lines as $l) {
|
||||
$this->lines[] = $l;
|
||||
}
|
||||
}
|
||||
|
||||
function appendHeader(ReceiptLine $line) {
|
||||
$this->header[] = $line;
|
||||
}
|
||||
|
||||
function appendFooter(ReceiptLine $line) {
|
||||
$this->footer[] = $line;
|
||||
}
|
||||
|
||||
function appendBreak() {
|
||||
$this->lines[] = new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR);
|
||||
}
|
||||
|
||||
function appendBlank() {
|
||||
$this->lines[] = new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_BLANK);
|
||||
}
|
||||
|
||||
function getHtml($title = "") {
|
||||
global $SECURE_NONCE;
|
||||
$html = <<<END
|
||||
<!DOCTYPE html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>$title</title>
|
||||
<style nonce="$SECURE_NONCE">
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.centered {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
END;
|
||||
if (count($this->header) > 0) {
|
||||
foreach ($this->header as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
$html .= (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getHtml();
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
if (count($this->footer) > 0) {
|
||||
$html .= (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getHtml();
|
||||
foreach ($this->footer as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
function getPlainText($width) {
|
||||
$lines = [];
|
||||
if (count($this->header) > 0) {
|
||||
foreach ($this->header as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
$lines[] = (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getPlainText($width);
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
if (count($this->footer) > 0) {
|
||||
$lines[] = (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getPlainText($width);
|
||||
foreach ($this->footer as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
}
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
function getArray($width = 64) {
|
||||
$header = [];
|
||||
$lines = [];
|
||||
$footer = [];
|
||||
foreach ($this->header as $line) {
|
||||
$header[] = $line->getArray($width);
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$lines[] = $line->getArray($width);
|
||||
}
|
||||
foreach ($this->footer as $line) {
|
||||
$footer[] = $line->getArray($width);
|
||||
}
|
||||
return ["header" => $header, "lines" => $lines, "footer" => $footer];
|
||||
}
|
||||
|
||||
function getJson($width = 64) {
|
||||
return json_encode($this->getArray($width));
|
||||
}
|
||||
|
||||
}
|
@ -135,120 +135,3 @@ class ReceiptLine {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Receipt {
|
||||
|
||||
private $lines = [];
|
||||
private $header = [];
|
||||
private $footer = [];
|
||||
|
||||
function __construct() {
|
||||
|
||||
}
|
||||
|
||||
function appendLine(ReceiptLine $line) {
|
||||
$this->lines[] = $line;
|
||||
}
|
||||
|
||||
function appendLines($lines) {
|
||||
foreach ($lines as $l) {
|
||||
$this->lines[] = $l;
|
||||
}
|
||||
}
|
||||
|
||||
function appendHeader(ReceiptLine $line) {
|
||||
$this->header[] = $line;
|
||||
}
|
||||
|
||||
function appendFooter(ReceiptLine $line) {
|
||||
$this->footer[] = $line;
|
||||
}
|
||||
|
||||
function appendBreak() {
|
||||
$this->lines[] = new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR);
|
||||
}
|
||||
|
||||
function appendBlank() {
|
||||
$this->lines[] = new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_BLANK);
|
||||
}
|
||||
|
||||
function getHtml($title = "") {
|
||||
global $SECURE_NONCE;
|
||||
$html = <<<END
|
||||
<!DOCTYPE html>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>$title</title>
|
||||
<style nonce="$SECURE_NONCE">
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin: 0;
|
||||
}
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
.centered {
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
END;
|
||||
if (count($this->header) > 0) {
|
||||
foreach ($this->header as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
$html .= (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getHtml();
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
if (count($this->footer) > 0) {
|
||||
$html .= (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getHtml();
|
||||
foreach ($this->footer as $line) {
|
||||
$html .= $line->getHtml() . "\n";
|
||||
}
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
function getPlainText($width) {
|
||||
$lines = [];
|
||||
if (count($this->header) > 0) {
|
||||
foreach ($this->header as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
$lines[] = (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getPlainText($width);
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
if (count($this->footer) > 0) {
|
||||
$lines[] = (new ReceiptLine("", "", "", ReceiptLine::LINEFORMAT_HR))->getPlainText($width);
|
||||
foreach ($this->footer as $line) {
|
||||
$lines[] = $line->getPlainText($width);
|
||||
}
|
||||
}
|
||||
return implode("\n", $lines);
|
||||
}
|
||||
|
||||
function getArray($width = 64) {
|
||||
$header = [];
|
||||
$lines = [];
|
||||
$footer = [];
|
||||
foreach ($this->header as $line) {
|
||||
$header[] = $line->getArray($width);
|
||||
}
|
||||
foreach ($this->lines as $line) {
|
||||
$lines[] = $line->getArray($width);
|
||||
}
|
||||
foreach ($this->footer as $line) {
|
||||
$footer[] = $line->getArray($width);
|
||||
}
|
||||
return ["header" => $header, "lines" => $lines, "footer" => $footer];
|
||||
}
|
||||
|
||||
function getJson($width = 64) {
|
||||
return json_encode($this->getArray($width));
|
||||
}
|
||||
|
||||
}
|
137
lib/Report.lib.php
Normal file
137
lib/Report.lib.php
Normal file
@ -0,0 +1,137 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
use League\Csv\Writer;
|
||||
use League\Csv\HTMLConverter;
|
||||
use odsPhpGenerator\ods;
|
||||
use odsPhpGenerator\odsTable;
|
||||
use odsPhpGenerator\odsTableRow;
|
||||
use odsPhpGenerator\odsTableColumn;
|
||||
use odsPhpGenerator\odsTableCellString;
|
||||
use odsPhpGenerator\odsStyleTableColumn;
|
||||
use odsPhpGenerator\odsStyleTableCell;
|
||||
|
||||
class Report {
|
||||
|
||||
private $title = "";
|
||||
private $header = [];
|
||||
private $data = [];
|
||||
|
||||
public function __construct(string $title = "", array $header = [], array $data = []) {
|
||||
$this->title = $title;
|
||||
$this->header = $header;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function setHeader(array $header) {
|
||||
$this->header = $header;
|
||||
}
|
||||
|
||||
public function addDataRow(array $columns) {
|
||||
$this->data[] = $columns;
|
||||
}
|
||||
|
||||
public function getHeader(): array {
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
public function getData(): array {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function output(string $format) {
|
||||
switch ($format) {
|
||||
case "ods":
|
||||
$this->toODS();
|
||||
break;
|
||||
case "html":
|
||||
$this->toHTML();
|
||||
break;
|
||||
case "csv":
|
||||
default:
|
||||
$this->toCSV();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function toODS() {
|
||||
$ods = new ods();
|
||||
$styleColumn = new odsStyleTableColumn();
|
||||
$styleColumn->setUseOptimalColumnWidth(true);
|
||||
$headerstyle = new odsStyleTableCell();
|
||||
$headerstyle->setFontWeight("bold");
|
||||
$table = new odsTable($this->title);
|
||||
|
||||
for ($i = 0; $i < count($this->header); $i++) {
|
||||
$table->addTableColumn(new odsTableColumn($styleColumn));
|
||||
}
|
||||
|
||||
$row = new odsTableRow();
|
||||
foreach ($this->header as $cell) {
|
||||
$row->addCell(new odsTableCellString($cell, $headerstyle));
|
||||
}
|
||||
$table->addRow($row);
|
||||
|
||||
foreach ($this->data as $cols) {
|
||||
$row = new odsTableRow();
|
||||
foreach ($cols as $cell) {
|
||||
$row->addCell(new odsTableCellString($cell));
|
||||
}
|
||||
$table->addRow($row);
|
||||
}
|
||||
$ods->addTable($table);
|
||||
// The @ is a workaround to silence the tempnam notice,
|
||||
// which breaks the file. This is apparently the intended behavior:
|
||||
// https://bugs.php.net/bug.php?id=69489
|
||||
@$ods->downloadOdsFile($this->title . "_" . date("Y-m-d_Hi") . ".ods");
|
||||
}
|
||||
|
||||
private function toHTML() {
|
||||
global $SECURE_NONCE;
|
||||
$data = array_merge([$this->header], $this->data);
|
||||
// HTML exporter doesn't like null values
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
for ($j = 0; $j < count($data[$i]); $j++) {
|
||||
if (is_null($data[$i][$j])) {
|
||||
$data[$i][$j] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
header('Content-type: text/html');
|
||||
$converter = new HTMLConverter();
|
||||
$out = "<!DOCTYPE html>\n"
|
||||
. "<meta charset=\"utf-8\">\n"
|
||||
. "<meta name=\"viewport\" content=\"width=device-width\">\n"
|
||||
. "<title>" . $this->title . "_" . date("Y-m-d_Hi") . "</title>\n"
|
||||
. <<<STYLE
|
||||
<style nonce="$SECURE_NONCE">
|
||||
.table-csv-data {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.table-csv-data tr:first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
.table-csv-data tr td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
STYLE
|
||||
. $converter->convert($data);
|
||||
echo $out;
|
||||
}
|
||||
|
||||
private function toCSV() {
|
||||
$csv = Writer::createFromString('');
|
||||
$data = array_merge([$this->header], $this->data);
|
||||
$csv->insertAll($data);
|
||||
header('Content-type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="' . $this->title . "_" . date("Y-m-d_Hi") . ".csv" . '"');
|
||||
echo $csv;
|
||||
}
|
||||
|
||||
}
|
19
lib/Session.lib.php
Normal file
19
lib/Session.lib.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class Session {
|
||||
|
||||
public static function start(User $user) {
|
||||
$_SESSION['username'] = $user->getUsername();
|
||||
$_SESSION['uid'] = $user->getUID();
|
||||
$_SESSION['email'] = $user->getEmail();
|
||||
$_SESSION['realname'] = $user->getName();
|
||||
$_SESSION['loggedin'] = true;
|
||||
}
|
||||
|
||||
}
|
118
lib/Strings.lib.php
Normal file
118
lib/Strings.lib.php
Normal file
@ -0,0 +1,118 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides translated language strings.
|
||||
*/
|
||||
class Strings {
|
||||
|
||||
private $language = "en";
|
||||
private $strings = [];
|
||||
|
||||
public function __construct($language = "en") {
|
||||
if (!preg_match("/[a-zA-Z\_\-]+/", $language)) {
|
||||
throw new Exception("Invalid language code $language");
|
||||
}
|
||||
|
||||
$this->load("en");
|
||||
|
||||
if (file_exists(__DIR__ . "/../langs/$language/")) {
|
||||
$this->language = $language;
|
||||
$this->load($language);
|
||||
} else {
|
||||
trigger_error("Language $language could not be found.", E_USER_WARNING);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all JSON files for the specified language.
|
||||
* @param string $language
|
||||
*/
|
||||
private function load(string $language) {
|
||||
$files = glob(__DIR__ . "/../langs/$language/*.json");
|
||||
foreach ($files as $file) {
|
||||
$strings = json_decode(file_get_contents($file), true);
|
||||
foreach ($strings as $key => $val) {
|
||||
if (array_key_exists($key, $this->strings)) {
|
||||
trigger_error("Language key \"$key\" is defined more than once.", E_USER_WARNING);
|
||||
}
|
||||
$this->strings[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add language strings dynamically.
|
||||
* @param array $strings ["key" => "value", ...]
|
||||
*/
|
||||
public function addStrings(array $strings) {
|
||||
foreach ($strings as $key => $val) {
|
||||
$this->strings[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* I18N string getter. If the key isn't found, it outputs the key itself.
|
||||
* @param string $key
|
||||
* @param bool $echo True to echo the result, false to return it. Default is true.
|
||||
* @return string
|
||||
*/
|
||||
public function get(string $key, bool $echo = true): string {
|
||||
$str = $key;
|
||||
if (array_key_exists($key, $this->strings)) {
|
||||
$str = $this->strings[$key];
|
||||
} else {
|
||||
trigger_error("Language key \"$key\" does not exist in " . $this->language, E_USER_WARNING);
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo $str;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* I18N string getter (with builder). If the key doesn't exist, outputs the key itself.
|
||||
* @param string $key
|
||||
* @param array $replace key-value array of replacements.
|
||||
* If the string value is "hello {abc}" and you give ["abc" => "123"], the
|
||||
* result will be "hello 123".
|
||||
* @param bool $echo True to echo the result, false to return it. Default is true.
|
||||
* @return string
|
||||
*/
|
||||
public function build(string $key, array $replace, bool $echo = true): string {
|
||||
$str = $key;
|
||||
if (array_key_exists($key, $this->strings)) {
|
||||
$str = $this->strings[$key];
|
||||
} else {
|
||||
trigger_error("Language key \"$key\" does not exist in " . $this->language, E_USER_WARNING);
|
||||
}
|
||||
|
||||
foreach ($replace as $find => $repl) {
|
||||
$str = str_replace("{" . $find . "}", $repl, $str);
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo $str;
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and returns a JSON key:value string for the supplied array of keys.
|
||||
* @param array $keys ["key1", "key2", ...]
|
||||
*/
|
||||
public function getJSON(array $keys): string {
|
||||
$strings = [];
|
||||
foreach ($keys as $k) {
|
||||
$strings[$k] = $this->get($k, false);
|
||||
}
|
||||
return json_encode($strings);
|
||||
}
|
||||
|
||||
}
|
352
lib/User.lib.php
Normal file
352
lib/User.lib.php
Normal file
@ -0,0 +1,352 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class User {
|
||||
|
||||
private $uid = null;
|
||||
private $username;
|
||||
private $email;
|
||||
private $realname;
|
||||
private $has2fa = false;
|
||||
private $exists = false;
|
||||
|
||||
public function __construct(int $uid, string $username = "") {
|
||||
// Check if user exists
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userexists",
|
||||
'uid' => $uid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK" && $resp['exists'] === true) {
|
||||
$this->exists = true;
|
||||
} else {
|
||||
$this->uid = $uid;
|
||||
$this->username = $username;
|
||||
$this->exists = false;
|
||||
}
|
||||
|
||||
if ($this->exists) {
|
||||
// Get user info
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userinfo",
|
||||
'uid' => $uid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
$this->uid = $resp['data']['uid'] * 1;
|
||||
$this->username = $resp['data']['username'];
|
||||
$this->email = $resp['data']['email'];
|
||||
$this->realname = $resp['data']['name'];
|
||||
} else {
|
||||
sendError("Login server error: " . $resp['msg']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function byUsername(string $username): User {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'username' => $username,
|
||||
'action' => "userinfo"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if (!isset($resp['status'])) {
|
||||
sendError("Login server error: " . $resp);
|
||||
}
|
||||
if ($resp['status'] == "OK") {
|
||||
return new self($resp['data']['uid'] * 1);
|
||||
} else {
|
||||
return new self(-1, $username);
|
||||
}
|
||||
}
|
||||
|
||||
public function exists(): bool {
|
||||
return $this->exists;
|
||||
}
|
||||
|
||||
public function has2fa(): bool {
|
||||
if (!$this->exists) {
|
||||
return false;
|
||||
}
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "hastotp",
|
||||
'username' => $this->username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['otp'] == true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function getUsername() {
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
function getUID() {
|
||||
return $this->uid;
|
||||
}
|
||||
|
||||
function getEmail() {
|
||||
return $this->email;
|
||||
}
|
||||
|
||||
function getName() {
|
||||
return $this->realname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the given plaintext password against the stored hash.
|
||||
* @param string $password
|
||||
* @return bool
|
||||
*/
|
||||
function checkPassword(string $password): bool {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "auth",
|
||||
'username' => $this->username,
|
||||
'password' => $password
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function check2fa(string $code): bool {
|
||||
if (!$this->has2fa) {
|
||||
return true;
|
||||
}
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "verifytotp",
|
||||
'username' => $this->username,
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['valid'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given username has the given permission (or admin access)
|
||||
* @global $database $database
|
||||
* @param string $code
|
||||
* @return boolean TRUE if the user has the permission (or admin access), else FALSE
|
||||
*/
|
||||
function hasPermission(string $code): bool {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "permission",
|
||||
'username' => $this->username,
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['has_permission'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account status.
|
||||
* @return \AccountStatus
|
||||
*/
|
||||
function getStatus(): AccountStatus {
|
||||
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "acctstatus",
|
||||
'username' => $this->username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return AccountStatus::fromString($resp['account']);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function sendAlertEmail(string $appname = SITE_TITLE) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "alertemail",
|
||||
'username' => $this->username,
|
||||
'appname' => SITE_TITLE
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
return "An unknown error occurred.";
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return $resp['msg'];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class AccountStatus {
|
||||
|
||||
const NORMAL = 1;
|
||||
const LOCKED_OR_DISABLED = 2;
|
||||
const CHANGE_PASSWORD = 3;
|
||||
const TERMINATED = 4;
|
||||
const ALERT_ON_ACCESS = 5;
|
||||
|
||||
private $status;
|
||||
|
||||
public function __construct(int $status) {
|
||||
$this->status = $status;
|
||||
}
|
||||
|
||||
public static function fromString(string $status): AccountStatus {
|
||||
switch ($status) {
|
||||
case "NORMAL":
|
||||
return new self(self::NORMAL);
|
||||
case "LOCKED_OR_DISABLED":
|
||||
return new self(self::LOCKED_OR_DISABLED);
|
||||
case "CHANGE_PASSWORD":
|
||||
return new self(self::CHANGE_PASSWORD);
|
||||
case "TERMINATED":
|
||||
return new self(self::TERMINATED);
|
||||
case "ALERT_ON_ACCESS":
|
||||
return new self(self::ALERT_ON_ACCESS);
|
||||
default:
|
||||
return new self(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account status/state as an integer.
|
||||
* @return int
|
||||
*/
|
||||
public function get(): int {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account status/state as a string representation.
|
||||
* @return string
|
||||
*/
|
||||
public function getString(): string {
|
||||
switch ($this->status) {
|
||||
case self::NORMAL:
|
||||
return "NORMAL";
|
||||
case self::LOCKED_OR_DISABLED:
|
||||
return "LOCKED_OR_DISABLED";
|
||||
case self::CHANGE_PASSWORD:
|
||||
return "CHANGE_PASSWORD";
|
||||
case self::TERMINATED:
|
||||
return "TERMINATED";
|
||||
case self::ALERT_ON_ACCESS:
|
||||
return "ALERT_ON_ACCESS";
|
||||
default:
|
||||
return "OTHER_" . $this->status;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -12,10 +12,10 @@ $registers = $database->select("registers", ['[>]cash_drawer' => ['registerid' =
|
||||
<div class="col-12 col-sm-8 col-md-6 col-lg-4">
|
||||
<form class="card border-green" action="action.php" method="POST">
|
||||
<h3 class="card-header text-green">
|
||||
<?php lang("point of sale"); ?>
|
||||
<?php $Strings->get("point of sale"); ?>
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php lang("choose register"); ?></h5>
|
||||
<h5 class="card-title"><?php $Strings->get("choose register"); ?></h5>
|
||||
<?php
|
||||
if (count($registers) > 0) {
|
||||
?>
|
||||
@ -34,7 +34,7 @@ $registers = $database->select("registers", ['[>]cash_drawer' => ['registerid' =
|
||||
} else {
|
||||
?>
|
||||
<div class="alert alert-info">
|
||||
<?php lang("no open registers"); ?>
|
||||
<?php $Strings->get("no open registers"); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
131
lib/iputils.php
131
lib/iputils.php
@ -1,131 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Check if a given ipv4 address is in a given cidr
|
||||
* @param string $ip IP to check in IPV4 format eg. 127.0.0.1
|
||||
* @param string $range IP/CIDR netmask eg. 127.0.0.0/24, also 127.0.0.1 is accepted and /32 assumed
|
||||
* @return boolean true if the ip is in this range / false if not.
|
||||
* @author Thorsten Ott <https://gist.github.com/tott/7684443>
|
||||
*/
|
||||
function ip4_in_cidr($ip, $cidr) {
|
||||
if (strpos($cidr, '/') == false) {
|
||||
$cidr .= '/32';
|
||||
}
|
||||
// $range is in IP/CIDR format eg 127.0.0.1/24
|
||||
list( $cidr, $netmask ) = explode('/', $cidr, 2);
|
||||
$range_decimal = ip2long($cidr);
|
||||
$ip_decimal = ip2long($ip);
|
||||
$wildcard_decimal = pow(2, ( 32 - $netmask)) - 1;
|
||||
$netmask_decimal = ~ $wildcard_decimal;
|
||||
return ( ( $ip_decimal & $netmask_decimal ) == ( $range_decimal & $netmask_decimal ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a given ipv6 address is in a given cidr
|
||||
* @param string $ip IP to check in IPV6 format
|
||||
* @param string $cidr CIDR netmask
|
||||
* @return boolean true if the IP is in this range, false otherwise.
|
||||
* @author MW. <https://stackoverflow.com/a/7952169>
|
||||
*/
|
||||
function ip6_in_cidr($ip, $cidr) {
|
||||
$address = inet_pton($ip);
|
||||
$subnetAddress = inet_pton(explode("/", $cidr)[0]);
|
||||
$subnetMask = explode("/", $cidr)[1];
|
||||
|
||||
$addr = str_repeat("f", $subnetMask / 4);
|
||||
switch ($subnetMask % 4) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
$addr .= "8";
|
||||
break;
|
||||
case 2:
|
||||
$addr .= "c";
|
||||
break;
|
||||
case 3:
|
||||
$addr .= "e";
|
||||
break;
|
||||
}
|
||||
$addr = str_pad($addr, 32, '0');
|
||||
$addr = pack("H*", $addr);
|
||||
|
||||
$binMask = $addr;
|
||||
return ($address & $binMask) == $subnetAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the REMOTE_ADDR is on Cloudflare's network.
|
||||
* @return boolean true if it is, otherwise false
|
||||
*/
|
||||
function validateCloudflare() {
|
||||
if (filter_var($_SERVER["REMOTE_ADDR"], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
|
||||
// Using IPv6
|
||||
$cloudflare_ips_v6 = [
|
||||
"2400:cb00::/32",
|
||||
"2405:8100::/32",
|
||||
"2405:b500::/32",
|
||||
"2606:4700::/32",
|
||||
"2803:f800::/32",
|
||||
"2c0f:f248::/32",
|
||||
"2a06:98c0::/29"
|
||||
];
|
||||
$valid = false;
|
||||
foreach ($cloudflare_ips_v6 as $cidr) {
|
||||
if (ip6_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Using IPv4
|
||||
$cloudflare_ips_v4 = [
|
||||
"103.21.244.0/22",
|
||||
"103.22.200.0/22",
|
||||
"103.31.4.0/22",
|
||||
"104.16.0.0/12",
|
||||
"108.162.192.0/18",
|
||||
"131.0.72.0/22",
|
||||
"141.101.64.0/18",
|
||||
"162.158.0.0/15",
|
||||
"172.64.0.0/13",
|
||||
"173.245.48.0/20",
|
||||
"188.114.96.0/20",
|
||||
"190.93.240.0/20",
|
||||
"197.234.240.0/22",
|
||||
"198.41.128.0/17"
|
||||
];
|
||||
$valid = false;
|
||||
foreach ($cloudflare_ips_v4 as $cidr) {
|
||||
if (ip4_in_cidr($_SERVER["REMOTE_ADDR"], $cidr)) {
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $valid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a good guess at the client's real IP address.
|
||||
*
|
||||
* @return string Client IP or `0.0.0.0` if we can't find anything
|
||||
*/
|
||||
function getClientIP() {
|
||||
// If CloudFlare is in the mix, we should use it.
|
||||
// Check if the request is actually from CloudFlare before trusting it.
|
||||
if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
|
||||
if (validateCloudflare()) {
|
||||
return $_SERVER["HTTP_CF_CONNECTING_IP"];
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_SERVER["REMOTE_ADDR"])) {
|
||||
return $_SERVER["REMOTE_ADDR"];
|
||||
}
|
||||
|
||||
return "0.0.0.0"; // This will not happen unless we aren't a web server
|
||||
}
|
402
lib/login.php
402
lib/login.php
@ -1,402 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Authentication and account functions. Connects to an AccountHub instance.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Check the login server API for sanity
|
||||
* @return boolean true if OK, else false
|
||||
*/
|
||||
function checkLoginServer() {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given AccountHub API key is valid by attempting to
|
||||
* access the API with it.
|
||||
* @param String $key The API key to check
|
||||
* @return boolean TRUE if the key is valid, FALSE if invalid or something went wrong
|
||||
*/
|
||||
function checkAPIKey($key) {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => $key,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Account handling //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Checks the given credentials against the API.
|
||||
* @param string $username
|
||||
* @param string $password
|
||||
* @return boolean True if OK, else false
|
||||
*/
|
||||
function authenticate_user($username, $password, &$errmsg) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "auth",
|
||||
'username' => $username,
|
||||
'password' => $password
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
$errmsg = $resp['msg'];
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a username exists.
|
||||
* @param String $username
|
||||
*/
|
||||
function user_exists($username) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userexists",
|
||||
'username' => $username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK" && $resp['exists'] === true) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a UID exists.
|
||||
* @param String $uid
|
||||
*/
|
||||
function uid_exists($uid) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userexists",
|
||||
'uid' => $uid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK" && $resp['exists'] === true) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the account status: NORMAL, TERMINATED, LOCKED_OR_DISABLED,
|
||||
* CHANGE_PASSWORD, or ALERT_ON_ACCESS
|
||||
* @param string $username
|
||||
* @return string
|
||||
*/
|
||||
function get_account_status($username) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "acctstatus",
|
||||
'username' => $username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['account'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the given username has the given permission (or admin access)
|
||||
* @param string $username
|
||||
* @param string $permcode
|
||||
* @return boolean TRUE if the user has the permission (or admin access), else FALSE
|
||||
*/
|
||||
function account_has_permission($username, $permcode) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "permission",
|
||||
'username' => $username,
|
||||
'code' => $permcode
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['has_permission'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Login handling //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Setup $_SESSION values with user data and set loggedin flag to true
|
||||
* @param string $username
|
||||
*/
|
||||
function doLoginUser($username) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userinfo",
|
||||
'username' => $username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
|
||||
if ($resp['status'] == "OK") {
|
||||
$userinfo = $resp['data'];
|
||||
session_regenerate_id(true);
|
||||
$newSession = session_id();
|
||||
session_write_close();
|
||||
session_id($newSession);
|
||||
session_start();
|
||||
$_SESSION['username'] = $username;
|
||||
$_SESSION['uid'] = $userinfo['uid'];
|
||||
$_SESSION['email'] = $userinfo['email'];
|
||||
$_SESSION['realname'] = $userinfo['name'];
|
||||
$_SESSION['loggedin'] = true;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function sendLoginAlertEmail($username) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "alertemail",
|
||||
'username' => $username,
|
||||
'appname' => SITE_TITLE
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
return "An unknown error occurred.";
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return $resp['msg'];
|
||||
}
|
||||
}
|
||||
|
||||
function simLogin($username, $password) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "login",
|
||||
'username' => $username,
|
||||
'password' => $password
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
return $resp['msg'];
|
||||
}
|
||||
}
|
||||
|
||||
function verifyCaptcheck($session, $answer, $url) {
|
||||
$data = [
|
||||
'session_id' => $session,
|
||||
'answer_id' => $answer,
|
||||
'action' => "verify"
|
||||
];
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data)
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
$resp = json_decode($result, TRUE);
|
||||
if (!$resp['result']) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 2-factor authentication //
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/**
|
||||
* Check if a user has TOTP setup
|
||||
* @param string $username
|
||||
* @return boolean true if TOTP secret exists, else false
|
||||
*/
|
||||
function userHasTOTP($username) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "hastotp",
|
||||
'username' => $username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['otp'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a TOTP multiauth code
|
||||
* @global $database
|
||||
* @param string $username
|
||||
* @param int $code
|
||||
* @return boolean true if it's legit, else false
|
||||
*/
|
||||
function verifyTOTP($username, $code) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "verifytotp",
|
||||
'username' => $username,
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['valid'];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
188
lib/reports.php
188
lib/reports.php
@ -14,63 +14,31 @@ if (count(get_included_files()) == 1) {
|
||||
|
||||
require_once __DIR__ . "/../required.php";
|
||||
|
||||
use League\Csv\Writer;
|
||||
use League\Csv\HTMLConverter;
|
||||
use odsPhpGenerator\ods;
|
||||
use odsPhpGenerator\odsTable;
|
||||
use odsPhpGenerator\odsTableRow;
|
||||
use odsPhpGenerator\odsTableColumn;
|
||||
use odsPhpGenerator\odsTableCellString;
|
||||
use odsPhpGenerator\odsStyleTableColumn;
|
||||
use odsPhpGenerator\odsStyleTableCell;
|
||||
|
||||
require_once __DIR__ . "/userinfo.php";
|
||||
require_once __DIR__ . "/login.php";
|
||||
|
||||
// Allow access with a download code, for mobile app and stuff
|
||||
$date = date("Y-m-d H:i:s");
|
||||
$allowed_users = [];
|
||||
$requester = -1;
|
||||
if (isset($VARS['code']) && LOADED) {
|
||||
if (!$database->has('report_access_codes', ["AND" => ['code' => $VARS['code'], 'expires[>]' => $date]])) {
|
||||
dieifnotloggedin();
|
||||
$requester = $_SESSION['uid'];
|
||||
} else {
|
||||
$requester = $database->get('report_access_codes', 'uid', ['code' => $VARS['code']]);
|
||||
}
|
||||
} else {
|
||||
dieifnotloggedin();
|
||||
$requester = $_SESSION['uid'];
|
||||
}
|
||||
|
||||
if (account_has_permission($_SESSION['username'], "ADMIN")) {
|
||||
$allowed_users = true;
|
||||
} else {
|
||||
if (account_has_permission($_SESSION['username'], "QWIKCLOCK_MANAGE")) {
|
||||
$allowed_users = getManagedUIDs($requester);
|
||||
}
|
||||
|
||||
if (account_has_permission($_SESSION['username'], "QWIKCLOCK_EDITSELF")) {
|
||||
$allowed_users[] = $_SESSION['uid'];
|
||||
}
|
||||
}
|
||||
|
||||
// Delete old DB entries
|
||||
$database->delete('report_access_codes', ['expires[<=]' => $date]);
|
||||
|
||||
if (LOADED) {
|
||||
$user = null;
|
||||
if (isset($VARS['type']) && isset($VARS['format'])) {
|
||||
generateReport($VARS['type'], $VARS['format'], $VARS['register'], $VARS['startdate'], $VARS['enddate']);
|
||||
die();
|
||||
} else {
|
||||
lang("invalid parameters");
|
||||
$Strings->get("invalid parameters");
|
||||
die();
|
||||
}
|
||||
}
|
||||
|
||||
function getCashFlowReport($register = null, $start = null, $end = null) {
|
||||
global $database;
|
||||
global $database, $Strings;
|
||||
$where = [];
|
||||
|
||||
if (!is_null($register) && $database->has('registers', ['registerid' => $register])) {
|
||||
@ -108,8 +76,9 @@ function getCashFlowReport($register = null, $start = null, $end = null) {
|
||||
"payment_types.typename"
|
||||
], $where
|
||||
);
|
||||
$header = [lang("register", false), lang("open", false), lang("close", false), lang("cash", false), lang("card", false), lang("check", false), lang("crypto", false), lang("gift card", false), lang("free", false)];
|
||||
$out = [$header];
|
||||
|
||||
$report = new Report($Strings->get("cashflow", false));
|
||||
$report->setHeader([$Strings->get("register", false), $Strings->get("open", false), $Strings->get("close", false), $Strings->get("cash", false), $Strings->get("card", false), $Strings->get("check", false), $Strings->get("crypto", false), $Strings->get("gift card", false), $Strings->get("free", false)]);
|
||||
|
||||
$registers = [];
|
||||
|
||||
@ -135,7 +104,7 @@ function getCashFlowReport($register = null, $start = null, $end = null) {
|
||||
$r[$t] = 0.0;
|
||||
}
|
||||
}
|
||||
$out[] = [
|
||||
$report->addDataRow([
|
||||
$r['name'],
|
||||
$r['open'],
|
||||
$r['close'],
|
||||
@ -145,153 +114,22 @@ function getCashFlowReport($register = null, $start = null, $end = null) {
|
||||
$r['crypto'] . "",
|
||||
$r['giftcard'] . "",
|
||||
$r['free'] . ""
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
return $out;
|
||||
return $report;
|
||||
}
|
||||
|
||||
function getReportData($type, $register = null, $start = null, $end = null) {
|
||||
function getReport(string $type, $register = null, $start = null, $end = null): Report {
|
||||
switch ($type) {
|
||||
case "cashflow":
|
||||
return getCashFlowReport($register, $start, $end);
|
||||
default:
|
||||
return [["error"]];
|
||||
return new Report("error", ["ERROR"], ["Invalid report type."]);
|
||||
}
|
||||
}
|
||||
|
||||
function dataToCSV($data, $name = "report", $register = null, $start = null, $end = null) {
|
||||
$csv = Writer::createFromString('');
|
||||
$usernotice = "";
|
||||
$usertitle = "";
|
||||
$datetitle = "";
|
||||
if ($start != null && (bool) strtotime($start)) {
|
||||
$datenotice = lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false);
|
||||
$datetitle = "_" . date(DATE_FORMAT, strtotime($start));
|
||||
$csv->insertOne([$datenotice]);
|
||||
}
|
||||
if ($end != null && (bool) strtotime($end)) {
|
||||
$datenotice = lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false);
|
||||
$datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
|
||||
$csv->insertOne([$datenotice]);
|
||||
}
|
||||
$csv->insertAll($data);
|
||||
header('Content-type: text/csv');
|
||||
header('Content-Disposition: attachment; filename="' . $name . $usertitle . $datetitle . "_" . date("Y-m-d_Hi") . ".csv" . '"');
|
||||
echo $csv;
|
||||
die();
|
||||
}
|
||||
|
||||
function dataToODS($data, $name = "report", $register = null, $start = null, $end = null) {
|
||||
$ods = new ods();
|
||||
$styleColumn = new odsStyleTableColumn();
|
||||
$styleColumn->setUseOptimalColumnWidth(true);
|
||||
$headerstyle = new odsStyleTableCell();
|
||||
$headerstyle->setFontWeight("bold");
|
||||
$table = new odsTable($name);
|
||||
|
||||
for ($i = 0; $i < count($data[0]); $i++) {
|
||||
$table->addTableColumn(new odsTableColumn($styleColumn));
|
||||
}
|
||||
|
||||
$usernotice = "";
|
||||
$usertitle = "";
|
||||
$datetitle = "";
|
||||
if ($user != null && array_key_exists('username', $user) && array_key_exists('name', $user)) {
|
||||
$usernotice = lang2("report filtered to user", ["name" => $user['name'], "username" => $user['username']], false);
|
||||
$usertitle = "_" . $user['username'];
|
||||
$row = new odsTableRow();
|
||||
$row->addCell(new odsTableCellString($usernotice));
|
||||
$table->addRow($row);
|
||||
}
|
||||
if ($start != null && (bool) strtotime($start)) {
|
||||
$datenotice = lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false);
|
||||
$datetitle = "_" . date(DATE_FORMAT, strtotime($start));
|
||||
$row = new odsTableRow();
|
||||
$row->addCell(new odsTableCellString($datenotice));
|
||||
$table->addRow($row);
|
||||
}
|
||||
if ($end != null && (bool) strtotime($end)) {
|
||||
$datenotice = lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false);
|
||||
$datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
|
||||
$row = new odsTableRow();
|
||||
$row->addCell(new odsTableCellString($datenotice));
|
||||
$table->addRow($row);
|
||||
}
|
||||
|
||||
$rowid = 0;
|
||||
foreach ($data as $datarow) {
|
||||
$row = new odsTableRow();
|
||||
foreach ($datarow as $cell) {
|
||||
if ($rowid == 0) {
|
||||
$row->addCell(new odsTableCellString($cell, $headerstyle));
|
||||
} else {
|
||||
$row->addCell(new odsTableCellString($cell));
|
||||
}
|
||||
}
|
||||
$table->addRow($row);
|
||||
$rowid++;
|
||||
}
|
||||
$ods->addTable($table);
|
||||
$ods->downloadOdsFile($name . $usertitle . $datetitle . "_" . date("Y-m-d_Hi") . ".ods");
|
||||
}
|
||||
|
||||
function dataToHTML($data, $name = "report", $register = null, $start = null, $end = null) {
|
||||
global $SECURE_NONCE;
|
||||
// HTML exporter doesn't like null values
|
||||
for ($i = 0; $i < count($data); $i++) {
|
||||
for ($j = 0; $j < count($data[$i]); $j++) {
|
||||
if (is_null($data[$i][$j])) {
|
||||
$data[$i][$j] = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
$datenotice = "";
|
||||
$datetitle = "";
|
||||
if ($start != null && (bool) strtotime($start)) {
|
||||
$datenotice = "<span>" . lang2("report filtered to start date", ["date" => date(DATE_FORMAT, strtotime($start))], false) . "</span><br />";
|
||||
$datetitle = "_" . date(DATE_FORMAT, strtotime($start));
|
||||
}
|
||||
if ($end != null && (bool) strtotime($end)) {
|
||||
$datenotice .= "<span>" . lang2("report filtered to end date", ["date" => date(DATE_FORMAT, strtotime($end))], false) . "</span><br />";
|
||||
$datetitle .= ($datetitle == "" ? "_" : "-") . date(DATE_FORMAT, strtotime($end));
|
||||
}
|
||||
header('Content-type: text/html');
|
||||
$converter = new HTMLConverter();
|
||||
$out = "<!DOCTYPE html>\n"
|
||||
. "<meta charset=\"utf-8\">\n"
|
||||
. "<meta name=\"viewport\" content=\"width=device-width\">\n"
|
||||
. "<title>" . $name . $datetitle . "_" . date("Y-m-d_Hi") . "</title>\n"
|
||||
. <<<STYLE
|
||||
<style nonce="$SECURE_NONCE">
|
||||
.table-csv-data {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.table-csv-data tr:first-child {
|
||||
font-weight: bold;
|
||||
}
|
||||
.table-csv-data tr td {
|
||||
border: 1px solid black;
|
||||
}
|
||||
</style>
|
||||
STYLE
|
||||
. $datenotice
|
||||
. $converter->convert($data);
|
||||
echo $out;
|
||||
}
|
||||
|
||||
function generateReport($type, $format, $register = null, $start = null, $end = null, $deleted = true) {
|
||||
$data = getReportData($type, $register, $start, $end, $deleted);
|
||||
switch ($format) {
|
||||
case "ods":
|
||||
dataToODS($data, $type, $register, $start, $end);
|
||||
break;
|
||||
case "html":
|
||||
dataToHTML($data, $type, $register, $start, $end);
|
||||
break;
|
||||
case "csv":
|
||||
default:
|
||||
echo dataToCSV($data, $type, $register, $start, $end);
|
||||
break;
|
||||
}
|
||||
function generateReport($type, $format, $register = null, $start = null, $end = null) {
|
||||
$report = getReport($type, $register, $start, $end);
|
||||
$report->output($format);
|
||||
}
|
||||
|
127
lib/userinfo.php
127
lib/userinfo.php
@ -1,127 +0,0 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Get user info for the given username.
|
||||
* @param int $u username
|
||||
* @return [string] Array of [uid, username, name]
|
||||
*/
|
||||
function getUserByUsername($u) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userinfo",
|
||||
'username' => $u
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['data'];
|
||||
} else {
|
||||
// this shouldn't happen, but in case it does just fake it.
|
||||
return ["name" => $u, "username" => $u, "uid" => $u];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user info for the given UID.
|
||||
* @param int $u user ID
|
||||
* @return [string] Array of [uid, username, name]
|
||||
*/
|
||||
function getUserByID($u) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userinfo",
|
||||
'uid' => $u
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['data'];
|
||||
} else {
|
||||
// this shouldn't happen, but in case it does just fake it.
|
||||
return ["name" => $u, "username" => $u, "uid" => $u];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the first UID is a manager of the second UID.
|
||||
* @param int $m Manager UID
|
||||
* @param int $e Employee UID
|
||||
* @return boolean
|
||||
*/
|
||||
function isManagerOf($m, $e) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "ismanagerof",
|
||||
'manager' => $m,
|
||||
'employee' => $e,
|
||||
'uid' => 1
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['managerof'] === true;
|
||||
} else {
|
||||
// this shouldn't happen, but in case it does just fake it.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of UIDs the given UID is a manager of.
|
||||
* @param int $manageruid The UID of the manager to find employees for.
|
||||
* @return [int]
|
||||
*/
|
||||
function getManagedUIDs($manageruid) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "getmanaged",
|
||||
'uid' => $manageruid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['employees'];
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
@ -14,8 +14,6 @@ $access_permission = null;
|
||||
|
||||
require __DIR__ . "/../required.php";
|
||||
|
||||
require __DIR__ . "/../lib/login.php";
|
||||
|
||||
header('Content-Type: application/json');
|
||||
header('Access-Control-Allow-Origin: *');
|
||||
|
||||
@ -73,7 +71,7 @@ function mobile_valid($username, $code) {
|
||||
}
|
||||
|
||||
if (mobile_enabled() !== TRUE) {
|
||||
exit(json_encode(["status" => "ERROR", "msg" => lang("mobile login disabled", false)]));
|
||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("mobile login disabled", false)]));
|
||||
}
|
||||
|
||||
// Make sure we have a username and access key
|
||||
@ -93,20 +91,21 @@ if (!mobile_valid($VARS['username'], $VARS['key'])) {
|
||||
switch ($VARS['action']) {
|
||||
case "start_session":
|
||||
// Do a web login.
|
||||
if (user_exists($VARS['username'])) {
|
||||
if (get_account_status($VARS['username']) == "NORMAL") {
|
||||
if (authenticate_user($VARS['username'], $VARS['password'], $autherror)) {
|
||||
if (is_null($access_permission) || account_has_permission($VARS['username'], $access_permission)) {
|
||||
doLoginUser($VARS['username'], $VARS['password']);
|
||||
$user = User::byUsername($VARS['username']);
|
||||
if ($user->exists()) {
|
||||
if ($user->getStatus()->getString() == "NORMAL") {
|
||||
if ($user->checkPassword($VARS['password'])) {
|
||||
if (is_null($access_permission) || $user->hasPermission($access_permission)) {
|
||||
Session::start($user);
|
||||
$_SESSION['mobile'] = true;
|
||||
exit(json_encode(["status" => "OK"]));
|
||||
} else {
|
||||
exit(json_encode(["status" => "ERROR", "msg" => lang("no admin permission", false)]));
|
||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("no admin permission", false)]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
exit(json_encode(["status" => "ERROR", "msg" => lang("login incorrect", false)]));
|
||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
|
||||
default:
|
||||
http_response_code(404);
|
||||
die(json_encode(["status" => "ERROR", "msg" => "The requested action is not available."]));
|
||||
|
@ -5,6 +5,6 @@
|
||||
?>
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-sm-10 col-md-8 col-lg-6">
|
||||
<div class="alert alert-warning"><b><?php lang("404 error");?></b><br /> <?php lang("page not found"); ?></div>
|
||||
<div class="alert alert-warning"><b><?php $Strings->get("404 error");?></b><br /> <?php $Strings->get("page not found"); ?></div>
|
||||
</div>
|
||||
</div>
|
@ -7,26 +7,24 @@
|
||||
|
||||
require_once __DIR__ . "/../required.php";
|
||||
|
||||
use Medoo\Medoo;
|
||||
|
||||
redirectIfNotLoggedIn();
|
||||
|
||||
$cards = $database->select('certificates', ['certid (id)', 'certcode (code)', 'amount', 'start_amount (start)', 'issued'], ['deleted[!]' => 1]);
|
||||
?>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<a href="app.php?page=editcertificate" class="btn btn-success"><i class="fas fa-plus"></i> <?php lang("add card"); ?></a>
|
||||
<a href="app.php?page=editcertificate" class="btn btn-success"><i class="fas fa-plus"></i> <?php $Strings->get("add card"); ?></a>
|
||||
</div>
|
||||
|
||||
<table id="certificatetable" class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-hashtag d-none d-md-inline"></i> <?php lang('card number'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php lang('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php lang('start balance'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-calendar d-none d-md-inline"></i> <?php lang('issued'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-hashtag d-none d-md-inline"></i> <?php $Strings->get('card number'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php $Strings->get('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php $Strings->get('start balance'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-calendar d-none d-md-inline"></i> <?php $Strings->get('issued'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -36,7 +34,7 @@ $cards = $database->select('certificates', ['certid (id)', 'certcode (code)', 'a
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcertificate&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php lang("edit"); ?></a>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcertificate&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php $Strings->get("edit"); ?></a>
|
||||
</td>
|
||||
<td><?php echo $c['code']; ?></td>
|
||||
<td>$<?php echo number_format($c['amount'], 2); ?></td>
|
||||
@ -50,11 +48,11 @@ $cards = $database->select('certificates', ['certid (id)', 'certcode (code)', 'a
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-hashtag d-none d-md-inline"></i> <?php lang('card number'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php lang('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php lang('start balance'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-calendar d-none d-md-inline"></i> <?php lang('issued'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-hashtag d-none d-md-inline"></i> <?php $Strings->get('card number'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php $Strings->get('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php $Strings->get('start balance'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-calendar d-none d-md-inline"></i> <?php $Strings->get('issued'); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -13,19 +13,19 @@ $customers = $database->select('customers', ['customerid (id)', 'name', 'email',
|
||||
?>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<a href="app.php?page=editcustomer" class="btn btn-success"><i class="fas fa-user-plus"></i> <?php lang("new customer"); ?></a>
|
||||
<a href="app.php?page=editcustomer" class="btn btn-success"><i class="fas fa-user-plus"></i> <?php $Strings->get("new customer"); ?></a>
|
||||
</div>
|
||||
|
||||
<table id="customertable" class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php lang('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php lang('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php lang('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php lang('notes'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php $Strings->get('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php $Strings->get('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php $Strings->get('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php $Strings->get('notes'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -35,7 +35,7 @@ $customers = $database->select('customers', ['customerid (id)', 'name', 'email',
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcustomer&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php lang("edit"); ?></a>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcustomer&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php $Strings->get("edit"); ?></a>
|
||||
</td>
|
||||
<td><?php echo $c['name']; ?></td>
|
||||
<td><?php echo $c['phone']; ?></td>
|
||||
@ -50,12 +50,12 @@ $customers = $database->select('customers', ['customerid (id)', 'name', 'email',
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php lang('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php lang('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php lang('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php lang('notes'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php $Strings->get('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php $Strings->get('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php $Strings->get('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php $Strings->get('notes'); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -50,22 +50,22 @@ if (!$editing) {
|
||||
<?php
|
||||
if ($editing) {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang2("editing card x", ['code' => htmlspecialchars($carddata['code'])]); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->build("editing card x", ['code' => htmlspecialchars($carddata['code'])]); ?>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang("adding card"); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->get("adding card"); ?>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</h3>
|
||||
<div class="card-body row">
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="code"><i class="fas fa-hashtag"></i> <?php lang("card number"); ?></label>
|
||||
<label for="code"><i class="fas fa-hashtag"></i> <?php $Strings->get("card number"); ?></label>
|
||||
<input type="text" class="form-control" id="code" name="code" value="<?php echo htmlspecialchars($carddata['code']); ?>" />
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="balance"><i class="fas fa-balance-scale"></i> <?php lang("balance"); ?></label>
|
||||
<label for="balance"><i class="fas fa-balance-scale"></i> <?php $Strings->get("balance"); ?></label>
|
||||
<input type="money" class="form-control" id="balance" name="balance" value="<?php echo number_format($carddata['amount'], 2); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
@ -75,7 +75,7 @@ if (!$editing) {
|
||||
<input type="hidden" name="source" value="certificates" />
|
||||
|
||||
<div class="card-footer d-flex">
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php lang("save"); ?></button>
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -54,7 +54,7 @@ if ($editing) {
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-user-tag"></i> <?php lang("add customer price"); ?></h5>
|
||||
<h5 class="modal-title"><i class="fas fa-user-tag"></i> <?php $Strings->get("add customer price"); ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@ -64,7 +64,7 @@ if ($editing) {
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text px-2"><i class="fas fa-barcode"></i></span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="pricemodalitem" placeholder="<?php lang("barcode or search"); ?>" />
|
||||
<input type="text" class="form-control" id="pricemodalitem" placeholder="<?php $Strings->get("barcode or search"); ?>" />
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-link" type="button" id="pricemodalsearch"><i class="fas fa-search"></i></button>
|
||||
</div>
|
||||
@ -72,15 +72,15 @@ if ($editing) {
|
||||
|
||||
<div class="card mt-2">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fas fa-box"></i> <?php lang("item"); ?></h5>
|
||||
<h5 class="card-title"><i class="fas fa-box"></i> <?php $Strings->get("item"); ?></h5>
|
||||
<p>
|
||||
<?php lang("name"); ?>: <span id="pricemodalitemname">---</span><br />
|
||||
<?php lang("cost"); ?>: <span id="pricemodalitemcost">---</span><br />
|
||||
<?php lang("price"); ?>: <span id="pricemodalitemprice">---</span><br />
|
||||
<?php $Strings->get("name"); ?>: <span id="pricemodalitemname">---</span><br />
|
||||
<?php $Strings->get("cost"); ?>: <span id="pricemodalitemcost">---</span><br />
|
||||
<?php $Strings->get("price"); ?>: <span id="pricemodalitemprice">---</span><br />
|
||||
</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="fas fa-user-tag"></i> <?php lang("customer price"); ?></h5>
|
||||
<h5 class="card-title"><i class="fas fa-user-tag"></i> <?php $Strings->get("customer price"); ?></h5>
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">$</span>
|
||||
@ -92,8 +92,8 @@ if ($editing) {
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("cancel"); ?></button>
|
||||
<button type="button" class="btn btn-primary" id="pricemodalsave"><?php lang("save"); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php $Strings->get("cancel"); ?></button>
|
||||
<button type="button" class="btn btn-primary" id="pricemodalsave"><?php $Strings->get("save"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -105,34 +105,34 @@ if ($editing) {
|
||||
<?php
|
||||
if ($editing) {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang2("editing customer", ['name' => "<span id=\"name_title\">" . htmlspecialchars($custdata['name']) . "</span>"]); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->build("editing customer", ['name' => "<span id=\"name_title\">" . htmlspecialchars($custdata['name']) . "</span>"]); ?>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang("adding customer"); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->get("adding customer"); ?>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</h3>
|
||||
<div class="card-body row">
|
||||
<div class="form-group col-12">
|
||||
<label for="name"><i class="fas fa-user"></i> <?php lang("name"); ?></label>
|
||||
<label for="name"><i class="fas fa-user"></i> <?php $Strings->get("name"); ?></label>
|
||||
<input type="text" class="form-control" id="name" name="name" placeholder="Foo Bar" required="required" value="<?php echo htmlspecialchars($custdata['name']); ?>" />
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="email"><i class="fas fa-at"></i> <?php lang("email"); ?></label>
|
||||
<label for="email"><i class="fas fa-at"></i> <?php $Strings->get("email"); ?></label>
|
||||
<input type="email" class="form-control" id="email" name="email" placeholder="user@example.com" value="<?php echo htmlspecialchars($custdata['email']); ?>" />
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="phone"><i class="fas fa-phone"></i> <?php lang("phone"); ?></label>
|
||||
<label for="phone"><i class="fas fa-phone"></i> <?php $Strings->get("phone"); ?></label>
|
||||
<input type="phone" class="form-control" id="phone" name="phone" placeholder="" value="<?php echo htmlspecialchars($custdata['phone']); ?>" />
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="address"><i class="fas fa-map-marker"></i> <?php lang("address"); ?></label>
|
||||
<label for="address"><i class="fas fa-map-marker"></i> <?php $Strings->get("address"); ?></label>
|
||||
<textarea rows="3" class="form-control" id="address" name="address" placeholder=""><?php echo htmlspecialchars($custdata['address']); ?></textarea>
|
||||
</div>
|
||||
<div class="form-group col-sm-6">
|
||||
<label for="notes"><i class="fas fa-sticky-note"></i> <?php lang("notes"); ?></label>
|
||||
<label for="notes"><i class="fas fa-sticky-note"></i> <?php $Strings->get("notes"); ?></label>
|
||||
<textarea rows="3" class="form-control" id="notes" name="notes" placeholder=""><?php echo htmlspecialchars($custdata['notes']); ?></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@ -140,21 +140,21 @@ if ($editing) {
|
||||
<hr />
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php lang("customer pricing"); ?></h5>
|
||||
<h5 class="card-title"><?php $Strings->get("customer pricing"); ?></h5>
|
||||
<div class="btn-toolbar">
|
||||
<div class="btn btn-success" id="addcustomerpricebtn">
|
||||
<i class="fas fa-plus"></i> <?php lang("add price"); ?>
|
||||
<i class="fas fa-plus"></i> <?php $Strings->get("add price"); ?>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-hover table-sm" id="pricingtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><?php lang("item"); ?></th>
|
||||
<th data-priority="4"><?php lang("cost"); ?></th>
|
||||
<th data-priority="3"><?php lang("normal price"); ?></th>
|
||||
<th data-priority="2"><?php lang("customer price"); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get("item"); ?></th>
|
||||
<th data-priority="4"><?php $Strings->get("cost"); ?></th>
|
||||
<th data-priority="3"><?php $Strings->get("normal price"); ?></th>
|
||||
<th data-priority="2"><?php $Strings->get("customer price"); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -165,7 +165,7 @@ if ($editing) {
|
||||
<tr data-itemid="<?php echo $p['itemid']; ?>">
|
||||
<td></td>
|
||||
<td>
|
||||
<div class="btn btn-sm btn-danger deletepricebtn" data-itemid="<?php echo $p['itemid']; ?>"><i class="fas fa-trash"></i> <?php lang("delete"); ?></div>
|
||||
<div class="btn btn-sm btn-danger deletepricebtn" data-itemid="<?php echo $p['itemid']; ?>"><i class="fas fa-trash"></i> <?php $Strings->get("delete"); ?></div>
|
||||
</td>
|
||||
<td>
|
||||
<input type="hidden" name="pricing[<?php echo $i; ?>][item]" value="<?php echo $p['itemid']; ?>" />
|
||||
@ -191,7 +191,7 @@ if ($editing) {
|
||||
<input type="hidden" name="source" value="customers" />
|
||||
|
||||
<div class="card-footer d-flex">
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php lang("save"); ?></button>
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -44,28 +44,28 @@ if ($editing) {
|
||||
<?php
|
||||
if ($editing) {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang2("editing register", ['name' => "<span id=\"name_title\">" . htmlspecialchars($regdata['name']) . "</span>"]); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->build("editing register", ['name' => "<span id=\"name_title\">" . htmlspecialchars($regdata['name']) . "</span>"]); ?>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<i class="fas fa-edit"></i> <?php lang("adding register"); ?>
|
||||
<i class="fas fa-edit"></i> <?php $Strings->get("adding register"); ?>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
</h3>
|
||||
<div class="card-body row">
|
||||
<div class="form-group col-12">
|
||||
<label for="name"><i class="fas fa-font"></i> <?php lang("name"); ?></label>
|
||||
<label for="name"><i class="fas fa-font"></i> <?php $Strings->get("name"); ?></label>
|
||||
<input type="text" class="form-control" id="name" name="name" placeholder="Register #1" required="required" value="<?php echo htmlspecialchars($regdata['name']); ?>" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body row">
|
||||
<div class="form-group col-12 col-md-6">
|
||||
<label for="zreport"><i class="fas fa-receipt"></i> <?php lang("z report"); ?></label>
|
||||
<label for="zreport"><i class="fas fa-receipt"></i> <?php $Strings->get("z report"); ?></label>
|
||||
<div class="input-group">
|
||||
<select id="zreport" class="form-control">
|
||||
<option value=""><?php lang("pick cash") ?></option>
|
||||
<option value=""><?php $Strings->get("pick cash") ?></option>
|
||||
<?php
|
||||
for ($i = count($cash) - 1; $i >= 0; $i--) {
|
||||
$c = $cash[$i];
|
||||
@ -78,7 +78,7 @@ if ($editing) {
|
||||
?>
|
||||
</select>
|
||||
<div class="input-group-append">
|
||||
<button type="button" id="printzreportbtn" class="btn btn-primary"><i class="fas fa-print"></i> <?php lang("print"); ?></button>
|
||||
<button type="button" id="printzreportbtn" class="btn btn-primary"><i class="fas fa-print"></i> <?php $Strings->get("print"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
<iframe id="zframe" class="w-100 shadow-lg"></iframe>
|
||||
@ -89,7 +89,7 @@ if ($editing) {
|
||||
<input type="hidden" name="source" value="registers" />
|
||||
|
||||
<div class="card-footer d-flex">
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php lang("save"); ?></button>
|
||||
<button type="submit" class="btn btn-success mr-auto"><i class="fas fa-save"></i> <?php $Strings->get("save"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
@ -16,12 +16,12 @@ $unshipped = $database->select('transactions', ['[>]customers' => 'customerid'],
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php lang('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php lang('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php lang('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php lang('notes'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php $Strings->get('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php $Strings->get('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php $Strings->get('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php $Strings->get('notes'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -31,7 +31,7 @@ $unshipped = $database->select('transactions', ['[>]customers' => 'customerid'],
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcustomer&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php lang("edit"); ?></a>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editcustomer&id=<?php echo $c['id']; ?>"><i class="fas fa-edit"></i> <?php $Strings->get("edit"); ?></a>
|
||||
</td>
|
||||
<td><?php echo $o['name']; ?></td>
|
||||
<td><?php echo $o['phone']; ?></td>
|
||||
@ -46,12 +46,12 @@ $unshipped = $database->select('transactions', ['[>]customers' => 'customerid'],
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php lang('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php lang('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php lang('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php lang('notes'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-user d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-phone d-none d-md-inline"></i> <?php $Strings->get('phone'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-at d-none d-md-inline"></i> <?php $Strings->get('email'); ?></th>
|
||||
<th data-priority="4"><i class="fas fa-fw fa-map-marker d-none d-md-inline"></i> <?php $Strings->get('address'); ?></th>
|
||||
<th data-priority="5"><i class="fas fa-fw fa-sticky-note d-none d-md-inline"></i> <?php $Strings->get('notes'); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
@ -5,7 +5,7 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
$register = [
|
||||
"name" => lang("no cash", false),
|
||||
"name" => $Strings->get("no cash", false),
|
||||
"id" => ""
|
||||
];
|
||||
|
||||
@ -45,18 +45,18 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-receipt"></i> <?php lang("receipt"); ?></h5>
|
||||
<h5 class="modal-title"><i class="fas fa-receipt"></i> <?php $Strings->get("receipt"); ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="display-4 text-center" id="receiptchangediv"><?php lang("change"); ?>: $<span id="receiptchange">0.00</span></div>
|
||||
<div class="display-4 text-center" id="receiptchangediv"><?php $Strings->get("change"); ?>: $<span id="receiptchange">0.00</span></div>
|
||||
<iframe class="w-100 shadow-lg" id="receiptframe"></iframe>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
|
||||
<button type="button" class="btn btn-primary" id="receiptprintbtn"><i class="fas fa-print"></i> <?php lang("print"); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php $Strings->get("close"); ?></button>
|
||||
<button type="button" class="btn btn-primary" id="receiptprintbtn"><i class="fas fa-print"></i> <?php $Strings->get("print"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -66,14 +66,14 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-user"></i> <?php lang("customer"); ?></h5>
|
||||
<h5 class="modal-title"><i class="fas fa-user"></i> <?php $Strings->get("customer"); ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="customersearch" placeholder="<?php lang("customer search"); ?>" />
|
||||
<input type="text" class="form-control" id="customersearch" placeholder="<?php $Strings->get("customer search"); ?>" />
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-link" type="button" id="customersearchbtn"><i class="fas fa-search"></i></button>
|
||||
</div>
|
||||
@ -82,7 +82,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php $Strings->get("close"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -92,7 +92,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title"><i class="fas fa-cog"></i> <?php lang("register management"); ?></h5>
|
||||
<h5 class="modal-title"><i class="fas fa-cog"></i> <?php $Strings->get("register management"); ?></h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@ -101,7 +101,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" id="transactionsearch" placeholder="<?php lang("transaction search"); ?>" />
|
||||
<input type="text" class="form-control" id="transactionsearch" placeholder="<?php $Strings->get("transaction search"); ?>" />
|
||||
<div class="input-group-append">
|
||||
<span class="btn btn-link open-number-pad-btn">
|
||||
<i class="fas fa-keyboard"></i>
|
||||
@ -135,15 +135,15 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="d-flex justify-content-between flex-wrap">
|
||||
<button type="button" class="btn btn-default" id="opendrawerbtn"><i class="fas fa-lock-open"></i> <?php lang("open drawer"); ?></button>
|
||||
<button type="button" class="btn btn-primary ml-auto" id="xprintbtn"><i class="fas fa-print"></i> <?php lang("print"); ?></button>
|
||||
<button type="button" class="btn btn-default" id="opendrawerbtn"><i class="fas fa-lock-open"></i> <?php $Strings->get("open drawer"); ?></button>
|
||||
<button type="button" class="btn btn-primary ml-auto" id="xprintbtn"><i class="fas fa-print"></i> <?php $Strings->get("print"); ?></button>
|
||||
</div>
|
||||
<iframe class="w-100 shadow-lg" id="xframe" src="action.php?action=xreport&format=html®ister=<?php echo $register['id']; ?>"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php lang("close"); ?></button>
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><?php $Strings->get("close"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -155,7 +155,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="card-header p-1">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="btn btn-default" id="gridviewbtn" title="<?php lang("grid view"); ?>"><i class="fas fa-th fa-fw"></i></span>
|
||||
<span class="btn btn-default" id="gridviewbtn" title="<?php $Strings->get("grid view"); ?>"><i class="fas fa-th fa-fw"></i></span>
|
||||
</div>
|
||||
<?php
|
||||
if (isset($_SESSION['mobile'])) {
|
||||
@ -166,7 +166,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
</span>
|
||||
</div>
|
||||
<?php } ?>
|
||||
<input type="text" class="form-control" id="barcode" placeholder="<?php lang("barcode or search"); ?>" />
|
||||
<input type="text" class="form-control" id="barcode" placeholder="<?php $Strings->get("barcode or search"); ?>" />
|
||||
<div class="input-group-append">
|
||||
<button class="btn btn-link" type="button" id="barcodebtn"><i class="fas fa-search"></i></button>
|
||||
</div>
|
||||
@ -235,8 +235,8 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="col-12 col-md-6 order-0 order-md-1">
|
||||
<div class="card mb-3 mb-md-0">
|
||||
<div class="w-100 position-absolute d-flex align-items-start px-3 pt-2">
|
||||
<a href="#" class="mr-auto text-body" id="openmanagement" data-toggle="tooltip" title="<?php lang("manage register") ?>"><i class="fas fa-cog"></i> <?php lang("manage"); ?></a>
|
||||
<a href="app.php?page=pos&switch" class="ml-auto text-body" id="register" data-id="<?php echo $register['id']; ?>" data-toggle="tooltip" title="<?php lang("change register") ?>"><i class="fas fa-exchange-alt"></i> <?php echo $register['name']; ?></a>
|
||||
<a href="#" class="mr-auto text-body" id="openmanagement" data-toggle="tooltip" title="<?php $Strings->get("manage register") ?>"><i class="fas fa-cog"></i> <?php $Strings->get("manage"); ?></a>
|
||||
<a href="app.php?page=pos&switch" class="ml-auto text-body" id="register" data-id="<?php echo $register['id']; ?>" data-toggle="tooltip" title="<?php $Strings->get("change register") ?>"><i class="fas fa-exchange-alt"></i> <?php echo $register['name']; ?></a>
|
||||
</div>
|
||||
<div class="display-4 p-1 p-md-3 text-center">$<span id="grand-total">0.00</span></div>
|
||||
<div class="card-body d-flex justify-content-center flex-wrap py-0 my-0">
|
||||
@ -247,7 +247,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
echo $tx['customername'];
|
||||
}
|
||||
?></span>
|
||||
<span class="sr-only"><?php lang("customer"); ?></span>
|
||||
<span class="sr-only"><?php $Strings->get("customer"); ?></span>
|
||||
</div>
|
||||
<?php
|
||||
if (!$returning) {
|
||||
@ -265,23 +265,23 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
}
|
||||
?></span>
|
||||
<i class="fas fa-percent"></i>
|
||||
<span class="sr-only"><?php lang("transaction discount"); ?></span>
|
||||
<span class="sr-only"><?php $Strings->get("transaction discount"); ?></span>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<div class="btn m-1" id="deletetxbtn">
|
||||
<i class="fas fa-trash"></i>
|
||||
<span class="sr-only"><?php lang("delete transaction"); ?></span>
|
||||
<span class="sr-only"><?php $Strings->get("delete transaction"); ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body d-md-none">
|
||||
<span class="btn btn-green btn-lg btn-block" id="paymentbtn"><i class="fas fa-money-bill-wave"></i> <?php
|
||||
if ($returning) {
|
||||
lang("enter refund");
|
||||
$Strings->get("enter refund");
|
||||
} else {
|
||||
lang("enter payment");
|
||||
$Strings->get("enter payment");
|
||||
}
|
||||
?></span>
|
||||
</div>
|
||||
@ -297,9 +297,9 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
continue;
|
||||
}
|
||||
?>
|
||||
<div class="card p-2 text-center m-1 payment-method-button" data-payment-method="<?php echo $data['name']; ?>" data-icon="<?php echo $data['icon']; ?>" data-text="<?php lang($data['text']); ?>">
|
||||
<div class="card p-2 text-center m-1 payment-method-button" data-payment-method="<?php echo $data['name']; ?>" data-icon="<?php echo $data['icon']; ?>" data-text="<?php $Strings->get($data['text']); ?>">
|
||||
<i class="<?php echo $data['icon']; ?> fa-3x fa-fw"></i>
|
||||
<?php lang($data['text']); ?>
|
||||
<?php $Strings->get($data['text']); ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@ -312,25 +312,25 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
if ($returning) {
|
||||
?>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("refund"); ?> $<span id="paid-amount">0.00</span>
|
||||
<?php $Strings->get("refund"); ?> $<span id="paid-amount">0.00</span>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("owed"); ?> $<span id="owed-amount">0.00</span>
|
||||
<?php $Strings->get("owed"); ?> $<span id="owed-amount">0.00</span>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("change"); ?> $<span id="change-amount">0.00</span>
|
||||
<?php $Strings->get("change"); ?> $<span id="change-amount">0.00</span>
|
||||
</div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("paid"); ?> $<span id="paid-amount">0.00</span>
|
||||
<?php $Strings->get("paid"); ?> $<span id="paid-amount">0.00</span>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("owed"); ?> $<span id="owed-amount">0.00</span>
|
||||
<?php $Strings->get("owed"); ?> $<span id="owed-amount">0.00</span>
|
||||
</div>
|
||||
<div class="col-12 col-sm-4">
|
||||
<?php lang("change"); ?> $<span id="change-amount">0.00</span>
|
||||
<?php $Strings->get("change"); ?> $<span id="change-amount">0.00</span>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
@ -349,7 +349,7 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="<?php echo $p['icon']; ?> fa-fw mr-1"></i>
|
||||
<?php lang($p['text']); ?>
|
||||
<?php $Strings->get($p['text']); ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="input-group-prepend">
|
||||
@ -397,11 +397,11 @@ if (isset($_GET['switch']) || !isset($_SESSION['register']) || !$registeropen) {
|
||||
<div class="card-body">
|
||||
<span class="btn btn-green btn-lg btn-block" id="finishbtn"><i class="fas fa-receipt"></i> <?php
|
||||
if ($editing) {
|
||||
lang("update");
|
||||
$Strings->get("update");
|
||||
} else if ($returning) {
|
||||
lang("return");
|
||||
$Strings->get("return");
|
||||
} else {
|
||||
lang("finish");
|
||||
$Strings->get("finish");
|
||||
}
|
||||
?></span>
|
||||
</div>
|
||||
|
@ -15,18 +15,18 @@ $registers = $database->select('registers', ['registerid (id)', 'registername (n
|
||||
?>
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<a href="app.php?page=editregister" class="btn btn-success"><i class="fas fa-plus"></i> <?php lang("add register"); ?></a>
|
||||
<a href="app.php?page=editregister" class="btn btn-success"><i class="fas fa-plus"></i> <?php $Strings->get("add register"); ?></a>
|
||||
</div>
|
||||
|
||||
<table id="registertable" class="table table-bordered table-hover table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-font d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php lang('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php lang('last opened'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-stop d-none d-md-inline"></i> <?php lang('closed'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-font d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php $Strings->get('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php $Strings->get('last opened'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-stop d-none d-md-inline"></i> <?php $Strings->get('closed'); ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@ -46,8 +46,8 @@ $registers = $database->select('registers', ['registerid (id)', 'registername (n
|
||||
$open = "";
|
||||
$close = "";
|
||||
if ($cash === false) {
|
||||
$open = lang("never", false);
|
||||
$close = lang("never", false);
|
||||
$open = $Strings->get("never", false);
|
||||
$close = $Strings->get("never", false);
|
||||
} else {
|
||||
if (!is_null($cash['end_amount']) && !is_null($cash['close'])) {
|
||||
$balance = (float) $cash['end_amount'];
|
||||
@ -66,13 +66,13 @@ $registers = $database->select('registers', ['registerid (id)', 'registername (n
|
||||
}
|
||||
}
|
||||
$open = date(DATETIME_FORMAT, strtotime($cash['open']));
|
||||
$close = is_null($cash['close']) ? lang("still open", false) : date(DATETIME_FORMAT, strtotime($cash['close']));
|
||||
$close = is_null($cash['close']) ? $Strings->get("still open", false) : date(DATETIME_FORMAT, strtotime($cash['close']));
|
||||
}
|
||||
?>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editregister&id=<?php echo $r['id']; ?>"><i class="fas fa-edit"></i> <?php lang("edit"); ?></a>
|
||||
<a class="btn btn-primary btn-sm" href="app.php?page=editregister&id=<?php echo $r['id']; ?>"><i class="fas fa-edit"></i> <?php $Strings->get("edit"); ?></a>
|
||||
<?php
|
||||
if (is_null($cash['close']) && !is_null($cash['open'])) {
|
||||
?>
|
||||
@ -80,12 +80,12 @@ $registers = $database->select('registers', ['registerid (id)', 'registername (n
|
||||
<input type="hidden" name="action" value="closecash" />
|
||||
<input type="hidden" name="source" value="registers" />
|
||||
<input type="hidden" name="register" value="<?php echo $r['id']; ?>" />
|
||||
<button class="btn btn-danger btn-sm" type="submit"><i class="fas fa-stop"></i> <?php lang("close"); ?></button>
|
||||
<button class="btn btn-danger btn-sm" type="submit"><i class="fas fa-stop"></i> <?php $Strings->get("close"); ?></button>
|
||||
</form>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
<button class="btn btn-success btn-sm btn-opencash" data-register="<?php echo $r['id']; ?>"><i class="fas fa-play"></i> <?php lang("open"); ?></button>
|
||||
<button class="btn btn-success btn-sm btn-opencash" data-register="<?php echo $r['id']; ?>"><i class="fas fa-play"></i> <?php $Strings->get("open"); ?></button>
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
@ -102,11 +102,11 @@ $registers = $database->select('registers', ['registerid (id)', 'registername (n
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th data-priority="0"></th>
|
||||
<th data-priority="1"><?php lang('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-font d-none d-md-inline"></i> <?php lang('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php lang('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php lang('last opened'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-stop d-none d-md-inline"></i> <?php lang('closed'); ?></th>
|
||||
<th data-priority="1"><?php $Strings->get('actions'); ?></th>
|
||||
<th data-priority="1"><i class="fas fa-fw fa-font d-none d-md-inline"></i> <?php $Strings->get('name'); ?></th>
|
||||
<th data-priority="2"><i class="fas fa-fw fa-balance-scale d-none d-md-inline"></i> <?php $Strings->get('balance'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-play d-none d-md-inline"></i> <?php $Strings->get('last opened'); ?></th>
|
||||
<th data-priority="3"><i class="fas fa-fw fa-stop d-none d-md-inline"></i> <?php $Strings->get('closed'); ?></th>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
@ -9,7 +9,7 @@ redirectifnotloggedin();
|
||||
|
||||
if (false) {
|
||||
?>
|
||||
<div class="alert alert-danger"><?php lang("missing permission") ?></div>
|
||||
<div class="alert alert-danger"><?php $Strings->get("missing permission") ?></div>
|
||||
<?php
|
||||
} else {
|
||||
?>
|
||||
@ -20,9 +20,9 @@ if (false) {
|
||||
<div class="col-12 col-sm-6 col-md-12 col-lg-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title"><label for="type"><i class="fas fa-list"></i> <?php lang("report type"); ?></label></h4>
|
||||
<h4 class="card-title"><label for="type"><i class="fas fa-list"></i> <?php $Strings->get("report type"); ?></label></h4>
|
||||
<select name="type" id="type" class="form-control" required>
|
||||
<option value="cashflow"><?php lang("cashflow") ?></option>
|
||||
<option value="cashflow"><?php $Strings->get("cashflow") ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@ -30,17 +30,17 @@ if (false) {
|
||||
<div class="col-12 col-sm-6 col-md-12 col-lg-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title"><label for="format"><i class="fas fa-file"></i> <?php lang("format"); ?></label></h4>
|
||||
<h4 class="card-title"><label for="format"><i class="fas fa-file"></i> <?php $Strings->get("format"); ?></label></h4>
|
||||
<select name="format" class="form-control" required>
|
||||
<option value="csv"><?php lang("csv file") ?></option>
|
||||
<option value="ods"><?php lang("ods file") ?></option>
|
||||
<option value="html"><?php lang("html file") ?></option>
|
||||
<option value="csv"><?php $Strings->get("csv file") ?></option>
|
||||
<option value="ods"><?php $Strings->get("ods file") ?></option>
|
||||
<option value="html"><?php $Strings->get("html file") ?></option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<button type="submit" class="btn btn-success btn-block d-none d-lg-block genrptbtn"><i class="fas fa-download"></i> <?php lang("generate report"); ?></button>
|
||||
<button type="submit" class="btn btn-success btn-block d-none d-lg-block genrptbtn"><i class="fas fa-download"></i> <?php $Strings->get("generate report"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -49,9 +49,9 @@ if (false) {
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h4 class="card-title"><label><i class="fas fa-filter"></i> <?php lang("filter"); ?></label></h4>
|
||||
<h4 class="card-title"><label><i class="fas fa-filter"></i> <?php $Strings->get("filter"); ?></label></h4>
|
||||
<div id="date-filter">
|
||||
<label><i class="fas fa-calendar"></i> <?php lang("date range") ?></label><br />
|
||||
<label><i class="fas fa-calendar"></i> <?php $Strings->get("date range") ?></label><br />
|
||||
<div class="input-group">
|
||||
<input type="text" id="startdate" name="startdate" data-toggle="datetimepicker" data-target="#startdate" class="form-control" />
|
||||
<span class="input-group-text"><i class="fas fa-chevron-right"></i></span>
|
||||
@ -59,9 +59,9 @@ if (false) {
|
||||
</div>
|
||||
</div>
|
||||
<div id="register-filter" class="mt-2">
|
||||
<label for="register"><i class="fas fa-store-alt"></i> <?php lang("register"); ?></label>
|
||||
<label for="register"><i class="fas fa-store-alt"></i> <?php $Strings->get("register"); ?></label>
|
||||
<select name="register" class="form-control">
|
||||
<option value=""><?php lang("all"); ?></option>
|
||||
<option value=""><?php $Strings->get("all"); ?></option>
|
||||
<?php
|
||||
$registers = $database->select('registers', ['registerid (id)', 'registername (name)']);
|
||||
foreach ($registers as $r) {
|
||||
@ -80,7 +80,7 @@ if (false) {
|
||||
?>
|
||||
<input type="hidden" name="code" value="<?php echo $code; ?>" />
|
||||
|
||||
<button type="submit" class="btn btn-success btn-block d-lg-none genrptbtn"><i class="fas fa-download"></i> <?php lang("generate report"); ?></button>
|
||||
<button type="submit" class="btn btn-success btn-block d-lg-none genrptbtn"><i class="fas fa-download"></i> <?php $Strings->get("generate report"); ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
57
required.php
57
required.php
@ -62,9 +62,14 @@ if ($_SESSION['mobile'] === TRUE) {
|
||||
require __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
// List of alert messages
|
||||
require __DIR__ . '/lang/messages.php';
|
||||
// text strings (i18n)
|
||||
require __DIR__ . '/lang/' . LANGUAGE . ".php";
|
||||
require __DIR__ . '/langs/messages.php';
|
||||
|
||||
$libs = glob(__DIR__ . "/lib/*.lib.php");
|
||||
foreach ($libs as $lib) {
|
||||
require_once $lib;
|
||||
}
|
||||
|
||||
$Strings = new Strings(LANGUAGE);
|
||||
|
||||
/**
|
||||
* Kill off the running process and spit out an error message
|
||||
@ -145,52 +150,6 @@ function is_empty($str) {
|
||||
return (is_null($str) || !isset($str) || $str == '');
|
||||
}
|
||||
|
||||
/**
|
||||
* I18N string getter. If the key doesn't exist, outputs the key itself.
|
||||
* @param string $key I18N string key
|
||||
* @param boolean $echo whether to echo the result or return it (default echo)
|
||||
*/
|
||||
function lang($key, $echo = true) {
|
||||
if (array_key_exists($key, STRINGS)) {
|
||||
$str = STRINGS[$key];
|
||||
} else {
|
||||
trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
|
||||
$str = $key;
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo $str;
|
||||
} else {
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* I18N string getter (with builder). If the key doesn't exist, outputs the key itself.
|
||||
* @param string $key I18N string key
|
||||
* @param array $replace key-value array of replacements.
|
||||
* If the string value is "hello {abc}" and you give ["abc" => "123"], the
|
||||
* result will be "hello 123".
|
||||
* @param boolean $echo whether to echo the result or return it (default echo)
|
||||
*/
|
||||
function lang2($key, $replace, $echo = true) {
|
||||
if (array_key_exists($key, STRINGS)) {
|
||||
$str = STRINGS[$key];
|
||||
} else {
|
||||
trigger_error("Language key \"$key\" does not exist in " . LANGUAGE, E_USER_WARNING);
|
||||
$str = $key;
|
||||
}
|
||||
|
||||
foreach ($replace as $find => $repl) {
|
||||
$str = str_replace("{" . $find . "}", $repl, $str);
|
||||
}
|
||||
|
||||
if ($echo) {
|
||||
echo $str;
|
||||
} else {
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
function dieifnotloggedin() {
|
||||
if ($_SESSION['loggedin'] != true) {
|
||||
|
6
static/css/bootstrap.min.css
vendored
6
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,5 +1,5 @@
|
||||
/*!
|
||||
* Font Awesome Free 5.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Font Awesome Free 5.3.1 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
*/
|
||||
.svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible}.svg-inline--fa{display:inline-block;font-size:inherit;height:1em;vertical-align:-.125em}.svg-inline--fa.fa-lg{vertical-align:-.225em}.svg-inline--fa.fa-w-1{width:.0625em}.svg-inline--fa.fa-w-2{width:.125em}.svg-inline--fa.fa-w-3{width:.1875em}.svg-inline--fa.fa-w-4{width:.25em}.svg-inline--fa.fa-w-5{width:.3125em}.svg-inline--fa.fa-w-6{width:.375em}.svg-inline--fa.fa-w-7{width:.4375em}.svg-inline--fa.fa-w-8{width:.5em}.svg-inline--fa.fa-w-9{width:.5625em}.svg-inline--fa.fa-w-10{width:.625em}.svg-inline--fa.fa-w-11{width:.6875em}.svg-inline--fa.fa-w-12{width:.75em}.svg-inline--fa.fa-w-13{width:.8125em}.svg-inline--fa.fa-w-14{width:.875em}.svg-inline--fa.fa-w-15{width:.9375em}.svg-inline--fa.fa-w-16{width:1em}.svg-inline--fa.fa-w-17{width:1.0625em}.svg-inline--fa.fa-w-18{width:1.125em}.svg-inline--fa.fa-w-19{width:1.1875em}.svg-inline--fa.fa-w-20{width:1.25em}.svg-inline--fa.fa-pull-left{margin-right:.3em;width:auto}.svg-inline--fa.fa-pull-right{margin-left:.3em;width:auto}.svg-inline--fa.fa-border{height:1.5em}.svg-inline--fa.fa-li{width:2em}.svg-inline--fa.fa-fw{width:1.25em}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{transform-origin:center center}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:#ff253a;border-radius:1em;box-sizing:border-box;color:#fff;height:1.5em;line-height:1;max-width:5em;min-width:1.5em;overflow:hidden;padding:.25em;right:0;text-overflow:ellipsis;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-bottom-right{bottom:0;right:0;top:auto;transform:scale(.25);transform-origin:bottom right}.fa-layers-bottom-left{bottom:0;left:0;right:auto;top:auto;transform:scale(.25);transform-origin:bottom left}.fa-layers-top-right{right:0;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-top-left{left:0;right:auto;top:0;transform:scale(.25);transform-origin:top left}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:a 2s infinite linear}.fa-pulse{animation:a 1s infinite steps(8)}@keyframes a{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1em}.svg-inline--fa.fa-stack-2x{height:2em;width:2em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}
|
||||
.svg-inline--fa,svg:not(:root).svg-inline--fa{overflow:visible}.svg-inline--fa{display:inline-block;font-size:inherit;height:1em;vertical-align:-.125em}.svg-inline--fa.fa-lg{vertical-align:-.225em}.svg-inline--fa.fa-w-1{width:.0625em}.svg-inline--fa.fa-w-2{width:.125em}.svg-inline--fa.fa-w-3{width:.1875em}.svg-inline--fa.fa-w-4{width:.25em}.svg-inline--fa.fa-w-5{width:.3125em}.svg-inline--fa.fa-w-6{width:.375em}.svg-inline--fa.fa-w-7{width:.4375em}.svg-inline--fa.fa-w-8{width:.5em}.svg-inline--fa.fa-w-9{width:.5625em}.svg-inline--fa.fa-w-10{width:.625em}.svg-inline--fa.fa-w-11{width:.6875em}.svg-inline--fa.fa-w-12{width:.75em}.svg-inline--fa.fa-w-13{width:.8125em}.svg-inline--fa.fa-w-14{width:.875em}.svg-inline--fa.fa-w-15{width:.9375em}.svg-inline--fa.fa-w-16{width:1em}.svg-inline--fa.fa-w-17{width:1.0625em}.svg-inline--fa.fa-w-18{width:1.125em}.svg-inline--fa.fa-w-19{width:1.1875em}.svg-inline--fa.fa-w-20{width:1.25em}.svg-inline--fa.fa-pull-left{margin-right:.3em;width:auto}.svg-inline--fa.fa-pull-right{margin-left:.3em;width:auto}.svg-inline--fa.fa-border{height:1.5em}.svg-inline--fa.fa-li{width:2em}.svg-inline--fa.fa-fw{width:1.25em}.fa-layers svg.svg-inline--fa{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:1em}.fa-layers svg.svg-inline--fa{transform-origin:center center}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:#ff253a;border-radius:1em;box-sizing:border-box;color:#fff;height:1.5em;line-height:1;max-width:5em;min-width:1.5em;overflow:hidden;padding:.25em;right:0;text-overflow:ellipsis;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-bottom-right{bottom:0;right:0;top:auto;transform:scale(.25);transform-origin:bottom right}.fa-layers-bottom-left{bottom:0;left:0;right:auto;top:auto;transform:scale(.25);transform-origin:bottom left}.fa-layers-top-right{right:0;top:0;transform:scale(.25);transform-origin:top right}.fa-layers-top-left{left:0;right:auto;top:0;transform:scale(.25);transform-origin:top left}.fa-lg{font-size:1.33333em;line-height:.75em;vertical-align:-.0667em}.fa-xs{font-size:.75em}.fa-sm{font-size:.875em}.fa-1x{font-size:1em}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-6x{font-size:6em}.fa-7x{font-size:7em}.fa-8x{font-size:8em}.fa-9x{font-size:9em}.fa-10x{font-size:10em}.fa-fw{text-align:center;width:1.25em}.fa-ul{list-style-type:none;margin-left:2.5em;padding-left:0}.fa-ul>li{position:relative}.fa-li{left:-2em;position:absolute;text-align:center;width:2em;line-height:inherit}.fa-border{border:.08em solid #eee;border-radius:.1em;padding:.2em .25em .15em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left,.fab.fa-pull-left,.fal.fa-pull-left,.far.fa-pull-left,.fas.fa-pull-left{margin-right:.3em}.fa.fa-pull-right,.fab.fa-pull-right,.fal.fa-pull-right,.far.fa-pull-right,.fas.fa-pull-right{margin-left:.3em}.fa-spin{animation:fa-spin 2s infinite linear}.fa-pulse{animation:fa-spin 1s infinite steps(8)}@keyframes fa-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";transform:scaleX(-1)}.fa-flip-vertical{transform:scaleY(-1)}.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-horizontal,:root .fa-flip-vertical,:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270{-webkit-filter:none;filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2em}.fa-stack-1x,.fa-stack-2x{bottom:0;left:0;margin:auto;position:absolute;right:0;top:0}.svg-inline--fa.fa-stack-1x{height:1em;width:1em}.svg-inline--fa.fa-stack-2x{height:2em;width:2em}.fa-inverse{color:#fff}.sr-only{border:0;clip:rect(0,0,0,0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.sr-only-focusable:active,.sr-only-focusable:focus{clip:auto;height:auto;margin:0;overflow:visible;position:static;width:auto}
|
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
7
static/js/bootstrap.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
7
static/js/bootstrap.min.js
vendored
7
static/js/bootstrap.min.js
vendored
File diff suppressed because one or more lines are too long
6
static/js/fontawesome-all.min.js
vendored
6
static/js/fontawesome-all.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user