Some more functionalities

This commit is contained in:
Roman Hergenreder 2020-02-10 00:52:25 +01:00
parent f4ed99fc72
commit 1853756db4
25 changed files with 897 additions and 188 deletions

@ -1,8 +1,5 @@
php_flag display_errors on
Options -Indexes
ErrorDocument 404 /s/404
RewriteEngine on
RewriteRule ^s/(.*)?$ index.php?s=$1&$2 [L,QSA]
RewriteEngine On
RewriteRule ^api/(.*)?$ index.php?api=$1&$2 [L,QSA]

@ -16,21 +16,19 @@ class ExecuteSelect extends Request {
$this->variableParamCount = true;
}
public function getDescription() { return 'Führt ein SELECT Statement aus.'; }
public function getSection() { return "Internal"; }
public function execute($aValues = array()) {
if(!parent::execute($aValues)) {
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}
$sql = $this->user->getSQL();
$this->success = false;
$this->result['rows'] = array();
if(count($this->params) === 1) {
$res = $this->user->getSQL()->query($this->getParam('query'));
$res = $sql->query($this->getParam('query'));
if(!$res) {
$this->lastError = 'Database Error: query() failed with ' . $this->user->getSQL()->getLastError();
$this->lastError = 'Database Error: query() failed with ' . $sql->getLastError();
return false;
}
@ -77,7 +75,7 @@ class ExecuteSelect extends Request {
$tmp = array();
foreach($aSqlParams as $key => $value) $tmp[$key] = &$aSqlParams[$key];
if($stmt = $this->user->getSQL()->connection->prepare($this->getParam('query'))) {
if($stmt = $sql->connection->prepare($this->getParam('query'))) {
if(call_user_func_array(array($stmt, "bind_param"), $tmp))
{
if($stmt->execute()) {
@ -89,18 +87,18 @@ class ExecuteSelect extends Request {
$res->close();
$this->success = true;
} else {
$this->lastError = 'Database Error: execute() failed with ' . $this->user->getSQL()->getLastError();
$this->lastError = 'Database Error: execute() failed with ' . $sql->getLastError();
}
} else {
$this->lastError = 'Database Error: get_result() failed with ' . $this->user->getSQL()->getLastError();
$this->lastError = 'Database Error: get_result() failed with ' . $sql->getLastError();
}
} else {
$this->lastError = 'Database Error: bind_param() failed with ' . $this->user->getSQL()->getLastError();
$this->lastError = 'Database Error: bind_param() failed with ' . $sql->getLastError();
}
$stmt->close();
} else {
$this->lastError = 'Database Error: prepare failed with() ' . $this->user->getSQL()->getLastError();
$this->lastError = 'Database Error: prepare failed with() ' . $sql->getLastError();
}
}

@ -16,8 +16,8 @@ class ExecuteStatement extends Request {
$this->variableParamCount = true;
}
public function execute($aValues = array()) {
if(!parent::execute($aValues)) {
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}

@ -0,0 +1,36 @@
<?php
namespace Api;
class GetLanguages extends Request {
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall, array());
}
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}
$query = 'SELECT uid, code, name FROM Language';
$request = new ExecuteSelect($this->user);
$this->success = $request->execute(array('query' => $query));
$this->lastError = $request->getLastError();
if($this->success) {
$this->result['languages'] = array();
if(count($request->getResult()['rows']) === 0) {
$this->lastError = L("No languages found");
} else {
foreach($request->getResult()['rows'] as $row) {
$this->result['languages'][$row['uid']] = $row;
}
}
}
return $this->success;
}
};
?>

75
core/Api/Login.class.php Normal file

@ -0,0 +1,75 @@
<?php
namespace Api;
use Api\Parameter\Parameter;
use Api\Parameter\StringType;
class Login extends Request {
private $startedAt;
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall, array(
'username' => new StringType('username', 32),
'password' => new StringType('password'),
));
$this->forbidMethod("GET");
}
private function wrongCredentials() {
$runtime = microtime(true) - $this->startedAt;
$sleepTime = round(3e6 - $runtime);
if($sleepTime > 0) usleep($sleepTime);
return $this->createError(L('Wrong username or password'));
}
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}
if($this->user->isLoggedIn()) {
$this->lastError = L('You are already logged in');
$this->success = true;
return true;
}
$this->startedAt = microtime(true);
$this->success = false;
$username = $this->getParam('username');
$password = $this->getParam('password');
$query = 'SELECT User.uid, User.password, User.salt FROM User WHERE User.name=?';
$request = new ExecuteSelect($this->user);
$this->success = $request->execute(array('query' => $query, $username));
$this->lastError = $request->getLastError();
if($this->success) {
$this->success = false;
if(count($request->getResult()['rows']) === 0) {
return $this->wrongCredentials();
$this->lastError = L('Wrong username or password');
} else {
$row = $request->getResult()['rows'][0];
$salt = $row['salt'];
$uid = $row['uid'];
$hash = hash('sha256', $password . $salt);
if($hash === $row['password']) {
if(!($this->success = $this->user->createSession($uid))) {
return $this->createError("Error creating Session");
} else {
$this->result['logoutIn'] = $this->user->getSession()->getExpiresSeconds();
}
}
else {
return $this->wrongCredentials();
}
}
}
return $this->success;
}
};
?>

