Skip to content

Commit 91b0b7e

Browse files
author
Ahmad Gneady
committed
As generated by AppGini 22.12
1 parent a911f9f commit 91b0b7e

39 files changed

+270
-140
lines changed

app/admin/getUsers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This script and data application were generated by AppGini 22.11
2+
// This script and data application were generated by AppGini 22.12
33
// Download AppGini for free from https://bigprof.com/appgini/download/
44

55
/*

app/admin/incFunctions.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,7 +1347,10 @@ function is_allowed_username($username, $exception = false) {
13471347
2. when validating a submitted form: if(!csrf_token(true)) { reject_submission_somehow(); }
13481348
*/
13491349
function csrf_token($validate = false, $token_only = false) {
1350-
$token_age = 60 * 60;
1350+
// a long token age is better for UX with SPA and browser back/forward buttons
1351+
// and it would expire when the session ends anyway
1352+
$token_age = 86400 * 2;
1353+
13511354
/* retrieve token from session */
13521355
$csrf_token = (isset($_SESSION['csrf_token']) ? $_SESSION['csrf_token'] : false);
13531356
$csrf_token_expiry = (isset($_SESSION['csrf_token_expiry']) ? $_SESSION['csrf_token_expiry'] : false);
@@ -1443,7 +1446,7 @@ class Notification{
14431446
* in the main document, initiate notifications support using this PHP code:
14441447
echo Notification::placeholder();
14451448
1446-
* whenever you want to show a notifcation, use this PHP code:
1449+
* whenever you want to show a notifcation, use this PHP code inside a script tag:
14471450
echo Notification::show([
14481451
'message' => 'Notification text to display',
14491452
'class' => 'danger', // or other bootstrap state cues, 'default' if not provided

app/admin/incHeader.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,10 @@ function hideDialogs() {
201201
<li class="dropdown">
202202
<a href="#" class="dropdown-toggle" data-toggle="dropdown"><i class="glyphicon glyphicon-cog"></i> <?php echo $Translation['utilities']; ?> <b class="caret"></b></a>
203203
<ul class="dropdown-menu">
204-
<li><a href="pageSettings.php"><i class="glyphicon menu-item-icon text-info glyphicon-cog"></i> <?php echo $Translation['admin settings']; ?></a></li>
205-
<li class="divider"></li>
204+
<?php if(Authentication::getSuperAdmin()) { ?>
205+
<li><a href="pageSettings.php"><i class="glyphicon menu-item-icon text-info glyphicon-cog"></i> <?php echo $Translation['admin settings']; ?></a></li>
206+
<li class="divider"></li>
207+
<?php } ?>
206208
<li><a href="pageMail.php?sendToAll=1"><i class="glyphicon menu-item-icon text-info glyphicon-envelope"></i> <?php echo $Translation['mail all users']; ?></a></li>
207209
<li><a href="pageServerStatus.php"><i class="glyphicon menu-item-icon text-info glyphicon-hdd"></i> <?php echo $Translation['server status']; ?></a></li>
208210
<li><a href="app-documentation.php"><i class="glyphicon menu-item-icon text-info glyphicon-book"></i> <?php echo $Translation['app documentation']; ?></a></li>

app/admin/pageQueryLogs.php

Lines changed: 59 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@
44
// Retrieve request (type, page)
55
$queryTypes = [
66
'slow' => 'COALESCE(`duration`, 0) > 0',
7-
'error' => 'CHAR_LENGTH(COALESCE(`error`, \'\')) > 0',
7+
'error' => "CHAR_LENGTH(COALESCE(`error`, '')) > 0",
88
];
99

10-
$type = Request::val('type');
11-
if(!in_array($type, array_keys($queryTypes))) $type = 'slow';
10+
$type = Request::oneOf('type', array_keys($queryTypes), 'slow');
11+
$clear = Request::oneOf('clear', array_keys($queryTypes), false);
1212

1313
$page = intval(Request::val('page'));
1414
if($page < 1) $page = 1;
@@ -21,9 +21,14 @@
2121
if(!createQueryLogTable())
2222
dieErrorPage($Translation['Query log table does not exist']);
2323

24-
// only keep queries that are at most 2 months old
24+
// clear logs of specified type if requested
2525
$eo = ['silentErrors' => true];
26-
sql("DELETE FROM `appgini_query_log` WHERE `datetime` < DATE_SUB(NOW(), INTERVAL 2 MONTH)", $eo);
26+
if($clear) {
27+
sql("DELETE FROM `appgini_query_log` WHERE {$queryTypes[$clear]}", $eo);
28+
redirect('admin/pageQueryLogs.php?cleared=1&type=' . $clear);
29+
} else
30+
// only keep queries that are at most 2 months old
31+
sql("DELETE FROM `appgini_query_log` WHERE `datetime` < DATE_SUB(NOW(), INTERVAL 2 MONTH)", $eo);
2732

2833
/* TODO future work: allow filtering by user, date period, ... etc, and allow sorting */
2934

@@ -57,16 +62,27 @@
5762
?>
5863

5964
<!-- Page content -->
65+
<div class="alert alert-info alert-dismissible cleared-notification hidden">
66+
<button type="button" class="close" data-dismiss="alert">&times;</button>
67+
<?php echo str_replace('<TYPE>', ucfirst($type), $Translation['query logs cleared']); ?>
68+
</div>
69+
6070
<div class="page-header"><h1><?php echo $Translation['Query logs']; ?></h1></div>
6171

72+
<p><?php echo $Translation['query logs older than 2 month auto cleared']; ?></p>
73+
6274
<ul class="nav nav-tabs" style="margin-bottom: 2em;">
63-
<li class="query-type slow"><a href="#" class="text-warning bg-warning">
64-
<span class="label label-warning"><?php echo $counts['slow']; ?></span>
65-
<?php echo $Translation['slow queries']; ?>
75+
<li class="query-type slow"><a href="#" class="bg-warning">
76+
<span class="text-warning">
77+
<?php echo $Translation['slow queries']; ?>
78+
<span class="badge"><?php echo $counts['slow']; ?></span>
79+
</span>
6680
</a></li>
67-
<li class="query-type error"><a href="#" class="text-danger bg-danger">
68-
<span class="label label-danger"><?php echo $counts['error']; ?></span>
69-
<?php echo $Translation['error queries']; ?>
81+
<li class="query-type error"><a href="#" class="bg-danger">
82+
<span class="text-danger">
83+
<?php echo $Translation['error queries']; ?>
84+
<span class="badge"><?php echo $counts['error']; ?></span>
85+
</span>
7086
</a></li>
7187
</ul>
7288

@@ -75,14 +91,30 @@
7591
<?php echo $Translation['no matching results found']; ?>
7692
</div>
7793

94+
<!-- buttons to clear logs -->
95+
<div class="text-center bspacer-lg">
96+
<a href="pageQueryLogs.php?clear=slow&type=slow" class="btn btn-default clear-logs clear-slow hidden">
97+
<span class="text-warning">
98+
<i class="glyphicon glyphicon-trash"></i>
99+
<?php echo $Translation['clear slow queries']; ?>
100+
</span>
101+
</a>
102+
<a href="pageQueryLogs.php?clear=error&type=error" class="btn btn-default clear-logs clear-error hidden">
103+
<span class="text-danger">
104+
<i class="glyphicon glyphicon-trash"></i>
105+
<?php echo $Translation['clear error queries']; ?>
106+
</span>
107+
</a>
108+
</div>
109+
78110
<div class="table-responsive">
79111
<table class="table table-striped table-hover table-bordered" id="queryLogs">
80112
<thead>
81113
<tr>
82114
<th><?php echo $Translation['date/time']; ?> <i class="glyphicon glyphicon-sort-by-attributes-alt"></i> </th>
83115
<th><?php echo $Translation['username']; ?></th>
84116
<th><?php echo $Translation['page address']; ?></th>
85-
<th><?php echo $Translation['query']; ?></th>
117+
<th style="min-width: 40vw;"><?php echo $Translation['query']; ?></th>
86118
</tr>
87119
</thead>
88120

@@ -142,12 +174,18 @@
142174
var type = '<?php echo $type; ?>';
143175
var page = <?php echo $page; ?>, lastPage = <?php echo $lastPage; ?>;
144176

177+
// show cleared message if 'cleared=1'
178+
$j('.cleared-notification').toggleClass('hidden', location.search.indexOf('cleared=1') == -1);
179+
145180
// set active query-type button based on type
146-
$j('.query-type.' + type).addClass('active text-bold');
181+
$j(`.query-type.${type}`).addClass('active text-bold');
182+
183+
// show clear button for specified type
184+
$j(`.clear-logs.clear-${type}`).removeClass('hidden');
147185

148186
// hide table and show no matches if 0 records
149187
var noMatches = !$j('#queryLogs > tbody > tr').length;
150-
$j('#queryLogs').toggleClass('hidden', noMatches);
188+
$j(`#queryLogs, .clear-logs.clear-${type}`).toggleClass('hidden', noMatches);
151189
$j('#noMatches').toggleClass('hidden', !noMatches);
152190

153191
// hide non relevent parts based on type
@@ -160,7 +198,13 @@
160198
location.href = 'pageQueryLogs.php?type=' + (type == 'slow' ? 'error' : 'slow');
161199
})
162200

163-
// toogle next/previous links
201+
// confirm clearing logs
202+
$j('.clear-logs').on('click', (e) => {
203+
if(!confirm(<?php echo json_encode($Translation['confirm clear logs']); ?>))
204+
e.preventDefault();
205+
})
206+
207+
// toggle next/previous links
164208
var prevPage = page > 1 ? page - 1 : 1;
165209
var nextPage = page < lastPage ? page + 1 : lastPage;
166210
$j('.btn-previous').toggleClass('hidden', page == 1);

app/admin/pageServerStatus.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?php
2-
$appgini_version = '22.11.1257';
3-
$generated_ts = '10/2/2022 11:22:18 PM';
2+
$appgini_version = '22.12.1273';
3+
$generated_ts = '3/3/2022 5:58:15 PM';
44

55
require(__DIR__ . '/incCommon.php');
66

app/admin/pageSettings.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
<?php
22
require(__DIR__ . '/incCommon.php');
3+
4+
// only super admin can access this page!
5+
if(!Authentication::getSuperAdmin()) redirect('admin');
6+
37
$GLOBALS['page_title'] = $Translation['admin settings'];
48
include(__DIR__ . '/incHeader.php');
59

app/admin/pageViewRecords.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@
174174
<div class="modal-content">
175175
<button type="button" class="close hspacer-md vspacer-md" data-dismiss="modal">&times;</button>
176176
<div class="modal-body" style="-webkit-overflow-scrolling:touch !important; overflow-y: auto;">
177-
<iframe width="100%" height="100%" sandbox="allow-forms allow-scripts allow-same-origin allow-popups-to-escape-sandbox" src="" id="view-record-iframe"></iframe>
177+
<iframe width="100%" height="100%" sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-downloads" src="" id="view-record-iframe"></iframe>
178178
</div>
179179
</div>
180180
</div>

app/ajax_combo.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This script and data application were generated by AppGini 22.11
2+
// This script and data application were generated by AppGini 22.12
33
// Download AppGini for free from https://bigprof.com/appgini/download/
44

55
/*
@@ -188,7 +188,7 @@
188188
?><span id="<?php echo $field_name; ?>-combo-list"><?php echo $combo->HTML; ?></span><?php
189189
} else {
190190
?>
191-
<span id="<?php echo $field_name; ?>-match-text"><?php echo $combo->MatchText; ?></span>
191+
<span class="match-text" id="<?php echo $field_name; ?>-match-text"><?php echo $combo->MatchText; ?></span>
192192
<input type="hidden" id="<?php echo $field_name; ?>" value="<?php echo html_attr($combo->SelectedData); ?>">
193193
<?php
194194
}

app/assignments_autofill.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This script and data application were generated by AppGini 22.11
2+
// This script and data application were generated by AppGini 22.12
33
// Download AppGini for free from https://bigprof.com/appgini/download/
44

55
include_once(__DIR__ . '/lib.php');

app/assignments_dml.php

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
// Data functions (insert, update, delete, form) for table assignments
44

5-
// This script and data application were generated by AppGini 22.11
5+
// This script and data application were generated by AppGini 22.12
66
// Download AppGini for free from https://bigprof.com/appgini/download/
77

88
function assignments_insert(&$error_message = '') {
@@ -323,7 +323,7 @@ function ProjectId_reload__RAND__() {
323323
text: resp.results[0].text
324324
});
325325
$j('[name="ProjectId"]').val(resp.results[0].id);
326-
$j('[id=ProjectId-container-readonly__RAND__]').html('<span id="ProjectId-match-text">' + resp.results[0].text + '</span>');
326+
$j('[id=ProjectId-container-readonly__RAND__]').html('<span class="match-text" id="ProjectId-match-text">' + resp.results[0].text + '</span>');
327327
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=projects_view_parent]').hide(); } else { $j('.btn[id=projects_view_parent]').show(); }
328328

329329

@@ -360,7 +360,7 @@ function ProjectId_reload__RAND__() {
360360
data: { id: AppGini.current_ProjectId__RAND__.value, t: 'assignments', f: 'ProjectId' },
361361
success: function(resp) {
362362
$j('[name="ProjectId"]').val(resp.results[0].id);
363-
$j('[id=ProjectId-container-readonly__RAND__]').html('<span id="ProjectId-match-text">' + resp.results[0].text + '</span>');
363+
$j('[id=ProjectId-container-readonly__RAND__]').html('<span class="match-text" id="ProjectId-match-text">' + resp.results[0].text + '</span>');
364364
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=projects_view_parent]').hide(); } else { $j('.btn[id=projects_view_parent]').show(); }
365365

366366
if(typeof(ProjectId_update_autofills__RAND__) == 'function') ProjectId_update_autofills__RAND__();
@@ -375,7 +375,7 @@ function ProjectId_reload__RAND__() {
375375
dataType: 'json',
376376
data: { id: AppGini.current_ProjectId__RAND__.value, t: 'assignments', f: 'ProjectId' },
377377
success: function(resp) {
378-
$j('[id=ProjectId-container__RAND__], [id=ProjectId-container-readonly__RAND__]').html('<span id="ProjectId-match-text">' + resp.results[0].text + '</span>');
378+
$j('[id=ProjectId-container__RAND__], [id=ProjectId-container-readonly__RAND__]').html('<span class="match-text" id="ProjectId-match-text">' + resp.results[0].text + '</span>');
379379
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=projects_view_parent]').hide(); } else { $j('.btn[id=projects_view_parent]').show(); }
380380

381381
if(typeof(ProjectId_update_autofills__RAND__) == 'function') ProjectId_update_autofills__RAND__();
@@ -400,7 +400,7 @@ function ResourceId_reload__RAND__() {
400400
text: resp.results[0].text
401401
});
402402
$j('[name="ResourceId"]').val(resp.results[0].id);
403-
$j('[id=ResourceId-container-readonly__RAND__]').html('<span id="ResourceId-match-text">' + resp.results[0].text + '</span>');
403+
$j('[id=ResourceId-container-readonly__RAND__]').html('<span class="match-text" id="ResourceId-match-text">' + resp.results[0].text + '</span>');
404404
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=resources_view_parent]').hide(); } else { $j('.btn[id=resources_view_parent]').show(); }
405405

406406

@@ -437,7 +437,7 @@ function ResourceId_reload__RAND__() {
437437
data: { id: AppGini.current_ResourceId__RAND__.value, t: 'assignments', f: 'ResourceId' },
438438
success: function(resp) {
439439
$j('[name="ResourceId"]').val(resp.results[0].id);
440-
$j('[id=ResourceId-container-readonly__RAND__]').html('<span id="ResourceId-match-text">' + resp.results[0].text + '</span>');
440+
$j('[id=ResourceId-container-readonly__RAND__]').html('<span class="match-text" id="ResourceId-match-text">' + resp.results[0].text + '</span>');
441441
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=resources_view_parent]').hide(); } else { $j('.btn[id=resources_view_parent]').show(); }
442442

443443
if(typeof(ResourceId_update_autofills__RAND__) == 'function') ResourceId_update_autofills__RAND__();
@@ -452,7 +452,7 @@ function ResourceId_reload__RAND__() {
452452
dataType: 'json',
453453
data: { id: AppGini.current_ResourceId__RAND__.value, t: 'assignments', f: 'ResourceId' },
454454
success: function(resp) {
455-
$j('[id=ResourceId-container__RAND__], [id=ResourceId-container-readonly__RAND__]').html('<span id="ResourceId-match-text">' + resp.results[0].text + '</span>');
455+
$j('[id=ResourceId-container__RAND__], [id=ResourceId-container-readonly__RAND__]').html('<span class="match-text" id="ResourceId-match-text">' + resp.results[0].text + '</span>');
456456
if(resp.results[0].id == '<?php echo empty_lookup_value; ?>') { $j('.btn[id=resources_view_parent]').hide(); } else { $j('.btn[id=resources_view_parent]').show(); }
457457

458458
if(typeof(ResourceId_update_autofills__RAND__) == 'function') ResourceId_update_autofills__RAND__();
@@ -557,12 +557,12 @@ function ResourceId_reload__RAND__() {
557557

558558
// process foreign key links
559559
if($pt_perm['view'] || $pt_perm['edit']) {
560-
$templateCode = str_replace("<%%PLINK({$luf})%%>", '<button type="button" class="btn btn-default view_parent hspacer-md" id="' . $ptfc[0] . '_view_parent" title="' . html_attr($Translation['View'] . ' ' . $ptfc[1]) . '"><i class="glyphicon glyphicon-eye-open"></i></button>', $templateCode);
560+
$templateCode = str_replace("<%%PLINK({$luf})%%>", '<button type="button" class="btn btn-default view_parent" id="' . $ptfc[0] . '_view_parent" title="' . html_attr($Translation['View'] . ' ' . $ptfc[1]) . '"><i class="glyphicon glyphicon-eye-open"></i></button>', $templateCode);
561561
}
562562

563563
// if user has insert permission to parent table of a lookup field, put an add new button
564564
if($pt_perm['insert'] /* && !Request::val('Embedded')*/) {
565-
$templateCode = str_replace("<%%ADDNEW({$ptfc[0]})%%>", '<button type="button" class="btn btn-success add_new_parent hspacer-md" id="' . $ptfc[0] . '_add_new" title="' . html_attr($Translation['Add New'] . ' ' . $ptfc[1]) . '"><i class="glyphicon glyphicon-plus-sign"></i></button>', $templateCode);
565+
$templateCode = str_replace("<%%ADDNEW({$ptfc[0]})%%>", '<button type="button" class="btn btn-default add_new_parent" id="' . $ptfc[0] . '_add_new" title="' . html_attr($Translation['Add New'] . ' ' . $ptfc[1]) . '"><i class="glyphicon glyphicon-plus text-success"></i></button>', $templateCode);
566566
}
567567
}
568568

@@ -588,10 +588,10 @@ function ResourceId_reload__RAND__() {
588588
if( $dvprint) $templateCode = str_replace('<%%VALUE(Commitment)%%>', safe_html($urow['Commitment']), $templateCode);
589589
if(!$dvprint) $templateCode = str_replace('<%%VALUE(Commitment)%%>', html_attr($row['Commitment']), $templateCode);
590590
$templateCode = str_replace('<%%URLVALUE(Commitment)%%>', urlencode($urow['Commitment']), $templateCode);
591-
$templateCode = str_replace('<%%VALUE(StartDate)%%>', @date('d/m/Y', @strtotime(html_attr($row['StartDate']))), $templateCode);
592-
$templateCode = str_replace('<%%URLVALUE(StartDate)%%>', urlencode(@date('d/m/Y', @strtotime(html_attr($urow['StartDate'])))), $templateCode);
593-
$templateCode = str_replace('<%%VALUE(EndDate)%%>', @date('d/m/Y', @strtotime(html_attr($row['EndDate']))), $templateCode);
594-
$templateCode = str_replace('<%%URLVALUE(EndDate)%%>', urlencode(@date('d/m/Y', @strtotime(html_attr($urow['EndDate'])))), $templateCode);
591+
$templateCode = str_replace('<%%VALUE(StartDate)%%>', app_datetime($row['StartDate']), $templateCode);
592+
$templateCode = str_replace('<%%URLVALUE(StartDate)%%>', urlencode(app_datetime($urow['StartDate'])), $templateCode);
593+
$templateCode = str_replace('<%%VALUE(EndDate)%%>', app_datetime($row['EndDate']), $templateCode);
594+
$templateCode = str_replace('<%%URLVALUE(EndDate)%%>', urlencode(app_datetime($urow['EndDate'])), $templateCode);
595595
} else {
596596
$templateCode = str_replace('<%%VALUE(Id)%%>', '', $templateCode);
597597
$templateCode = str_replace('<%%URLVALUE(Id)%%>', urlencode(''), $templateCode);
@@ -646,8 +646,8 @@ function ResourceId_reload__RAND__() {
646646
$templateCode .= "\t\t\turl: 'assignments_autofill.php?rnd1=$rnd1&mfk=ProjectId&id=' + encodeURIComponent(AppGini.current_ProjectId{$rnd1}.value),\n";
647647
$templateCode .= "\t\t\tcontentType: 'application/x-www-form-urlencoded; charset=" . datalist_db_encoding . "',\n";
648648
$templateCode .= "\t\t\ttype: 'GET',\n";
649-
$templateCode .= "\t\t\tbeforeSend: function() { \$j('#ProjectId$rnd1').prop('disabled', true); \$j('#ProjectIdLoading').html('<img src=loading.gif align=top>'); },\n";
650-
$templateCode .= "\t\t\tcomplete: function() { " . (($arrPerm[1] || (($arrPerm[3] == 1 && $ownerMemberID == getLoggedMemberID()) || ($arrPerm[3] == 2 && $ownerGroupID == getLoggedGroupID()) || $arrPerm[3] == 3)) ? "\$j('#ProjectId$rnd1').prop('disabled', false); " : "\$j('#ProjectId$rnd1').prop('disabled', true); ")."\$j('#ProjectIdLoading').html(''); \$j(window).resize(); }\n";
649+
$templateCode .= "\t\t\tbeforeSend: function() { \$j('#ProjectId$rnd1').prop('disabled', true); },\n";
650+
$templateCode .= "\t\t\tcomplete: function() { " . (($arrPerm[1] || (($arrPerm[3] == 1 && $ownerMemberID == getLoggedMemberID()) || ($arrPerm[3] == 2 && $ownerGroupID == getLoggedGroupID()) || $arrPerm[3] == 3)) ? "\$j('#ProjectId$rnd1').prop('disabled', false); " : "\$j('#ProjectId$rnd1').prop('disabled', true); ")." \$j(window).resize(); }\n";
651651
}
652652
$templateCode .= "\t\t});\n";
653653
$templateCode .= "\t};\n";

app/assignments_view.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<?php
2-
// This script and data application were generated by AppGini 22.11
2+
// This script and data application were generated by AppGini 22.12
33
// Download AppGini for free from https://bigprof.com/appgini/download/
44

55
include_once(__DIR__ . '/lib.php');

0 commit comments

Comments
 (0)