Merge ../../BizApps/BusinessAppTemplate
# Conflicts: # LICENSE.md # README.md # pages/form.php # settings.template.php
This commit is contained in:
commit
d1acda3349
18
LICENSE.md
18
LICENSE.md
@ -1,19 +1,7 @@
|
|||||||
Copyright (c) 2017-2019 Netsyms Technologies.
|
Copyright (c) 2017-2019 Netsyms Technologies. Some rights reserved.
|
||||||
|
|
||||||
If you modify and redistribute this project, you must replace the branding
|
Licensed under the Mozilla Public License Version 2.0. Files without MPL header
|
||||||
assets with your own.
|
comments, including third party code, may be under a different license.
|
||||||
|
|
||||||
The branding assets include:
|
|
||||||
* the application icon
|
|
||||||
* the Netsyms N punchcard logo
|
|
||||||
* the Netsyms for Business graph logo
|
|
||||||
|
|
||||||
If you are unsure if your usage is allowed, please contact us:
|
|
||||||
https://netsyms.com/contact
|
|
||||||
legal@netsyms.com
|
|
||||||
|
|
||||||
All other portions of this application,
|
|
||||||
unless otherwise noted (in comments, headers, etc), are licensed as follows:
|
|
||||||
|
|
||||||
Mozilla Public License Version 2.0
|
Mozilla Public License Version 2.0
|
||||||
==================================
|
==================================
|
||||||
|
@ -21,11 +21,11 @@ if ($VARS['action'] !== "signout") {
|
|||||||
*/
|
*/
|
||||||
function returnToSender($msg, $arg = "") {
|
function returnToSender($msg, $arg = "") {
|
||||||
global $VARS;
|
global $VARS;
|
||||||
if ($arg == "") {
|
$header = "Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg";
|
||||||
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg);
|
if ($arg != "") {
|
||||||
} else {
|
$header .= "&arg=$arg";
|
||||||
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=$arg");
|
|
||||||
}
|
}
|
||||||
|
header($header);
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,24 +48,48 @@ function getCensoredKey() {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the request is allowed
|
* Check if the request is allowed
|
||||||
* @global type $VARS
|
* @global array $VARS
|
||||||
* @global type $database
|
|
||||||
* @return bool true if the request should continue, false if the request is bad
|
* @return bool true if the request should continue, false if the request is bad
|
||||||
*/
|
*/
|
||||||
function authenticate(): bool {
|
function authenticate(): bool {
|
||||||
global $VARS, $database;
|
global $VARS, $SETTINGS;
|
||||||
if (empty($VARS['key'])) {
|
// HTTP basic auth
|
||||||
return false;
|
if (!empty($_SERVER['PHP_AUTH_USER']) && !empty($_SERVER['PHP_AUTH_PW'])) {
|
||||||
|
$username = $_SERVER['PHP_AUTH_USER'];
|
||||||
|
$password = $_SERVER['PHP_AUTH_PW'];
|
||||||
|
} else if (!empty($VARS['username']) && !empty($VARS['password'])) {
|
||||||
|
$username = $VARS['username'];
|
||||||
|
$password = $VARS['password'];
|
||||||
} else {
|
} else {
|
||||||
$key = $VARS['key'];
|
return false;
|
||||||
if ($database->has('apikeys', ['key' => $key]) !== TRUE) {
|
}
|
||||||
engageRateLimit();
|
$user = User::byUsername($username);
|
||||||
http_response_code(403);
|
if (!$user->exists()) {
|
||||||
Log::insert(LogType::API_BAD_KEY, null, "Key: " . $key);
|
return false;
|
||||||
|
}
|
||||||
|
if ($user->checkPassword($password, true)) {
|
||||||
|
// Check that the user has permission to access the app
|
||||||
|
$perms = is_array($SETTINGS['api_permissions']) ? $SETTINGS['api_permissions'] : $SETTINGS['permissions'];
|
||||||
|
foreach ($perms as $perm) {
|
||||||
|
if (!$user->hasPermission($perm)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the User whose credentials were used to make the request.
|
||||||
|
*/
|
||||||
|
function getRequestUser(): User {
|
||||||
|
global $VARS;
|
||||||
|
if (!empty($_SERVER['PHP_AUTH_USER'])) {
|
||||||
|
return User::byUsername($_SERVER['PHP_AUTH_USER']);
|
||||||
|
} else {
|
||||||
|
return User::byUsername($VARS['username']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkVars($vars, $or = false) {
|
function checkVars($vars, $or = false) {
|
||||||
@ -90,11 +114,13 @@ function checkVars($vars, $or = false) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$checkmethod = "is_$val";
|
|
||||||
if ($checkmethod($VARS[$key]) !== true) {
|
if (strpos($val, "/") === 0) {
|
||||||
$ok[$key] = false;
|
// regex
|
||||||
|
$ok[$key] = preg_match($val, $VARS[$key]) === 1;
|
||||||
} else {
|
} else {
|
||||||
$ok[$key] = true;
|
$checkmethod = "is_$val";
|
||||||
|
$ok[$key] = !($checkmethod($VARS[$key]) !== true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($or) {
|
if ($or) {
|
||||||
|
@ -10,6 +10,8 @@ require __DIR__ . '/../required.php';
|
|||||||
require __DIR__ . '/functions.php';
|
require __DIR__ . '/functions.php';
|
||||||
require __DIR__ . '/apisettings.php';
|
require __DIR__ . '/apisettings.php';
|
||||||
|
|
||||||
|
header("Access-Control-Allow-Origin: *");
|
||||||
|
|
||||||
$VARS = $_GET;
|
$VARS = $_GET;
|
||||||
if ($_SERVER['REQUEST_METHOD'] != "GET") {
|
if ($_SERVER['REQUEST_METHOD'] != "GET") {
|
||||||
$VARS = array_merge($VARS, $_POST);
|
$VARS = array_merge($VARS, $_POST);
|
||||||
@ -25,13 +27,14 @@ if (json_last_error() == JSON_ERROR_NONE) {
|
|||||||
if (strpos($_SERVER['REQUEST_URI'], "/api.php") === FALSE) {
|
if (strpos($_SERVER['REQUEST_URI'], "/api.php") === FALSE) {
|
||||||
$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/") + 4));
|
$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/") + 4));
|
||||||
|
|
||||||
if (count($route) > 1) {
|
if (count($route) >= 1) {
|
||||||
$VARS["action"] = $route[0];
|
$VARS["action"] = $route[0];
|
||||||
}
|
}
|
||||||
if (count($route) >= 2 && strpos($route[1], "?") !== 0) {
|
if (count($route) >= 2 && strpos($route[1], "?") !== 0) {
|
||||||
$VARS["key"] = $route[1];
|
for ($i = 1; $i < count($route); $i++) {
|
||||||
|
if (empty($route[$i]) || strpos($route[$i], "=") === false) {
|
||||||
for ($i = 2; $i < count($route); $i++) {
|
continue;
|
||||||
|
}
|
||||||
$key = explode("=", $route[$i], 2)[0];
|
$key = explode("=", $route[$i], 2)[0];
|
||||||
$val = explode("=", $route[$i], 2)[1];
|
$val = explode("=", $route[$i], 2)[1];
|
||||||
$VARS[$key] = $val;
|
$VARS[$key] = $val;
|
||||||
@ -49,8 +52,9 @@ if (strpos($_SERVER['REQUEST_URI'], "/api.php") === FALSE) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!authenticate()) {
|
if (!authenticate()) {
|
||||||
http_response_code(403);
|
header('WWW-Authenticate: Basic realm="' . $SETTINGS['site_title'] . '"');
|
||||||
die("403 Unauthorized");
|
header('HTTP/1.1 401 Unauthorized');
|
||||||
|
die("401 Unauthorized: you need to supply valid credentials.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($VARS['action'])) {
|
if (empty($VARS['action'])) {
|
||||||
|
46
index.php
46
index.php
@ -13,8 +13,19 @@ if (!empty($_SESSION['loggedin']) && $_SESSION['loggedin'] === true && !isset($_
|
|||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_GET['logout'])) {
|
/**
|
||||||
// Show a logout message instead of immediately redirecting to login flow
|
* Show a simple HTML page with a line of text and a button. Matches the UI of
|
||||||
|
* the AccountHub login flow.
|
||||||
|
*
|
||||||
|
* @global type $SETTINGS
|
||||||
|
* @global type $SECURE_NONCE
|
||||||
|
* @global type $Strings
|
||||||
|
* @param string $title Text to show, passed through i18n
|
||||||
|
* @param string $button Button text, passed through i18n
|
||||||
|
* @param string $url URL for the button
|
||||||
|
*/
|
||||||
|
function showHTML(string $title, string $button, string $url) {
|
||||||
|
global $SETTINGS, $SECURE_NONCE, $Strings;
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
@ -26,7 +37,6 @@ if (!empty($_GET['logout'])) {
|
|||||||
<link rel="icon" href="static/img/logo.svg">
|
<link rel="icon" href="static/img/logo.svg">
|
||||||
|
|
||||||
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="static/css/svg-with-js.min.css" rel="stylesheet">
|
|
||||||
<style nonce="<?php echo $SECURE_NONCE; ?>">
|
<style nonce="<?php echo $SECURE_NONCE; ?>">
|
||||||
.display-5 {
|
.display-5 {
|
||||||
font-size: 2.5rem;
|
font-size: 2.5rem;
|
||||||
@ -40,11 +50,6 @@ if (!empty($_GET['logout'])) {
|
|||||||
border: 1px solid grey;
|
border: 1px solid grey;
|
||||||
border-radius: 15%;
|
border-radius: 15%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.blank-image {
|
|
||||||
height: 100px;
|
|
||||||
margin: 2em auto;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
@ -54,24 +59,25 @@ if (!empty($_GET['logout'])) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 text-center">
|
<div class="col-12 text-center">
|
||||||
<h1 class="display-5 mb-4"><?php $Strings->get("You have been logged out.") ?></h1>
|
<h1 class="display-5 mb-4"><?php $Strings->get($title); ?></h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-8 col-lg-6">
|
<div class="col-12 col-sm-8 col-lg-6">
|
||||||
<div class="card mt-4">
|
<div class="card mt-4">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<a href="./index.php" class="btn btn-primary btn-block"><?php $Strings->get("Log in again"); ?></a>
|
<a href="<?php echo $url; ?>" class="btn btn-primary btn-block"><?php $Strings->get($button); ?></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="static/js/fontawesome-all.min.js"></script>
|
|
||||||
<?php
|
<?php
|
||||||
die();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!empty($_GET['logout'])) {
|
||||||
|
showHTML("You have been logged out.", "Log in again", "./index.php");
|
||||||
|
die();
|
||||||
|
}
|
||||||
if (empty($_SESSION["login_code"])) {
|
if (empty($_SESSION["login_code"])) {
|
||||||
$redirecttologin = true;
|
$redirecttologin = true;
|
||||||
} else {
|
} else {
|
||||||
@ -82,10 +88,17 @@ if (empty($_SESSION["login_code"])) {
|
|||||||
}
|
}
|
||||||
if (is_numeric($uidinfo['uid'])) {
|
if (is_numeric($uidinfo['uid'])) {
|
||||||
$user = new User($uidinfo['uid'] * 1);
|
$user = new User($uidinfo['uid'] * 1);
|
||||||
|
foreach ($SETTINGS['permissions'] as $perm) {
|
||||||
|
if (!$user->hasPermission($perm)) {
|
||||||
|
showHTML("no access permission", "sign out", "./action.php?action=signout");
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
}
|
||||||
Session::start($user);
|
Session::start($user);
|
||||||
$_SESSION["login_code"] = null;
|
$_SESSION["login_code"] = null;
|
||||||
header('Location: app.php');
|
header('Location: app.php');
|
||||||
die("Logged in, go to app.php");
|
showHTML("Logged in", "Continue", "./app.php");
|
||||||
|
die();
|
||||||
} else {
|
} else {
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
@ -108,7 +121,10 @@ if ($redirecttologin) {
|
|||||||
|
|
||||||
$_SESSION["login_code"] = $codedata["code"];
|
$_SESSION["login_code"] = $codedata["code"];
|
||||||
|
|
||||||
header("Location: " . $codedata["loginurl"] . "?code=" . htmlentities($codedata["code"]) . "&redirect=" . htmlentities($redirecturl));
|
$locationurl = $codedata["loginurl"] . "?code=" . htmlentities($codedata["code"]) . "&redirect=" . htmlentities($redirecturl);
|
||||||
|
header("Location: $locationurl");
|
||||||
|
showHTML("Continue", "Continue", $locationurl);
|
||||||
|
die();
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
sendError($ex->getMessage());
|
sendError($ex->getMessage());
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,7 @@
|
|||||||
{
|
{
|
||||||
"You have been logged out.": "You have been logged out.",
|
|
||||||
"Log in again": "Log in again",
|
|
||||||
"login server unavailable": "Login server unavailable. Try again later or contact technical support.",
|
|
||||||
"welcome user": "Welcome, {user}!",
|
|
||||||
"sign out": "Sign out",
|
"sign out": "Sign out",
|
||||||
"settings": "Settings",
|
|
||||||
"options": "Options",
|
|
||||||
"404 error": "404 Error",
|
"404 error": "404 Error",
|
||||||
"page not found": "Page not found.",
|
"page not found": "Page not found.",
|
||||||
"invalid parameters": "Invalid request parameters.",
|
"invalid parameters": "Invalid request parameters.",
|
||||||
"login server error": "The login server returned an error: {arg}",
|
"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."
|
|
||||||
}
|
}
|
||||||
|
8
langs/en/index.json
Normal file
8
langs/en/index.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"You have been logged out.": "You have been logged out.",
|
||||||
|
"Log in again": "Log in again",
|
||||||
|
"login server unavailable": "Login server unavailable. Try again later or contact technical support.",
|
||||||
|
"no access permission": "You do not have permission to access this system.",
|
||||||
|
"Logged in": "Logged in",
|
||||||
|
"Continue": "Continue"
|
||||||
|
}
|
@ -116,6 +116,41 @@ class FormBuilder {
|
|||||||
$this->items[] = $item;
|
$this->items[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a text input.
|
||||||
|
*
|
||||||
|
* @param string $name Element name
|
||||||
|
* @param string $value Element value
|
||||||
|
* @param bool $required If the element is required for form submission.
|
||||||
|
* @param string $id Element ID
|
||||||
|
* @param string $label Text label to display near the input
|
||||||
|
* @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
|
||||||
|
* @param int $width Bootstrap column width for the input, out of 12.
|
||||||
|
* @param int $minlength Minimum number of characters for the input.
|
||||||
|
* @param int $maxlength Maximum number of characters for the input.
|
||||||
|
* @param string $pattern Regex pattern for custom client-side validation.
|
||||||
|
* @param string $error Message to show if the input doesn't validate.
|
||||||
|
*/
|
||||||
|
public function addTextInput(string $name, string $value = "", bool $required = true, string $id = "", string $label = "", string $icon = "", int $width = 4, int $minlength = 1, int $maxlength = 100, string $pattern = "", string $error = "") {
|
||||||
|
$this->addInput($name, $value, "text", $required, $id, null, $label, $icon, $width, $minlength, $maxlength, $pattern, $error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a select dropdown.
|
||||||
|
*
|
||||||
|
* @param string $name Element name
|
||||||
|
* @param string $value Element value
|
||||||
|
* @param bool $required If the element is required for form submission.
|
||||||
|
* @param string $id Element ID
|
||||||
|
* @param array $options Array of [value => text] pairs for a select element
|
||||||
|
* @param string $label Text label to display near the input
|
||||||
|
* @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
|
||||||
|
* @param int $width Bootstrap column width for the input, out of 12.
|
||||||
|
*/
|
||||||
|
public function addSelect(string $name, string $value = "", bool $required = true, string $id = null, array $options = null, string $label = "", string $icon = "", int $width = 4) {
|
||||||
|
$this->addInput($name, $value, "select", $required, $id, $options, $label, $icon, $width);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a button to the form.
|
* Add a button to the form.
|
||||||
*
|
*
|
||||||
@ -173,24 +208,34 @@ HTMLTOP;
|
|||||||
$required = $item["required"] ? "required" : "";
|
$required = $item["required"] ? "required" : "";
|
||||||
$id = empty($item["id"]) ? "" : "id=\"$item[id]\"";
|
$id = empty($item["id"]) ? "" : "id=\"$item[id]\"";
|
||||||
$pattern = empty($item["pattern"]) ? "" : "pattern=\"$item[pattern]\"";
|
$pattern = empty($item["pattern"]) ? "" : "pattern=\"$item[pattern]\"";
|
||||||
|
if (empty($item['type'])) {
|
||||||
|
$item['type'] = "text";
|
||||||
|
}
|
||||||
$itemhtml = "";
|
$itemhtml = "";
|
||||||
|
$itemlabel = "";
|
||||||
|
|
||||||
|
if ($item['type'] == "textarea") {
|
||||||
|
$itemlabel = "<label class=\"mb-0\"><i class=\"$item[icon]\"></i> $item[label]:</label>";
|
||||||
|
} else if ($item['type'] != "checkbox") {
|
||||||
|
$itemlabel = "<label class=\"mb-0\">$item[label]:</label>";
|
||||||
|
}
|
||||||
|
$strippedlabel = strip_tags($item['label']);
|
||||||
$itemhtml .= <<<ITEMTOP
|
$itemhtml .= <<<ITEMTOP
|
||||||
\n\n <div class="col-12 col-md-$item[width]">
|
\n\n <div class="col-12 col-md-$item[width]">
|
||||||
<div class="form-group mb-3">
|
<div class="form-group mb-3">
|
||||||
<label class="mb-0">$item[label]:</label>
|
$itemlabel
|
||||||
<div class="input-group">
|
ITEMTOP;
|
||||||
|
$inputgrouptop = <<<INPUTG
|
||||||
|
\n <div class="input-group">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<span class="input-group-text"><i class="$item[icon]"></i></span>
|
<span class="input-group-text"><i class="$item[icon]"></i></span>
|
||||||
</div>
|
</div>
|
||||||
ITEMTOP;
|
INPUTG;
|
||||||
if (empty($item['type']) || $item['type'] != "select") {
|
switch ($item['type']) {
|
||||||
$itemhtml .= <<<INPUT
|
case "select":
|
||||||
\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 />
|
$itemhtml .= $inputgrouptop;
|
||||||
INPUT;
|
|
||||||
} else {
|
|
||||||
$itemhtml .= <<<SELECT
|
$itemhtml .= <<<SELECT
|
||||||
\n <select class="form-control" name="$item[name]" aria-label="$item[label]" $required>
|
\n <select class="form-control" name="$item[name]" aria-label="$strippedlabel" $required>
|
||||||
SELECT;
|
SELECT;
|
||||||
foreach ($item['options'] as $value => $label) {
|
foreach ($item['options'] as $value => $label) {
|
||||||
$selected = "";
|
$selected = "";
|
||||||
@ -200,6 +245,28 @@ SELECT;
|
|||||||
$itemhtml .= "\n <option value=\"$value\"$selected>$label</option>";
|
$itemhtml .= "\n <option value=\"$value\"$selected>$label</option>";
|
||||||
}
|
}
|
||||||
$itemhtml .= "\n </select>";
|
$itemhtml .= "\n </select>";
|
||||||
|
break;
|
||||||
|
case "checkbox":
|
||||||
|
$itemhtml .= $inputgrouptop;
|
||||||
|
$itemhtml .= <<<CHECKBOX
|
||||||
|
\n <div class="form-group form-check">
|
||||||
|
<input type="checkbox" name="$item[name]" $id class="form-check-input" value="$item[value]" $required aria-label="$strippedlabel">
|
||||||
|
<label class="form-check-label">$item[label]</label>
|
||||||
|
</div>
|
||||||
|
CHECKBOX;
|
||||||
|
break;
|
||||||
|
case "textarea":
|
||||||
|
$val = htmlentities($item['value']);
|
||||||
|
$itemhtml .= <<<TEXTAREA
|
||||||
|
\n <textarea class="form-control" id="info" name="$item[name]" aria-label="$strippedlabel" minlength="$item[minlength]" maxlength="$item[maxlength]" $required>$val</textarea>
|
||||||
|
TEXTAREA;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$itemhtml .= $inputgrouptop;
|
||||||
|
$itemhtml .= <<<INPUT
|
||||||
|
\n <input type="$item[type]" name="$item[name]" $id class="form-control" aria-label="$strippedlabel" minlength="$item[minlength]" maxlength="$item[maxlength]" $pattern value="$item[value]" $required />
|
||||||
|
INPUT;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($item["error"])) {
|
if (!empty($item["error"])) {
|
||||||
@ -209,9 +276,11 @@ SELECT;
|
|||||||
</div>
|
</div>
|
||||||
ERROR;
|
ERROR;
|
||||||
}
|
}
|
||||||
|
if ($item["type"] != "textarea") {
|
||||||
|
$itemhtml .= "\n </div>";
|
||||||
|
}
|
||||||
$itemhtml .= <<<ITEMBOTTOM
|
$itemhtml .= <<<ITEMBOTTOM
|
||||||
\n </div>
|
\n </div>
|
||||||
</div>
|
|
||||||
</div>\n
|
</div>\n
|
||||||
ITEMBOTTOM;
|
ITEMBOTTOM;
|
||||||
$html .= $itemhtml;
|
$html .= $itemhtml;
|
||||||
@ -224,7 +293,7 @@ ITEMBOTTOM;
|
|||||||
HTMLBOTTOM;
|
HTMLBOTTOM;
|
||||||
|
|
||||||
if (!empty($this->buttons)) {
|
if (!empty($this->buttons)) {
|
||||||
$html .= "\n <div class=\"card-footer\">";
|
$html .= "\n <div class=\"card-footer d-flex\">";
|
||||||
foreach ($this->buttons as $btn) {
|
foreach ($this->buttons as $btn) {
|
||||||
$btnhtml = "";
|
$btnhtml = "";
|
||||||
$inner = "<i class=\"$btn[icon]\"></i> $btn[text]";
|
$inner = "<i class=\"$btn[icon]\"></i> $btn[text]";
|
||||||
|
@ -45,29 +45,6 @@ class Login {
|
|||||||
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
|
* Check the login server API for sanity
|
||||||
* @return boolean true if OK, else false
|
* @return boolean true if OK, else false
|
||||||
|
@ -88,10 +88,11 @@ class User {
|
|||||||
/**
|
/**
|
||||||
* Check the given plaintext password against the stored hash.
|
* Check the given plaintext password against the stored hash.
|
||||||
* @param string $password
|
* @param string $password
|
||||||
|
* @param bool $apppass Set to true to enforce app passwords when 2fa is on.
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
function checkPassword(string $password): bool {
|
function checkPassword(string $password, bool $apppass = false): bool {
|
||||||
$resp = AccountHubApi::get("auth", ['username' => $this->username, 'password' => $password]);
|
$resp = AccountHubApi::get("auth", ['username' => $this->username, 'password' => $password, 'apppass' => ($apppass ? "1" : "0")]);
|
||||||
if ($resp['status'] == "OK") {
|
if ($resp['status'] == "OK") {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -99,6 +100,7 @@ class User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function check2fa(string $code): bool {
|
function check2fa(string $code): bool {
|
||||||
if (!$this->has2fa) {
|
if (!$this->has2fa) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -8,10 +8,6 @@
|
|||||||
* Mobile app API
|
* Mobile app API
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// The name of the permission needed to log in.
|
|
||||||
// Set to null if you don't need it.
|
|
||||||
$access_permission = null;
|
|
||||||
|
|
||||||
require __DIR__ . "/../required.php";
|
require __DIR__ . "/../required.php";
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
header('Content-Type: application/json');
|
||||||
@ -70,13 +66,14 @@ switch ($VARS['action']) {
|
|||||||
if ($user->exists()) {
|
if ($user->exists()) {
|
||||||
if ($user->getStatus()->getString() == "NORMAL") {
|
if ($user->getStatus()->getString() == "NORMAL") {
|
||||||
if ($user->checkPassword($VARS['password'])) {
|
if ($user->checkPassword($VARS['password'])) {
|
||||||
if (is_null($access_permission) || $user->hasPermission($access_permission)) {
|
foreach ($SETTINGS['permissions'] as $perm) {
|
||||||
|
if (!$user->hasPermission($perm)) {
|
||||||
|
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("no permission", false)]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Session::start($user);
|
Session::start($user);
|
||||||
$_SESSION['mobile'] = true;
|
$_SESSION['mobile'] = true;
|
||||||
exit(json_encode(["status" => "OK"]));
|
exit(json_encode(["status" => "OK"]));
|
||||||
} else {
|
|
||||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("no admin permission", false)]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
27
required.php
27
required.php
@ -32,7 +32,6 @@ session_start(); // stick some cookies in it
|
|||||||
// renew session cookie
|
// renew session cookie
|
||||||
setcookie(session_name(), session_id(), time() + $session_length, "/", false, false);
|
setcookie(session_name(), session_id(), time() + $session_length, "/", false, false);
|
||||||
|
|
||||||
$captcha_server = ($SETTINGS['captcha']['enabled'] === true ? preg_replace("/http(s)?:\/\//", "", $SETTINGS['captcha']['server']) : "");
|
|
||||||
if ($_SESSION['mobile'] === TRUE) {
|
if ($_SESSION['mobile'] === TRUE) {
|
||||||
header("Content-Security-Policy: "
|
header("Content-Security-Policy: "
|
||||||
. "default-src 'self';"
|
. "default-src 'self';"
|
||||||
@ -42,8 +41,8 @@ if ($_SESSION['mobile'] === TRUE) {
|
|||||||
. "frame-src 'none'; "
|
. "frame-src 'none'; "
|
||||||
. "font-src 'self'; "
|
. "font-src 'self'; "
|
||||||
. "connect-src *; "
|
. "connect-src *; "
|
||||||
. "style-src 'self' 'unsafe-inline' $captcha_server; "
|
. "style-src 'self' 'unsafe-inline'; "
|
||||||
. "script-src 'self' 'unsafe-inline' $captcha_server");
|
. "script-src 'self' 'unsafe-inline'");
|
||||||
} else {
|
} else {
|
||||||
header("Content-Security-Policy: "
|
header("Content-Security-Policy: "
|
||||||
. "default-src 'self';"
|
. "default-src 'self';"
|
||||||
@ -53,8 +52,8 @@ if ($_SESSION['mobile'] === TRUE) {
|
|||||||
. "frame-src 'none'; "
|
. "frame-src 'none'; "
|
||||||
. "font-src 'self'; "
|
. "font-src 'self'; "
|
||||||
. "connect-src *; "
|
. "connect-src *; "
|
||||||
. "style-src 'self' 'nonce-$SECURE_NONCE' $captcha_server; "
|
. "style-src 'self' 'nonce-$SECURE_NONCE'; "
|
||||||
. "script-src 'self' 'nonce-$SECURE_NONCE' $captcha_server");
|
. "script-src 'self' 'nonce-$SECURE_NONCE'");
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@ -132,11 +131,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
define("GET", true);
|
define("GET", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function dieifnotloggedin() {
|
function dieifnotloggedin() {
|
||||||
|
global $SETTINGS;
|
||||||
if ($_SESSION['loggedin'] != true) {
|
if ($_SESSION['loggedin'] != true) {
|
||||||
sendError("Session expired. Please log out and log in again.");
|
sendError("Session expired. Please log out and log in again.");
|
||||||
}
|
}
|
||||||
|
$user = new User($_SESSION['uid']);
|
||||||
|
foreach ($SETTINGS['permissions'] as $perm) {
|
||||||
|
if (!$user->hasPermission($perm)) {
|
||||||
|
session_destroy();
|
||||||
|
die("You don't have permission to be here.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,8 +163,17 @@ function checkDBError($specials = []) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function redirectIfNotLoggedIn() {
|
function redirectIfNotLoggedIn() {
|
||||||
|
global $SETTINGS;
|
||||||
if ($_SESSION['loggedin'] !== TRUE) {
|
if ($_SESSION['loggedin'] !== TRUE) {
|
||||||
header('Location: ' . $SETTINGS['url'] . '/index.php');
|
header('Location: ' . $SETTINGS['url'] . '/index.php');
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
$user = new User($_SESSION['uid']);
|
||||||
|
foreach ($SETTINGS['permissions'] as $perm) {
|
||||||
|
if (!$user->hasPermission($perm)) {
|
||||||
|
session_destroy();
|
||||||
|
header('Location: ./index.php');
|
||||||
|
die("You don't have permission to be here.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,15 @@ $SETTINGS = [
|
|||||||
"enabled" => false,
|
"enabled" => false,
|
||||||
"server" => "https://captcheck.netsyms.com"
|
"server" => "https://captcheck.netsyms.com"
|
||||||
],
|
],
|
||||||
|
// List of required user permissions to access this app.
|
||||||
|
"permissions" => [
|
||||||
|
],
|
||||||
|
// List of permissions required for API access. Remove to use the value of
|
||||||
|
// "permissions" instead.
|
||||||
|
"api_permissions" => [
|
||||||
|
],
|
||||||
|
// For supported values, see http://php.net/manual/en/timezones.php
|
||||||
|
"timezone" => "America/Denver",
|
||||||
// Language to use for localization. See langs folder to add a language.
|
// Language to use for localization. See langs folder to add a language.
|
||||||
"language" => "en",
|
"language" => "en",
|
||||||
// Shown in the footer of all the pages.
|
// Shown in the footer of all the pages.
|
||||||
|
12
static/css/bootstrap.min.css
vendored
12
static/css/bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
@ -1,15 +0,0 @@
|
|||||||
/* 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/. */
|
|
||||||
|
|
||||||
.banner-image {
|
|
||||||
max-height: 100px;
|
|
||||||
margin: 2em auto;
|
|
||||||
border: 1px solid grey;
|
|
||||||
border-radius: 15%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
margin-top: 10em;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
6
static/css/svg-with-js.min.css
vendored
6
static/css/svg-with-js.min.css
vendored
@ -1,5 +1 @@
|
|||||||
/*!
|
.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-both,.fa-flip-horizontal.fa-flip-vertical,.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)"}.fa-flip-both,.fa-flip-horizontal.fa-flip-vertical{transform:scale(-1)}:root .fa-flip-both,: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}
|
||||||
* Font Awesome Free 5.6.0 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: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}
|
|
@ -13,7 +13,7 @@ $(document).ready(function () {
|
|||||||
var gone = 20;
|
var gone = 20;
|
||||||
|
|
||||||
var msgticker = setInterval(function () {
|
var msgticker = setInterval(function () {
|
||||||
if ($('#msg-alert-box .alert:hover').length) {
|
if ($("#msg-alert-box .alert:hover").length) {
|
||||||
msginteractiontick = 0;
|
msginteractiontick = 0;
|
||||||
} else {
|
} else {
|
||||||
msginteractiontick++;
|
msginteractiontick++;
|
||||||
@ -55,7 +55,6 @@ $(document).ready(function () {
|
|||||||
$("#msg-alert-box").on("mouseenter", function () {
|
$("#msg-alert-box").on("mouseenter", function () {
|
||||||
$("#msg-alert-box").css("opacity", "1");
|
$("#msg-alert-box").css("opacity", "1");
|
||||||
msginteractiontick = 0;
|
msginteractiontick = 0;
|
||||||
console.log("👈😎👈 zoop");
|
|
||||||
});
|
});
|
||||||
$("#msg-alert-box").on("click", ".close", function (e) {
|
$("#msg-alert-box").on("click", ".close", function (e) {
|
||||||
$("#msg-alert-box").fadeOut("slow");
|
$("#msg-alert-box").fadeOut("slow");
|
||||||
|
6
static/js/bootstrap.bundle.min.js
vendored
6
static/js/bootstrap.bundle.min.js
vendored
File diff suppressed because one or more lines are too long
@ -12,5 +12,5 @@ $("#savebutton").click(function (event) {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
}
|
}
|
||||||
form.addClass('was-validated');
|
form.addClass("was-validated");
|
||||||
});
|
});
|
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