Browse Source

Merge BusinessAppTemplate (new settings.php format)

# Conflicts:
#	api.php
#	api/apisettings.php
#	api/index.php
#	app.php
#	index.php
#	langs/en/titles.json
#	lib/Login.lib.php
#	lib/Notifications.lib.php
#	lib/User.lib.php
#	mobile/index.php
#	required.php
#	settings.template.php
Skylar Ittner 4 months ago
parent
commit
bb5639c447

+ 3
- 1
api.php View File

@@ -4,4 +4,6 @@
4 4
  * License, v. 2.0. If a copy of the MPL was not distributed with this
5 5
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 6
 
7
-require __DIR__ . "/api/index.php";
7
+
8
+// Load in new API from legacy location (a.k.a. here)
9
+require __DIR__ . "/api/index.php";

+ 1
- 1
api/actions/listapps.php View File

@@ -6,7 +6,7 @@
6 6
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 7
  */
8 8
 
9
-$apps = EXTERNAL_APPS;
9
+$apps = $SETTINGS['apps'];
10 10
 // Format paths as absolute URLs
11 11
 foreach ($apps as $k => $v) {
12 12
     if (strpos($apps[$k]['url'], "http") === FALSE) {

+ 1
- 1
api/actions/mobileenabled.php View File

@@ -6,4 +6,4 @@
6 6
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 7
  */
8 8
 
9
-exitWithJson(["status" => "OK", "mobile" => MOBILE_ENABLED]);
9
+exitWithJson(["status" => "OK", "mobile" => $SETTINGS['mobile_enabled']]);

+ 34
- 28
app.php View File

@@ -1,5 +1,4 @@
1 1
 <?php
2
-
3 2
 /* This Source Code Form is subject to the terms of the Mozilla Public
4 3
  * License, v. 2.0. If a copy of the MPL was not distributed with this
5 4
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@@ -14,7 +13,7 @@ if ($_SESSION['loggedin'] != true) {
14 13
 require_once __DIR__ . "/pages.php";
15 14
 
16 15
 $pageid = "home";
17
-if (isset($_GET['page']) && !empty($_GET['page'])) {
16
+if (!empty($_GET['page'])) {
18 17
     $pg = strtolower($_GET['page']);
19 18
     $pg = preg_replace('/[^0-9a-z_]/', "", $pg);
20 19
     if (array_key_exists($pg, PAGES) && file_exists(__DIR__ . "/pages/" . $pg . ".php")) {
@@ -40,7 +39,7 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
40 39
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
41 40
         <meta name="viewport" content="width=device-width, initial-scale=1">
42 41
 
43
-        <title><?php echo SITE_TITLE; ?></title>
42
+        <title><?php echo $SETTINGS['site_title']; ?></title>
44 43
 
45 44
         <link rel="icon" href="static/img/logo.svg">
46 45
 
@@ -66,28 +65,35 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
66 65
 
67 66
         <?php
68 67
 // Alert messages
69
-        if (isset($_GET['msg']) && !empty($_GET['msg']) && array_key_exists($_GET['msg'], MESSAGES)) {
70
-            // optional string generation argument
71
-            if (!isset($_GET['arg']) || empty($_GET['arg'])) {
72
-                $alertmsg = $Strings->get(MESSAGES[$_GET['msg']]['string'], false);
68
+        if (!empty($_GET['msg'])) {
69
+            if (array_key_exists($_GET['msg'], MESSAGES)) {
70
+                // optional string generation argument
71
+                if (empty($_GET['arg'])) {
72
+                    $alertmsg = $Strings->get(MESSAGES[$_GET['msg']]['string'], false);
73
+                } else {
74
+                    $alertmsg = $Strings->build(MESSAGES[$_GET['msg']]['string'], ["arg" => strip_tags($_GET['arg'])], false);
75
+                }
76
+                $alerttype = MESSAGES[$_GET['msg']]['type'];
77
+                $alerticon = "square-o";
78
+                switch (MESSAGES[$_GET['msg']]['type']) {
79
+                    case "danger":
80
+                        $alerticon = "times";
81
+                        break;
82
+                    case "warning":
83
+                        $alerticon = "exclamation-triangle";
84
+                        break;
85
+                    case "info":
86
+                        $alerticon = "info-circle";
87
+                        break;
88
+                    case "success":
89
+                        $alerticon = "check";
90
+                        break;
91
+                }
73 92
             } else {
74
-                $alertmsg = $Strings->build(MESSAGES[$_GET['msg']]['string'], ["arg" => strip_tags($_GET['arg'])], false);
75
-            }
76
-            $alerttype = MESSAGES[$_GET['msg']]['type'];
77
-            $alerticon = "square-o";
78
-            switch (MESSAGES[$_GET['msg']]['type']) {
79
-                case "danger":
80
-                    $alerticon = "times";
81
-                    break;
82
-                case "warning":
83
-                    $alerticon = "exclamation-triangle";
84
-                    break;
85
-                case "info":
86
-                    $alerticon = "info-circle";
87
-                    break;
88
-                case "success":
89
-                    $alerticon = "check";
90
-                    break;
93
+                // We don't have a message for this, so just assume an error and escape stuff.
94
+                $alertmsg = htmlentities($Strings->get($_GET['msg'], false));
95
+                $alerticon = "times";
96
+                $alerttype = "danger";
91 97
             }
92 98
             echo <<<END
93 99
             <div class="row justify-content-center" id="msg-alert-box">
@@ -121,7 +127,7 @@ END;
121 127
             </button>
122 128
             <a class="navbar-brand py-0 mr-auto" href="app.php">
123 129
                 <img src="static/img/logo.svg" alt="" class="d-none d-<?php echo $navbar_breakpoint; ?>-inline brand-img py-0" />
124
-                <?php echo SITE_TITLE; ?>
130
+                <?php echo $SETTINGS['site_title']; ?>
125 131
             </a>
126 132
 
127 133
             <div class="collapse navbar-collapse py-0" id="navbar-collapse">
@@ -177,8 +183,8 @@ END;
177 183
                 ?>
178 184
             </div>
179 185
             <div class="footer">
180
-                <?php echo FOOTER_TEXT; ?><br />
181
-                Copyright &copy; <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
186
+                <?php echo $SETTINGS['footer_text']; ?><br />
187
+                Copyright &copy; <?php echo date('Y'); ?> <?php echo $SETTINGS['copyright']; ?>
182 188
             </div>
183 189
         </div>
184 190
         <script src="static/js/jquery-3.3.1.min.js"></script>
@@ -194,4 +200,4 @@ END;
194 200
         }
195 201
         ?>
196 202
     </body>
197
-</html>
203
+</html>

+ 4
- 4
feed.php View File

@@ -54,12 +54,12 @@ switch ($_GET['type']) {
54 54
         die("400 Bad Request: feed parameter must have a value of \"rss\", \"rss1\", \"rss2\" or \"atom\".");
55 55
 }
56 56
 
57
-$feed->setTitle($Strings->build("Notifications from server for user", ['server' => SITE_TITLE, 'user' => $user->getName()], false));
57
+$feed->setTitle($Strings->build("Notifications from server for user", ['server' => $SETTINGS['site_title'], 'user' => $user->getName()], false));
58 58
 
59
-if (strpos(URL, "http") === 0) {
60
-    $url = URL;
59
+if (strpos($SETTINGS['url'], "http") === 0) {
60
+    $url = $SETTINGS['url'];
61 61
 } else {
62
-    $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . URL;
62
+    $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . $SETTINGS['url'];
63 63
 }
64 64
 
65 65
 $feed->setLink($url);

+ 14
- 15
index.php View File

@@ -20,7 +20,7 @@ if (empty($VARS['progress'])) {
20 20
     // Easy way to remove "undefined" warnings.
21 21
 } else if ($VARS['progress'] == "1") {
22 22
     engageRateLimit();
23
-    if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
23
+    if (!$SETTINGS['captcha']['enabled'] || ($SETTINGS['captcha']['enabled'] && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], $SETTINGS['captcha']['server'] . "/api.php"))) {
24 24
         $autherror = "";
25 25
         $user = User::byUsername($VARS['username']);
26 26
         if ($user->exists()) {
@@ -44,7 +44,7 @@ if (empty($VARS['progress'])) {
44 44
                     break;
45 45
                 case "ALERT_ON_ACCESS":
46 46
                     $mail_resp = $user->sendAlertEmail();
47
-                    if (DEBUG) {
47
+                    if ($SETTINGS['debug']) {
48 48
                         var_dump($mail_resp);
49 49
                     }
50 50
                     $username_ok = true;
@@ -143,16 +143,16 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
143 143
         <meta http-equiv="X-UA-Compatible" content="IE=edge">
144 144
         <meta name="viewport" content="width=device-width, initial-scale=1">
145 145
 
146
-        <title><?php echo SITE_TITLE; ?></title>
146
+        <title><?php echo $SETTINGS['site_title']; ?></title>
147 147
 
148 148
         <link rel="icon" href="static/img/logo.svg">
149 149
 
150 150
         <link href="static/css/bootstrap.min.css" rel="stylesheet">
151 151
         <link href="static/css/material-color/material-color.min.css" rel="stylesheet">
152 152
         <link href="static/css/index.css" rel="stylesheet">
153
-        <?php if (CAPTCHA_ENABLED) { ?>
154
-            <script src="<?php echo CAPTCHA_SERVER ?>/captcheck.dist.js"></script>
155
-<?php } ?>
153
+        <?php if ($SETTINGS['captcha']['enabled']) { ?>
154
+            <script src="<?php echo $SETTINGS['captcha']['server'] ?>/captcheck.dist.js"></script>
155
+        <?php } ?>
156 156
     </head>
157 157
     <body>
158 158
         <div class="row justify-content-center">
@@ -197,7 +197,7 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
197 197
                             ?>
198 198
                             <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 />
199 199
                             <input type="password" class="form-control" name="password" placeholder="<?php $Strings->get("password"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /><br />
200
-    <?php if (CAPTCHA_ENABLED) { ?>
200
+                            <?php if ($SETTINGS['captcha']['enabled']) { ?>
201 201
                                 <div class="captcheck_container" data-stylenonce="<?php echo $SECURE_NONCE; ?>"></div>
202 202
                                 <br />
203 203
                             <?php } ?>
@@ -206,7 +206,7 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
206 206
                         } else if ($multiauth) {
207 207
                             ?>
208 208
                             <div class="alert alert-info">
209
-    <?php $Strings->get("2fa prompt"); ?>
209
+                                <?php $Strings->get("2fa prompt"); ?>
210 210
                             </div>
211 211
                             <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 />
212 212
                             <input type="hidden" name="progress" value="2" />
@@ -222,18 +222,17 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
222 222
                         }
223 223
                         ?>
224 224
                         <button type="submit" class="btn btn-primary">
225
-<?php $Strings->get("continue"); ?>
225
+                            <?php $Strings->get("continue"); ?>
226 226
                         </button>
227 227
                     </form>
228 228
                 </div>
229 229
             </div>
230 230
         </div>
231 231
         <div class="footer">
232
-<?php echo FOOTER_TEXT; ?><br />
233
-            Copyright &copy; <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
232
+            <?php echo $SETTINGS['footer_text']; ?><br />
233
+            Copyright &copy; <?php echo date('Y'); ?> <?php echo $SETTINGS['copyright']; ?>
234 234
         </div>
235
-    </div>
236
-    <script src="static/js/jquery-3.3.1.min.js"></script>
237
-    <script src="static/js/bootstrap.bundle.min.js"></script>
238
-</body>
235
+        <script src="static/js/jquery-3.3.1.min.js"></script>
236
+        <script src="static/js/bootstrap.bundle.min.js"></script>
237
+    </body>
239 238
 </html>

+ 257
- 0
lib/FormBuilder.lib.php View File

@@ -0,0 +1,257 @@
1
+<?php
2
+
3
+/*
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
5
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+class FormBuilder {
10
+
11
+    private $items = [];
12
+    private $hiddenitems = [];
13
+    private $title = "";
14
+    private $icon = "";
15
+    private $buttons = [];
16
+    private $action = "action.php";
17
+    private $method = "POST";
18
+    private $id = "editform";
19
+
20
+    /**
21
+     * Create a form with autogenerated HTML.
22
+     *
23
+     * @param string $title Form title/heading
24
+     * @param string $icon FontAwesone icon next to the title.
25
+     * @param string $action URL to submit the form to.
26
+     * @param string $method Form submission method (POST, GET, etc.)
27
+     */
28
+    public function __construct(string $title = "Untitled Form", string $icon = "fas fa-file-alt", string $action = "action.php", string $method = "POST") {
29
+        $this->title = $title;
30
+        $this->icon = $icon;
31
+        $this->action = $action;
32
+        $this->method = $method;
33
+    }
34
+
35
+    /**
36
+     * Set the title of the form.
37
+     * @param string $title
38
+     */
39
+    public function setTitle(string $title) {
40
+        $this->title = $title;
41
+    }
42
+
43
+    /**
44
+     * Set the icon for the form.
45
+     * @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
46
+     */
47
+    public function setIcon(string $icon) {
48
+        $this->icon = $icon;
49
+    }
50
+
51
+    /**
52
+     * Set the URL the form will submit to.
53
+     * @param string $action
54
+     */
55
+    public function setAction(string $action) {
56
+        $this->action = $action;
57
+    }
58
+
59
+    /**
60
+     * Set the form submission method (GET, POST, etc)
61
+     * @param string $method
62
+     */
63
+    public function setMethod(string $method = "POST") {
64
+        $this->method = $method;
65
+    }
66
+
67
+    /**
68
+     * Set the form ID.
69
+     * @param string $id
70
+     */
71
+    public function setID(string $id = "editform") {
72
+        $this->id = $id;
73
+    }
74
+
75
+    /**
76
+     * Add an input to the form.
77
+     *
78
+     * @param string $name Element name
79
+     * @param string $value Element value
80
+     * @param string $type Input type (text, number, date, select, tel...)
81
+     * @param bool $required If the element is required for form submission.
82
+     * @param string $id Element ID
83
+     * @param array $options Array of [value => text] pairs for a select element
84
+     * @param string $label Text label to display near the input
85
+     * @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
86
+     * @param int $width Bootstrap column width for the input, out of 12.
87
+     * @param int $minlength Minimum number of characters for the input.
88
+     * @param int $maxlength Maximum number of characters for the input.
89
+     * @param string $pattern Regex pattern for custom client-side validation.
90
+     * @param string $error Message to show if the input doesn't validate.
91
+     */
92
+    public function addInput(string $name, string $value = "", string $type = "text", bool $required = true, string $id = null, array $options = null, string $label = "", string $icon = "", int $width = 4, int $minlength = 1, int $maxlength = 100, string $pattern = "", string $error = "") {
93
+        $item = [
94
+            "name" => $name,
95
+            "value" => $value,
96
+            "type" => $type,
97
+            "required" => $required,
98
+            "label" => $label,
99
+            "icon" => $icon,
100
+            "width" => $width,
101
+            "minlength" => $minlength,
102
+            "maxlength" => $maxlength
103
+        ];
104
+        if (!empty($id)) {
105
+            $item["id"] = $id;
106
+        }
107
+        if (!empty($options) && $type == "select") {
108
+            $item["options"] = $options;
109
+        }
110
+        if (!empty($pattern)) {
111
+            $item["pattern"] = $pattern;
112
+        }
113
+        if (!empty($error)) {
114
+            $item["error"] = $error;
115
+        }
116
+        $this->items[] = $item;
117
+    }
118
+
119
+    /**
120
+     * Add a button to the form.
121
+     *
122
+     * @param string $text Text string to show on the button.
123
+     * @param string $icon FontAwesome icon to show next to the text.
124
+     * @param string $href If not null, the button will actually be a hyperlink.
125
+     * @param string $type Usually "button" or "submit".  Ignored if $href is set.
126
+     * @param string $id The element ID.
127
+     * @param string $name The element name for the button.
128
+     * @param string $value The form value for the button. Ignored if $name is null.
129
+     * @param string $class The CSS classes for the button, if a standard success-colored one isn't right.
130
+     */
131
+    public function addButton(string $text, string $icon = "", string $href = null, string $type = "button", string $id = null, string $name = null, string $value = "", string $class = "btn btn-success") {
132
+        $button = [
133
+            "text" => $text,
134
+            "icon" => $icon,
135
+            "class" => $class,
136
+            "type" => $type,
137
+            "id" => $id,
138
+            "href" => $href,
139
+            "name" => $name,
140
+            "value" => $value
141
+        ];
142
+        $this->buttons[] = $button;
143
+    }
144
+
145
+    /**
146
+     * Add a hidden input.
147
+     * @param string $name
148
+     * @param string $value
149
+     */
150
+    public function addHiddenInput(string $name, string $value) {
151
+        $this->hiddenitems[$name] = $value;
152
+    }
153
+
154
+    /**
155
+     * Generate the form HTML.
156
+     * @param bool $echo If false, returns HTML string instead of outputting it.
157
+     */
158
+    public function generate(bool $echo = true) {
159
+        $html = <<<HTMLTOP
160
+<form action="$this->action" method="$this->method" id="$this->id">
161
+    <div class="card">
162
+         <h3 class="card-header d-flex">
163
+            <div>
164
+                <i class="$this->icon"></i> $this->title
165
+            </div>
166
+        </h3>
167
+
168
+        <div class="card-body">
169
+            <div class="row">
170
+HTMLTOP;
171
+
172
+        foreach ($this->items as $item) {
173
+            $required = $item["required"] ? "required" : "";
174
+            $id = empty($item["id"]) ? "" : "id=\"$item[id]\"";
175
+            $pattern = empty($item["pattern"]) ? "" : "pattern=\"$item[pattern]\"";
176
+
177
+            $itemhtml = "";
178
+            $itemhtml .= <<<ITEMTOP
179
+\n\n                <div class="col-12 col-md-$item[width]">
180
+                    <div class="form-group mb-3">
181
+                        <label class="mb-0">$item[label]:</label>
182
+                        <div class="input-group">
183
+                            <div class="input-group-prepend">
184
+                                <span class="input-group-text"><i class="$item[icon]"></i></span>
185
+                            </div>
186
+ITEMTOP;
187
+            if (empty($item['type']) || $item['type'] != "select") {
188
+                $itemhtml .= <<<INPUT
189
+\n                            <input type="$item[type]" name="$item[name]" $id class="form-control" aria-label="$item[label]" minlength="$item[minlength]" maxlength="$item[maxlength]" $pattern value="$item[value]" $required />
190
+INPUT;
191
+            } else {
192
+                $itemhtml .= <<<SELECT
193
+\n                            <select class="form-control" name="$item[name]" aria-label="$item[label]" $required>
194
+SELECT;
195
+                foreach ($item['options'] as $value => $label) {
196
+                    $selected = "";
197
+                    if (!empty($item['value']) && $value == $item['value']) {
198
+                        $selected = " selected";
199
+                    }
200
+                    $itemhtml .= "\n                                <option value=\"$value\"$selected>$label</option>";
201
+                }
202
+                $itemhtml .= "\n                            </select>";
203
+            }
204
+
205
+            if (!empty($item["error"])) {
206
+                $itemhtml .= <<<ERROR
207
+\n                            <div class="invalid-feedback">
208
+                                $item[error]
209
+                            </div>
210
+ERROR;
211
+            }
212
+            $itemhtml .= <<<ITEMBOTTOM
213
+\n                        </div>
214
+                    </div>
215
+                </div>\n
216
+ITEMBOTTOM;
217
+            $html .= $itemhtml;
218
+        }
219
+
220
+        $html .= <<<HTMLBOTTOM
221
+
222
+            </div>
223
+        </div>
224
+HTMLBOTTOM;
225
+
226
+        if (!empty($this->buttons)) {
227
+            $html .= "\n        <div class=\"card-footer\">";
228
+            foreach ($this->buttons as $btn) {
229
+                $btnhtml = "";
230
+                $inner = "<i class=\"$btn[icon]\"></i> $btn[text]";
231
+                $id = empty($btn['id']) ? "" : "id=\"$btn[id]\"";
232
+                if (!empty($btn['href'])) {
233
+                    $btnhtml = "<a href=\"$btn[href]\" class=\"$btn[class]\" $id>$inner</a>";
234
+                } else {
235
+                    $name = empty($btn['name']) ? "" : "name=\"$btn[name]\"";
236
+                    $value = (!empty($btn['name']) && !empty($btn['value'])) ? "value=\"$btn[value]\"" : "";
237
+                    $btnhtml = "<button type=\"$btn[type]\" class=\"$btn[class]\" $id $name $value>$inner</button>";
238
+                }
239
+                $html .= "\n            $btnhtml";
240
+            }
241
+            $html .= "\n        </div>";
242
+        }
243
+
244
+        $html .= "\n    </div>";
245
+        foreach ($this->hiddenitems as $name => $value) {
246
+            $value = htmlentities($value);
247
+            $html .= "\n    <input type=\"hidden\" name=\"$name\" value=\"$value\" />";
248
+        }
249
+        $html .= "\n</form>\n";
250
+
251
+        if ($echo) {
252
+            echo $html;
253
+        }
254
+        return $html;
255
+    }
256
+
257
+}

+ 18
- 13
lib/User.lib.php View File

@@ -119,7 +119,7 @@ class User {
119 119
      * @throws WeakPasswordException
120 120
      */
121 121
     function changePassword(string $old, string $new, string $new2) {
122
-        global $database;
122
+        global $database, $SETTINGS;
123 123
         if ($old == $new) {
124 124
             throw new PasswordMatchException();
125 125
         }
@@ -137,7 +137,7 @@ class User {
137 137
         if ($passrank !== FALSE) {
138 138
             throw new WeakPasswordException();
139 139
         }
140
-        if (strlen($new) < MIN_PASSWORD_LENGTH) {
140
+        if (strlen($new) < $SETTINGS['min_password_length']) {
141 141
             throw new WeakPasswordException();
142 142
         }
143 143
 
@@ -171,10 +171,11 @@ class User {
171 171
      * @return string OTP provisioning URI (for generating a QR code)
172 172
      */
173 173
     function generate2fa(): string {
174
+        global $SETTINGS;
174 175
         $secret = random_bytes(20);
175 176
         $encoded_secret = Base32::encode($secret);
176 177
         $totp = new TOTP((empty($this->email) ? $this->realname : $this->email), $encoded_secret);
177
-        $totp->setIssuer(SYSTEM_NAME);
178
+        $totp->setIssuer($SETTINGS['system_name']);
178 179
         return $totp->getProvisioningUri();
179 180
     }
180 181
 
@@ -214,7 +215,11 @@ class User {
214 215
         return new AccountStatus($statuscode);
215 216
     }
216 217
 
217
-    function sendAlertEmail(string $appname = SITE_TITLE) {
218
+    function sendAlertEmail(string $appname = null) {
219
+        global $SETTINGS;
220
+        if (is_null($appname)) {
221
+            $appname = $SETTINGS['site_title'];
222
+        }
218 223
         if (empty(ADMIN_EMAIL) || filter_var(ADMIN_EMAIL, FILTER_VALIDATE_EMAIL) === FALSE) {
219 224
             return "invalid_to_email";
220 225
         }
@@ -224,19 +229,19 @@ class User {
224 229
 
225 230
         $mail = new PHPMailer;
226 231
 
227
-        if (DEBUG) {
232
+        if ($SETTINGS['debug']) {
228 233
             $mail->SMTPDebug = 2;
229 234
         }
230 235
 
231
-        if (USE_SMTP) {
236
+        if ($SETTINGS['email']['use_smtp']) {
232 237
             $mail->isSMTP();
233
-            $mail->Host = SMTP_HOST;
234
-            $mail->SMTPAuth = SMTP_AUTH;
235
-            $mail->Username = SMTP_USER;
236
-            $mail->Password = SMTP_PASS;
237
-            $mail->SMTPSecure = SMTP_SECURE;
238
-            $mail->Port = SMTP_PORT;
239
-            if (SMTP_ALLOW_INVALID_CERTIFICATE) {
238
+            $mail->Host = $SETTINGS['email']['host'];
239
+            $mail->SMTPAuth = $SETTINGS['email']['auth'];
240
+            $mail->Username = $SETTINGS['email']['user'];
241
+            $mail->Password = $SETTINGS['email']['password'];
242
+            $mail->SMTPSecure = $SETTINGS['email']['secure'];
243
+            $mail->Port = $SETTINGS['email']['port'];
244
+            if ($SETTINGS['email']['allow_invalid_certificate']) {
240 245
                 $mail->SMTPOptions = array(
241 246
                     'ssl' => array(
242 247
                         'verify_peer' => false,

+ 2
- 2
mobile/index.php View File

@@ -18,7 +18,7 @@ if ($VARS['action'] == "ping") {
18 18
     exit(json_encode(["status" => "OK"]));
19 19
 }
20 20
 
21
-if (MOBILE_ENABLED !== TRUE) {
21
+if ($SETTINGS['mobile_enabled'] !== TRUE) {
22 22
     exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("mobile login disabled", false)]));
23 23
 }
24 24
 
@@ -106,7 +106,7 @@ switch ($VARS['action']) {
106 106
         Log::insert(LogType::MOBILE_LOGIN_FAILED, null, "Username: " . $username . ", Key: " . $key);
107 107
         exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("login incorrect", false)]));
108 108
     case "listapps":
109
-        $apps = EXTERNAL_APPS;
109
+        $apps = $SETTINGS['apps'];
110 110
         // Format paths as absolute URLs
111 111
         foreach ($apps as $k => $v) {
112 112
             if (strpos($apps[$k]['url'], "http") === FALSE) {

+ 1
- 1
pages.php View File

@@ -7,7 +7,7 @@
7 7
 // List of pages and metadata
8 8
 define("PAGES", [
9 9
     "home" => [
10
-        "title" => "home",
10
+        "title" => "Home",
11 11
         "navbar" => true,
12 12
         "icon" => "fas fa-home",
13 13
         "styles" => [

+ 3
- 30
pages/home.php View File

@@ -6,36 +6,9 @@
6 6
  */
7 7
 ?>
8 8
 
9
-<?php
10
-/*
11
-<div class="d-flex justify-content-center flex-wrap">
12
-    <?php
13
-    foreach (EXTERNAL_APPS as $a) {
14
-        ?>
15
-        <div class="app-dock-item m-2 mobile-app-hide">
16
-            <p class="mb-0">
17
-                <a href="<?php echo $a['url']; ?>">
18
-                    <img class="img-responsive app-icon" src="<?php
19
-                    if (strpos($a['icon'], "http") !== 0) {
20
-                        echo $a['url'] . $a['icon'];
21
-                    } else {
22
-                        echo $a['icon'];
23
-                    }
24
-                    ?>"/>
25
-                    <span class="d-block text-center"><?php echo $a['title']; ?></span>
26
-                </a>
27
-            </p>
28
-        </div>
29
-        <?php
30
-    }
31
-    ?>
32
-</div>
33
- */
34
-?>
35
-
36 9
 <div class="row mt-2">
37 10
     <?php
38
-    foreach (EXTERNAL_APPS as $a) {
11
+    foreach ($SETTINGS['apps'] as $a) {
39 12
         if (!isset($a['card'])) {
40 13
             continue;
41 14
         }
@@ -141,9 +114,9 @@
141 114
                         <?php
142 115
                         $ts = strtotime($n['timestamp']);
143 116
                         if (time() - $ts < 60 * 60 * 12) {
144
-                            echo date(TIME_FORMAT, $ts);
117
+                            echo date($SETTINGS['time_format'], $ts);
145 118
                         } else {
146
-                            echo date(DATETIME_FORMAT, $ts);
119
+                            echo date($SETTINGS['datetime_format'], $ts);
147 120
                         }
148 121
                         ?>
149 122
                     </div>

+ 3
- 3
pages/security.php View File

@@ -30,7 +30,7 @@ $user = new User($_SESSION['uid']);
30 30
         </div>
31 31
     </div>
32 32
     <?php
33
-    if (STATION_KIOSK) {
33
+    if ($SETTINGS['station_kiosk']) {
34 34
         ?>
35 35
         <div class="col-sm-6 col-lg-4">
36 36
             <div class="card mb-4">
@@ -71,8 +71,8 @@ $user = new User($_SESSION['uid']);
71 71
                     <?php
72 72
                 } else if (!empty($_GET['2fa']) && $_GET['2fa'] == "generate") {
73 73
                     $codeuri = $user->generate2fa();
74
-                    $label = SYSTEM_NAME . ":" . is_null($user->getEmail()) ? $user->getName() : $user->getEmail();
75
-                    $issuer = SYSTEM_NAME;
74
+                    $label = $SETTINGS['system_name'] . ":" . is_null($user->getEmail()) ? $user->getName() : $user->getEmail();
75
+                    $issuer = $SETTINGS['system_name'];
76 76
                     $qrCode = new QrCode($codeuri);
77 77
                     $qrCode->setWriterByName('svg');
78 78
                     $qrCode->setSize(550);

+ 7
- 7
pages/sync.php View File

@@ -25,10 +25,10 @@ if (!empty($_GET['delsynccode'])) {
25 25
                     $code = strtoupper(substr(md5(mt_rand() . uniqid("", true)), 0, 20));
26 26
                     $desc = htmlspecialchars($_POST['desc']);
27 27
                     $database->insert('mobile_codes', ['uid' => $_SESSION['uid'], 'code' => $code, 'description' => $desc]);
28
-                    if (strpos(URL, "http") === 0) {
29
-                        $url = URL . "mobile/index.php";
28
+                    if (strpos($SETTINGS['url'], "http") === 0) {
29
+                        $url = $SETTINGS['url'] . "mobile/index.php";
30 30
                     } else {
31
-                        $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . URL . "mobile/index.php";
31
+                        $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . $SETTINGS['url'] . "mobile/index.php";
32 32
                     }
33 33
                     $encodedurl = str_replace("/", "\\", $url);
34 34
                     $codeuri = "bizsync://" . $encodedurl . "/" . $_SESSION['username'] . "/" . $code;
@@ -68,7 +68,7 @@ if (!empty($_GET['delsynccode'])) {
68 68
                 $activecodes = $database->select("mobile_codes", ["codeid", "code", "description"], ["uid" => $_SESSION['uid']]);
69 69
                 ?>
70 70
                 <p class="card-text">
71
-                    <?php $Strings->build("sync explained", ["site_name" => SITE_TITLE]); ?>
71
+                    <?php $Strings->build("sync explained", ["site_name" => $SETTINGS['site_title']]); ?>
72 72
                 </p>
73 73
                 <form action="app.php?page=sync&mobilecode=generate" method="POST">
74 74
                     <input type="text" name="desc" class="form-control" placeholder="<?php $Strings->get("sync code name"); ?>" required />
@@ -142,10 +142,10 @@ if (!empty($_GET['delsynccode'])) {
142 142
                 $database->insert('userkeys', ['uid' => $_SESSION['uid'], 'typeid' => 1, 'created' => date('Y-m-d H:i:s'), 'key' => $key]);
143 143
             }
144 144
 
145
-            if (strpos(URL, "http") === 0) {
146
-                $url = URL;
145
+            if (strpos($SETTINGS['url'], "http") === 0) {
146
+                $url = $SETTINGS['url'];
147 147
             } else {
148
-                $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . URL;
148
+                $url = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "") . $SETTINGS['url'];
149 149
             }
150 150
             $url = $url . "feed.php?key=$key";
151 151
             ?>

+ 12
- 13
required.php View File

@@ -33,7 +33,7 @@ session_start(); // stick some cookies in it
33 33
 // renew session cookie
34 34
 setcookie(session_name(), session_id(), time() + $session_length, "/", false, false);
35 35
 
36
-$captcha_server = (CAPTCHA_ENABLED === true ? preg_replace("/http(s)?:\/\//", "", CAPTCHA_SERVER) : "");
36
+$captcha_server = ($SETTINGS['captcha']['enabled'] === true ? preg_replace("/http(s)?:\/\//", "", $SETTINGS['captcha']['server']) : "");
37 37
 if ($_SESSION['mobile'] === TRUE) {
38 38
     header("Content-Security-Policy: "
39 39
             . "default-src 'self';"
@@ -70,7 +70,7 @@ foreach ($libs as $lib) {
70 70
     require_once $lib;
71 71
 }
72 72
 
73
-$Strings = new Strings(LANGUAGE);
73
+$Strings = new Strings($SETTINGS['language']);
74 74
 
75 75
 /**
76 76
  * Kill off the running process and spit out an error message
@@ -94,7 +94,7 @@ function sendError($error) {
94 94
             . "<p>" . htmlspecialchars($error) . "</p>");
95 95
 }
96 96
 
97
-date_default_timezone_set(TIMEZONE);
97
+date_default_timezone_set($SETTINGS['timezone']);
98 98
 
99 99
 // Database settings
100 100
 // Also inits database and stuff
@@ -103,12 +103,12 @@ use Medoo\Medoo;
103 103
 $database;
104 104
 try {
105 105
     $database = new Medoo([
106
-        'database_type' => DB_TYPE,
107
-        'database_name' => DB_NAME,
108
-        'server' => DB_SERVER,
109
-        'username' => DB_USER,
110
-        'password' => DB_PASS,
111
-        'charset' => DB_CHARSET
106
+        'database_type' => $SETTINGS['database']['type'],
107
+        'database_name' => $SETTINGS['database']['name'],
108
+        'server' => $SETTINGS['database']['server'],
109
+        'username' => $SETTINGS['database']['user'],
110
+        'password' => $SETTINGS['database']['password'],
111
+        'charset' => $SETTINGS['database']['charset']
112 112
     ]);
113 113
 } catch (Exception $ex) {
114 114
     //header('HTTP/1.1 500 Internal Server Error');
@@ -116,7 +116,7 @@ try {
116 116
 }
117 117
 
118 118
 
119
-if (!DEBUG) {
119
+if (!$SETTINGS['debug']) {
120 120
     error_reporting(0);
121 121
 } else {
122 122
     error_reporting(E_ALL);
@@ -157,10 +157,9 @@ function checkDBError($specials = []) {
157 157
     }
158 158
 }
159 159
 
160
-
161 160
 function redirectIfNotLoggedIn() {
162 161
     if ($_SESSION['loggedin'] !== TRUE) {
163
-        header('Location: ' . URL . '/login.php');
162
+        header('Location: ' . $SETTINGS['url'] . '/index.php');
164 163
         die();
165 164
     }
166 165
 }
@@ -186,4 +185,4 @@ function engageRateLimit() {
186 185
         // Add a record for the IP address
187 186
         $database->insert('rate_limit', ["ipaddr" => IPUtils::getClientIP(), "lastaction" => date("Y-m-d H:i:s")]);
188 187
     }
189
-}
188
+}

+ 155
- 163
settings.template.php View File

@@ -1,172 +1,164 @@
1 1
 <?php
2 2
 
3
-/* This Source Code Form is subject to the terms of the Mozilla Public
3
+/*
4
+ * This Source Code Form is subject to the terms of the Mozilla Public
4 5
  * License, v. 2.0. If a copy of the MPL was not distributed with this
5
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
-
7
-
8
-// Whether to show debugging data in output.
9
-// DO NOT SET TO TRUE IN PRODUCTION!!!
10
-define("DEBUG", false);
11
-
12
-// Database connection settings
13
-// See http://medoo.in/api/new for info
14
-define("DB_TYPE", "mysql");
15
-define("DB_NAME", "accounthub");
16
-define("DB_SERVER", "localhost");
17
-define("DB_USER", "accounthub");
18
-define("DB_PASS", "");
19
-define("DB_CHARSET", "utf8");
20
-
21
-define("SITE_TITLE", "AccountHub");
22
-
23
-// Used to identify the system in OTP and other places
24
-define("SYSTEM_NAME", "Netsyms SSO Demo");
25
-
26
-// For supported values, see http://php.net/manual/en/timezones.php
27
-define("TIMEZONE", "America/Denver");
28
-
29
-// Allow or prevent users from logging in via the mobile app.
30
-define("MOBILE_ENABLED", TRUE);
31
-
32
-// Base URL for site links.
33
-define('URL', 'http://localhost/accounthub');
34
-
35
-// Use Captcheck on login screen
36
-// https://captcheck.netsyms.com
37
-define("CAPTCHA_ENABLED", FALSE);
38
-define('CAPTCHA_SERVER', 'https://captcheck.netsyms.com');
39
-
40
-// See lang folder for language options
41
-define('LANGUAGE', "en");
42
-
43
-// List of available applications, icons, and other info.
44
-// Used in the mobile app and in the "dock" in AccountHub.
45
-define('EXTERNAL_APPS', [
46
-    "accounthub" => [
47
-        "url" => "/accounthub",
48
-        "mobileapi" => "/mobile/index.php",
49
-        "icon" => "/static/img/logo.svg",
50
-        "title" => SITE_TITLE
6
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
+ */
8
+
9
+// Settings for the app.
10
+// Copy to settings.php and customize.
11
+
12
+$SETTINGS = [
13
+    // Whether to output debugging info like PHP notices, warnings,
14
+    // and stacktraces.
15
+    // Turning this on in production is a security risk and can sometimes break
16
+    // things, such as JSON output where extra content is not expected.
17
+    "debug" => false,
18
+    // Database connection settings
19
+    // See http://medoo.in/api/new for info
20
+    "database" => [
21
+        "type" => "mysql",
22
+        "name" => "accounthub",
23
+        "server" => "localhost",
24
+        "user" => "accounthub",
25
+        "password" => "",
26
+        "charset" => "utf8"
51 27
     ],
52
-    "qwikclock" => [
53
-        "url" => "/qwikclock",
54
-        "mobileapi" => "/mobile/index.php",
55
-        "icon" => "/static/img/logo.svg",
56
-        "title" => "QwikClock",
57
-        "station_features" => [
58
-            "qwikclock_punchinout",
59
-            "qwikclock_myshifts",
60
-            "qwikclock_jobs"
28
+    // Name of the app.
29
+    "site_title" => "AccountHub",
30
+    // Used to identify the system in OTP and other places
31
+    "system_name" => "Netsyms AccountHub",
32
+    // Allow login from the Netsyms mobile app
33
+    "mobile_enabled" => true,
34
+    // For supported values, see http://php.net/manual/en/timezones.php
35
+    "timezone" => "America/Denver",
36
+    // List of external apps connected to this system.
37
+    // This list is used for generating the dashboard cards and in the
38
+    // mobile app.
39
+    "apps" => [
40
+        "accounthub" => [
41
+            "url" => "/accounthub",
42
+            "mobileapi" => "/mobile/index.php",
43
+            "icon" => "/static/img/logo.svg",
44
+            "title" => $SETTINGS['site_title']
61 45
         ],
62
-        "card" => [
63
-            "color" => "blue",
64
-            "string" => "Punch in and check work schedule"
65
-        ]
66
-    ],
67
-    "binstack" => [
68
-        "url" => "/binstack",
69
-        "mobileapi" => "/mobile/index.php",
70
-        "icon" => "/static/img/logo.svg",
71
-        "title" => "BinStack",
72
-        "card" => [
73
-            "color" => "green",
74
-            "string" => "Manage physical items"
75
-        ]
76
-    ],
77
-    "newspen" => [
78
-        "url" => "/newspen",
79
-        "mobileapi" => "/mobile/index.php",
80
-        "icon" => "/static/img/logo.svg",
81
-        "title" => "NewsPen",
82
-        "card" => [
83
-            "color" => "purple",
84
-            "string" => "Create and publish e-newsletters"
85
-        ]
86
-    ],
87
-    "managepanel" => [
88
-        "url" => "/managepanel",
89
-        "mobileapi" => "/mobile/index.php",
90
-        "icon" => "/static/img/logo.svg",
91
-        "title" => "ManagePanel",
92
-        "card" => [
93
-            "color" => "brown",
94
-            "string" => "Manage users, permissions, and security"
95
-        ]
96
-    ],
97
-    "nickelbox" => [
98
-        "url" => "/nickelbox",
99
-        "mobileapi" => "/mobile/index.php",
100
-        "icon" => "/static/img/logo.svg",
101
-        "title" => "NickelBox",
102
-        "card" => [
103
-            "color" => "light-green",
104
-            "text" => "dark",
105
-            "string" => "Checkout customers and manage online orders"
46
+        "qwikclock" => [
47
+            "url" => "/qwikclock",
48
+            "mobileapi" => "/mobile/index.php",
49
+            "icon" => "/static/img/logo.svg",
50
+            "title" => "QwikClock",
51
+            "station_features" => [
52
+                "qwikclock_punchinout",
53
+                "qwikclock_myshifts",
54
+                "qwikclock_jobs"
55
+            ],
56
+            "card" => [
57
+                "color" => "blue",
58
+                "string" => "Punch in and check work schedule"
59
+            ]
60
+        ],
61
+        "binstack" => [
62
+            "url" => "/binstack",
63
+            "mobileapi" => "/mobile/index.php",
64
+            "icon" => "/static/img/logo.svg",
65
+            "title" => "BinStack",
66
+            "card" => [
67
+                "color" => "green",
68
+                "string" => "Manage physical items"
69
+            ]
70
+        ],
71
+        "newspen" => [
72
+            "url" => "/newspen",
73
+            "mobileapi" => "/mobile/index.php",
74
+            "icon" => "/static/img/logo.svg",
75
+            "title" => "NewsPen",
76
+            "card" => [
77
+                "color" => "purple",
78
+                "string" => "Create and publish e-newsletters"
79
+            ]
80
+        ],
81
+        "managepanel" => [
82
+            "url" => "/managepanel",
83
+            "mobileapi" => "/mobile/index.php",
84
+            "icon" => "/static/img/logo.svg",
85
+            "title" => "ManagePanel",
86
+            "card" => [
87
+                "color" => "brown",
88
+                "string" => "Manage users, permissions, and security"
89
+            ]
90
+        ],
91
+        "nickelbox" => [
92
+            "url" => "/nickelbox",
93
+            "mobileapi" => "/mobile/index.php",
94
+            "icon" => "/static/img/logo.svg",
95
+            "title" => "NickelBox",
96
+            "card" => [
97
+                "color" => "light-green",
98
+                "text" => "dark",
99
+                "string" => "Checkout customers and manage online orders"
100
+            ]
101
+        ],
102
+        "sitewriter" => [
103
+            "url" => "/sitewriter",
104
+            "mobileapi" => "/mobile/index.php",
105
+            "icon" => "/static/img/logo.svg",
106
+            "title" => "SiteWriter",
107
+            "card" => [
108
+                "color" => "light-blue",
109
+                "string" => "Build websites and manage contact form messages"
110
+            ]
111
+        ],
112
+        "taskfloor" => [
113
+            "url" => "/taskfloor",
114
+            "mobileapi" => "/mobile/index.php",
115
+            "icon" => "/static/img/logo.svg",
116
+            "title" => "TaskFloor",
117
+            "station_features" => [
118
+                "taskfloor_viewtasks",
119
+                "taskfloor_viewmessages"
120
+            ],
121
+            "card" => [
122
+                "color" => "blue-grey",
123
+                "string" => "Track jobs and assigned tasks"
124
+            ]
106 125
         ]
107 126
     ],
108
-    "sitewriter" => [
109
-        "url" => "/sitewriter",
110
-        "mobileapi" => "/mobile/index.php",
111
-        "icon" => "/static/img/logo.svg",
112
-        "title" => "SiteWriter",
113
-        "card" => [
114
-            "color" => "light-blue",
115
-            "string" => "Build websites and manage contact form messages"
116
-        ]
127
+    // Settings for sending emails.
128
+    "email" => [
129
+        // If false, will use PHP mail() instead of a server
130
+        "use_smtp" => true,
131
+        // Admin email for alerts
132
+        "admin_email" => "",
133
+        "from" => "alert-noreply@example.com",
134
+        "host" => "",
135
+        "auth" => true,
136
+        "port" => 587,
137
+        "secure" => "tls",
138
+        "user" => "",
139
+        "password" => "",
140
+        "allow_invalid_certificate" => true
117 141
     ],
118
-    "taskfloor" => [
119
-        "url" => "/taskfloor",
120
-        "mobileapi" => "/mobile/index.php",
121
-        "icon" => "/static/img/logo.svg",
122
-        "title" => "TaskFloor",
123
-        "station_features" => [
124
-            "taskfloor_viewtasks",
125
-            "taskfloor_viewmessages"
126
-        ],
127
-        "card" => [
128
-            "color" => "blue-grey",
129
-            "string" => "Track jobs and assigned tasks"
130
-        ]
142
+    "min_password_length" => 8,
143
+    // Show or hide the Station PIN setup option.
144
+    "station_kiosk" => true,
145
+    // Used for notification timestamp display.
146
+    "datetime_format" => "M j, g:i a",
147
+    "time_format" => "g:i",
148
+    // Use Captcheck on login screen to slow down bots
149
+    // https://captcheck.netsyms.com
150
+    "captcha" => [
151
+        "enabled" => false,
152
+        "server" => "https://captcheck.netsyms.com"
131 153
     ],
132
-]);
133
-
134
-// Show or hide the Station PIN setup option.
135
-define("STATION_KIOSK", true);
136
-
137
-// Used for notification timestamp display.
138
-define("DATETIME_FORMAT", "M j, g:i a");
139
-define("TIME_FORMAT", "g:i");
140
-
141
-
142
-// Email settings for receiving admin alerts.
143
-define("USE_SMTP", TRUE); // if FALSE, will use PHP's mail() instead
144
-define("ADMIN_EMAIL", "");
145
-define("FROM_EMAIL", "alert-noreply@apps.biz.netsyms.com");
146
-define("SMTP_HOST", "");
147
-define("SMTP_AUTH", true);
148
-define("SMTP_PORT", 587);
149
-define("SMTP_SECURE", 'tls');
150
-define("SMTP_USER", "");
151
-define("SMTP_PASS", "");
152
-define("SMTP_ALLOW_INVALID_CERTIFICATE", TRUE);
153
-
154
-// Minimum length for new passwords
155
-// The system checks new passwords against the 500 worst passwords and rejects
156
-// any matches.
157
-// If you want to have additional password requirements, go edit action.php.
158
-// However, all that does is encourage people to use the infamous
159
-// "post-it password manager".  See also https://xkcd.com/936/ and
160
-// http://stackoverflow.com/a/34166252 for reasons why forcing passwords
161
-// like CaPs45$% is not actually a great idea.
162
-// Encourage users to use 2-factor auth whenever possible.
163
-define("MIN_PASSWORD_LENGTH", 8);
164
-
165
-// Maximum number of rows to get in a query.
166
-define("QUERY_LIMIT", 1000);
167
-
168
-
169
-
170
-define("FOOTER_TEXT", "");
171
-define("COPYRIGHT_NAME", "Netsyms Technologies");
172
-//////////////////////////////////////////////////////////////
154
+    // Language to use for localization. See langs folder to add a language.
155
+    "language" => "en",
156
+    // Shown in the footer of all the pages.
157
+    "footer_text" => "",
158
+    // Also shown in the footer, but with "Copyright <current_year>" in front.
159
+    "copyright" => "Netsyms Technologies",
160
+    // Base URL for building links relative to the location of the app.
161
+    // Only used when there's no good context for the path.
162
+    // The default is almost definitely fine.
163
+    "url" => "."
164
+];

+ 2
- 2
static/css/svg-with-js.min.css View File

@@ -1,5 +1,5 @@
1 1
 /*!
2
- * Font Awesome Free 5.3.1 by @fontawesome - https://fontawesome.com
2
+ * Font Awesome Free 5.6.0 by @fontawesome - https://fontawesome.com
3 3
  * License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
4 4
  */
5
-.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}
5
+.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{filter:none}.fa-stack{display:inline-block;height:2em;position:relative;width:2.5em}.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:1.25em}.svg-inline--fa.fa-stack-2x{height:2em;width:2.5em}.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}

+ 2
- 2
static/js/fontawesome-all.min.js
File diff suppressed because it is too large
View File


Loading…
Cancel
Save