24
core/Api/Logout.class.php Normal file

@ -0,0 +1,24 @@
<?php
namespace Api;
class Logout extends Request {
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall);
$this->loginRequired = true;
$this->apiKeyAllowed = false;
}
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}
$this->success = true;
$this->user->logout();
return true;
}
};
?>

@ -53,9 +53,9 @@ class Request {
return "($str)";
}
public function parseParams($aValues) {
public function parseParams($values) {
foreach($this->params as $name => $param) {
$value = (isset($aValues[$name]) ? $aValues[$name] : NULL);
$value = (isset($values[$name]) ? $values[$name] : NULL);
if(!$param->optional && is_null($value)) {
$this->lastError = 'Missing parameter: ' . $name;
@ -73,8 +73,8 @@ class Request {
return true;
}
public function parseVariableParams($aValues) {
foreach($aValues as $name => $value) {
public function parseVariableParams($values) {
foreach($values as $name => $value) {
if(isset($this->params[$name])) continue;
$type = Parameter\Parameter::parseType($value);
$param = new Parameter\Parameter($name, $type, true);
@ -83,7 +83,7 @@ class Request {
}
}
public function execute($aValues = array()) {
public function execute($values = array()) {
$this->params = $this->aDefaultParams;
$this->success = false;
$this->result = array();
@ -94,10 +94,10 @@ class Request {
}
if($this->externCall) {
$aValues = $_REQUEST;
$values = $_REQUEST;
if($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_SERVER["CONTENT_TYPE"]) && in_array("application/json", explode(";", $_SERVER["CONTENT_TYPE"]))) {
$jsonData = json_decode(file_get_contents('php://input'), true);
$aValues = array_merge($aValues, $jsonData);
$values = array_merge($values, $jsonData);
}
}
@ -121,8 +121,8 @@ class Request {
if($this->loginRequired) {
$authorized = false;
if(isset($aValues['api_key']) && $this->apiKeyAllowed) {
$apiKey = $aValues['api_key'];
if(isset($values['api_key']) && $this->apiKeyAllowed) {
$apiKey = $values['api_key'];
$authorized = $this->user->authorize($apiKey);
}
@ -133,11 +133,11 @@ class Request {
}
}
if(!$this->parseParams($aValues))
if(!$this->parseParams($values))
return false;
if($this->variableParamCount)
$this->parseVariableParams($aValues);
$this->parseVariableParams($values);
if(!$this->user->getSQL()->isConnected()) {
$this->lastError = $this->user->getSQL()->getLastError();

75
core/Api/SetLanguage.php Normal file

@ -0,0 +1,75 @@
<?php
namespace Api;
use Api\Parameter\Parameter;
use Api\Parameter\StringType;
class SetLanguage extends Request {
private $language;
public function __construct($user, $externCall = false) {
parent::__construct($user, $externCall, array(
'langId' => new Parameter('langId', Parameter::TYPE_INT, true, NULL),
'langCode' => new StringType('langCode', 5, true, NULL),
));
}
private function checkLanguage() {
$langId = $this->getParam("langId");
$langCode = $this->getParam("langCode");
if(is_null($langId) && is_null($langCode)) {
return $this->createError(L("Either langId or langCode must be given"));
}
$query = "SELECT uid, code, name FROM Language WHERE uid=? OR code=?";
$request = new ExecuteSelect($this->user);
$this->success = $request->execute(array("query" => $query, $langId, $langCode));
$this->lastError = $request->getLastError();
if($this->success) {
if(count($request->getResult()['rows']) == 0) {
return $this->createError(L("This Language does not exist"));
} else {
$row = $request->getResult()['rows'][0];
$this->language = \Objects\Language::newInstance($row['uid'], $row['code'], $row['name']);
if(!$this->language) {
return $this->createError(L("Error while loading language"));
}
}
}
return $this->success;
}
private function updateLanguage() {
$languageId = $this->language->getId();
$userId = $this->user->getId();
$query = "UPDATE User SET uidLanguage = ? WHERE uid = ?";
$request = new ExecuteStatement($this->user);
$this->success = $request->execute(array("query" => $query, $languageId, $userId));
$this->lastError = $request->getLastError();
return $this->success;
}
public function execute($values = array()) {
if(!parent::execute($values)) {
return false;
}
if(!$this->checkLanguage())
return false;
if($this->user->isLoggedIn()) {
$this->updateLanguage();
}
$this->user->setLangauge($this->language);
return $this->success;
}
};
?>

@ -30,7 +30,7 @@ CREATE TABLE IF NOT EXISTS UserGroup (
UNIQUE(`uid`, `gid`)
);
CREATE TABLE Session IF NOT EXISTS (
CREATE TABLE IF NOT EXISTS Session (
`uid` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`expires` timestamp NOT NULL,
`uidUser` int(11) NOT NULL,
@ -52,8 +52,13 @@ CREATE TABLE IF NOT EXISTS Language (
`name` VARCHAR(32) UNIQUE NOT NULL
);
INSERT INTO Language (`uid`, `code`, `name`) VALUES
(1, 'en_US', 'American English'),
(2, 'de_DE', 'Deutsch Standard')
ON DUPLICATE KEY UPDATE name=name;
CREATE TABLE IF NOT EXISTS ExternalSiteCache (
`url` VARCHAR(256) PRIMARY KEY,
`data` TEXT NOT NULL,
`expires` TIMESTAMP DEFAULT NULL
`expires` DATETIME DEFAULT NULL
);

@ -0,0 +1,70 @@
<?php
namespace Documents {
class Admin extends \Elements\Document {
public function __construct($user) {
parent::__construct($user, Admin\Head::class, Admin\Body::class);
$this->databseRequired = false;
}
}
}
namespace Documents\Admin {
class Head extends \Elements\Head {
public function __construct($document) {
parent::__construct($document);
}
protected function initSources() {
$this->loadJQuery();
$this->loadBootstrap();
$this->loadFontawesome();
$this->addJS(\Elements\Script::CORE);
$this->addCSS(\Elements\Link::CORE);
$this->addJS(\Elements\Script::ADMIN);
$this->addCSS(\Elements\Link::ADMIN);
}
protected function initMetas() {
return array(
array('name' => 'viewport', 'content' => 'width=device-width, initial-scale=1.0'),
array('name' => 'format-detection', 'content' => 'telephone=yes'),
array('charset' => 'utf-8'),
array("http-equiv" => 'expires', 'content' => '0'),
array("name" => 'robots', 'content' => 'noarchive'),
);
}
protected function initRawFields() {
return array();
}
protected function initTitle() {
return "WebBase - Administration";
}
}
class Body extends \Elements\Body {
public function __construct($document) {
parent::__construct($document);
}
public function getCode() {
$html = parent::getCode();
$document = $this->getDocument();
if(!$document->getUser()->isLoggedIn()) {
$html .= new \Views\Login($document);
} else {
$html .= "You are logged in :]";
}
return $html;
}
}
}
?>

@ -97,24 +97,23 @@ namespace Documents\Install {
return self::DATABASE_CONFIGURATION;
}
$query = "SELECT * FROM User";
$sql = $user->getSQL();
if(!is_null($sql) && $sql->isConnected()) {
$res = $sql->query($query);
if($res) {
if($res->num_rows === 0) {
$step = self::CREATE_USER;
} else {
$step = self::ADD_MAIL_SERVICE;
}
$request = new \Api\ExecuteSelect($user);
$success = $request->execute(array("query" => "SELECT COUNT(*) AS count FROM User"));
$this->errorString = $request->getLastError();
if($success) {
if($request->getResult()['rows'][0]["count"] > 0) {
$step = self::ADD_MAIL_SERVICE;
} else {
return self::CREATE_USER;
}
} else {
$step = self::DATABASE_CONFIGURATION;
return self::DATABASE_CONFIGURATION;
}
if($step == self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) {
if($step === self::ADD_MAIL_SERVICE && $config->isFilePresent("Mail")) {
$step = self::FINISH_INSTALLATION;
if(!$config->isFilePresent("JWT") && $config->create("JWT", generateRandomString(32))) {
if(!$config->isFilePresent("JWT") && !$config->create("JWT", generateRandomString(32))) {
$this->errorString = "Unable to create jwt file";
}
}
@ -532,7 +531,8 @@ namespace Documents\Install {
array("title" => "Username", "name" => "username", "type" => "text", "required" => true),
array("title" => "Password", "name" => "password", "type" => "password", "required" => true),
array("title" => "Confirm Password", "name" => "confirmPassword", "type" => "password", "required" => true),
)
),
"previousButton" => true
),
self::ADD_MAIL_SERVICE => array(
"title" => "Optional: Add Mail Service",
@ -550,7 +550,8 @@ namespace Documents\Install {
)
)),
),
"skip" => true
"skip" => true,
"previousButton" => true
),
self::FINISH_INSTALLATION => array(
"title" => "Finish Installation",
@ -562,8 +563,8 @@ namespace Documents\Install {
return "";
}
$prevDisabled = ($this->currentStep <= self::DATABASE_CONFIGURATION);
$currentView = $views[$this->currentStep];
$prevDisabled = !isset($currentView["previousButton"]) || !$currentView["previousButton"];
$spinnerIcon = $this->createIcon("spinner");
$title = $currentView["title"];
@ -695,6 +696,7 @@ namespace Documents\Install {
$progressSidebar = $this->createProgressSidebar();
$progressMainview = $this->createProgessMainview();
$errorStyle = ($this->errorString ? '' : ' style="display:none"');
$errorClass = ($this->errorString ? ' alert-danger' : '');
$html .= "
<body class=\"bg-light\">
@ -718,7 +720,7 @@ namespace Documents\Install {
</div>
<div class=\"col-md-8 order-md-1\">
$progressMainview
<div class=\"alert margin-top-m\" id=\"status\"$errorStyle>$this->errorString</div>
<div class=\"alert$errorClass margin-top-m\" id=\"status\"$errorStyle>$this->errorString</div>
</div>
</div>
</div>

@ -97,6 +97,15 @@ class SQL {
return false;
}
while (($success = $this->connection->next_result())) {
if (!$this->connection->more_results()) break;
}
if(!$success) {
$this->lastError = mysqli_error($this->connection);
return false;
}
return true;
}

@ -17,7 +17,7 @@ class Link extends Source {
// const HIGHLIGHT = '/css/highlight.css';
// const HIGHLIGHT_THEME = '/css/theme.css';
const CORE = "/css/style.css";
// const ADMIN = "/css/admin.css";
const ADMIN = "/css/admin.css";
// const HOME = "/css/home.css";
// const REVEALJS = "/css/reveal.css";
// const REVEALJS_THEME_MOON = "/css/reveal_moon.css";

@ -8,7 +8,7 @@ class Script extends Source {
const CORE = "/js/script.js";
// const HOME = "/js/home.js";
// const ADMIN = "/js/admin.js";
const ADMIN = "/js/admin.js";
// const SORTTABLE = "/js/sorttable.js";
const JQUERY = "/js/jquery.min.js";
// const JQUERY_UI = "/js/jquery-ui.js";

@ -1,133 +1,135 @@
<?php
namespace Objects;
namespace Objects {
class Language extends ApiObject {
class Language extends ApiObject {
const LANG_CODE_PATTERN = "/^[a-zA-Z]+_[a-zA-Z]+$/";
const LANG_CODE_PATTERN = "/^[a-zA-Z]+_[a-zA-Z]+$/";
private $languageId;
private $langCode;
private $langName;
private $modules;
private $languageId;
private $langCode;
private $langName;
private $modules;
protected $entries;
protected $entries;
public function __construct($languageId, $langCode, $langName) {
$this->languageId = $languageId;
$this->langCode = $langCode;
$this->langName = $langName;
$this->entries = array();
$this->modules = array();
}
public function getId() { return $this->languageId; }
public function getCode() { return $this->langCode; }
public function getShortCode() { return substr($this->langCode, 0, 2); }
public function getName() { return $this->langName; }
public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; }
public function getEntries() { return $this->entries; }
public function getModules() { return $this->modules; }
public function loadModule($module) {
if(!is_object($module))
$module = new $module;
$aModuleEntries = $module->getEntries($this->langCode);
$this->entries = array_merge($this->entries, $aModuleEntries);
$this->modules[] = $module;
}
public function translate($key) {
if(isset($this->entries[$key]))
return $this->entries[$key];
return $key;
}
public function sendCookie() {
setcookie('lang', $this->langCode, 0, "/", "");
}
public function jsonSerialize() {
return array(
'uid' => $this->languageId,
'code' => $this->langCode,
'name' => $this->langName,
);
}
public static function newInstance($languageId, $langCode, $langName) {
if(!preg_match(Language::LANG_CODE_PATTERN, $langCode)) {
return false;
public function __construct($languageId, $langCode, $langName) {
$this->languageId = $languageId;
$this->langCode = $langCode;
$this->langName = $langName;
$this->entries = array();
$this->modules = array();
}
// TODO: include dynamically wanted Language
return new Language($languageId, $langCode, $langName);
public function getId() { return $this->languageId; }
public function getCode() { return $this->langCode; }
public function getShortCode() { return substr($this->langCode, 0, 2); }
public function getName() { return $this->langName; }
public function getIconPath() { return "/img/icons/lang/$this->langCode.gif"; }
public function getEntries() { return $this->entries; }
public function getModules() { return $this->modules; }
// $className = $langCode
// return new $className($languageId, $langCode);
}
public function loadModule($module) {
if(!is_object($module))
$module = new $module;
public function load() {
global $LANGUAGE;
$LANGUAGE = $this;
}
$aModuleEntries = $module->getEntries($this->langCode);
$this->entries = array_merge($this->entries, $aModuleEntries);
$this->modules[] = $module;
}
public static function DEFAULT_LANGUAGE() {
if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$aSplit = explode(',',$acceptLanguage);
foreach($aSplit as $code) {
if(strlen($code) == 2) {
$code = $code . '_' . strtoupper($code);
}
public function translate($key) {
if(isset($this->entries[$key]))
return $this->entries[$key];
$code = str_replace("-", "_", $code);
if(strlen($code) != 5)
continue;
return $key;
}
$lang = Language::newInstance(0, $code, "");
if($lang)
return $lang;
public function sendCookie() {
setcookie('lang', $this->langCode, 0, "/", "");
}
public function jsonSerialize() {
return array(
'uid' => $this->languageId,
'code' => $this->langCode,
'name' => $this->langName,
);
}
public static function newInstance($languageId, $langCode, $langName) {
if(!preg_match(Language::LANG_CODE_PATTERN, $langCode)) {
return false;
}
// TODO: include dynamically wanted Language
return new Language($languageId, $langCode, $langName);
// $className = $langCode
// return new $className($languageId, $langCode);
}
return Language::newInstance(1, "en_US", "American English");
public function load() {
global $LANGUAGE;
$LANGUAGE = $this;
}
public static function DEFAULT_LANGUAGE() {
if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
$acceptLanguage = $_SERVER['HTTP_ACCEPT_LANGUAGE'];
$aSplit = explode(',',$acceptLanguage);
foreach($aSplit as $code) {
if(strlen($code) == 2) {
$code = $code . '_' . strtoupper($code);
}
$code = str_replace("-", "_", $code);
if(strlen($code) != 5)
continue;
$lang = Language::newInstance(0, $code, "");
if($lang)
return $lang;
}
}
return Language::newInstance(1, "en_US", "American English");
}
};
}
namespace {
function L($key) {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return $key;
global $LANGUAGE;
return $LANGUAGE->translate($key);
}
};
function L($key) {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return $key;
function LANG_NAME() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "LANG_NAME";
global $LANGUAGE;
return $LANGUAGE->translate($key);
global $LANGUAGE;
return $LANGUAGE->getName();
}
function LANG_CODE() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "LANG_CODE";
global $LANGUAGE;
return $LANGUAGE->getCode();
}
function SHORT_LANG_CODE() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "SHORT_LANG_CODE";
global $LANGUAGE;
return $LANGUAGE->getShortCode();
}
}
function LANG_NAME() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "LANG_NAME";
global $LANGUAGE;
return $LANGUAGE->getName();
}
function LANG_CODE() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "LANG_CODE";
global $LANGUAGE;
return $LANGUAGE->getCode();
}
function SHORT_LANG_CODE() {
if(!array_key_exists('LANGUAGE', $GLOBALS))
return "SHORT_LANG_CODE";
global $LANGUAGE;
return $LANGUAGE->getShortCode();
}
?>

@ -19,18 +19,27 @@ class Session extends ApiObject {
}
private function updateMetaData() {
$userAgent = get_browser($_SERVER['HTTP_USER_AGENT'], true);
$this->expires = time() + Session::DURATION * 60;
$this->ipAddress = $_SERVER['REMOTE_ADDR'];
$this->os = $userAgent['platform'];
$this->browser = $userAgent['parent'];
try {
$userAgent = @get_browser($_SERVER['HTTP_USER_AGENT'], true);
$this->os = $userAgent['platform'] ?? "Unknown";
$this->browser = $userAgent['parent'] ?? "Unknown";
} catch(\Exception $ex) {
$this->os = "Unknown";
$this->browser = "Unknown";
}
}
public function sendCookie() {
$this->updateMetaData();
$token = array('userId' => $this->user->getId(), 'sessionId' => $this->sessionId);
$sessionCookie = JWT::encode($token, getJwtKey());
setcookie('session', $sessionCookie, $this->expires, "/", "", true);
$jwt = $this->user->getConfiguration()->getJwt();
if($jwt) {
$token = array('userId' => $this->user->getId(), 'sessionId' => $this->sessionId);
$sessionCookie = \External\JWT::encode($token, $jwt->getKey());
$secure = strcmp(getProtocol(), "https") === 0;
setcookie('session', $sessionCookie, $this->expires, "/", "", $secure);
}
}
public function getExpiresTime() {
@ -56,7 +65,7 @@ class Session extends ApiObject {
$this->updateMetaData();
$query = 'INSERT INTO Session (expires, uidUser, ipAddress, os, browser)
VALUES (DATE_ADD(NOW(), INTERVAL ? MINUTE),?,?,?,?)';
$request = new CExecuteStatement($this->user);
$request = new \Api\ExecuteStatement($this->user);
$success = $request->execute(array(
'query' => $query,
@ -77,7 +86,7 @@ class Session extends ApiObject {
public function destroy() {
$query = 'DELETE FROM Session WHERE Session.uid=? OR Session.expires<=NOW()';
$request = new CExecuteStatement($this->user);
$request = new \Api\ExecuteStatement($this->user);
$success = $request->execute(array('query' => $query, $this->sessionId));
return $success;
}
@ -88,7 +97,7 @@ class Session extends ApiObject {
SET Session.expires=DATE_ADD(NOW(), INTERVAL ? MINUTE), Session.ipAddress=?,
Session.os=?, Session.browser=?
WHERE Session.uid=?';
$request = new CExecuteStatement($this->user);
$request = new \Api\ExecuteStatement($this->user);
$success = $request->execute(array(
'query' => $query,
Session::DURATION,

@ -80,8 +80,10 @@ class User extends ApiObject {
}
public function updateLanguage($lang) {
$request = new CSetLanguage($this);
return $request->execute(array("langCode" => $lang));
if($this->sql) {
$request = new \Api\SetLanguage($this);
return $request->execute(array("langCode" => $lang));
}
}
public function sendCookies() {
@ -93,12 +95,12 @@ class User extends ApiObject {
}
public function readData($userId, $sessionId, $sessionUpdate = true) {
$query = 'SELECT User.name as userName, Language.uid as langId, Language.code as langCode
$query = 'SELECT User.name as userName, Language.uid as langId, Language.code as langCode, Language.name as langName
FROM User
INNER JOIN Session ON User.uid=Session.uidUser
LEFT JOIN Language ON User.uidLanguage=Language.uid
WHERE User.uid=? AND Session.uid=? AND Session.expires>now()';
$request = new CExecuteSelect($this);
$request = new \Api\ExecuteSelect($this);
$success = $request->execute(array('query' => $query, $userId, $sessionId));
if($success) {
@ -108,12 +110,12 @@ class User extends ApiObject {
$row = $request->getResult()['rows'][0];
$this->username = $row['userName'];
$this->uid = $userId;
$this->session = new CSession($this, $sessionId);
$this->session = new Session($this, $sessionId);
if($sessionUpdate) $this->session->update();
$this->loggedIn = true;
if(!is_null($row['langId'])) {
$this->setLangauge(CLanguage::newInstance($row['langId'], $row['langCode']));
$this->setLangauge(Language::newInstance($row['langId'], $row['langCode'], $row['langName']));
}
}
}
@ -128,7 +130,7 @@ class User extends ApiObject {
&& ($jwt = $this->configuration->getJWT())) {
try {
$token = $_COOKIE['session'];
$decoded = (array)External\JWT::decode($token, $jwt->getKey());
$decoded = (array)\External\JWT::decode($token, $jwt->getKey());
if(!is_null($decoded)) {
$userId = (isset($decoded['userId']) ? $decoded['userId'] : NULL);
$sessionId = (isset($decoded['sessionId']) ? $decoded['sessionId'] : NULL);
@ -143,9 +145,9 @@ class User extends ApiObject {
if(isset($_GET['lang']) && is_string($_GET["lang"]) && !empty($_GET["lang"])) {
$this->updateLanguage($_GET['lang']);
} else if(isset($_COOKIE['lang']) && is_string($_COOKIE["lang"]) && !empty($_COOKIE["lang"])) {
}/* else if(isset($_COOKIE['lang']) && is_string($_COOKIE["lang"]) && !empty($_COOKIE["lang"])) {
$this->updateLanguage($_COOKIE['lang']);
}
}*/
}
public function createSession($userId) {
@ -164,7 +166,7 @@ class User extends ApiObject {
LEFT JOIN Language ON User.uidLanguage=Language.uid
WHERE api_key=? AND valid_until > now() AND User.uid = ApiKey.uidUser';
$request = new CExecuteSelect($this);
$request = new \Api\ExecuteSelect($this);
$success = $request->execute(array('query' => $query, $apiKey));
if($success) {

@ -226,4 +226,5 @@ abstract class View {
}
};
?>

@ -0,0 +1,54 @@
<?php
namespace Views;
class LanguageFlags extends \View {
public function __construct($document) {
parent::__construct($document);
}
public function getCode() {
$requestUri = $_SERVER["REQUEST_URI"];
$queryString = $_SERVER['QUERY_STRING'];
$flags = array();
$request = new \Api\GetLanguages($this->getDocument()->getUser());
$params = explode("&", $queryString);
$query = array();
foreach($params as $param) {
$aParam = explode("=", $param);
$key = $aParam[0];
if($key == "s" && startsWith($requestUri, "/s/"))
continue;
$val = (isset($aParam[1]) ? $aParam[1] : "");
if(!empty($key)) {
$query[$key] = $val;
}
}
$url = parse_url($requestUri, PHP_URL_PATH) . "?";
if($request->execute()) {
foreach($request->getResult()['languages'] as $lang) {
$langCode = $lang['code'];
$langName = $lang['name'];
$query['lang'] = $langCode;
$queryString = http_build_query($query);
$flags[] = $this->createLink(
"$url$queryString",
"<img src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
);
}
} else {
$flags[] = $this->createErrorText($request->getLastError());
}
return implode('', $flags);
}
}
?>

@ -0,0 +1,57 @@
<?php
namespace Views;
class Login extends \View {
public function __construct($document) {
parent::__construct($document);
}
public function getCode() {
$html = parent::getCode();
$username = L("Username");
$password = L("Password");
$rememberMe = L("Remember me");
$login = L("Login");
$backToStartPage = L("Back to Start Page");
$flags = new LanguageFlags($this->getDocument());
$iconBack = $this->createIcon("arrow-circle-left", "right");
$domain = $_SERVER['HTTP_HOST'];
$protocol = getProtocol();
$accountCreated = "";
if(isset($_GET["accountCreated"])) {
$accountCreated .= '
<div class="alert alert-success margin-top-xs" id="accountCreated">
Your account was successfully created, you may now login with your credentials
</div>';
}
$html = "
<div class=\"container margin-top-xxl\">
<div class=\"title text-center\">
<h2>Admin Control Panel</h2>
</div>
<div class=\"loginContainer margin-center\">
<form class=\"loginForm\">
<label for=\"username\">$username</label>
<input type=\"text\" class=\"form-control\" name=\"username\" id=\"username\" placeholder=\"$username\" required autofocus />
<label for=\"password\">$password</label>
<input type=\"password\" class=\"form-control\" name=\"password\" id=\"password\" placeholder=\"$password\" required />
<button class=\"btn btn-lg btn-primary btn-block\" id=\"btnLogin\" type=\"button\">$login</button>
<div class=\"alert alert-danger hidden\" role=\"alert\" id=\"loginError\"></div>
</form>
<span class=\"subtitle flags-container\"><span class=\"flags\">$flags</span></span>
<span class=\"subtitle\"><a class=\"link\" href=\"$protocol://$domain\">$iconBack$backToStartPage</a></span>
$accountCreated
</div>
</div>";
return $html;
return $html;
}
}
?>

@ -24,6 +24,10 @@
return $result;
}
function getProtocol() {
return stripos($_SERVER['SERVER_PROTOCOL'],'https') === 0 ? 'https://' : 'http://';
}
function includeDir($dir, $aIgnore = array(), $recursive = false) {
$aIgnore[] = '.';
$aIgnore[] = '..';

229
css/admin.css Normal file

@ -0,0 +1,229 @@
.loginContainer {
border-radius: 5px;
width: 600px;
position: relative;
}
.loginForm {
padding: 25px;
border: 1px solid #bbb;
border-radius: 5px;
background-color: #bbb;
color: black;
}
.loginForm input {
margin-bottom: 10px;
}
.loginForm button, .loginForm div {
margin-top: 15px;
margin-bottom: 0;
}
.loginForm input[type="checkbox"] {
padding: 0;
margin-bottom: 0;
vertical-align: bottom;
}
.device-table > tbody > tr:hover {
cursor: pointer;
background-color: grey;
}
.device-table > tbody > tr > td:nth-child(3) {
text-align: center;
}
.apikey-table > tbody > tr > td:last-child {
float: right;
}
.apikey-table > tbody > tr > td:first-child {
word-break: break-all;
}
.apikey-table > tbody > tr:hover {
background-color: grey;
cursor: pointer;
}
.sidebar {
margin: 0;
padding: 0;
color: white;
text-align: center;
}
.sidebar a {
color: white;
}
.status { font-size: 12px; }
.status-ok { color: #38e40d; }
.status-error { color: red; }
.status-offline { color: gray; }
.sidebar .nav-item {
line-height: 30px;
border-bottom: 1px solid gray;
}
.nav-link {
grid-template-columns: 20px auto 20px;
}
.nav-link-center {
grid-column-start: 2;
grid-column-end: 2;
}
.sidebar-title {
font-size: 18px;
margin-bottom: 0.5em;
font-weight: bold;
}
.sidebar-top {
height: 100px;
padding: 10px;
border-bottom: 5px solid gray;
background-color: #555;
}
.sidebar-bottom {
height: 100px;
padding: 10px;
position: absolute;
bottom: 0;
border-top: 5px solid gray;
width: 100%;
grid-template-rows: 50% 50%;
}
.grid {
display: grid;
}
.grid > span {
align-self: center;
}
.sidebar-bottom > a {
grid-template-columns: 20px auto;
align-self: center;
}
.sidebar-bottom > a:hover {
text-decoration: none;
font-weight: bold;
}
.sidebar .active .nav-link-center {
text-decoration: underline;
font-weight: bold;
}
.sidebar .nav-item:hover {
background-color: gray;
}
.nav-device {
background-color: #5a5a5a;
}
.service {
margin: 10px;
}
.service > .card-header {
color: black;
cursor: pointer;
display: grid;
grid-template-columns: 20px auto;
}
.service-icon {
width: 32px;
height: 32px;
}
.fs-listview > tbody > tr:hover {
cursor: pointer;
}
.fs-listview > tbody > tr.downloading > td, .fs-gridview > div.downloading > span {
font-style: italic;
color: gray;
}
.fs-toolbar {
display: grid;
grid-template-columns: 1px 40px 40px 1px auto 1px 40px 1px 40px 40px 1px;
}
.fs-toolbar > i {
align-self: center;
cursor: pointer;
}
.fs-toolbar > span {
text-align: left;
align-self: center;
}
.fs-gridview {
display: grid;
grid-template-columns: repeat(4, auto);
}
.fs-gridview div {
align-self: center;
text-align: center;
}
.fs-gridview > div {
padding: 5px;
display: grid;
grid-template-rows: 48px auto;
}
.fs-gridview > div:hover {
background: #ddd;
cursor: pointer;
}
.vr {
border-left: 1px solid #dee2e6;
height: 100%;
}
.camera-stream {
cursor: pointer;
}
.temperature-controls {
display: grid;
grid-template-columns: auto 30px;
}
.temperature-controls > div {
align-self: center;
text-align: left;
}
.shell-tabs > li > a {
color: black;
line-height: inherit;
outline: none;
}
.shell {
text-align: left
}
.speaker-controls {
display:grid;
grid-template-columns: auto auto auto;
}

@ -10,6 +10,10 @@ function getWebRoot() {
return dirname(__FILE__);
}
function createError($msg) {
return json_encode(array("success" => false, "msg" => $msg));
}
spl_autoload_extensions(".php");
spl_autoload_register(function($class) {
$full_path = getClassPath($class);
@ -27,11 +31,42 @@ $config = new Configuration\Configuration();
$installation = (!$config->load());
$user = new Objects\User($config);
if ($installation) {
$document = new Documents\Install($user);
if(isset($_GET["api"]) && is_string($_GET["api"])) {
header("Content-Type: application/json");
if($installation) {
$response = createError("Not installed");
} else {
$apiFunction = $_GET["api"];
if(empty($apiFunction)) {
header("403 Forbidden");
$response = "";
} else if(!preg_match("/[a-zA-Z]+(\/[a-zA-Z]+)*/", $apiFunction)) {
$response = createError("Invalid Method");
} else {
$apiFunction = strtoupper($apiFunction[0]) . substr($apiFunction, 1);
$class = "\\Api\\$apiFunction";
$file = getClassPath($class);
if(!file_exists($file)) {
header("404 Not Found");
$response = createError("Not found");
} else {
$request = new $class($user, true);
$success = $request->execute();
$msg = $request->getLastError();
$response = $request->getJsonResult();
}
}
}
} else {
print("DON'T INSTALL");
if ($installation) {
$document = new Documents\Install($user);
} else {
$document = new Documents\Admin($user);
}
$response = $document->getCode();
}
die($document->getCode());
$user->sendCookies();
die($response);
?>

25
js/admin.js Normal file

@ -0,0 +1,25 @@
$(document).ready(function() {
$("#username").keypress(function(e) { if(e.which == 13) $("#password").focus(); });
$("#password").keypress(function(e) { if(e.which == 13) $("#btnLogin").click(); });
$("#btnLogin").click(function() {
var username = $("#username").val();
var password = $("#password").val();
var errorDiv = $("#loginError");
var createdDiv = $("#accountCreated");
var btn = $(this);
errorDiv.hide();
btn.prop("disabled", true);
btn.html("Logging in… <i class=\"fa fa-spin fa-circle-notch\"></i>");
jsCore.apiCall("login", {"username": username, "password": password}, function(data) {
window.location.reload();
}, function(err) {
btn.html("Login");
btn.prop("disabled", false);
$("#password").val("");
createdDiv.hide();
errorDiv.html(err);
errorDiv.show();
});
});
});

@ -11,7 +11,7 @@ var Core = function() {
callback = typeof callback !== 'undefined' ? callback : function(data) { };
onerror = typeof onerror !== 'undefined' ? onerror : function(msg) { bootbox.alert("Ein Fehler ist aufgetreten: " + msg); };
$.post('/php/api/' + func + '.php', aParams, function(data) {
$.post('/api/' + func, aParams, function(data) {
console.log(func + "(): success=" + data.success + " msg=" + data.msg);
if(data.hasOwnProperty('logoutIn') && $("#logoutTimer").length > 0) {
$("#logoutTimer").attr("data-time", data.logoutIn);
@ -24,7 +24,7 @@ var Core = function() {
}
}, "json").fail(function(jqXHR, textStatus, errorThrown) {
console.log("API-Function Error: " + func + " Status: " + textStatus + " error thrown: " + errorThrown);
onerror.call(this, "Ein Fehler ist aufgetreten. API-Funktion: " + func + " Status: " + textStatus + " - " + errorThrown);
onerror.call(this, "An error occurred. API-Function: " + func + " Status: " + textStatus + " - " + errorThrown);
});
};
@ -32,7 +32,7 @@ var Core = function() {
var name = cname + "=";
var decodedCookie = decodeURIComponent(document.cookie);
var ca = decodedCookie.split(";");
for(var i = 0; i <ca.length; i++) {
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);