Merge ../BusinessAppTemplate
# Conflicts: # README.md # langs/en/titles.json # pages.php # required.php # settings.template.php
This commit is contained in:
commit
063c8398a9
18
LICENSE.md
18
LICENSE.md
@ -1,19 +1,7 @@
|
||||
Copyright (c) 2018 Netsyms Technologies.
|
||||
Copyright (c) 2017-2019 Netsyms Technologies. Some rights reserved.
|
||||
|
||||
If you modify and redistribute this project, you must replace the branding
|
||||
assets with your own.
|
||||
|
||||
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:
|
||||
Licensed under the Mozilla Public License Version 2.0. Files without MPL header
|
||||
comments, including third party code, may be under a different license.
|
||||
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
@ -39,4 +39,4 @@ Installing
|
||||
8. Set the URL of this app ("URL")
|
||||
9. Copy webroot.htaccess to your webroot and adjust paths if needed
|
||||
10. Run `composer install` (or `composer.phar install`) to install dependency libraries
|
||||
11. Run `git submodule init` and `git submodule update` to install other dependencies via git.
|
||||
11. Run `git submodule init` and `git submodule update` to install other dependencies via git.
|
||||
|
28
action.php
28
action.php
@ -22,11 +22,11 @@ if ($VARS['action'] !== "signout") {
|
||||
*/
|
||||
function returnToSender($msg, $arg = "") {
|
||||
global $VARS;
|
||||
if ($arg == "") {
|
||||
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=" . $msg);
|
||||
} else {
|
||||
header("Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg&arg=$arg");
|
||||
$header = "Location: app.php?page=" . urlencode($VARS['source']) . "&msg=$msg";
|
||||
if ($arg != "") {
|
||||
$header .= "&arg=$arg";
|
||||
}
|
||||
header($header);
|
||||
die();
|
||||
}
|
||||
|
||||
@ -242,8 +242,8 @@ switch ($VARS['action']) {
|
||||
if (!$user->hasPermission("SITEWRITER") && !$user->hasPermission("SITEWRITER_FILES")) {
|
||||
returnToSender("no_permission");
|
||||
}
|
||||
$destpath = FILE_UPLOAD_PATH . $VARS['path'];
|
||||
if (strpos(realpath($destpath), FILE_UPLOAD_PATH) !== 0) {
|
||||
$destpath = $SETTINGS["file_upload_path"] . $VARS['path'];
|
||||
if (strpos(realpath($destpath), $SETTINGS["file_upload_path"]) !== 0) {
|
||||
returnToSender("file_security_error");
|
||||
}
|
||||
if (!file_exists($destpath) || !is_dir($destpath)) {
|
||||
@ -315,7 +315,7 @@ switch ($VARS['action']) {
|
||||
returnToSender("no_permission");
|
||||
}
|
||||
$foldername = preg_replace("/[^a-z0-9_\-]/", "_", strtolower($VARS['folder']));
|
||||
$newfolder = FILE_UPLOAD_PATH . $VARS['path'] . '/' . $foldername;
|
||||
$newfolder = $SETTINGS["file_upload_path"] . $VARS['path'] . '/' . $foldername;
|
||||
|
||||
if (mkdir($newfolder, 0755)) {
|
||||
returnToSender("folder_created", "&path=" . $VARS['path']);
|
||||
@ -326,15 +326,15 @@ switch ($VARS['action']) {
|
||||
if (!$user->hasPermission("SITEWRITER") && !$user->hasPermission("SITEWRITER_FILES")) {
|
||||
returnToSender("no_permission");
|
||||
}
|
||||
$file = FILE_UPLOAD_PATH . $VARS['file'];
|
||||
if (strpos(realpath($file), FILE_UPLOAD_PATH) !== 0) {
|
||||
$file = $SETTINGS["file_upload_path"] . $VARS['file'];
|
||||
if (strpos(realpath($file), $SETTINGS["file_upload_path"]) !== 0) {
|
||||
returnToSender("file_security_error");
|
||||
}
|
||||
if (!file_exists($file)) {
|
||||
// Either way the file is gone
|
||||
returnToSender("file_deleted");
|
||||
}
|
||||
if (!is_writable($file) || realpath($file) == realpath(FILE_UPLOAD_PATH)) {
|
||||
if (!is_writable($file) || realpath($file) == realpath($SETTINGS["file_upload_path"])) {
|
||||
returnToSender("undeletable_file");
|
||||
}
|
||||
if (is_dir($file)) {
|
||||
@ -350,9 +350,9 @@ switch ($VARS['action']) {
|
||||
break;
|
||||
case "unsplash_download":
|
||||
Crew\Unsplash\HttpClient::init([
|
||||
'applicationId' => UNSPLASH_ACCESSKEY,
|
||||
'secret' => UNSPLASH_SECRETKEY,
|
||||
'utmSource' => UNSPLASH_UTMSOURCE
|
||||
'applicationId' => $SETTINGS["unsplash"]["accesskey"],
|
||||
'secret' => $SETTINGS["unsplash"]["secretkey"],
|
||||
'utmSource' => $SETTINGS["unsplash"]["utmsource"]
|
||||
]);
|
||||
Crew\Unsplash\Photo::find($VARS['imageid'])->download();
|
||||
header('Content-Type: application/json');
|
||||
@ -360,6 +360,6 @@ switch ($VARS['action']) {
|
||||
break;
|
||||
case "signout":
|
||||
session_destroy();
|
||||
header('Location: index.php');
|
||||
header('Location: index.php?logout=1');
|
||||
die("Logged out.");
|
||||
}
|
33
api.php
33
api.php
@ -4,35 +4,6 @@
|
||||
* 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/. */
|
||||
|
||||
/**
|
||||
* Simple JSON API to allow other apps to access data from this app.
|
||||
*
|
||||
* Requests can be sent via either GET or POST requests. POST is recommended
|
||||
* as it has a lower chance of being logged on the server, exposing unencrypted
|
||||
* user passwords.
|
||||
*/
|
||||
require __DIR__ . '/required.php';
|
||||
header("Content-Type: application/json");
|
||||
|
||||
$username = $VARS['username'];
|
||||
$password = $VARS['password'];
|
||||
$user = User::byUsername($username);
|
||||
if ($user->exists() !== true || Login::auth($username, $password) !== Login::LOGIN_OK) {
|
||||
header("HTTP/1.1 403 Unauthorized");
|
||||
die("\"403 Unauthorized\"");
|
||||
}
|
||||
|
||||
// query max results
|
||||
$max = 20;
|
||||
if (preg_match("/^[0-9]+$/", $VARS['max']) === 1 && $VARS['max'] <= 1000) {
|
||||
$max = (int) $VARS['max'];
|
||||
}
|
||||
|
||||
switch ($VARS['action']) {
|
||||
case "ping":
|
||||
$out = ["status" => "OK", "maxresults" => $max, "pong" => true];
|
||||
exit(json_encode($out));
|
||||
default:
|
||||
header("HTTP/1.1 400 Bad Request");
|
||||
die("\"400 Bad Request\"");
|
||||
}
|
||||
// Load in new API from legacy location (a.k.a. here)
|
||||
require __DIR__ . "/api/index.php";
|
5
api/.htaccess
Normal file
5
api/.htaccess
Normal file
@ -0,0 +1,5 @@
|
||||
# Rewrite for Nextcloud Notes API
|
||||
<IfModule mod_rewrite.c>
|
||||
RewriteEngine on
|
||||
RewriteRule ([a-zA-Z0-9]+) index.php?action=$1 [PT]
|
||||
</IfModule>
|
9
api/actions/ping.php
Normal file
9
api/actions/ping.php
Normal file
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
sendJsonResp();
|
15
api/apisettings.php
Normal file
15
api/apisettings.php
Normal file
@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
$APIS = [
|
||||
"ping" => [
|
||||
"load" => "ping.php",
|
||||
"vars" => [
|
||||
]
|
||||
]
|
||||
];
|
149
api/functions.php
Normal file
149
api/functions.php
Normal file
@ -0,0 +1,149 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Build and send a simple JSON response.
|
||||
* @param string $msg A message
|
||||
* @param string $status "OK" or "ERROR"
|
||||
* @param array $data More JSON data
|
||||
*/
|
||||
function sendJsonResp(string $msg = null, string $status = "OK", array $data = null) {
|
||||
$resp = [];
|
||||
if (!is_null($data)) {
|
||||
$resp = $data;
|
||||
}
|
||||
if (!is_null($msg)) {
|
||||
$resp["msg"] = $msg;
|
||||
}
|
||||
$resp["status"] = $status;
|
||||
header("Content-Type: application/json");
|
||||
exit(json_encode($resp));
|
||||
}
|
||||
|
||||
function exitWithJson(array $json) {
|
||||
header("Content-Type: application/json");
|
||||
exit(json_encode($json));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the API key with most of the characters replaced with *s.
|
||||
* @global string $key
|
||||
* @return string
|
||||
*/
|
||||
function getCensoredKey() {
|
||||
global $key;
|
||||
$resp = $key;
|
||||
if (strlen($key) > 5) {
|
||||
for ($i = 2; $i < strlen($key) - 2; $i++) {
|
||||
$resp[$i] = "*";
|
||||
}
|
||||
}
|
||||
return $resp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the request is allowed
|
||||
* @global array $VARS
|
||||
* @return bool true if the request should continue, false if the request is bad
|
||||
*/
|
||||
function authenticate(): bool {
|
||||
global $VARS, $SETTINGS;
|
||||
// HTTP basic auth
|
||||
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 {
|
||||
return false;
|
||||
}
|
||||
$user = User::byUsername($username);
|
||||
if (!$user->exists()) {
|
||||
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 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) {
|
||||
global $VARS;
|
||||
$ok = [];
|
||||
foreach ($vars as $key => $val) {
|
||||
if (strpos($key, "OR") === 0) {
|
||||
checkVars($vars[$key], true);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only check type of optional variables if they're set, and don't
|
||||
// mark them as bad if they're not set
|
||||
if (strpos($key, " (optional)") !== false) {
|
||||
$key = str_replace(" (optional)", "", $key);
|
||||
if (empty($VARS[$key])) {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
if (empty($VARS[$key])) {
|
||||
$ok[$key] = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($val, "/") === 0) {
|
||||
// regex
|
||||
$ok[$key] = preg_match($val, $VARS[$key]) === 1;
|
||||
} else {
|
||||
$checkmethod = "is_$val";
|
||||
$ok[$key] = !($checkmethod($VARS[$key]) !== true);
|
||||
}
|
||||
}
|
||||
if ($or) {
|
||||
$success = false;
|
||||
$bad = "";
|
||||
foreach ($ok as $k => $v) {
|
||||
if ($v) {
|
||||
$success = true;
|
||||
break;
|
||||
} else {
|
||||
$bad = $k;
|
||||
}
|
||||
}
|
||||
if (!$success) {
|
||||
http_response_code(400);
|
||||
die("400 Bad request: variable $bad is missing or invalid");
|
||||
}
|
||||
} else {
|
||||
foreach ($ok as $key => $bool) {
|
||||
if (!$bool) {
|
||||
http_response_code(400);
|
||||
die("400 Bad request: variable $key is missing or invalid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
81
api/index.php
Normal file
81
api/index.php
Normal file
@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
require __DIR__ . '/../required.php';
|
||||
require __DIR__ . '/functions.php';
|
||||
require __DIR__ . '/apisettings.php';
|
||||
|
||||
header("Access-Control-Allow-Origin: *");
|
||||
|
||||
$VARS = $_GET;
|
||||
if ($_SERVER['REQUEST_METHOD'] != "GET") {
|
||||
$VARS = array_merge($VARS, $_POST);
|
||||
}
|
||||
|
||||
$requestbody = file_get_contents('php://input');
|
||||
$requestjson = json_decode($requestbody, TRUE);
|
||||
if (json_last_error() == JSON_ERROR_NONE) {
|
||||
$VARS = array_merge($VARS, $requestjson);
|
||||
}
|
||||
|
||||
// If we're not using the old api.php file, allow more flexible requests
|
||||
if (strpos($_SERVER['REQUEST_URI'], "/api.php") === FALSE) {
|
||||
$route = explode("/", substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], "api/") + 4));
|
||||
|
||||
if (count($route) >= 1) {
|
||||
$VARS["action"] = $route[0];
|
||||
}
|
||||
if (count($route) >= 2 && strpos($route[1], "?") !== 0) {
|
||||
for ($i = 1; $i < count($route); $i++) {
|
||||
if (empty($route[$i]) || strpos($route[$i], "=") === false) {
|
||||
continue;
|
||||
}
|
||||
$key = explode("=", $route[$i], 2)[0];
|
||||
$val = explode("=", $route[$i], 2)[1];
|
||||
$VARS[$key] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
if (strpos($route[count($route) - 1], "?") === 0) {
|
||||
$morevars = explode("&", substr($route[count($route) - 1], 1));
|
||||
foreach ($morevars as $var) {
|
||||
$key = explode("=", $var, 2)[0];
|
||||
$val = explode("=", $var, 2)[1];
|
||||
$VARS[$key] = $val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!authenticate()) {
|
||||
header('WWW-Authenticate: Basic realm="' . $SETTINGS['site_title'] . '"');
|
||||
header('HTTP/1.1 401 Unauthorized');
|
||||
die("401 Unauthorized: you need to supply valid credentials.");
|
||||
}
|
||||
|
||||
if (empty($VARS['action'])) {
|
||||
http_response_code(404);
|
||||
die("404 No action specified");
|
||||
}
|
||||
|
||||
if (!isset($APIS[$VARS['action']])) {
|
||||
http_response_code(404);
|
||||
die("404 Action not defined");
|
||||
}
|
||||
|
||||
$APIACTION = $APIS[$VARS["action"]];
|
||||
|
||||
if (!file_exists(__DIR__ . "/actions/" . $APIACTION["load"])) {
|
||||
http_response_code(404);
|
||||
die("404 Action not found");
|
||||
}
|
||||
|
||||
if (!empty($APIACTION["vars"])) {
|
||||
checkVars($APIACTION["vars"]);
|
||||
}
|
||||
|
||||
require_once __DIR__ . "/actions/" . $APIACTION["load"];
|
10
app.php
10
app.php
@ -39,7 +39,7 @@ header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", fals
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<title><?php echo SITE_TITLE; ?></title>
|
||||
<title><?php echo $SETTINGS['site_title']; ?></title>
|
||||
|
||||
<link rel="icon" href="static/img/logo.svg">
|
||||
|
||||
@ -127,7 +127,7 @@ END;
|
||||
</button>
|
||||
<a class="navbar-brand py-0 mr-auto" href="app.php">
|
||||
<img src="static/img/logo.svg" alt="" class="d-none d-<?php echo $navbar_breakpoint; ?>-inline brand-img py-0" />
|
||||
<?php echo SITE_TITLE; ?>
|
||||
<?php echo $SETTINGS['site_title']; ?>
|
||||
</a>
|
||||
|
||||
<div class="collapse navbar-collapse py-0" id="navbar-collapse">
|
||||
@ -163,7 +163,7 @@ END;
|
||||
</div>
|
||||
<div class="navbar-nav ml-auto py-0" id="navbar-right">
|
||||
<span class="nav-item py-<?php echo $navbar_breakpoint; ?>-0">
|
||||
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="<?php echo PORTAL_URL; ?>">
|
||||
<a class="nav-link py-<?php echo $navbar_breakpoint; ?>-0" href="<?php echo $SETTINGS['accounthub']['home']; ?>">
|
||||
<i class="fas fa-user fa-fw"></i><span> <?php echo $_SESSION['realname'] ?></span>
|
||||
</a>
|
||||
</span>
|
||||
@ -183,8 +183,8 @@ END;
|
||||
?>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<?php echo FOOTER_TEXT; ?><br />
|
||||
Copyright © <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
|
||||
<?php echo $SETTINGS['footer_text']; ?><br />
|
||||
Copyright © <?php echo date('Y'); ?> <?php echo $SETTINGS['copyright']; ?>
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/js/jquery-3.3.1.min.js"></script>
|
||||
|
264
index.php
264
index.php
@ -1,175 +1,131 @@
|
||||
<?php
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/*
|
||||
* 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/. */
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
require_once __DIR__ . "/required.php";
|
||||
|
||||
// if we're logged in, we don't need to be here.
|
||||
if (!empty($_SESSION['loggedin']) && $_SESSION['loggedin'] === true && !isset($_GET['permissionerror'])) {
|
||||
header('Location: app.php');
|
||||
die();
|
||||
}
|
||||
|
||||
if (isset($_GET['permissionerror'])) {
|
||||
$alert = $Strings->get("no access permission", false);
|
||||
}
|
||||
/**
|
||||
* 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>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
/* Authenticate user */
|
||||
$userpass_ok = false;
|
||||
$multiauth = false;
|
||||
if (Login::checkLoginServer()) {
|
||||
if (empty($VARS['progress'])) {
|
||||
// Easy way to remove "undefined" warnings.
|
||||
} else if ($VARS['progress'] == "1") {
|
||||
if (!CAPTCHA_ENABLED || (CAPTCHA_ENABLED && Login::verifyCaptcha($VARS['captcheck_session_code'], $VARS['captcheck_selected_answer'], CAPTCHA_SERVER . "/api.php"))) {
|
||||
$autherror = "";
|
||||
$user = User::byUsername($VARS['username']);
|
||||
if ($user->exists()) {
|
||||
$status = $user->getStatus()->getString();
|
||||
switch ($status) {
|
||||
case "LOCKED_OR_DISABLED":
|
||||
$alert = $Strings->get("account locked", false);
|
||||
break;
|
||||
case "TERMINATED":
|
||||
$alert = $Strings->get("account terminated", false);
|
||||
break;
|
||||
case "CHANGE_PASSWORD":
|
||||
$alert = $Strings->get("password expired", false);
|
||||
break;
|
||||
case "NORMAL":
|
||||
$username_ok = true;
|
||||
break;
|
||||
case "ALERT_ON_ACCESS":
|
||||
$mail_resp = $user->sendAlertEmail();
|
||||
if (DEBUG) {
|
||||
var_dump($mail_resp);
|
||||
}
|
||||
$username_ok = true;
|
||||
break;
|
||||
default:
|
||||
if (!empty($error)) {
|
||||
$alert = $error;
|
||||
} else {
|
||||
$alert = $Strings->get("login error", false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($username_ok) {
|
||||
if ($user->checkPassword($VARS['password'])) {
|
||||
$_SESSION['passok'] = true; // stop logins using only username and authcode
|
||||
if ($user->has2fa()) {
|
||||
$multiauth = true;
|
||||
} else {
|
||||
Session::start($user);
|
||||
header('Location: app.php');
|
||||
die("Logged in, go to app.php");
|
||||
}
|
||||
} else {
|
||||
$alert = $Strings->get("login incorrect", false);
|
||||
}
|
||||
}
|
||||
} else { // User does not exist anywhere
|
||||
$alert = $Strings->get("login incorrect", false);
|
||||
}
|
||||
} else {
|
||||
$alert = $Strings->get("captcha error", false);
|
||||
<title><?php echo $SETTINGS['site_title']; ?></title>
|
||||
|
||||
<link rel="icon" href="static/img/logo.svg">
|
||||
|
||||
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style nonce="<?php echo $SECURE_NONCE; ?>">
|
||||
.display-5 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.2;
|
||||
}
|
||||
} else if ($VARS['progress'] == "2") {
|
||||
$user = User::byUsername($VARS['username']);
|
||||
if ($_SESSION['passok'] !== true) {
|
||||
// stop logins using only username and authcode
|
||||
sendError("Password integrity check failed!");
|
||||
|
||||
.banner-image {
|
||||
max-height: 100px;
|
||||
margin: 2em auto;
|
||||
border: 1px solid grey;
|
||||
border-radius: 15%;
|
||||
}
|
||||
if ($user->check2fa($VARS['authcode'])) {
|
||||
Session::start($user);
|
||||
header('Location: app.php');
|
||||
die("Logged in, go to app.php");
|
||||
} else {
|
||||
$alert = $Strings->get("2fa incorrect", false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$alert = $Strings->get("login server unavailable", false);
|
||||
}
|
||||
header("Link: <static/fonts/Roboto.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/bootstrap.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/material-color/material-color.min.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/css/index.css>; rel=preload; as=style", false);
|
||||
header("Link: <static/js/jquery-3.3.1.min.js>; rel=preload; as=script", false);
|
||||
header("Link: <static/js/bootstrap.bundle.min.js>; rel=preload; as=script", false);
|
||||
?>
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</style>
|
||||
|
||||
<title><?php echo SITE_TITLE; ?></title>
|
||||
|
||||
<link rel="icon" href="static/img/logo.svg">
|
||||
|
||||
<link href="static/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="static/css/material-color/material-color.min.css" rel="stylesheet">
|
||||
<link href="static/css/index.css" rel="stylesheet">
|
||||
<?php if (CAPTCHA_ENABLED) { ?>
|
||||
<script src="<?php echo CAPTCHA_SERVER ?>/captcheck.dist.js"></script>
|
||||
<?php } ?>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-4">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-auto">
|
||||
<img class="banner-image" src="static/img/logo.svg" />
|
||||
<div class="col-12 text-center">
|
||||
<img class="banner-image" src="./static/img/logo.svg" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row justify-content-center">
|
||||
<div class="card col-11 col-xs-11 col-sm-8 col-md-6 col-lg-4">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><?php $Strings->get("sign in"); ?></h5>
|
||||
<form action="" method="POST">
|
||||
<?php
|
||||
if (!empty($alert)) {
|
||||
?>
|
||||
<div class="alert alert-danger">
|
||||
<i class="fa fa-fw fa-exclamation-triangle"></i> <?php echo $alert; ?>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
if ($multiauth != true) {
|
||||
?>
|
||||
<input type="text" class="form-control" name="username" placeholder="<?php $Strings->get("username"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="password" class="form-control" name="password" placeholder="<?php $Strings->get("password"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /><br />
|
||||
<?php if (CAPTCHA_ENABLED) { ?>
|
||||
<div class="captcheck_container" data-stylenonce="<?php echo $SECURE_NONCE; ?>"></div>
|
||||
<br />
|
||||
<?php } ?>
|
||||
<input type="hidden" name="progress" value="1" />
|
||||
<?php
|
||||
} else if ($multiauth) {
|
||||
?>
|
||||
<div class="alert alert-info">
|
||||
<?php $Strings->get("2fa prompt"); ?>
|
||||
</div>
|
||||
<input type="text" class="form-control" name="authcode" placeholder="<?php $Strings->get("authcode"); ?>" required="required" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" autofocus /><br />
|
||||
<input type="hidden" name="progress" value="2" />
|
||||
<input type="hidden" name="username" value="<?php echo $VARS['username']; ?>" />
|
||||
<?php
|
||||
}
|
||||
?>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<?php $Strings->get("continue"); ?>
|
||||
</button>
|
||||
</form>
|
||||
<div class="col-12 text-center">
|
||||
<h1 class="display-5 mb-4"><?php $Strings->get($title); ?></h1>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-8 col-lg-6">
|
||||
<div class="card mt-4">
|
||||
<div class="card-body">
|
||||
<a href="<?php echo $url; ?>" class="btn btn-primary btn-block"><?php $Strings->get($button); ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<?php echo FOOTER_TEXT; ?><br />
|
||||
Copyright © <?php echo date('Y'); ?> <?php echo COPYRIGHT_NAME; ?>
|
||||
</div>
|
||||
</div>
|
||||
<script src="static/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="static/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
<?php
|
||||
}
|
||||
|
||||
if (!empty($_GET['logout'])) {
|
||||
showHTML("You have been logged out.", "Log in again", "./index.php");
|
||||
die();
|
||||
}
|
||||
if (empty($_SESSION["login_code"])) {
|
||||
$redirecttologin = true;
|
||||
} else {
|
||||
try {
|
||||
$uidinfo = AccountHubApi::get("checkloginkey", ["code" => $_SESSION["login_code"]]);
|
||||
if ($uidinfo["status"] == "ERROR") {
|
||||
throw new Exception();
|
||||
}
|
||||
if (is_numeric($uidinfo['uid'])) {
|
||||
$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["login_code"] = null;
|
||||
header('Location: app.php');
|
||||
showHTML("Logged in", "Continue", "./app.php");
|
||||
die();
|
||||
} else {
|
||||
throw new Exception();
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
$redirecttologin = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($redirecttologin) {
|
||||
try {
|
||||
$urlbase = (isset($_SERVER['HTTPS']) ? "https" : "http") . "://" . $_SERVER['HTTP_HOST'] . (($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) ? ":" . $_SERVER['SERVER_PORT'] : "");
|
||||
$iconurl = $urlbase . str_replace("index.php", "", $_SERVER["REQUEST_URI"]) . "static/img/logo.svg";
|
||||
$codedata = AccountHubApi::get("getloginkey", ["appname" => $SETTINGS["site_title"], "appicon" => $iconurl]);
|
||||
|
||||
if ($codedata['status'] != "OK") {
|
||||
throw new Exception($Strings->get("login server unavailable", false));
|
||||
}
|
||||
|
||||
$redirecturl = $urlbase . $_SERVER['REQUEST_URI'];
|
||||
|
||||
$_SESSION["login_code"] = $codedata["code"];
|
||||
|
||||
$locationurl = $codedata["loginurl"] . "?code=" . htmlentities($codedata["code"]) . "&redirect=" . htmlentities($redirecturl);
|
||||
header("Location: $locationurl");
|
||||
showHTML("Continue", "Continue", $locationurl);
|
||||
die();
|
||||
} catch (Exception $ex) {
|
||||
sendError($ex->getMessage());
|
||||
}
|
||||
}
|
@ -1,26 +1,7 @@
|
||||
{
|
||||
"sign in": "Sign In",
|
||||
"username": "Username",
|
||||
"password": "Password",
|
||||
"continue": "Continue",
|
||||
"authcode": "Authentication code",
|
||||
"2fa prompt": "Enter the six-digit code from your mobile authenticator app.",
|
||||
"2fa incorrect": "Authentication code incorrect.",
|
||||
"login incorrect": "Login incorrect.",
|
||||
"login server unavailable": "Login server unavailable. Try again later or contact technical support.",
|
||||
"account locked": "This account has been disabled. Contact technical support.",
|
||||
"password expired": "You must change your password before continuing.",
|
||||
"account terminated": "Account terminated. Access denied.",
|
||||
"account state error": "Your account state is not stable. Log out, restart your browser, and try again.",
|
||||
"welcome user": "Welcome, {user}!",
|
||||
"sign out": "Sign out",
|
||||
"settings": "Settings",
|
||||
"options": "Options",
|
||||
"404 error": "404 Error",
|
||||
"page not found": "Page not found.",
|
||||
"invalid parameters": "Invalid request parameters.",
|
||||
"login server error": "The login server returned an error: {arg}",
|
||||
"login server user data error": "The login server refused to provide account information. Try again or contact technical support.",
|
||||
"captcha error": "There was a problem with the CAPTCHA (robot test). Try again.",
|
||||
"no access permission": "You do not have permission to access this system."
|
||||
"login server error": "The login server returned an error: {arg}"
|
||||
}
|
||||
|
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"
|
||||
}
|
@ -111,5 +111,6 @@
|
||||
"search": "Search",
|
||||
"no results": "No results.",
|
||||
"contact form": "Contact Form",
|
||||
"contact form messages will be forwarded to this email address": "Contact form messages will be forwarded to this email address, if it is set."
|
||||
"contact form messages will be forwarded to this email address": "Contact form messages will be forwarded to this email address, if it is set.",
|
||||
"settings": "Settings"
|
||||
}
|
56
lib/AccountHubApi.lib.php
Normal file
56
lib/AccountHubApi.lib.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class AccountHubApi {
|
||||
|
||||
public static function get(string $action, array $data = null, bool $throwex = false) {
|
||||
global $SETTINGS;
|
||||
|
||||
$content = [
|
||||
"action" => $action,
|
||||
"key" => $SETTINGS['accounthub']['key']
|
||||
];
|
||||
if (!is_null($data)) {
|
||||
$content = array_merge($content, $data);
|
||||
}
|
||||
$options = [
|
||||
'http' => [
|
||||
'method' => 'POST',
|
||||
'content' => json_encode($content),
|
||||
'header' => "Content-Type: application/json\r\n" .
|
||||
"Accept: application/json\r\n",
|
||||
"ignore_errors" => true
|
||||
]
|
||||
];
|
||||
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($SETTINGS['accounthub']['api'], false, $context);
|
||||
$response = json_decode($result, true);
|
||||
if ($result === false || !AccountHubApi::checkHttpRespCode($http_response_header) || json_last_error() != JSON_ERROR_NONE) {
|
||||
if ($throwex) {
|
||||
throw new Exception($result);
|
||||
} else {
|
||||
sendError($result);
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
private static function checkHttpRespCode(array $headers): bool {
|
||||
foreach ($headers as $header) {
|
||||
if (preg_match("/HTTP\/[0-9]\.[0-9] [0-9]{3}.*/", $header)) {
|
||||
$respcode = explode(" ", $header)[1] * 1;
|
||||
if ($respcode >= 200 && $respcode < 300) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
326
lib/FormBuilder.lib.php
Normal file
326
lib/FormBuilder.lib.php
Normal file
@ -0,0 +1,326 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
class FormBuilder {
|
||||
|
||||
private $items = [];
|
||||
private $hiddenitems = [];
|
||||
private $title = "";
|
||||
private $icon = "";
|
||||
private $buttons = [];
|
||||
private $action = "action.php";
|
||||
private $method = "POST";
|
||||
private $id = "editform";
|
||||
|
||||
/**
|
||||
* Create a form with autogenerated HTML.
|
||||
*
|
||||
* @param string $title Form title/heading
|
||||
* @param string $icon FontAwesone icon next to the title.
|
||||
* @param string $action URL to submit the form to.
|
||||
* @param string $method Form submission method (POST, GET, etc.)
|
||||
*/
|
||||
public function __construct(string $title = "Untitled Form", string $icon = "fas fa-file-alt", string $action = "action.php", string $method = "POST") {
|
||||
$this->title = $title;
|
||||
$this->icon = $icon;
|
||||
$this->action = $action;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the title of the form.
|
||||
* @param string $title
|
||||
*/
|
||||
public function setTitle(string $title) {
|
||||
$this->title = $title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the icon for the form.
|
||||
* @param string $icon FontAwesome icon (example: "fas fa-toilet-paper")
|
||||
*/
|
||||
public function setIcon(string $icon) {
|
||||
$this->icon = $icon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the URL the form will submit to.
|
||||
* @param string $action
|
||||
*/
|
||||
public function setAction(string $action) {
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the form submission method (GET, POST, etc)
|
||||
* @param string $method
|
||||
*/
|
||||
public function setMethod(string $method = "POST") {
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the form ID.
|
||||
* @param string $id
|
||||
*/
|
||||
public function setID(string $id = "editform") {
|
||||
$this->id = $id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input to the form.
|
||||
*
|
||||
* @param string $name Element name
|
||||
* @param string $value Element value
|
||||
* @param string $type Input type (text, number, date, select, tel...)
|
||||
* @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.
|
||||
* @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 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 = "") {
|
||||
$item = [
|
||||
"name" => $name,
|
||||
"value" => $value,
|
||||
"type" => $type,
|
||||
"required" => $required,
|
||||
"label" => $label,
|
||||
"icon" => $icon,
|
||||
"width" => $width,
|
||||
"minlength" => $minlength,
|
||||
"maxlength" => $maxlength
|
||||
];
|
||||
if (!empty($id)) {
|
||||
$item["id"] = $id;
|
||||
}
|
||||
if (!empty($options) && $type == "select") {
|
||||
$item["options"] = $options;
|
||||
}
|
||||
if (!empty($pattern)) {
|
||||
$item["pattern"] = $pattern;
|
||||
}
|
||||
if (!empty($error)) {
|
||||
$item["error"] = $error;
|
||||
}
|
||||
$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.
|
||||
*
|
||||
* @param string $text Text string to show on the button.
|
||||
* @param string $icon FontAwesome icon to show next to the text.
|
||||
* @param string $href If not null, the button will actually be a hyperlink.
|
||||
* @param string $type Usually "button" or "submit". Ignored if $href is set.
|
||||
* @param string $id The element ID.
|
||||
* @param string $name The element name for the button.
|
||||
* @param string $value The form value for the button. Ignored if $name is null.
|
||||
* @param string $class The CSS classes for the button, if a standard success-colored one isn't right.
|
||||
*/
|
||||
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") {
|
||||
$button = [
|
||||
"text" => $text,
|
||||
"icon" => $icon,
|
||||
"class" => $class,
|
||||
"type" => $type,
|
||||
"id" => $id,
|
||||
"href" => $href,
|
||||
"name" => $name,
|
||||
"value" => $value
|
||||
];
|
||||
$this->buttons[] = $button;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a hidden input.
|
||||
* @param string $name
|
||||
* @param string $value
|
||||
*/
|
||||
public function addHiddenInput(string $name, string $value) {
|
||||
$this->hiddenitems[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the form HTML.
|
||||
* @param bool $echo If false, returns HTML string instead of outputting it.
|
||||
*/
|
||||
public function generate(bool $echo = true) {
|
||||
$html = <<<HTMLTOP
|
||||
<form action="$this->action" method="$this->method" id="$this->id">
|
||||
<div class="card">
|
||||
<h3 class="card-header d-flex">
|
||||
<div>
|
||||
<i class="$this->icon"></i> $this->title
|
||||
</div>
|
||||
</h3>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
HTMLTOP;
|
||||
|
||||
foreach ($this->items as $item) {
|
||||
$required = $item["required"] ? "required" : "";
|
||||
$id = empty($item["id"]) ? "" : "id=\"$item[id]\"";
|
||||
$pattern = empty($item["pattern"]) ? "" : "pattern=\"$item[pattern]\"";
|
||||
if (empty($item['type'])) {
|
||||
$item['type'] = "text";
|
||||
}
|
||||
$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
|
||||
\n\n <div class="col-12 col-md-$item[width]">
|
||||
<div class="form-group mb-3">
|
||||
$itemlabel
|
||||
ITEMTOP;
|
||||
$inputgrouptop = <<<INPUTG
|
||||
\n <div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text"><i class="$item[icon]"></i></span>
|
||||
</div>
|
||||
INPUTG;
|
||||
switch ($item['type']) {
|
||||
case "select":
|
||||
$itemhtml .= $inputgrouptop;
|
||||
$itemhtml .= <<<SELECT
|
||||
\n <select class="form-control" name="$item[name]" aria-label="$strippedlabel" $required>
|
||||
SELECT;
|
||||
foreach ($item['options'] as $value => $label) {
|
||||
$selected = "";
|
||||
if (!empty($item['value']) && $value == $item['value']) {
|
||||
$selected = " selected";
|
||||
}
|
||||
$itemhtml .= "\n <option value=\"$value\"$selected>$label</option>";
|
||||
}
|
||||
$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"])) {
|
||||
$itemhtml .= <<<ERROR
|
||||
\n <div class="invalid-feedback">
|
||||
$item[error]
|
||||
</div>
|
||||
ERROR;
|
||||
}
|
||||
if ($item["type"] != "textarea") {
|
||||
$itemhtml .= "\n </div>";
|
||||
}
|
||||
$itemhtml .= <<<ITEMBOTTOM
|
||||
\n </div>
|
||||
</div>\n
|
||||
ITEMBOTTOM;
|
||||
$html .= $itemhtml;
|
||||
}
|
||||
|
||||
$html .= <<<HTMLBOTTOM
|
||||
|
||||
</div>
|
||||
</div>
|
||||
HTMLBOTTOM;
|
||||
|
||||
if (!empty($this->buttons)) {
|
||||
$html .= "\n <div class=\"card-footer d-flex\">";
|
||||
foreach ($this->buttons as $btn) {
|
||||
$btnhtml = "";
|
||||
$inner = "<i class=\"$btn[icon]\"></i> $btn[text]";
|
||||
$id = empty($btn['id']) ? "" : "id=\"$btn[id]\"";
|
||||
if (!empty($btn['href'])) {
|
||||
$btnhtml = "<a href=\"$btn[href]\" class=\"$btn[class]\" $id>$inner</a>";
|
||||
} else {
|
||||
$name = empty($btn['name']) ? "" : "name=\"$btn[name]\"";
|
||||
$value = (!empty($btn['name']) && !empty($btn['value'])) ? "value=\"$btn[value]\"" : "";
|
||||
$btnhtml = "<button type=\"$btn[type]\" class=\"$btn[class]\" $id $name $value>$inner</button>";
|
||||
}
|
||||
$html .= "\n $btnhtml";
|
||||
}
|
||||
$html .= "\n </div>";
|
||||
}
|
||||
|
||||
$html .= "\n </div>";
|
||||
foreach ($this->hiddenitems as $name => $value) {
|
||||
$value = htmlentities($value);
|
||||
$html .= "\n <input type=\"hidden\" name=\"$name\" value=\"$value\" />";
|
||||
}
|
||||
$html .= "\n</form>\n";
|
||||
|
||||
if ($echo) {
|
||||
echo $html;
|
||||
}
|
||||
return $html;
|
||||
}
|
||||
|
||||
}
|
@ -45,50 +45,13 @@ class Login {
|
||||
return Login::LOGIN_OK;
|
||||
}
|
||||
|
||||
public static function verifyCaptcha(string $session, string $answer, string $url): bool {
|
||||
$data = [
|
||||
'session_id' => $session,
|
||||
'answer_id' => $answer,
|
||||
'action' => "verify"
|
||||
];
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data)
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
$result = file_get_contents($url, false, $context);
|
||||
$resp = json_decode($result, TRUE);
|
||||
if (!$resp['result']) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the login server API for sanity
|
||||
* @return boolean true if OK, else false
|
||||
*/
|
||||
public static function checkLoginServer() {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() != 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("ping");
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
@ -107,19 +70,7 @@ class Login {
|
||||
*/
|
||||
function checkAPIKey($key) {
|
||||
try {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => $key,
|
||||
'action' => "ping"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
return true;
|
||||
}
|
||||
$resp = AccountHubApi::get("ping", null, true);
|
||||
return false;
|
||||
} catch (Exception $e) {
|
||||
return false;
|
||||
|
@ -32,27 +32,15 @@ class Notifications {
|
||||
$timestamp = date("Y-m-d H:i:s", strtotime($timestamp));
|
||||
}
|
||||
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "addnotification",
|
||||
'uid' => $user->getUID(),
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'timestamp' => $timestamp,
|
||||
'url' => $url,
|
||||
'sensitive' => $sensitive
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("addnotification", [
|
||||
'uid' => $user->getUID(),
|
||||
'title' => $title,
|
||||
'content' => $content,
|
||||
'timestamp' => $timestamp,
|
||||
'url' => $url,
|
||||
'sensitive' => $sensitive
|
||||
]
|
||||
);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['id'] * 1;
|
||||
} else {
|
||||
|
163
lib/User.lib.php
163
lib/User.lib.php
@ -17,22 +17,7 @@ class User {
|
||||
|
||||
public function __construct(int $uid, string $username = "") {
|
||||
// Check if user exists
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userexists",
|
||||
'uid' => $uid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("userexists", ["uid" => $uid]);
|
||||
if ($resp['status'] == "OK" && $resp['exists'] === true) {
|
||||
$this->exists = true;
|
||||
} else {
|
||||
@ -43,22 +28,7 @@ class User {
|
||||
|
||||
if ($this->exists) {
|
||||
// Get user info
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "userinfo",
|
||||
'uid' => $uid
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("userinfo", ["uid" => $uid]);
|
||||
if ($resp['status'] == "OK") {
|
||||
$this->uid = $resp['data']['uid'] * 1;
|
||||
$this->username = $resp['data']['username'];
|
||||
@ -71,22 +41,7 @@ class User {
|
||||
}
|
||||
|
||||
public static function byUsername(string $username): User {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'username' => $username,
|
||||
'action' => "userinfo"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("userinfo", ["username" => $username]);
|
||||
if (!isset($resp['status'])) {
|
||||
sendError("Login server error: " . $resp);
|
||||
}
|
||||
@ -105,22 +60,8 @@ class User {
|
||||
if (!$this->exists) {
|
||||
return false;
|
||||
}
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "hastotp",
|
||||
'username' => $this->username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("hastotp", ['username' => $this->username]);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['otp'] == true;
|
||||
} else {
|
||||
@ -147,26 +88,11 @@ class User {
|
||||
/**
|
||||
* Check the given plaintext password against the stored hash.
|
||||
* @param string $password
|
||||
* @param bool $apppass Set to true to enforce app passwords when 2fa is on.
|
||||
* @return bool
|
||||
*/
|
||||
function checkPassword(string $password): bool {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "auth",
|
||||
'username' => $this->username,
|
||||
'password' => $password
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
function checkPassword(string $password, bool $apppass = false): bool {
|
||||
$resp = AccountHubApi::get("auth", ['username' => $this->username, 'password' => $password, 'apppass' => ($apppass ? "1" : "0")]);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
@ -174,27 +100,13 @@ class User {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function check2fa(string $code): bool {
|
||||
if (!$this->has2fa) {
|
||||
return true;
|
||||
}
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "verifytotp",
|
||||
'username' => $this->username,
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("verifytotp", ['username' => $this->username, 'code' => $code]);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['valid'];
|
||||
} else {
|
||||
@ -209,23 +121,7 @@ class User {
|
||||
* @return boolean TRUE if the user has the permission (or admin access), else FALSE
|
||||
*/
|
||||
function hasPermission(string $code): bool {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "permission",
|
||||
'username' => $this->username,
|
||||
'code' => $code
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("permission", ['username' => $this->username, 'code' => $code]);
|
||||
if ($resp['status'] == "OK") {
|
||||
return $resp['has_permission'];
|
||||
} else {
|
||||
@ -238,23 +134,7 @@ class User {
|
||||
* @return \AccountStatus
|
||||
*/
|
||||
function getStatus(): AccountStatus {
|
||||
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "acctstatus",
|
||||
'username' => $this->username
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
sendError("Login server error: " . $response->getBody());
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("acctstatus", ['username' => $this->username]);
|
||||
if ($resp['status'] == "OK") {
|
||||
return AccountStatus::fromString($resp['account']);
|
||||
} else {
|
||||
@ -262,24 +142,13 @@ class User {
|
||||
}
|
||||
}
|
||||
|
||||
function sendAlertEmail(string $appname = SITE_TITLE) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "alertemail",
|
||||
'username' => $this->username,
|
||||
'appname' => SITE_TITLE
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
return "An unknown error occurred.";
|
||||
function sendAlertEmail(string $appname = null) {
|
||||
global $SETTINGS;
|
||||
if (is_null($appname)) {
|
||||
$appname = $SETTINGS['site_title'];
|
||||
}
|
||||
$resp = AccountHubApi::get("alertemail", ['username' => $this->username, 'appname' => $SETTINGS['site_title']]);
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK") {
|
||||
return true;
|
||||
} else {
|
||||
|
@ -9,10 +9,10 @@ dieifnotloggedin();
|
||||
|
||||
include_once __DIR__ . "/../lib/mimetypes.php";
|
||||
|
||||
$base = FILE_UPLOAD_PATH;
|
||||
$base = $SETTINGS["file_upload_path"];
|
||||
|
||||
$folder = "";
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), FILE_UPLOAD_PATH) === 0) {
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), $SETTINGS["file_upload_path"]) === 0) {
|
||||
$folder = $VARS['path'];
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ if (isset($VARS['type']) && $VARS['type'] != "") {
|
||||
$type = explode("|", $VARS['type']);
|
||||
}
|
||||
|
||||
$enableunsplash = ENABLE_UNSPLASH;
|
||||
$enableunsplash = $SETTINGS["unsplash"]["enable"];
|
||||
if (count($type) > 0 && !in_array("image", $type)) {
|
||||
$enableunsplash = false;
|
||||
}
|
||||
@ -67,7 +67,7 @@ if ($enableunsplash) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span id="unsplashResults"></span> <span>via <a href="https://unsplash.com/?utm_source=<?php echo urlencode(UNSPLASH_UTMSOURCE); ?>&utm_medium=referral">Unsplash</a></span>
|
||||
<span id="unsplashResults"></span> <span>via <a href="https://unsplash.com/?utm_source=<?php echo urlencode($SETTINGS["unsplash"]["utmsource"]); ?>&utm_medium=referral">Unsplash</a></span>
|
||||
</div>
|
||||
<div id="unsplashPhotoBin" class="px-2 pr-3">
|
||||
</div>
|
||||
|
@ -9,10 +9,10 @@ dieifnotloggedin();
|
||||
|
||||
include_once __DIR__ . "/../lib/mimetypes.php";
|
||||
|
||||
$base = FILE_UPLOAD_PATH;
|
||||
$base = $SETTINGS["file_upload_path"];
|
||||
|
||||
$folder = "";
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), FILE_UPLOAD_PATH) === 0) {
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), $SETTINGS["file_upload_path"]) === 0) {
|
||||
$folder = $VARS['path'];
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ if (isset($VARS['type']) && $VARS['type'] != "") {
|
||||
$type = explode("|", $VARS['type']);
|
||||
}
|
||||
|
||||
$enableunsplash = ENABLE_UNSPLASH;
|
||||
$enableunsplash = $SETTINGS["unsplash"]["enable"];
|
||||
if (count($type) > 0 && !in_array("image", $type)) {
|
||||
$enableunsplash = false;
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ dieifnotloggedin();
|
||||
header('Content-Type: application/json');
|
||||
|
||||
Crew\Unsplash\HttpClient::init([
|
||||
'applicationId' => UNSPLASH_ACCESSKEY,
|
||||
'secret' => UNSPLASH_SECRETKEY,
|
||||
'utmSource' => UNSPLASH_UTMSOURCE
|
||||
'applicationId' => $SETTINGS["unsplash"]["appid"],
|
||||
'secret' => $SETTINGS["unsplash"]["secretkey"],
|
||||
'utmSource' => $SETTINGS["unsplash"]["utmsource"]
|
||||
]);
|
||||
|
||||
$page = 1;
|
||||
|
@ -78,7 +78,7 @@ if (!$database->has("settings", ["AND" => ["siteid" => getsiteid(), "key" => "an
|
||||
// Lookup IP address
|
||||
//
|
||||
|
||||
$reader = new Reader(GEOIP_DB);
|
||||
$reader = new Reader($SETTINGS["geoip_db"]);
|
||||
|
||||
$record = $reader->city($clientip);
|
||||
|
||||
@ -108,12 +108,12 @@ if (!$database->has("settings", ["AND" => ["siteid" => getsiteid(), "key" => "an
|
||||
"time" => $time
|
||||
]);
|
||||
} catch (GeoIp2\Exception\AddressNotFoundException $e) {
|
||||
if (DEBUG) {
|
||||
if ($SETTINGS["debug"]) {
|
||||
echo "<!-- The client IP was not found in the GeoIP database. -->";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Silently fail so the rest of the site still works
|
||||
if (DEBUG) {
|
||||
if ($SETTINGS["debug"]) {
|
||||
echo "<!-- Analytics error: " . $e->getMessage() . " -->";
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ ob_start(); // allow sending headers after content
|
||||
// Settings file
|
||||
require __DIR__ . '/../settings.php';
|
||||
|
||||
if (!DEBUG) {
|
||||
if (!$SETTINGS["debug"]) {
|
||||
error_reporting(0);
|
||||
} else {
|
||||
error_reporting(E_ALL);
|
||||
@ -57,7 +57,7 @@ function sendError($error) {
|
||||
. "<p>" . htmlspecialchars($error) . "</p>");
|
||||
}
|
||||
|
||||
date_default_timezone_set(TIMEZONE);
|
||||
date_default_timezone_set($SETTINGS['timezone']);
|
||||
|
||||
// Database settings
|
||||
// Also inits database and stuff
|
||||
@ -66,12 +66,12 @@ use Medoo\Medoo;
|
||||
$database;
|
||||
try {
|
||||
$database = new Medoo([
|
||||
'database_type' => DB_TYPE,
|
||||
'database_name' => DB_NAME,
|
||||
'server' => DB_SERVER,
|
||||
'username' => DB_USER,
|
||||
'password' => DB_PASS,
|
||||
'charset' => DB_CHARSET
|
||||
'database_type' => $SETTINGS['database']['type'],
|
||||
'database_name' => $SETTINGS['database']['name'],
|
||||
'server' => $SETTINGS['database']['server'],
|
||||
'username' => $SETTINGS['database']['user'],
|
||||
'password' => $SETTINGS['database']['password'],
|
||||
'charset' => $SETTINGS['database']['charset']
|
||||
]);
|
||||
} catch (Exception $ex) {
|
||||
//header('HTTP/1.1 500 Internal Server Error');
|
||||
|
@ -87,10 +87,11 @@ function get_page_slug($echo = true) {
|
||||
* @return string
|
||||
*/
|
||||
function get_page_clean_url($echo = true, $slug = null) {
|
||||
global $SETTINGS;
|
||||
if ($slug == null) {
|
||||
$slug = get_page_slug(false);
|
||||
}
|
||||
if (PRETTY_URLS) {
|
||||
if ($SETTINGS["pretty_urls"]) {
|
||||
$url = formatsiteurl(get_site_url(false)) . "$slug";
|
||||
} else {
|
||||
$url = formatsiteurl(get_site_url(false)) . "index.php?id=$slug";
|
||||
@ -108,6 +109,7 @@ function get_page_clean_url($echo = true, $slug = null) {
|
||||
* @return string
|
||||
*/
|
||||
function get_page_url($echo = true, $slug = null) {
|
||||
global $SETTINGS;
|
||||
if ($slug == null) {
|
||||
$slug = get_page_slug(false);
|
||||
}
|
||||
@ -132,7 +134,7 @@ function get_page_url($echo = true, $slug = null) {
|
||||
$siteid = "&siteid=" . preg_replace("/[^0-9]/", '', $_GET['siteid']);
|
||||
}
|
||||
$args = "$edit$theme$template$color$siteid";
|
||||
if (PRETTY_URLS) {
|
||||
if ($SETTINGS["pretty_urls"]) {
|
||||
if ($args != "") {
|
||||
$args = "?$args";
|
||||
}
|
||||
@ -214,6 +216,7 @@ function is_component_empty($name, $context = null) {
|
||||
* @return array
|
||||
*/
|
||||
function get_complex_component($name, $context = null, $include = []) {
|
||||
global $SETTINGS;
|
||||
$db = getdatabase();
|
||||
if ($context == null) {
|
||||
$context = getpageslug();
|
||||
@ -234,14 +237,14 @@ function get_complex_component($name, $context = null, $include = []) {
|
||||
$filtered = [];
|
||||
foreach ($include as $i) {
|
||||
if (array_key_exists($i, $content)) {
|
||||
if (!isset($_GET['edit']) && $i == "image" && $content[$i] == URL . "/static/img/no-image.svg") {
|
||||
if (!isset($_GET['edit']) && $i == "image" && $content[$i] == $SETTINGS["url"] . "/static/img/no-image.svg") {
|
||||
$filtered[$i] = "";
|
||||
} else {
|
||||
$filtered[$i] = $content[$i];
|
||||
}
|
||||
} else {
|
||||
if (isset($_GET['edit']) && $i == "image") {
|
||||
$filtered[$i] = URL . "/static/img/no-image.svg";
|
||||
$filtered[$i] = $SETTINGS["url"] . "/static/img/no-image.svg";
|
||||
} else {
|
||||
$filtered[$i] = "";
|
||||
}
|
||||
@ -258,12 +261,13 @@ function get_complex_component($name, $context = null, $include = []) {
|
||||
* @return boolean
|
||||
*/
|
||||
function is_complex_empty($name, $context = null) {
|
||||
global $SETTINGS;
|
||||
if (isset($_GET['edit'])) {
|
||||
return false;
|
||||
}
|
||||
$comp = get_complex_component($name, $context);
|
||||
foreach ($comp as $c => $v) {
|
||||
if ($c == "image" && $v == URL . "/static/img/no-image.svg") {
|
||||
if ($c == "image" && $v == $SETTINGS["url"] . "/static/img/no-image.svg") {
|
||||
continue;
|
||||
}
|
||||
if (isset($v) && !empty($v)) {
|
||||
@ -314,13 +318,14 @@ function get_url_or_slug($str, $echo = true) {
|
||||
* @return string
|
||||
*/
|
||||
function get_file_url($file, $echo = true) {
|
||||
global $SETTINGS;
|
||||
$url = "file.php?file=$file";
|
||||
$base = FILE_UPLOAD_PATH;
|
||||
$base = $SETTINGS["file_upload_path"];
|
||||
$filepath = $base . $file;
|
||||
if (!file_exists($filepath) || is_dir($filepath)) {
|
||||
$url = $file;
|
||||
} else {
|
||||
if (strpos(realpath($filepath), FILE_UPLOAD_PATH) !== 0) {
|
||||
if (strpos(realpath($filepath), $SETTINGS["file_upload_path"]) !== 0) {
|
||||
$url = $file;
|
||||
}
|
||||
}
|
||||
@ -382,10 +387,11 @@ function get_setting($key, $echo = false) {
|
||||
* @return string
|
||||
*/
|
||||
function get_theme_url($echo = true) {
|
||||
global $SETTINGS;
|
||||
$db = getdatabase();
|
||||
$site = $db->get('sites', ["sitename", "url", "theme"], ["siteid" => getsiteid()]);
|
||||
if (isset($_GET['edit']) || isset($_GET['in_sw'])) {
|
||||
$url = URL . "/public/themes/" . SITE_THEME;
|
||||
$url = $SETTINGS["url"] . "/public/themes/" . SITE_THEME;
|
||||
} else {
|
||||
$url = formatsiteurl($site["url"]) . "themes/" . SITE_THEME;
|
||||
}
|
||||
|
@ -8,10 +8,6 @@
|
||||
* 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";
|
||||
|
||||
header('Content-Type: application/json');
|
||||
@ -23,21 +19,7 @@ if ($VARS['action'] == "ping") {
|
||||
}
|
||||
|
||||
function mobile_enabled() {
|
||||
$client = new GuzzleHttp\Client();
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
'action' => "mobileenabled"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
$resp = AccountHubApi::get("mobileenabled");
|
||||
if ($resp['status'] == "OK" && $resp['mobile'] === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
@ -46,26 +28,15 @@ function mobile_enabled() {
|
||||
}
|
||||
|
||||
function mobile_valid($username, $code) {
|
||||
$client = new GuzzleHttp\Client();
|
||||
try {
|
||||
$resp = AccountHubApi::get("mobilevalid", ["code" => $code, "username" => $username], true);
|
||||
|
||||
$response = $client
|
||||
->request('POST', PORTAL_API, [
|
||||
'form_params' => [
|
||||
'key' => PORTAL_KEY,
|
||||
"code" => $code,
|
||||
"username" => $username,
|
||||
'action' => "mobilevalid"
|
||||
]
|
||||
]);
|
||||
|
||||
if ($response->getStatusCode() > 299) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$resp = json_decode($response->getBody(), TRUE);
|
||||
if ($resp['status'] == "OK" && $resp['valid'] === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
if ($resp['status'] == "OK" && $resp['valid'] === TRUE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -95,13 +66,14 @@ switch ($VARS['action']) {
|
||||
if ($user->exists()) {
|
||||
if ($user->getStatus()->getString() == "NORMAL") {
|
||||
if ($user->checkPassword($VARS['password'])) {
|
||||
if (is_null($access_permission) || $user->hasPermission($access_permission)) {
|
||||
Session::start($user);
|
||||
$_SESSION['mobile'] = true;
|
||||
exit(json_encode(["status" => "OK"]));
|
||||
} else {
|
||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("no admin permission", false)]));
|
||||
foreach ($SETTINGS['permissions'] as $perm) {
|
||||
if (!$user->hasPermission($perm)) {
|
||||
exit(json_encode(["status" => "ERROR", "msg" => $Strings->get("no permission", false)]));
|
||||
}
|
||||
}
|
||||
Session::start($user);
|
||||
$_SESSION['mobile'] = true;
|
||||
exit(json_encode(["status" => "OK"]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,5 +87,13 @@ define("PAGES", [
|
||||
],
|
||||
"404" => [
|
||||
"title" => "404 error"
|
||||
],
|
||||
"form" => [
|
||||
"title" => "Form",
|
||||
"navbar" => true,
|
||||
"icon" => "fas fa-file-alt",
|
||||
"scripts" => [
|
||||
"static/js/form.js"
|
||||
]
|
||||
]
|
||||
]);
|
||||
|
@ -19,10 +19,10 @@ if (!$user->hasPermission("SITEWRITER") && !$user->hasPermission("SITEWRITER_FIL
|
||||
|
||||
include_once __DIR__ . "/../lib/mimetypes.php";
|
||||
|
||||
$base = FILE_UPLOAD_PATH;
|
||||
$base = $SETTINGS["file_upload_path"];
|
||||
|
||||
$folder = "";
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), FILE_UPLOAD_PATH) === 0) {
|
||||
if (isset($VARS['path']) && file_exists($base . $VARS['path']) && strpos(realpath($base . $VARS['path']), $SETTINGS["file_upload_path"]) === 0) {
|
||||
$folder = $VARS['path'];
|
||||
}
|
||||
|
||||
|
26
pages/form.php
Normal file
26
pages/form.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file demonstrates creating a form with the FormBuilder class.
|
||||
*/
|
||||
|
||||
$form = new FormBuilder("Sample Form", "fas fa-code", "", "GET");
|
||||
|
||||
$form->setID("sampleform");
|
||||
|
||||
$form->addHiddenInput("page", "form");
|
||||
|
||||
$form->addInput("name", "John", "text", true, null, null, "Your name", "fas fa-user", 6, 5, 20, "John(ny)?|Steve", "Invalid name, please enter John, Johnny, or Steve.");
|
||||
$form->addInput("location", "", "select", true, null, ["1" => "Here", "2" => "There"], "Location", "fas fa-map-marker");
|
||||
$form->addInput("textbox", "Hello world", "textarea", true, null, null, "Text area", "fas fa-font");
|
||||
$form->addInput("box", "1", "checkbox", true, null, null, "I agree to the terms of service");
|
||||
|
||||
$form->addButton("Submit", "fas fa-save", null, "submit", "savebtn");
|
||||
|
||||
$form->generate();
|
@ -63,18 +63,18 @@ if ($database->has('settings', ["AND" => ['siteid' => $siteid, 'key' => 'contact
|
||||
// Setup mailer
|
||||
$mail = new PHPMailer(true);
|
||||
$mail->isSMTP();
|
||||
$mail->Host = SMTP_HOST;
|
||||
$mail->SMTPAuth = SMTP_AUTH;
|
||||
if (SMTP_AUTH) {
|
||||
$mail->Username = SMTP_USERNAME;
|
||||
$mail->Password = SMTP_PASSWORD;
|
||||
$mail->Host = $SETTINGS["email"]["host"];
|
||||
$mail->SMTPAuth = $SETTINGS["email"]["auth"];
|
||||
if ($SETTINGS["email"]["auth"]) {
|
||||
$mail->Username = $SETTINGS["email"]["user"];
|
||||
$mail->Password = $SETTINGS["email"]["password"];
|
||||
}
|
||||
if (SMTP_SECURITY != "none") {
|
||||
$mail->SMTPSecure = SMTP_SECURITY;
|
||||
if ($SETTINGS["email"]["secure"] != "none") {
|
||||
$mail->SMTPSecure = $SETTINGS["email"]["secure"];
|
||||
}
|
||||
$mail->Port = SMTP_PORT;
|
||||
$mail->Port = $SETTINGS["email"]["port"];
|
||||
$mail->isHTML(true);
|
||||
$mail->setFrom(SMTP_FROMADDRESS, SMTP_FROMNAME);
|
||||
$mail->setFrom($SETTINGS["email"]["fromaddress"], $SETTINGS["email"]["fromname"]);
|
||||
|
||||
$mail->addAddress($emailto);
|
||||
$mail->addReplyTo($_POST['email'], $_POST['name']);
|
||||
|
@ -8,11 +8,11 @@
|
||||
|
||||
require_once __DIR__ . "/../lib/requiredpublic.php";
|
||||
|
||||
$base = FILE_UPLOAD_PATH;
|
||||
$base = $SETTINGS["file_upload_path"];
|
||||
|
||||
$filepath = "";
|
||||
|
||||
if ($_GET['file'] === URL . "/static/img/no-image.svg") {
|
||||
if ($_GET['file'] === $SETTINGS["url"] . "/static/img/no-image.svg") {
|
||||
header("Content-Type: image/svg+xml");
|
||||
ob_end_flush();
|
||||
|
||||
@ -26,7 +26,7 @@ if (isset($_GET['file'])) {
|
||||
http_response_code(404);
|
||||
die("404 File Not Found");
|
||||
}
|
||||
if (strpos(realpath($filepath), FILE_UPLOAD_PATH) !== 0) {
|
||||
if (strpos(realpath($filepath), $SETTINGS["file_upload_path"]) !== 0) {
|
||||
http_response_code(404);
|
||||
die("404 File Not Found");
|
||||
}
|
||||
|
@ -23,8 +23,8 @@ if (!getsiteid()) {
|
||||
<h2 class="card-title">Welcome!</h2>
|
||||
<p>You're seeing this message because no website has been created yet.
|
||||
<br />
|
||||
Open <?php echo SITE_TITLE; ?> to make one.</p>
|
||||
<p><a href="<?php echo PORTAL_URL; ?>" class="btn btn-primary">Log In</a></p>
|
||||
Open <?php echo $SETTINGS["site_title"]; ?> to make one.</p>
|
||||
<p><a href="<?php echo $SETTINGS["accounthub"]["home"]; ?>" class="btn btn-primary">Log In</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -57,15 +57,15 @@ if (isset($_GET['edit'])) {
|
||||
}
|
||||
?>
|
||||
<style><?php echo file_get_contents(__DIR__ . "/../static/css/editor.css"); ?></style>
|
||||
<script src="<?php echo URL; ?>/static/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="<?php echo URL; ?>/static/js/tinymce/tinymce.min.js"></script>
|
||||
<script src="<?php echo $SETTINGS["url"]; ?>/static/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="<?php echo $SETTINGS["url"]; ?>/static/js/tinymce/tinymce.min.js"></script>
|
||||
<script>
|
||||
static_dir = "<?php echo URL; ?>/static";
|
||||
static_dir = "<?php echo $SETTINGS["url"]; ?>/static";
|
||||
page_slug = "<?php echo getpageslug(); ?>";
|
||||
site_id = "<?php echo getsiteid(); ?>";
|
||||
pages_list = <?php echo json_encode($allpages); ?>;
|
||||
</script>
|
||||
<script src="<?php echo URL; ?>/static/js/editor.js"></script>
|
||||
<script src="<?php echo $SETTINGS["url"]; ?>/static/js/editor.js"></script>
|
||||
<?php
|
||||
}
|
||||
?>
|
47
required.php
47
required.php
@ -32,7 +32,6 @@ session_start(); // stick some cookies in it
|
||||
// renew session cookie
|
||||
setcookie(session_name(), session_id(), time() + $session_length, "/", false, false);
|
||||
|
||||
$captcha_server = (CAPTCHA_ENABLED === true ? preg_replace("/http(s)?:\/\//", "", CAPTCHA_SERVER) : "");
|
||||
if ($_SESSION['mobile'] === TRUE) {
|
||||
header("Content-Security-Policy: "
|
||||
. "default-src 'self';"
|
||||
@ -42,8 +41,8 @@ if ($_SESSION['mobile'] === TRUE) {
|
||||
. "frame-src 'self'; "
|
||||
. "font-src 'self'; "
|
||||
. "connect-src *; "
|
||||
. "style-src 'self' 'unsafe-inline' $captcha_server; "
|
||||
. "script-src 'self' 'unsafe-inline' $captcha_server");
|
||||
. "style-src 'self' 'unsafe-inline'; "
|
||||
. "script-src 'self' 'unsafe-inline'");
|
||||
} else {
|
||||
header("Content-Security-Policy: "
|
||||
. "default-src 'self';"
|
||||
@ -53,8 +52,8 @@ if ($_SESSION['mobile'] === TRUE) {
|
||||
. "frame-src 'self'; "
|
||||
. "font-src 'self'; "
|
||||
. "connect-src *; "
|
||||
. "style-src 'self' 'unsafe-inline' $captcha_server; "
|
||||
. "script-src 'self' 'nonce-$SECURE_NONCE' $captcha_server");
|
||||
. "style-src 'self' 'nonce-$SECURE_NONCE'; "
|
||||
. "script-src 'self' 'nonce-$SECURE_NONCE'");
|
||||
}
|
||||
|
||||
//
|
||||
@ -69,7 +68,7 @@ foreach ($libs as $lib) {
|
||||
require_once $lib;
|
||||
}
|
||||
|
||||
$Strings = new Strings(LANGUAGE);
|
||||
$Strings = new Strings($SETTINGS['language']);
|
||||
|
||||
/**
|
||||
* Kill off the running process and spit out an error message
|
||||
@ -93,7 +92,7 @@ function sendError($error) {
|
||||
. "<p>" . htmlspecialchars($error) . "</p>");
|
||||
}
|
||||
|
||||
date_default_timezone_set(TIMEZONE);
|
||||
date_default_timezone_set($SETTINGS['timezone']);
|
||||
|
||||
// Database settings
|
||||
// Also inits database and stuff
|
||||
@ -102,12 +101,12 @@ use Medoo\Medoo;
|
||||
$database;
|
||||
try {
|
||||
$database = new Medoo([
|
||||
'database_type' => DB_TYPE,
|
||||
'database_name' => DB_NAME,
|
||||
'server' => DB_SERVER,
|
||||
'username' => DB_USER,
|
||||
'password' => DB_PASS,
|
||||
'charset' => DB_CHARSET
|
||||
'database_type' => $SETTINGS['database']['type'],
|
||||
'database_name' => $SETTINGS['database']['name'],
|
||||
'server' => $SETTINGS['database']['server'],
|
||||
'username' => $SETTINGS['database']['user'],
|
||||
'password' => $SETTINGS['database']['password'],
|
||||
'charset' => $SETTINGS['database']['charset']
|
||||
]);
|
||||
} catch (Exception $ex) {
|
||||
//header('HTTP/1.1 500 Internal Server Error');
|
||||
@ -115,7 +114,7 @@ try {
|
||||
}
|
||||
|
||||
|
||||
if (!DEBUG) {
|
||||
if (!$SETTINGS['debug']) {
|
||||
error_reporting(0);
|
||||
} else {
|
||||
error_reporting(E_ALL);
|
||||
@ -132,11 +131,18 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
define("GET", true);
|
||||
}
|
||||
|
||||
|
||||
function dieifnotloggedin() {
|
||||
global $SETTINGS;
|
||||
if ($_SESSION['loggedin'] != true) {
|
||||
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() {
|
||||
global $SETTINGS;
|
||||
if ($_SESSION['loggedin'] !== TRUE) {
|
||||
header('Location: ' . URL . '/index.php');
|
||||
header('Location: ' . $SETTINGS['url'] . '/index.php');
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,76 +1,85 @@
|
||||
<?php
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
/*
|
||||
* 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/. */
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
// Whether to show debugging data in output.
|
||||
// DO NOT SET TO TRUE IN PRODUCTION!!!
|
||||
define("DEBUG", false);
|
||||
// Settings for the app.
|
||||
// Copy to settings.php and customize.
|
||||
|
||||
// Database connection settings
|
||||
// See http://medoo.in/api/new for info
|
||||
define("DB_TYPE", "mysql");
|
||||
define("DB_NAME", "sitewriter");
|
||||
define("DB_SERVER", "localhost");
|
||||
define("DB_USER", "sitewriter");
|
||||
define("DB_PASS", "");
|
||||
define("DB_CHARSET", "utf8");
|
||||
|
||||
// Name of the app.
|
||||
define("SITE_TITLE", "SiteWriter");
|
||||
|
||||
define("SMTP_HOST", "");
|
||||
define("SMTP_AUTH", true);
|
||||
define("SMTP_SECURITY", "tls"); // tls, ssl, or none
|
||||
define("SMTP_PORT", 25);
|
||||
define("SMTP_USERNAME", "");
|
||||
define("SMTP_PASSWORD", "");
|
||||
define("SMTP_FROMADDRESS", "sitewriter@example.com");
|
||||
define("SMTP_FROMNAME", "SiteWriter");
|
||||
|
||||
// URL of the AccountHub API endpoint
|
||||
define("PORTAL_API", "http://localhost/accounthub/api.php");
|
||||
// URL of the AccountHub home page
|
||||
define("PORTAL_URL", "http://localhost/accounthub/home.php");
|
||||
// AccountHub API Key
|
||||
define("PORTAL_KEY", "123");
|
||||
|
||||
// For supported values, see http://php.net/manual/en/timezones.php
|
||||
define("TIMEZONE", "America/Denver");
|
||||
|
||||
// Base URL for site links.
|
||||
define('URL', '/sitewriter');
|
||||
|
||||
// Folder for public files
|
||||
// This should not be inside the web root for security reasons.
|
||||
define('FILE_UPLOAD_PATH', __DIR__ . '/public/files');
|
||||
|
||||
// Use pretty URLs (requires correct web server configuration)
|
||||
define('PRETTY_URLS', false);
|
||||
|
||||
// Location of MaxMind GeoIP database
|
||||
//
|
||||
// I'll just leave this here:
|
||||
// This product includes GeoLite2 data created by MaxMind, available from
|
||||
// http://www.maxmind.com
|
||||
define('GEOIP_DB', __DIR__ . "/GeoLite2-City.mmdb");
|
||||
|
||||
// Unsplash photo integration
|
||||
define('ENABLE_UNSPLASH', false);
|
||||
define('UNSPLASH_APPID', '');
|
||||
define('UNSPLASH_ACCESSKEY', '');
|
||||
define('UNSPLASH_SECRETKEY', '');
|
||||
define('UNSPLASH_UTMSOURCE', 'SiteWriter');
|
||||
|
||||
// Use Captcheck on login screen
|
||||
// https://captcheck.netsyms.com
|
||||
define("CAPTCHA_ENABLED", FALSE);
|
||||
define('CAPTCHA_SERVER', 'https://captcheck.netsyms.com');
|
||||
|
||||
// See lang folder for language options
|
||||
define('LANGUAGE', "en_us");
|
||||
|
||||
|
||||
define("FOOTER_TEXT", "");
|
||||
define("COPYRIGHT_NAME", "Netsyms Technologies");
|
||||
$SETTINGS = [
|
||||
// Whether to output debugging info like PHP notices, warnings,
|
||||
// and stacktraces.
|
||||
// Turning this on in production is a security risk and can sometimes break
|
||||
// things, such as JSON output where extra content is not expected.
|
||||
"debug" => false,
|
||||
// Database connection settings
|
||||
// See http://medoo.in/api/new for info
|
||||
"database" => [
|
||||
"type" => "mysql",
|
||||
"name" => "sitewriter",
|
||||
"server" => "localhost",
|
||||
"user" => "",
|
||||
"password" => "",
|
||||
"charset" => "utf8"
|
||||
],
|
||||
// Name of the app.
|
||||
"site_title" => "SiteWriter",
|
||||
// Settings for connecting to the AccountHub server.
|
||||
"accounthub" => [
|
||||
// URL for the API endpoint
|
||||
"api" => "http://localhost/accounthub/api/",
|
||||
// URL of the home page
|
||||
"home" => "http://localhost/accounthub/home.php",
|
||||
// API key
|
||||
"key" => "123"
|
||||
],
|
||||
// Folder for public files
|
||||
"file_upload_path" => __DIR__ . "/public/files",
|
||||
// Use pretty URLs (requires correct web server configuration)
|
||||
"pretty_urls" => false,
|
||||
// Location of MaxMind GeoIP database
|
||||
//
|
||||
// I'll just leave this here:
|
||||
// This product includes GeoLite2 data created by MaxMind, available from
|
||||
// http://www.maxmind.com
|
||||
"geoip_db" => __DIR__ . "/GeoLite2-City.mmdb",
|
||||
"unsplash" => [
|
||||
"enable" => false,
|
||||
"appid" => "",
|
||||
"accesskey" => "",
|
||||
"secretkey" => "",
|
||||
"utmsource" => "SiteWriter"
|
||||
],
|
||||
"email" => [
|
||||
"host" => "",
|
||||
"auth" => true,
|
||||
"secure" => "tls",
|
||||
"port" => 587,
|
||||
"user" => "",
|
||||
"password" => "",
|
||||
"fromaddress" => "",
|
||||
"fromname" => "SiteWriter"
|
||||
],
|
||||
// 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" => "en",
|
||||
// Shown in the footer of all the pages.
|
||||
"footer_text" => "",
|
||||
// Also shown in the footer, but with "Copyright <current_year>" in front.
|
||||
"copyright" => "Netsyms Technologies",
|
||||
// Base URL for building links relative to the location of the app.
|
||||
// Only used when there's no good context for the path.
|
||||
// The default is almost definitely fine.
|
||||
"url" => "."
|
||||
];
|
||||
|
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 @@
|
||||
/*!
|
||||
* 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}
|
||||
.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}
|
@ -13,7 +13,7 @@ $(document).ready(function () {
|
||||
var gone = 20;
|
||||
|
||||
var msgticker = setInterval(function () {
|
||||
if ($('#msg-alert-box .alert:hover').length) {
|
||||
if ($("#msg-alert-box .alert:hover").length) {
|
||||
msginteractiontick = 0;
|
||||
} else {
|
||||
msginteractiontick++;
|
||||
@ -55,7 +55,6 @@ $(document).ready(function () {
|
||||
$("#msg-alert-box").on("mouseenter", function () {
|
||||
$("#msg-alert-box").css("opacity", "1");
|
||||
msginteractiontick = 0;
|
||||
console.log("👈😎👈 zoop");
|
||||
});
|
||||
$("#msg-alert-box").on("click", ".close", function (e) {
|
||||
$("#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
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
16
static/js/form.js
Normal file
16
static/js/form.js
Normal file
@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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/.
|
||||
*/
|
||||
|
||||
|
||||
$("#savebtn").click(function (event) {
|
||||
var form = $("#sampleform");
|
||||
|
||||
if (form[0].checkValidity() === false) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
form.addClass("was-validated");
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user