Initial commit
This commit is contained in:
commit
fa7daba41e
7 changed files with 836 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
/data.json
|
30
.htaccess
Normal file
30
.htaccess
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<IfModule mod_rewrite.c>
|
||||||
|
|
||||||
|
RewriteEngine On
|
||||||
|
|
||||||
|
## Begin - Index
|
||||||
|
# If the requested path and file is not /index.php and the request
|
||||||
|
# has not already been internally rewritten to the index.php script
|
||||||
|
RewriteCond %{REQUEST_URI} !^/index\.php
|
||||||
|
# and the requested path and file doesn't directly match a physical file
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-f
|
||||||
|
# and the requested path and file doesn't directly match a physical folder
|
||||||
|
RewriteCond %{REQUEST_FILENAME} !-d
|
||||||
|
# internally rewrite the request to the index.php script
|
||||||
|
RewriteRule .* index.php [L]
|
||||||
|
## End - Index
|
||||||
|
|
||||||
|
## Begin - Security
|
||||||
|
# Block all direct access for these folders
|
||||||
|
RewriteRule ^(\.git|cache|bin|logs|backup|webserver-configs|tests)/(.*) error [F]
|
||||||
|
# Block all direct access to files and folders beginning with a dot
|
||||||
|
RewriteRule (^|/)\.(?!well-known) - [F]
|
||||||
|
# Block access to specific files in the root folder
|
||||||
|
RewriteRule ^(LICENSE\.txt|composer\.lock|composer\.json|\.htaccess|README\.md|data\.json)$ error [F]
|
||||||
|
## End - Security
|
||||||
|
|
||||||
|
</IfModule>
|
||||||
|
|
||||||
|
# Begin - Prevent Browsing and Set Default Resources
|
||||||
|
Options -Indexes
|
||||||
|
DirectoryIndex index.php
|
BIN
IconSmallSquareOutline.png
Normal file
BIN
IconSmallSquareOutline.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 23 KiB |
250
index.php
Normal file
250
index.php
Normal file
|
@ -0,0 +1,250 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
define('L_EXEC', true);
|
||||||
|
require_once './theme.php';
|
||||||
|
require_once './strings.php';
|
||||||
|
require_once './storageHelper.php';
|
||||||
|
|
||||||
|
class SyncedTimer
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_serverConfig = array(
|
||||||
|
"hideIndexPhp" => true
|
||||||
|
);
|
||||||
|
private $_themeConfig = array();
|
||||||
|
private $_translations;
|
||||||
|
private $_basepath;
|
||||||
|
private $_resourcePath;
|
||||||
|
private $_path;
|
||||||
|
private $_dataFile = "./data.json";
|
||||||
|
private $_storageHelper;
|
||||||
|
|
||||||
|
public function __construct($translations)
|
||||||
|
{
|
||||||
|
session_start();
|
||||||
|
$this->_translations = $translations;
|
||||||
|
|
||||||
|
$this->_storageHelper = new StorageHelper($this->_dataFile);
|
||||||
|
|
||||||
|
$this->_calculateBasepath();
|
||||||
|
$this->_themeConfig["basePath"] = $this->_basepath;
|
||||||
|
$this->_themeConfig["mainIcon"] = $this->_resourcePath . "IconSmallSquareOutline.png";
|
||||||
|
$this->_theme = new LandingpageTheme($this->_themeConfig, $this->_storageHelper, $this->_translations);
|
||||||
|
|
||||||
|
$this->_processRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _processRequest()
|
||||||
|
{
|
||||||
|
$this->_updatePermissions();
|
||||||
|
$this->_checkPagePermissions();
|
||||||
|
|
||||||
|
if ($this->_stringEndsWith($this->_path, "submit")) {
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||||
|
$this->_redirect('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($this->_path) {
|
||||||
|
case '/login/submit':
|
||||||
|
$this->_handleLoginSubmit();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/logout/submit':
|
||||||
|
unset($_SESSION["auth"]);
|
||||||
|
$this->_redirect('/');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '/manage/submit':
|
||||||
|
$this->_handleManageSubmit();
|
||||||
|
$this->_redirect('/manage');
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
$this->_redirect("/");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->_path === '/logout') {
|
||||||
|
$this->_redirect('/');
|
||||||
|
} else {
|
||||||
|
$paramList = explode('/', ltrim($this->_path, '/'), 2);
|
||||||
|
$endpoint = $paramList[0];
|
||||||
|
$username = $paramList[1];
|
||||||
|
|
||||||
|
if ($endpoint === "api" && isset($username))
|
||||||
|
$this->_printJson($username);
|
||||||
|
else
|
||||||
|
$this->_theme->printPage($endpoint, $username);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($_SESSION['lastResult']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _calculateBasepath()
|
||||||
|
{
|
||||||
|
if ($this->_serverConfig['hideIndexPhp']) {
|
||||||
|
$this->_basepath = str_replace(basename($_SERVER["SCRIPT_NAME"]), '', $_SERVER['SCRIPT_NAME']);
|
||||||
|
$this->_resourcePath = $this->_basepath;
|
||||||
|
} else {
|
||||||
|
$this->_basepath = $_SERVER["SCRIPT_NAME"];
|
||||||
|
$this->_resourcePath = str_replace(basename($_SERVER["SCRIPT_NAME"]), '', $_SERVER['SCRIPT_NAME']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->_basepath = rtrim($this->_basepath, "/ ");
|
||||||
|
|
||||||
|
if (($this->_basepath !== '' && strpos($_SERVER['REQUEST_URI'], $this->_basepath) === false) || $_SERVER['REQUEST_URI'] === $this->_basepath)
|
||||||
|
$this->_path = "/";
|
||||||
|
else
|
||||||
|
$this->_path = str_replace($this->_basepath, "", $_SERVER['REQUEST_URI']);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _redirect($path)
|
||||||
|
{
|
||||||
|
header('Location: ' . $this->_basepath . $path);
|
||||||
|
die();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// - Page printer (theme)
|
||||||
|
//
|
||||||
|
|
||||||
|
private function _printJson($username)
|
||||||
|
{
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
header('Access-Control-Allow-Origin: *');
|
||||||
|
$allUserdata = $this->_storageHelper->loadUserdata();
|
||||||
|
if (array_key_exists($username, $allUserdata)) {
|
||||||
|
$userdata = $allUserdata[$username];
|
||||||
|
die(json_encode(array(
|
||||||
|
"status" => 200,
|
||||||
|
"data" => array(
|
||||||
|
"time" => $userdata["time"],
|
||||||
|
"startedAt" => $userdata["startedAt"],
|
||||||
|
"header" => $userdata["header"]
|
||||||
|
)
|
||||||
|
)));
|
||||||
|
} else {
|
||||||
|
http_response_code(404);
|
||||||
|
die(json_encode(array(
|
||||||
|
"status" => 404,
|
||||||
|
"data" => null
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------
|
||||||
|
// - Permission handlers -
|
||||||
|
// -----------------------
|
||||||
|
|
||||||
|
private function _updatePermissions()
|
||||||
|
{
|
||||||
|
$_SESSION['permissions'][''] = false;
|
||||||
|
$_SESSION['permissions']['t'] = true;
|
||||||
|
$_SESSION['permissions']['api'] = true;
|
||||||
|
$_SESSION['permissions']['login'] = !$this->_isUserAuthenticated();
|
||||||
|
$_SESSION['permissions']['logout'] = $this->_isUserAuthenticated();
|
||||||
|
$_SESSION['permissions']['manage'] = $this->_isUserAuthenticated();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _checkPagePermissions()
|
||||||
|
{
|
||||||
|
$pageRedirectOnInsufficientPermissionsPriority = [
|
||||||
|
0 => "manage",
|
||||||
|
1 => "login"
|
||||||
|
];
|
||||||
|
|
||||||
|
$page = explode("/", $this->_path)[1];
|
||||||
|
|
||||||
|
if (!isset($_SESSION['permissions'][$page])) {
|
||||||
|
$this->_redirect('/');
|
||||||
|
} else if ($_SESSION['permissions'][$page] === false) {
|
||||||
|
if (!$this->_isUserAuthenticated()) {
|
||||||
|
$_SESSION['lastResult'] = 'loginRequired';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->_path === '/' || $this->_path === '') {
|
||||||
|
// if the root is opened, do not throw an error!
|
||||||
|
unset($_SESSION['lastResult']);
|
||||||
|
}
|
||||||
|
|
||||||
|
// redirect to the first page the user has access to
|
||||||
|
foreach ($pageRedirectOnInsufficientPermissionsPriority as $page) {
|
||||||
|
if ($_SESSION['permissions'][$page])
|
||||||
|
$this->_redirect("/" . $page);
|
||||||
|
}
|
||||||
|
|
||||||
|
die($this->_translations['results']['noPermissionToAnyPage']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------
|
||||||
|
// - Submit handlers -
|
||||||
|
// -------------------
|
||||||
|
|
||||||
|
private function _handleLoginSubmit()
|
||||||
|
{
|
||||||
|
$username = $_POST["username"];
|
||||||
|
$password = $_POST["password"];
|
||||||
|
$userData = $this->_storageHelper->loadUserdata();
|
||||||
|
if (array_key_exists($username, $userData) && password_verify($password, $userData[$username]['password'])) {
|
||||||
|
$_SESSION["auth"]["loggedIn"] = true;
|
||||||
|
$_SESSION["auth"]["username"] = $username;
|
||||||
|
$_SESSION['lastResult'] = "loginSuccess";
|
||||||
|
$this->_redirect("/manage");
|
||||||
|
} else {
|
||||||
|
$_SESSION['lastResult'] = "loginFailed";
|
||||||
|
$this->_redirect("/login");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _handleManageSubmit()
|
||||||
|
{
|
||||||
|
$time = $_POST["time"];
|
||||||
|
$header = $_POST["header"];
|
||||||
|
$startedAt = time();
|
||||||
|
$newData = array(
|
||||||
|
"time" => intval($time),
|
||||||
|
"header" => $header,
|
||||||
|
"startedAt" => $startedAt
|
||||||
|
);
|
||||||
|
$this->_storageHelper->writeUserdata($_SESSION["auth"]["username"], $newData);
|
||||||
|
$_SESSION['lastResult'] = "timerSetSuccessfully";
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------
|
||||||
|
// - General helper functions -
|
||||||
|
// ----------------------------
|
||||||
|
|
||||||
|
private function _isUserAuthenticated()
|
||||||
|
{
|
||||||
|
$authenticated =
|
||||||
|
isset($_SESSION['auth'])
|
||||||
|
&& isset($_SESSION['auth']['loggedIn'])
|
||||||
|
&& $_SESSION['auth']['loggedIn'] === true
|
||||||
|
&& isset($_SESSION['auth']['username']);
|
||||||
|
|
||||||
|
if (!$authenticated && isset($_SESSION['auth'])) {
|
||||||
|
unset($_SESSION['auth']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $authenticated;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _stringStartsWith($haystack, $needle)
|
||||||
|
{
|
||||||
|
$length = strlen($needle);
|
||||||
|
return substr($haystack, 0, $length) === $needle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _stringEndsWith($haystack, $needle)
|
||||||
|
{
|
||||||
|
$length = strlen($needle);
|
||||||
|
if (!$length) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return substr($haystack, -$length) === $needle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new SyncedTimer($translations);
|
31
storageHelper.php
Normal file
31
storageHelper.php
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class StorageHelper
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_dataFile;
|
||||||
|
|
||||||
|
public function __construct($dataFile)
|
||||||
|
{
|
||||||
|
$this->_dataFile = $dataFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadUserdata()
|
||||||
|
{
|
||||||
|
$data = json_decode(file_get_contents($this->_dataFile), true);
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
function writeUserdata($username, $newUserdata)
|
||||||
|
{
|
||||||
|
$allUserdata = $this->loadUserdata();
|
||||||
|
$currentUserdata = $allUserdata[$username];
|
||||||
|
$newUserdata = array_replace_recursive($currentUserdata, $newUserdata);
|
||||||
|
$dataFile = fopen($this->_dataFile, "w") or die("Unable to open file!");
|
||||||
|
$allUserdata[$username] = $newUserdata;
|
||||||
|
|
||||||
|
fwrite($dataFile, json_encode($allUserdata));
|
||||||
|
fclose($dataFile) or die("Unable to write to data file!");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
51
strings.php
Normal file
51
strings.php
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined('L_EXEC') or die();
|
||||||
|
|
||||||
|
// Available placeholders:
|
||||||
|
// {$customizationConfig['organizationName']}
|
||||||
|
// {$customizationConfig['fullOrganizationName']}
|
||||||
|
// {$customizationConfig['supportEmailAddress']}
|
||||||
|
|
||||||
|
$translations['home']['hello'] = "Hallo";
|
||||||
|
$translations['home']['welcomeMessage'] = "Wilkommen bei deinem {$customizationConfig['organizationName']}-Account - Ein Login für alles";
|
||||||
|
$translations['home']['menu']['linksLabel'] = "{$customizationConfig['organizationName']} Dienste";
|
||||||
|
$translations['home']['menu']['changePasswordLabel'] = "Passwort ändern";
|
||||||
|
$translations['home']['menu']['changeEmailLabel'] = "E-Mail-Adresse ändern";
|
||||||
|
$translations['home']['menu']['generateJitsiLinkLabel'] = "Jitsi Link generieren";
|
||||||
|
$translations['home']['menu']['logoutLabel'] = "Abmelden";
|
||||||
|
$translations['home']['menu']['loginLabel'] = "Anmelden";
|
||||||
|
|
||||||
|
// These rules are currently hard coded!
|
||||||
|
$translations['passwordRules'][0] = "Das Passwort muss mindestens 7 Zeichen lang sein.";
|
||||||
|
$translations['passwordRules'][1] = "Das Passwort muss mindestens eine Zahl oder ein Sonderzeichen enthalten.";
|
||||||
|
$translations['passwordRules'][2] = "Das Passwort muss mindestens einen Klein- und einen Großbuchstaben enthalten.";
|
||||||
|
|
||||||
|
$translations['results']['internalError'] = "Interner Fehler. Falls das Problem bestehen bleibt, kontaktieren sie bitte <a href=\"mailto:{$customizationConfig['supportEmailAddress']}?body=Fehlercode: %ERR%; URI: {$_SERVER['REQUEST_URI']}\">{$customizationConfig['supportEmailAddress']}</a> (%ERR%)";
|
||||||
|
$translations['results']['permissionDenied'] = "Zugriff verweigert";
|
||||||
|
$translations['results']['noPermissionToAnyPage'] = str_replace("%ERR%", "0005", $translations['results']['internalError']);
|
||||||
|
$translations['results']['loginSuccess'] = "Erfolgreich angemeldet";
|
||||||
|
$translations['results']['loginFailed'] = "Ungültige Zugangsdaten";
|
||||||
|
$translations['results']['loginRequired'] = "Bitte zuerst anmelden!";
|
||||||
|
$translations['results']['oldPasswordIsWrong'] = "Das aktulle Passwort ist falsch";
|
||||||
|
$translations['results']['newPasswordMustNotBeEqualToOldPassword'] = "Das neue Passwort darf nicht mit dem Alten übereinstimmen";
|
||||||
|
$translations['results']['newPasswordAndRepeatDidNotMatch'] = "Die Passwörter stimmen nicht überein";
|
||||||
|
$translations['results']['passwordIsTooShort'] = $translations['passwordRules'][0];
|
||||||
|
$translations['results']['newPasswordMustNotBeOldPassword'] = "Das neue Passwort darf nicht mit dem Alten übereinstimmen.";
|
||||||
|
$translations['results']['passwordChangedSuccessfully'] = "Dein Passwort wurde erfolgreich geändert.";
|
||||||
|
$translations['results']['timerSetSuccessfully'] = "The timer was started successfully";
|
||||||
|
|
||||||
|
$translations['globals']['title'] = "{$customizationConfig['organizationName']} Account";
|
||||||
|
$translations['globals']['usernameLabel'] = "Benutzername";
|
||||||
|
$translations['globals']['passwordLabel'] = "Passwort";
|
||||||
|
$translations['globals']['emailAddress'] = "E-Mail-Adresse";
|
||||||
|
|
||||||
|
$translations['login']['title'] = "Bitte anmelden";
|
||||||
|
$translations['login']['submitLabel'] = "Anmelden";
|
||||||
|
$translations['login']['footnote'] = "Login für Mitglieder des {$customizationConfig['fullOrganizationName']}";
|
||||||
|
|
||||||
|
$translations['changePassword']['passwordRulesHeader'] = "Regeln für Passwörter:";
|
||||||
|
$translations['changePassword']['currentPasswordLabel'] = "Aktuelles Passwort";
|
||||||
|
$translations['changePassword']['newPasswordLabel'] = "Neues Passwort";
|
||||||
|
$translations['changePassword']['repeatNewPasswordLabel'] = "Neues Password Wiederholen";
|
||||||
|
$translations['changePassword']['submitLabel'] = "Passwort ändern";
|
473
theme.php
Normal file
473
theme.php
Normal file
|
@ -0,0 +1,473 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
defined('L_EXEC') or die();
|
||||||
|
|
||||||
|
class LandingpageTheme
|
||||||
|
{
|
||||||
|
|
||||||
|
private $_storageHelper;
|
||||||
|
private $_globalConfig;
|
||||||
|
private $_translations;
|
||||||
|
private $_resultLevels;
|
||||||
|
|
||||||
|
public function __construct($config, $storageHelper, $translations)
|
||||||
|
{
|
||||||
|
$this->_globalConfig = $config;
|
||||||
|
$this->_storageHelper = $storageHelper;
|
||||||
|
$this->_translations = $translations;
|
||||||
|
|
||||||
|
$this->_resultLevels['loginSuccess'] = "success";
|
||||||
|
$this->_resultLevels['loginFailed'] = "danger";
|
||||||
|
$this->_resultLevels['ldapConnectFailed'] = "danger";
|
||||||
|
$this->_resultLevels['ldapSearchFailed'] = "danger";
|
||||||
|
$this->_resultLevels['ldapTlsInitializationFailed'] = "danger";
|
||||||
|
$this->_resultLevels['bindingToLdapAdminFailed'] = "danger";
|
||||||
|
$this->_resultLevels['loginRequired'] = "warning";
|
||||||
|
$this->_resultLevels['oldPasswordIsWrong'] = "danger";
|
||||||
|
$this->_resultLevels['newPasswordMustNotBeEqualToOldPassword'] = "danger";
|
||||||
|
$this->_resultLevels['newPasswordAndRepeatDidNotMatch'] = "danger";
|
||||||
|
$this->_resultLevels['passwordIsTooShort'] = "danger";
|
||||||
|
$this->_resultLevels['passwordDoesNotContainANumberOrSpecialCharacter'] = "danger";
|
||||||
|
$this->_resultLevels['passwordDoesNotContainALetter'] = "danger";
|
||||||
|
$this->_resultLevels['passwordDoesNotContainAnUppercaseLetter'] = "danger";
|
||||||
|
$this->_resultLevels['passwordDoesNotContainALowercaseLetter'] = "danger";
|
||||||
|
$this->_resultLevels['passwordChangeLdapError'] = "danger";
|
||||||
|
$this->_resultLevels['newPasswordMustNotBeOldPassword'] = "danger";
|
||||||
|
$this->_resultLevels['passwordChangedSuccessfully'] = 'success';
|
||||||
|
$this->_resultLevels['emailChangedSuccessfully'] = 'success';
|
||||||
|
$this->_resultLevels['emailChangeLdapError'] = 'danger';
|
||||||
|
$this->_resultLevels['invalidEmailError'] = 'danger';
|
||||||
|
$this->_resultLevels['permissionDenied'] = 'danger';
|
||||||
|
$this->_resultLevels['generateJitsiLinkRoomMustNotBeEmpty'] = 'danger';
|
||||||
|
$this->_resultLevels['generateJitsiLinkSuccessfull'] = 'success';
|
||||||
|
$this->_resultLevels['timerSetSuccessfully'] = 'success';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function printPage($page, $parameters)
|
||||||
|
{
|
||||||
|
switch ($page) {
|
||||||
|
case 'login':
|
||||||
|
$this->_printLogin();
|
||||||
|
break;
|
||||||
|
case "manage":
|
||||||
|
$this->_printManagePage();
|
||||||
|
break;
|
||||||
|
case "t":
|
||||||
|
$this->_printTimerPage($parameters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printLogin()
|
||||||
|
{
|
||||||
|
$this->_printHeader(true);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding-top: 40px;
|
||||||
|
padding-bottom: 40px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 330px;
|
||||||
|
padding: 15px;
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .checkbox {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .form-control {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: auto;
|
||||||
|
padding: 10px;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin .form-control:focus {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin input[type="email"] {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-signin input[type="password"] {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bd-placeholder-img {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
text-anchor: middle;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="text-center">
|
||||||
|
<main class="form-signin">
|
||||||
|
<form action="login/submit" method="post">
|
||||||
|
<img class="mb-4" src="<?= $this->_globalConfig['mainIcon'] ?>" alt="" height="150">
|
||||||
|
<h1 class="h3 mb-3 fw-normal"><?= $this->_trId("login.title"); ?></h1>
|
||||||
|
<?php $this->_printResultAlert(); ?>
|
||||||
|
<label for="inputEmail" class="visually-hidden"><?= $this->_trId("globals.usernameLabel"); ?></label>
|
||||||
|
<input type="text" id="inputUsername" class="form-control" placeholder="<?= $this->_trId("globals.usernameLabel"); ?>" name="username" required autofocus>
|
||||||
|
<label for="inputPassword" class="visually-hidden"><?= $this->_trId("globals.passwordLabel"); ?></label>
|
||||||
|
<input type="password" id="inputPassword" class="form-control" placeholder="<?= $this->_trId("globals.passwordLabel"); ?>" name="password" required>
|
||||||
|
<button class="w-100 btn btn-lg btn-primary" type="submit"><?= $this->_trId("login.submitLabel"); ?></button>
|
||||||
|
<p class="mt-5 mb-3 text-muted"><?= $this->_trId("login.footnote"); ?></p>
|
||||||
|
</form>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<?php
|
||||||
|
$this->_printFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printManagePage()
|
||||||
|
{
|
||||||
|
$userData = $this->_storageHelper->loadUserdata()[$_SESSION["auth"]["username"]];
|
||||||
|
$this->_printHeader();
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<main>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<a class="navbar-brand" href="#">Itsblue synced timer</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarTogglerDemo02" aria-controls="navbarTogglerDemo02" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarTogglerDemo02">
|
||||||
|
<ul class="navbar-nav nav-pills me-auto mb-2 mb-lg-0">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link active" aria-current="page" href="#">Home</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" aria-current="page" target="blank" href="<?= $this->_globalConfig["basePath"] ?>/t/<?= $_SESSION["auth"]["username"] ?>">Open timer</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<form action="logout/submit" method="post" class="d-felx">
|
||||||
|
<input type="submit" value="logout" class="btn btn-secondary">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div class="container" style="margin-top: 20px;">
|
||||||
|
<h1>Curent timer</h1>
|
||||||
|
<h4 id="header">Loading ...</h4>
|
||||||
|
<h4 id="timer">00:00:00</h4>
|
||||||
|
<h1>Manage timer</h1>
|
||||||
|
<?php $this->_printResultAlert(); ?>
|
||||||
|
<form method="post" action="manage/submit">
|
||||||
|
<div class="form-group">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<input name="cat" type="hidden" value="5-6">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="header">Header</label>
|
||||||
|
<textarea class="form-control" name="header" rows="3" required><?= $userData["header"] ?></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="time">Time</label>
|
||||||
|
<input type="number" name="time" class="form-control" value="<?= $userData["time"] ?>" required>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">Start timer</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
<?php
|
||||||
|
$this->_printTimerJs($_SESSION["auth"]["username"]);
|
||||||
|
$this->_printFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printTimerPage($username)
|
||||||
|
{
|
||||||
|
$userData = $this->_storageHelper->loadUserdata();
|
||||||
|
if (!array_key_exists($username, $userData)) {
|
||||||
|
$this->_printNotFoundPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
<html lang="en" class="h-100">
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.bd-placeholder-img {
|
||||||
|
font-size: 1.125rem;
|
||||||
|
text-anchor: middle;
|
||||||
|
-webkit-user-select: none;
|
||||||
|
-moz-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.bd-placeholder-img-lg {
|
||||||
|
font-size: 3.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
text-shadow: 0 .05rem .1rem rgba(0, 0, 0, .5);
|
||||||
|
box-shadow: inset 0 0 5rem rgba(0, 0, 0, .5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cover-container {
|
||||||
|
max-width: 42em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<!-- Bootstrap -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="d-flex h-100 text-center text-white bg-dark">
|
||||||
|
|
||||||
|
<div class="d-flex w-100 h-100 p-3 mx-auto flex-column">
|
||||||
|
<main class="px-3 h-100 mt-auto w-100">
|
||||||
|
<h4 id="header">Loading ...</h4>
|
||||||
|
<div style="justify-content:center; align-items:center; display:flex;">
|
||||||
|
<h4 id="timer">00:00:00</h4>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="mt-auto text-white-50">
|
||||||
|
<p>Synced timer by Itsblue Development. <a href="https://www.itsblue.de" class="text-white">www.itsblue.de</a></p>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<?= $this->_printTimerJs($username); ?>
|
||||||
|
<script>
|
||||||
|
var textElem = document.getElementById("timer");
|
||||||
|
var headerTextElem = document.getElementById("header");
|
||||||
|
var targetWidth = 0.9; // Proportion of full screen width
|
||||||
|
var curFontSize = 20; // Do not change
|
||||||
|
function updateTextSize() {
|
||||||
|
for (var i = 0; 3 > i; i++) { // Iterate for better better convergence
|
||||||
|
curFontSize *= targetWidth / (textElem.offsetWidth / textElem.parentNode.offsetWidth);
|
||||||
|
textElem.style.fontSize = curFontSize + "pt";
|
||||||
|
headerTextElem.fontSize = curFontSize / 2 + "pt"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateTextSize();
|
||||||
|
</script>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printNotFoundPage()
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<h1>Not found!</h1>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
// - JavaScript -
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
private function _printTimerJs($username)
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
var currentData = {};
|
||||||
|
|
||||||
|
const zeroPad = (num, places) => String(num).padStart(places, '0')
|
||||||
|
var processData = function() {
|
||||||
|
if (this.readyState === 4 && this.status === 200) {
|
||||||
|
currentData = JSON.parse(this.responseText);
|
||||||
|
if(currentData["status"] === 200) {
|
||||||
|
currentData = currentData["data"]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
document.getElementById("timer").innerHTML = "error: " + this.status
|
||||||
|
}
|
||||||
|
} else if (this.readyState === 4 && this.status !== 0) {
|
||||||
|
document.getElementById("timer").innerHTML = "error: " + this.status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadData() {
|
||||||
|
xmlhttp = new XMLHttpRequest();
|
||||||
|
xmlhttp.onreadystatechange = processData;
|
||||||
|
xmlhttp.open("GET", "<?= $this->_globalConfig["basePath"] ?>/api/<?= $username ?>", true);
|
||||||
|
xmlhttp.send();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTimerText() {
|
||||||
|
time = currentData["time"]
|
||||||
|
header = currentData["header"]
|
||||||
|
startedAt = currentData["startedAt"]
|
||||||
|
|
||||||
|
passedSeconds = (Date.now() / 1000) - startedAt;
|
||||||
|
remaningSeconds = parseInt(time * 60 - passedSeconds);
|
||||||
|
|
||||||
|
if (remaningSeconds < 0) {
|
||||||
|
remaningSeconds = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
var remaningHours = zeroPad(parseInt(remaningSeconds / 60 / 60) % (60 * 60), 2)
|
||||||
|
var remaningMinutes = zeroPad(parseInt(remaningSeconds / 60) % 60, 2)
|
||||||
|
var remaningSeconds = zeroPad(remaningSeconds % 60, 2)
|
||||||
|
document.getElementById("timer").innerHTML = remaningHours + ":" + remaningMinutes + ":" + remaningSeconds
|
||||||
|
document.getElementById("header").innerHTML = header
|
||||||
|
}
|
||||||
|
|
||||||
|
loadData();
|
||||||
|
|
||||||
|
var dataLoader = setInterval(function() {
|
||||||
|
loadData();
|
||||||
|
}, 5000)
|
||||||
|
|
||||||
|
var timerRefresher = setInterval(function() {
|
||||||
|
setTimerText()
|
||||||
|
}, 1000)
|
||||||
|
</script>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------
|
||||||
|
// - Helpers -
|
||||||
|
// -----------
|
||||||
|
|
||||||
|
private function _printHeader($printOnlySkeleton = false)
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<!-- Bootstrap -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--primary_500: 255, 0, 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mr-4 {
|
||||||
|
margin-right: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-4 {
|
||||||
|
margin-left: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-footer {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<title><?= $this->_trId("globals.title"); ?></title>
|
||||||
|
<?php
|
||||||
|
if (!$printOnlySkeleton) :
|
||||||
|
|
||||||
|
?>
|
||||||
|
</head>
|
||||||
|
<?php
|
||||||
|
endif;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printResultAlert()
|
||||||
|
{
|
||||||
|
if (!isset($_SESSION['lastResult']) || $_SESSION['lastResult'] === 'loginSuccess')
|
||||||
|
return;
|
||||||
|
|
||||||
|
$this->_printAlert($this->_resultToMessage($_SESSION['lastResult']), $this->_resultLevels[$_SESSION['lastResult']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printAlert($content, $level = 'waring', $dismissible = true)
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<div class="alert alert-<?= $level ?> <?php if ($dismissible) echo "alert-dismissible"; ?> fade show" role="alert">
|
||||||
|
<strong><?= $content ?></strong>
|
||||||
|
<?php if ($dismissible) : ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _printFooter()
|
||||||
|
{
|
||||||
|
?>
|
||||||
|
<script>
|
||||||
|
var forms = document.getElementsByTagName('form')
|
||||||
|
|
||||||
|
for (const form of forms) {
|
||||||
|
var formButtons = form.getElementsByTagName("button");
|
||||||
|
for (const button of formButtons) {
|
||||||
|
if (button.type === "submit") {
|
||||||
|
form.addEventListener("submit", () => {
|
||||||
|
console.log(button)
|
||||||
|
button.innerHTML += ' <div class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></div>'
|
||||||
|
button.disabled = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var formInputs = form.getElementsByTagName("input")
|
||||||
|
for (const input of formInputs) {
|
||||||
|
form.addEventListener("submit", () => {
|
||||||
|
input.readonly = true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
<?php
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _resultToMessage($result)
|
||||||
|
{
|
||||||
|
return $this->_translations['results'][$result];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function _trId($id)
|
||||||
|
{
|
||||||
|
$result = $this->_translations;
|
||||||
|
foreach (explode(".", $id) as $sub) {
|
||||||
|
$result = $result[$sub];
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue