Frontend, Bugfixes
This commit is contained in:
parent
efe3ada470
commit
8ce74edc38
@ -2,14 +2,38 @@
|
|||||||
|
|
||||||
namespace Api\User;
|
namespace Api\User;
|
||||||
|
|
||||||
|
use Api\Parameter\Parameter;
|
||||||
use \Api\Request;
|
use \Api\Request;
|
||||||
|
|
||||||
class Fetch extends Request {
|
class Fetch extends Request {
|
||||||
|
|
||||||
|
const SELECT_SIZE = 20;
|
||||||
|
|
||||||
|
private int $userCount;
|
||||||
|
|
||||||
public function __construct($user, $externalCall = false) {
|
public function __construct($user, $externalCall = false) {
|
||||||
parent::__construct($user, $externalCall, array());
|
|
||||||
|
parent::__construct($user, $externalCall, array(
|
||||||
|
'page' => new Parameter('page', Parameter::TYPE_INT, true, 1)
|
||||||
|
));
|
||||||
|
|
||||||
$this->loginRequired = true;
|
$this->loginRequired = true;
|
||||||
$this->requiredGroup = USER_GROUP_ADMIN;
|
$this->requiredGroup = USER_GROUP_ADMIN;
|
||||||
|
$this->userCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUserCount() {
|
||||||
|
|
||||||
|
$sql = $this->user->getSQL();
|
||||||
|
$res = $sql->select($sql->count())->from("User")->execute();
|
||||||
|
$this->success = ($res !== FALSE);
|
||||||
|
$this->lastError = $sql->getLastError();
|
||||||
|
|
||||||
|
if ($this->success) {
|
||||||
|
$this->userCount = $res[0]["count"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->success;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute($values = array()) {
|
public function execute($values = array()) {
|
||||||
@ -17,11 +41,25 @@ class Fetch extends Request {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$page = $this->getParam("page");
|
||||||
|
if($page < 1) {
|
||||||
|
return $this->createError("Invalid page count");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$this->getUserCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
$res = $sql->select("User.uid as userId", "User.name", "User.email", "Group.uid as groupId", "Group.name as groupName")
|
$res = $sql->select("User.uid as userId", "User.name", "User.email", "User.created_at",
|
||||||
|
"Group.uid as groupId", "Group.name as groupName")
|
||||||
->from("User")
|
->from("User")
|
||||||
->leftJoin("UserGroup", "User.uid", "UserGroup.user_id")
|
->leftJoin("UserGroup", "User.uid", "UserGroup.user_id")
|
||||||
->leftJoin("Group", "Group.uid", "UserGroup.group_id")
|
->leftJoin("Group", "Group.uid", "UserGroup.group_id")
|
||||||
|
->orderBy("User.uid")
|
||||||
|
->ascending()
|
||||||
|
->limit(Fetch::SELECT_SIZE)
|
||||||
|
->offset(($page - 1) * Fetch::SELECT_SIZE)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$this->success = ($res !== FALSE);
|
$this->success = ($res !== FALSE);
|
||||||
@ -30,14 +68,15 @@ class Fetch extends Request {
|
|||||||
if($this->success) {
|
if($this->success) {
|
||||||
$this->result["users"] = array();
|
$this->result["users"] = array();
|
||||||
foreach($res as $row) {
|
foreach($res as $row) {
|
||||||
$userId = $row["userId"];
|
$userId = intval($row["userId"]);
|
||||||
$groupId = $row["groupId"];
|
$groupId = intval($row["groupId"]);
|
||||||
$groupName = $row["groupName"];
|
$groupName = $row["groupName"];
|
||||||
if (!isset($this->result["users"][$userId])) {
|
if (!isset($this->result["users"][$userId])) {
|
||||||
$this->result["users"][$userId] = array(
|
$this->result["users"][$userId] = array(
|
||||||
"uid" => $userId,
|
"uid" => $userId,
|
||||||
"name" => $row["name"],
|
"name" => $row["name"],
|
||||||
"email" => $row["email"],
|
"email" => $row["email"],
|
||||||
|
"created_at" => $row["created_at"],
|
||||||
"groups" => array(),
|
"groups" => array(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -46,6 +85,7 @@ class Fetch extends Request {
|
|||||||
$this->result["users"][$userId]["groups"][$groupId] = $groupName;
|
$this->result["users"][$userId]["groups"][$groupId] = $groupName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->result["pages"] = intval(ceil($this->userCount / Fetch::SELECT_SIZE));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->success;
|
return $this->success;
|
||||||
|
@ -31,6 +31,7 @@ class CreateDatabase {
|
|||||||
->addString("salt", 16)
|
->addString("salt", 16)
|
||||||
->addString("password", 64)
|
->addString("password", 64)
|
||||||
->addInt("language_id", true, 1)
|
->addInt("language_id", true, 1)
|
||||||
|
->addDateTime("registered_at", false, $sql->currentTimestamp())
|
||||||
->primaryKey("uid")
|
->primaryKey("uid")
|
||||||
->unique("email")
|
->unique("email")
|
||||||
->unique("name")
|
->unique("name")
|
||||||
|
@ -5,12 +5,12 @@ namespace Documents {
|
|||||||
use Documents\Admin\AdminHead;
|
use Documents\Admin\AdminHead;
|
||||||
use Elements\Document;
|
use Elements\Document;
|
||||||
use Objects\User;
|
use Objects\User;
|
||||||
use Views\AdminDashboard;
|
use Views\Admin\AdminDashboardBody;
|
||||||
use Views\LoginBody;
|
use Views\LoginBody;
|
||||||
|
|
||||||
class Admin extends Document {
|
class Admin extends Document {
|
||||||
public function __construct(User $user) {
|
public function __construct(User $user) {
|
||||||
$body = $user->isLoggedIn() ? AdminDashboard::class : LoginBody::class;
|
$body = $user->isLoggedIn() ? AdminDashboardBody::class : LoginBody::class;
|
||||||
parent::__construct($user, AdminHead::class, $body);
|
parent::__construct($user, AdminHead::class, $body);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ namespace Documents\Document404 {
|
|||||||
|
|
||||||
use Elements\Body;
|
use Elements\Body;
|
||||||
use Elements\Head;
|
use Elements\Head;
|
||||||
|
use Views\View404;
|
||||||
|
|
||||||
class Head404 extends Head {
|
class Head404 extends Head {
|
||||||
|
|
||||||
@ -25,11 +26,6 @@ namespace Documents\Document404 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected function initSources() {
|
protected function initSources() {
|
||||||
// $this->loadJQuery();
|
|
||||||
// $this->loadBootstrap();
|
|
||||||
// $this->loadFontawesome();
|
|
||||||
// $this->addJS(\Elements\Script::CORE);
|
|
||||||
// $this->addCSS(\Elements\Link::CORE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function initMetas() {
|
protected function initMetas() {
|
||||||
@ -59,7 +55,7 @@ namespace Documents\Document404 {
|
|||||||
|
|
||||||
public function getCode() {
|
public function getCode() {
|
||||||
$html = parent::getCode();
|
$html = parent::getCode();
|
||||||
$html .= "<b>404 Not Found</b>";
|
$html .= "<body>" . (new View404($this->getDocument())) . "</body>";
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ namespace Documents {
|
|||||||
namespace Documents\Install {
|
namespace Documents\Install {
|
||||||
|
|
||||||
use Api\Notifications\Create;
|
use Api\Notifications\Create;
|
||||||
|
use Api\Parameter\Parameter;
|
||||||
use Configuration\CreateDatabase;
|
use Configuration\CreateDatabase;
|
||||||
use Driver\SQL\SQL;
|
use Driver\SQL\SQL;
|
||||||
use Elements\Body;
|
use Elements\Body;
|
||||||
@ -289,8 +290,8 @@ namespace Documents\Install {
|
|||||||
$username = $this->getParameter("username");
|
$username = $this->getParameter("username");
|
||||||
$password = $this->getParameter("password");
|
$password = $this->getParameter("password");
|
||||||
$confirmPassword = $this->getParameter("confirmPassword");
|
$confirmPassword = $this->getParameter("confirmPassword");
|
||||||
|
$email = $this->getParameter("email") ?? "";
|
||||||
|
|
||||||
$msg = $this->errorString;
|
|
||||||
$success = true;
|
$success = true;
|
||||||
$missingInputs = array();
|
$missingInputs = array();
|
||||||
|
|
||||||
@ -321,13 +322,16 @@ namespace Documents\Install {
|
|||||||
} else if(strlen($password) < 6) {
|
} else if(strlen($password) < 6) {
|
||||||
$msg = "The password should be at least 6 characters long";
|
$msg = "The password should be at least 6 characters long";
|
||||||
$success = false;
|
$success = false;
|
||||||
|
} else if($email && Parameter::parseType($email) !== Parameter::TYPE_EMAIL) {
|
||||||
|
$msg = "Invalid email address";
|
||||||
|
$success = false;
|
||||||
} else {
|
} else {
|
||||||
$salt = generateRandomString(16);
|
$salt = generateRandomString(16);
|
||||||
$hash = hash('sha256', $password . $salt);
|
$hash = hash('sha256', $password . $salt);
|
||||||
$sql = $user->getSQL();
|
$sql = $user->getSQL();
|
||||||
|
|
||||||
$success = $sql->insert("User", array("name", "salt", "password"))
|
$success = $sql->insert("User", array("name", "salt", "password", "email"))
|
||||||
->addRow($username, $salt, $hash)
|
->addRow($username, $salt, $hash, $email)
|
||||||
->returning("uid")
|
->returning("uid")
|
||||||
->execute()
|
->execute()
|
||||||
&& $sql->insert("UserGroup", array("group_id", "user_id"))
|
&& $sql->insert("UserGroup", array("group_id", "user_id"))
|
||||||
@ -606,6 +610,7 @@ namespace Documents\Install {
|
|||||||
"title" => "Create a User",
|
"title" => "Create a User",
|
||||||
"form" => array(
|
"form" => array(
|
||||||
array("title" => "Username", "name" => "username", "type" => "text", "required" => true),
|
array("title" => "Username", "name" => "username", "type" => "text", "required" => true),
|
||||||
|
array("title" => "Email", "name" => "email", "type" => "text"),
|
||||||
array("title" => "Password", "name" => "password", "type" => "password", "required" => true),
|
array("title" => "Password", "name" => "password", "type" => "password", "required" => true),
|
||||||
array("title" => "Confirm Password", "name" => "confirmPassword", "type" => "password", "required" => true),
|
array("title" => "Confirm Password", "name" => "confirmPassword", "type" => "password", "required" => true),
|
||||||
),
|
),
|
||||||
@ -704,7 +709,7 @@ namespace Documents\Install {
|
|||||||
$id = $button["id"];
|
$id = $button["id"];
|
||||||
$float = $button["float"];
|
$float = $button["float"];
|
||||||
$disabled = (isset($button["disabled"]) && $button["disabled"]) ? " disabled" : "";
|
$disabled = (isset($button["disabled"]) && $button["disabled"]) ? " disabled" : "";
|
||||||
$button = "<button type=\"button\" id=\"$id\" class=\"btn btn-$type margin-xs\"$disabled>$title</button>";
|
$button = "<button type=\"button\" id=\"$id\" class=\"btn btn-$type m-1\"$disabled>$title</button>";
|
||||||
|
|
||||||
if($float === "left") {
|
if($float === "left") {
|
||||||
$buttonsLeft .= $button;
|
$buttonsLeft .= $button;
|
||||||
|
@ -7,7 +7,7 @@ class CondOr extends Condition {
|
|||||||
private array $conditions;
|
private array $conditions;
|
||||||
|
|
||||||
public function __construct(...$conditions) {
|
public function __construct(...$conditions) {
|
||||||
$this->conditions = $conditions;
|
$this->conditions = (!empty($conditions) && is_array($conditions[0])) ? $conditions[0] : $conditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getConditions() { return $this->conditions; }
|
public function getConditions() { return $this->conditions; }
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Driver\SQL\Query;
|
namespace Driver\SQL\Query;
|
||||||
|
|
||||||
|
use Driver\SQL\Condition\CondOr;
|
||||||
|
|
||||||
class Delete extends Query {
|
class Delete extends Query {
|
||||||
|
|
||||||
private string $table;
|
private string $table;
|
||||||
@ -14,7 +16,7 @@ class Delete extends Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function where(...$conditions) {
|
public function where(...$conditions) {
|
||||||
$this->conditions = array_merge($this->conditions, $conditions);
|
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,9 +7,16 @@ use Driver\SQL\SQL;
|
|||||||
abstract class Query {
|
abstract class Query {
|
||||||
|
|
||||||
protected SQL $sql;
|
protected SQL $sql;
|
||||||
|
public bool $dump;
|
||||||
|
|
||||||
public function __construct($sql) {
|
public function __construct($sql) {
|
||||||
$this->sql = $sql;
|
$this->sql = $sql;
|
||||||
|
$this->dump = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function dump() {
|
||||||
|
$this->dump = true;
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract function execute();
|
public abstract function execute();
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace Driver\SQL\Query;
|
namespace Driver\SQL\Query;
|
||||||
|
|
||||||
|
use Driver\SQL\Condition\CondOr;
|
||||||
use Driver\SQL\Join;
|
use Driver\SQL\Join;
|
||||||
|
|
||||||
class Select extends Query {
|
class Select extends Query {
|
||||||
@ -33,7 +34,7 @@ class Select extends Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function where(...$conditions) {
|
public function where(...$conditions) {
|
||||||
$this->conditions = array_merge($this->conditions, $conditions);
|
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
namespace Driver\SQL\Query;
|
namespace Driver\SQL\Query;
|
||||||
|
|
||||||
|
use Driver\SQL\Condition\CondOr;
|
||||||
|
|
||||||
class Update extends Query {
|
class Update extends Query {
|
||||||
|
|
||||||
private array $values;
|
private array $values;
|
||||||
@ -16,7 +18,7 @@ class Update extends Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function where(...$conditions) {
|
public function where(...$conditions) {
|
||||||
$this->conditions = array_merge($this->conditions, $conditions);
|
$this->conditions[] = (count($conditions) === 1 ? $conditions : new CondOr($conditions));
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,6 +144,7 @@ abstract class SQL {
|
|||||||
$returning = $this->getReturning($returningCol);
|
$returning = $this->getReturning($returningCol);
|
||||||
|
|
||||||
$query = "INSERT INTO $tableName$columnStr VALUES$values$onDuplicateKey$returning";
|
$query = "INSERT INTO $tableName$columnStr VALUES$values$onDuplicateKey$returning";
|
||||||
|
if($insert->dump) { var_dump($query); var_dump($parameters); }
|
||||||
$res = $this->execute($query, $parameters, !empty($returning));
|
$res = $this->execute($query, $parameters, !empty($returning));
|
||||||
$success = ($res !== FALSE);
|
$success = ($res !== FALSE);
|
||||||
|
|
||||||
@ -189,6 +190,7 @@ abstract class SQL {
|
|||||||
$limit = ($select->getLimit() > 0 ? (" LIMIT " . $select->getLimit()) : "");
|
$limit = ($select->getLimit() > 0 ? (" LIMIT " . $select->getLimit()) : "");
|
||||||
$offset = ($select->getOffset() > 0 ? (" OFFSET " . $select->getOffset()) : "");
|
$offset = ($select->getOffset() > 0 ? (" OFFSET " . $select->getOffset()) : "");
|
||||||
$query = "SELECT $columns FROM $tables$joinStr$where$orderBy$limit$offset";
|
$query = "SELECT $columns FROM $tables$joinStr$where$orderBy$limit$offset";
|
||||||
|
if($select->dump) { var_dump($query); var_dump($params); }
|
||||||
return $this->execute($query, $params, true);
|
return $this->execute($query, $params, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +200,7 @@ abstract class SQL {
|
|||||||
$where = $this->getWhereClause($delete->getConditions(), $params);
|
$where = $this->getWhereClause($delete->getConditions(), $params);
|
||||||
|
|
||||||
$query = "DELETE FROM $table$where";
|
$query = "DELETE FROM $table$where";
|
||||||
|
if($delete->dump) { var_dump($query); }
|
||||||
return $this->execute($query);
|
return $this->execute($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,6 +221,7 @@ abstract class SQL {
|
|||||||
|
|
||||||
$where = $this->getWhereClause($update->getConditions(), $params);
|
$where = $this->getWhereClause($update->getConditions(), $params);
|
||||||
$query = "UPDATE $table SET $valueStr$where";
|
$query = "UPDATE $table SET $valueStr$where";
|
||||||
|
if($update->dump) { var_dump($query); var_dump($params); }
|
||||||
return $this->execute($query, $params);
|
return $this->execute($query, $params);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +294,7 @@ abstract class SQL {
|
|||||||
protected abstract function execute($query, $values=NULL, $returnValues=false);
|
protected abstract function execute($query, $values=NULL, $returnValues=false);
|
||||||
|
|
||||||
protected function buildCondition($condition, &$params) {
|
protected function buildCondition($condition, &$params) {
|
||||||
|
|
||||||
if ($condition instanceof CondOr) {
|
if ($condition instanceof CondOr) {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
foreach($condition->getConditions() as $cond) {
|
foreach($condition->getConditions() as $cond) {
|
||||||
@ -304,7 +309,7 @@ abstract class SQL {
|
|||||||
} else if ($condition instanceof CondBool) {
|
} else if ($condition instanceof CondBool) {
|
||||||
return $this->columnName($condition->getValue());
|
return $this->columnName($condition->getValue());
|
||||||
} else if (is_array($condition)) {
|
} else if (is_array($condition)) {
|
||||||
if (count($condition) == 1) {
|
if (count($condition) === 1) {
|
||||||
return $this->buildCondition($condition[0], $params);
|
return $this->buildCondition($condition[0], $params);
|
||||||
} else {
|
} else {
|
||||||
$conditions = array();
|
$conditions = array();
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
use View;
|
|
||||||
|
|
||||||
abstract class Body extends View {
|
abstract class Body extends View {
|
||||||
public function __construct($document) {
|
public function __construct($document) {
|
||||||
parent::__construct($document);
|
parent::__construct($document);
|
||||||
|
@ -2,8 +2,6 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
use View;
|
|
||||||
|
|
||||||
abstract class Head extends View {
|
abstract class Head extends View {
|
||||||
|
|
||||||
protected array $sources;
|
protected array $sources;
|
||||||
@ -47,7 +45,7 @@ abstract class Head extends View {
|
|||||||
public function addKeywords($keywords) { array_merge($this->keywords, $keywords); }
|
public function addKeywords($keywords) { array_merge($this->keywords, $keywords); }
|
||||||
public function getTitle() { return $this->title; }
|
public function getTitle() { return $this->title; }
|
||||||
|
|
||||||
public function addCSS($href, $type = Link::MIME_TEXT_CSS) { $this->sources[] = new Link("stylesheet", $href, $type); }
|
public function addCSS($href, $type = Link::MIME_TEXT_CSS) { $this->sources[] = new Link(Link::STYLESHEET, $href, $type); }
|
||||||
public function addStyle($style) { $this->sources[] = new Style($style); }
|
public function addStyle($style) { $this->sources[] = new Style($style); }
|
||||||
public function addJS($url) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, $url, ""); }
|
public function addJS($url) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, $url, ""); }
|
||||||
public function addJSCode($code) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, "", $code); }
|
public function addJSCode($code) { $this->sources[] = new Script(Script::MIME_TEXT_JAVASCRIPT, "", $code); }
|
||||||
|
@ -2,29 +2,16 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
use View;
|
class Link extends StaticView {
|
||||||
|
|
||||||
class Link extends View {
|
|
||||||
|
|
||||||
const STYLESHEET = "stylesheet";
|
const STYLESHEET = "stylesheet";
|
||||||
const MIME_TEXT_CSS = "text/css";
|
const MIME_TEXT_CSS = "text/css";
|
||||||
|
|
||||||
const FONTAWESOME = '/css/fontawesome.min.css';
|
const FONTAWESOME = "/css/fontawesome.min.css";
|
||||||
// const JQUERY_UI = '/css/jquery-ui.css';
|
const BOOTSTRAP = "/css/bootstrap.min.css";
|
||||||
// const JQUERY_TERMINAL = '/css/jquery.terminal.min.css';
|
const CORE = "/css/style.css";
|
||||||
const BOOTSTRAP = '/css/bootstrap.min.css';
|
const ADMIN = "/css/admin.css";
|
||||||
// const BOOTSTRAP_THEME = '/css/bootstrap-theme.min.css';
|
const ADMINLTE = "/css/adminlte.min.css";
|
||||||
// const BOOTSTRAP_DATEPICKER_CSS = '/css/bootstrap-datepicker.standalone.min.css';
|
|
||||||
// const BOOTSTRAP_DATEPICKER3_CSS = '/css/bootstrap-datepicker.standalone.min.css';
|
|
||||||
// const HIGHLIGHT = '/css/highlight.css';
|
|
||||||
// const HIGHLIGHT_THEME = '/css/theme.css';
|
|
||||||
const CORE = "/css/style.css";
|
|
||||||
const ADMIN = "/css/admin.css";
|
|
||||||
// const HOME = "/css/home.css";
|
|
||||||
// const REVEALJS = "/css/reveal.css";
|
|
||||||
// const REVEALJS_THEME_MOON = "/css/reveal_moon.css";
|
|
||||||
// const REVEALJS_THEME_BLACK = "/css/reveal_black.css";
|
|
||||||
const ADMINLTE = "/css/adminlte.min.css";
|
|
||||||
|
|
||||||
private string $type;
|
private string $type;
|
||||||
private string $rel;
|
private string $rel;
|
||||||
|
@ -2,37 +2,16 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
class Script extends \View {
|
class Script extends StaticView {
|
||||||
|
|
||||||
const MIME_TEXT_JAVASCRIPT = "text/javascript";
|
const MIME_TEXT_JAVASCRIPT = "text/javascript";
|
||||||
|
|
||||||
const CORE = "/js/script.js";
|
const CORE = "/js/script.js";
|
||||||
// const HOME = "/js/home.js";
|
const ADMIN = "/js/admin.js";
|
||||||
const ADMIN = "/js/admin.js";
|
const JQUERY = "/js/jquery.min.js";
|
||||||
// const SORTTABLE = "/js/sorttable.js";
|
const INSTALL = "/js/install.js";
|
||||||
const JQUERY = "/js/jquery.min.js";
|
|
||||||
// const JQUERY_UI = "/js/jquery-ui.js";
|
|
||||||
// const JQUERY_MASKED_INPUT = "/js/jquery.maskedinput.min.js";
|
|
||||||
// const JQUERY_CONTEXT_MENU = "/js/jquery.contextmenu.min.js";
|
|
||||||
// const JQUERY_TERMINAL = "/js/jquery.terminal.min.js";
|
|
||||||
// const JQUERY_TERMINAL_UNIX = "/js/unix_formatting.js";
|
|
||||||
// const JSCOLOR = "/js/jscolor.min.js";
|
|
||||||
// const SYNTAX_HIGHLIGHTER = "/js/syntaxhighlighter.js";
|
|
||||||
// const HIGHLIGHT = "/js/highlight.pack.js";
|
|
||||||
// const GOOGLE_CHARTS = "/js/loader.js";
|
|
||||||
// const BOOTSTRAP = "/js/bootstrap.min.js";
|
|
||||||
// const BOOTSTRAP_DATEPICKER_JS = "/js/bootstrap-datepicker.min.js";
|
|
||||||
// const POPPER = "/js/popper.min.js";
|
|
||||||
// const JSMPEG = "/js/jsmpeg.min.js";
|
|
||||||
// const MOMENT = "/js/moment.min.js";
|
|
||||||
// const CHART = "/js/chart.js";
|
|
||||||
// const REVEALJS = "/js/reveal.js";
|
|
||||||
// const REVEALJS_PLUGIN_NOTES = "/js/reveal_notes.js";
|
|
||||||
const INSTALL = "/js/install.js";
|
|
||||||
const BOOTSTRAP = "/js/bootstrap.bundle.min.js";
|
const BOOTSTRAP = "/js/bootstrap.bundle.min.js";
|
||||||
|
const ADMINLTE = "/js/adminlte.min.js";
|
||||||
const HIGHLIGHT_JS_LOADER = "\$(document).ready(function(){\$('code').each(function(i, block) { hljs.highlightBlock(block); }); })";
|
|
||||||
const ADMINLTE = "/js/adminlte.min.js";
|
|
||||||
|
|
||||||
private string $type;
|
private string $type;
|
||||||
private string $content;
|
private string $content;
|
||||||
|
9
core/Elements/StaticView.class.php
Normal file
9
core/Elements/StaticView.class.php
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Elements;
|
||||||
|
|
||||||
|
abstract class StaticView {
|
||||||
|
|
||||||
|
public abstract function getCode();
|
||||||
|
|
||||||
|
}
|
@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
namespace Elements;
|
namespace Elements;
|
||||||
|
|
||||||
use View;
|
class Style extends StaticView {
|
||||||
|
|
||||||
class Style extends View {
|
|
||||||
|
|
||||||
private string $style;
|
private string $style;
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Elements\Document;
|
namespace Elements;
|
||||||
|
|
||||||
abstract class View {
|
abstract class View extends StaticView {
|
||||||
|
|
||||||
private Document $document;
|
private Document $document;
|
||||||
private bool $loadView;
|
private bool $loadView;
|
||||||
@ -112,4 +112,9 @@ abstract class View {
|
|||||||
$hidden = ($hidden?" hidden" : "");
|
$hidden = ($hidden?" hidden" : "");
|
||||||
return "<div class=\"alert alert-$type$hidden\" role=\"alert\"$id>$text</div>";
|
return "<div class=\"alert alert-$type$hidden\" role=\"alert\"$id>$text</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function createBadge($type, $text) {
|
||||||
|
$text = htmlspecialchars($text);
|
||||||
|
return "<span class=\"badge badge-$type\">$text</span>";
|
||||||
|
}
|
||||||
}
|
}
|
@ -9,7 +9,8 @@ use External\JWT;
|
|||||||
|
|
||||||
class Session extends ApiObject {
|
class Session extends ApiObject {
|
||||||
|
|
||||||
const DURATION = 120;
|
# in minutes
|
||||||
|
const DURATION = 60*24;
|
||||||
|
|
||||||
private ?int $sessionId;
|
private ?int $sessionId;
|
||||||
private User $user;
|
private User $user;
|
||||||
@ -91,13 +92,13 @@ class Session extends ApiObject {
|
|||||||
$this->updateMetaData();
|
$this->updateMetaData();
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
|
|
||||||
$hours = Session::DURATION;
|
$minutes = Session::DURATION;
|
||||||
$columns = array("expires", "user_id", "ipAddress", "os", "browser", "data", "stay_logged_in");
|
$columns = array("expires", "user_id", "ipAddress", "os", "browser", "data", "stay_logged_in");
|
||||||
|
|
||||||
$success = $sql
|
$success = $sql
|
||||||
->insert("Session", $columns)
|
->insert("Session", $columns)
|
||||||
->addRow(
|
->addRow(
|
||||||
(new DateTime())->modify("+$hours hour"),
|
(new DateTime())->modify("+$minutes minute"),
|
||||||
$this->user->getId(),
|
$this->user->getId(),
|
||||||
$this->ipAddress,
|
$this->ipAddress,
|
||||||
$this->os,
|
$this->os,
|
||||||
@ -125,11 +126,11 @@ class Session extends ApiObject {
|
|||||||
|
|
||||||
public function update() {
|
public function update() {
|
||||||
$this->updateMetaData();
|
$this->updateMetaData();
|
||||||
$hours = Session::DURATION;
|
$minutes = Session::DURATION;
|
||||||
|
|
||||||
$sql = $this->user->getSQL();
|
$sql = $this->user->getSQL();
|
||||||
return $sql->update("Session")
|
return $sql->update("Session")
|
||||||
->set("Session.expires", (new DateTime())->modify("+$hours hour"))
|
->set("Session.expires", (new DateTime())->modify("+$minutes minute"))
|
||||||
->set("Session.ipAddress", $this->ipAddress)
|
->set("Session.ipAddress", $this->ipAddress)
|
||||||
->set("Session.os", $this->os)
|
->set("Session.os", $this->os)
|
||||||
->set("Session.browser", $this->browser)
|
->set("Session.browser", $this->browser)
|
||||||
|
@ -1,19 +1,24 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace Views;
|
namespace Views\Admin;
|
||||||
|
|
||||||
// Source: https://adminlte.io/themes/v3/
|
// Source: https://adminlte.io/themes/v3/
|
||||||
|
|
||||||
|
use Documents\Document404\Body404;
|
||||||
use Elements\Body;
|
use Elements\Body;
|
||||||
use Elements\Script;
|
use Elements\Script;
|
||||||
|
use Elements\View;
|
||||||
|
use Views\View404;
|
||||||
|
|
||||||
class AdminDashboard extends Body {
|
class AdminDashboardBody extends Body {
|
||||||
|
|
||||||
private array $errorMessages;
|
private array $errorMessages;
|
||||||
|
private array $notifications;
|
||||||
|
|
||||||
public function __construct($document) {
|
public function __construct($document) {
|
||||||
parent::__construct($document);
|
parent::__construct($document);
|
||||||
$this->errorMessages = array();
|
$this->errorMessages = array();
|
||||||
|
$this->notifications = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getNotifications() : array {
|
private function getNotifications() : array {
|
||||||
@ -49,8 +54,7 @@ class AdminDashboard extends Body {
|
|||||||
$iconMail = $this->createIcon("envelope", "fas");
|
$iconMail = $this->createIcon("envelope", "fas");
|
||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
$notifications = $this->getNotifications();
|
$numNotifications = count($this->notifications);
|
||||||
$numNotifications = count($notifications);
|
|
||||||
if ($numNotifications === 0) {
|
if ($numNotifications === 0) {
|
||||||
$notificationText = L("No new notifications");
|
$notificationText = L("No new notifications");
|
||||||
} else if($numNotifications === 1) {
|
} else if($numNotifications === 1) {
|
||||||
@ -98,7 +102,7 @@ class AdminDashboard extends Body {
|
|||||||
|
|
||||||
// Notifications
|
// Notifications
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach($notifications as $notification) {
|
foreach($this->notifications as $notification) {
|
||||||
|
|
||||||
$title = $notification["title"];
|
$title = $notification["title"];
|
||||||
$notificationId = $notification["uid"];
|
$notificationId = $notification["uid"];
|
||||||
@ -150,12 +154,17 @@ class AdminDashboard extends Body {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$notificationCount = count($this->notifications);
|
||||||
|
if ($notificationCount > 0) {
|
||||||
|
$menuEntries["dashboard"]["badge"] = array("type" => "warning", "value" => $notificationCount);
|
||||||
|
}
|
||||||
|
|
||||||
$currentView = $_GET["view"] ?? "dashboard";
|
$currentView = $_GET["view"] ?? "dashboard";
|
||||||
|
|
||||||
$html =
|
$html =
|
||||||
"<aside class=\"main-sidebar sidebar-dark-primary elevation-4\">
|
"<aside class=\"main-sidebar sidebar-dark-primary elevation-4\">
|
||||||
<!-- Brand Logo -->
|
<!-- Brand Logo -->
|
||||||
<a href=\"index3.html\" class=\"brand-link\">
|
<a href=\"/admin\" class=\"brand-link\">
|
||||||
<img src=\"/img/web_base_logo.png\" alt=\"WebBase Logo\" class=\"brand-image img-circle elevation-3\"
|
<img src=\"/img/web_base_logo.png\" alt=\"WebBase Logo\" class=\"brand-image img-circle elevation-3\"
|
||||||
style=\"opacity: .8\">
|
style=\"opacity: .8\">
|
||||||
<span class=\"brand-text font-weight-light\">WebBase</span>
|
<span class=\"brand-text font-weight-light\">WebBase</span>
|
||||||
@ -172,17 +181,23 @@ class AdminDashboard extends Body {
|
|||||||
$name = L($menuEntry["name"]);
|
$name = L($menuEntry["name"]);
|
||||||
$icon = $this->createIcon($menuEntry["icon"], "fas", "nav-icon");
|
$icon = $this->createIcon($menuEntry["icon"], "fas", "nav-icon");
|
||||||
$active = ($currentView === $view) ? " active" : "";
|
$active = ($currentView === $view) ? " active" : "";
|
||||||
|
$badge = $menuEntry["badge"] ?? "";
|
||||||
|
if($badge) {
|
||||||
|
$badgeType = $badge["type"];
|
||||||
|
$badgeValue = $badge["value"];
|
||||||
|
$badge = "<span class=\"badge badge-$badgeType right\">$badgeValue</span>";
|
||||||
|
}
|
||||||
|
|
||||||
$html .=
|
$html .=
|
||||||
"<li class=\"nav-item\">
|
"<li class=\"nav-item\">
|
||||||
<a href=\"?view=$view\" class=\"nav-link$active\">
|
<a href=\"?view=$view\" class=\"nav-link$active\">
|
||||||
$icon
|
$icon<p>$name$badge</p>
|
||||||
<p>$name </p>
|
|
||||||
</a>
|
</a>
|
||||||
</li>";
|
</li>";
|
||||||
}
|
}
|
||||||
|
|
||||||
$html .=
|
$html .=
|
||||||
"</ul>
|
"</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
</aside>";
|
</aside>";
|
||||||
@ -190,32 +205,64 @@ class AdminDashboard extends Body {
|
|||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getView() {
|
||||||
|
|
||||||
|
$views = array(
|
||||||
|
"dashboard" => Dashboard::class,
|
||||||
|
"users" => UserOverview::class,
|
||||||
|
"404" => View404::class,
|
||||||
|
);
|
||||||
|
|
||||||
|
$currentView = $_GET["view"] ?? "dashboard";
|
||||||
|
if (!isset($views[$currentView])) {
|
||||||
|
$currentView = "404";
|
||||||
|
}
|
||||||
|
|
||||||
|
$view = new $views[$currentView]($this->getDocument());
|
||||||
|
assert($view instanceof View);
|
||||||
|
$code = $view->getCode();
|
||||||
|
|
||||||
|
if ($view instanceof AdminView) {
|
||||||
|
$this->errorMessages = array_merge($this->errorMessages, $view->getErrorMessages());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadView() {
|
||||||
|
parent::loadView();
|
||||||
|
|
||||||
|
$head = $this->getDocument()->getHead();
|
||||||
|
$head->addJS(Script::BOOTSTRAP);
|
||||||
|
$head->loadAdminlte();
|
||||||
|
|
||||||
|
$this->notifications = $this->getNotifications();
|
||||||
|
}
|
||||||
|
|
||||||
private function getContent() {
|
private function getContent() {
|
||||||
|
|
||||||
$this->getUsers();
|
$this->getUsers();
|
||||||
|
|
||||||
|
$view = $this->getView();
|
||||||
$html = "<div class=\"content-wrapper p-2\">";
|
$html = "<div class=\"content-wrapper p-2\">";
|
||||||
|
|
||||||
foreach($this->errorMessages as $errorMessage) {
|
foreach($this->errorMessages as $errorMessage) {
|
||||||
$html .= $this->createErrorText($errorMessage);
|
$html .= $this->createErrorText($errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$html .= $view;
|
||||||
$html .= "</div>";
|
$html .= "</div>";
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCode() {
|
public function getCode() {
|
||||||
|
$html = parent::getCode();
|
||||||
$head = $this->getDocument()->getHead();
|
|
||||||
$head->addJS(Script::BOOTSTRAP);
|
|
||||||
$head->loadAdminlte();
|
|
||||||
|
|
||||||
$header = $this->getHeader();
|
$header = $this->getHeader();
|
||||||
$sidebar = $this->getSidebar();
|
$sidebar = $this->getSidebar();
|
||||||
$content = $this->getContent();
|
$content = $this->getContent();
|
||||||
|
|
||||||
$html =
|
$html .=
|
||||||
"<!-- LICENSE: /docs/LICENSE_ADMINLTE -->
|
"<!-- LICENSE: /docs/LICENSE_ADMINLTE -->
|
||||||
<body class=\"hold-transition sidebar-mini layout-fixed\">
|
<body class=\"hold-transition sidebar-mini layout-fixed\">
|
||||||
<div class=\"wrapper\">
|
<div class=\"wrapper\">
|
46
core/Views/Admin/AdminView.class.php
Normal file
46
core/Views/Admin/AdminView.class.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Views\Admin;
|
||||||
|
|
||||||
|
use Elements\Document;
|
||||||
|
use Elements\View;
|
||||||
|
|
||||||
|
class AdminView extends View {
|
||||||
|
|
||||||
|
protected array $errorMessages;
|
||||||
|
|
||||||
|
public function __construct(Document $document) {
|
||||||
|
parent::__construct($document);
|
||||||
|
$this->errorMessages = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getErrorMessages() {
|
||||||
|
return $this->errorMessages;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode() {
|
||||||
|
$html = parent::getCode();
|
||||||
|
|
||||||
|
$home = L("Home");
|
||||||
|
|
||||||
|
$html .=
|
||||||
|
"<div class=\"content-header\">
|
||||||
|
<div class=\"container-fluid\">
|
||||||
|
<div class=\"row mb-2\">
|
||||||
|
<div class=\"col-sm-6\">
|
||||||
|
<h1 class=\"m-0 text-dark\">$this->title</h1>
|
||||||
|
</div>
|
||||||
|
<div class=\"col-sm-6\">
|
||||||
|
<ol class=\"breadcrumb float-sm-right\">
|
||||||
|
<li class=\"breadcrumb-item\"><a href=\"/\">$home</a></li>
|
||||||
|
<li class=\"breadcrumb-item active\">$this->title</li>
|
||||||
|
</ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div><!-- /.container-fluid -->
|
||||||
|
</div>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
25
core/Views/Admin/Dashboard.class.php
Normal file
25
core/Views/Admin/Dashboard.class.php
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Views\Admin;
|
||||||
|
|
||||||
|
use Elements\Document;
|
||||||
|
|
||||||
|
class Dashboard extends AdminView {
|
||||||
|
|
||||||
|
public function __construct(Document $document) {
|
||||||
|
parent::__construct($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadView() {
|
||||||
|
parent::loadView();
|
||||||
|
$this->title = L("Dashboard");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode() {
|
||||||
|
$html = parent::getCode();
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
164
core/Views/Admin/UserOverview.class.php
Normal file
164
core/Views/Admin/UserOverview.class.php
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
namespace Views\Admin;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use Elements\Document;
|
||||||
|
|
||||||
|
class UserOverview extends AdminView {
|
||||||
|
|
||||||
|
private array $users;
|
||||||
|
private int $page;
|
||||||
|
private int $pageCount;
|
||||||
|
|
||||||
|
public function __construct(Document $document) {
|
||||||
|
parent::__construct($document);
|
||||||
|
$this->users = array();
|
||||||
|
$this->pageCount = 0;
|
||||||
|
$this->page = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadView() {
|
||||||
|
parent::loadView();
|
||||||
|
$this->title = L("User Control");
|
||||||
|
$this->requestUsers();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function requestUsers() {
|
||||||
|
|
||||||
|
if(isset($_GET["page"]) && is_numeric($_GET["page"])) {
|
||||||
|
$this->page = intval($_GET["page"]);
|
||||||
|
} else {
|
||||||
|
$this->page = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$req = new \Api\User\Fetch($this->getDocument()->getUser());
|
||||||
|
if (!$req->execute(array("page" => $this->page))) {
|
||||||
|
$this->errorMessages[] = $req->getLastError();
|
||||||
|
} else {
|
||||||
|
$result = $req->getResult();
|
||||||
|
$this->users = $result["users"];
|
||||||
|
$this->pageCount = $result["pages"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getGroups($groups) {
|
||||||
|
$badges = [];
|
||||||
|
|
||||||
|
foreach($groups as $groupId => $group) {
|
||||||
|
$badgeClass = "secondary";
|
||||||
|
if ($groupId === USER_GROUP_ADMIN) {
|
||||||
|
$badgeClass = "danger";
|
||||||
|
}
|
||||||
|
|
||||||
|
$badges[] = $this->createBadge($badgeClass, $group);
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(" ", $badges);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getPagination() {
|
||||||
|
|
||||||
|
$userPageNavigation = L("User page navigation");
|
||||||
|
$previousDisabled = ($this->page == 1 ? " disabled" : "");
|
||||||
|
$nextDisabled = ($this->page >= $this->pageCount ? " disabled" : "");
|
||||||
|
|
||||||
|
$html =
|
||||||
|
"<nav aria-label=\"$userPageNavigation\" id=\"userPageNavigation\">
|
||||||
|
<ul class=\"pagination p-2 m-0 justify-content-end\">
|
||||||
|
<li class=\"page-item$previousDisabled\"><a class=\"page-link\" href=\"#\">Previous</a></li>";
|
||||||
|
|
||||||
|
for($i = 1; $i <= $this->pageCount; $i++) {
|
||||||
|
$active = $i === $this->page ? " active" : "";
|
||||||
|
$html .=
|
||||||
|
"<li class=\"page-item$active\"><a class=\"page-link\" href=\"#\">$i</a></li>";
|
||||||
|
}
|
||||||
|
|
||||||
|
$html .=
|
||||||
|
"<li class=\"page-item$nextDisabled\"><a class=\"page-link\" href=\"#\">Next</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getUserRows() {
|
||||||
|
|
||||||
|
$dateFormat = L("Y/m/d");
|
||||||
|
$userRows = array();
|
||||||
|
|
||||||
|
foreach($this->users as $uid => $user) {
|
||||||
|
$name = $user["name"];
|
||||||
|
$email = $user["email"] ?? "";
|
||||||
|
$registeredAt = (new DateTime($user["created_at"]))->format($dateFormat);
|
||||||
|
$groups = $this->getGroups($user["groups"]);
|
||||||
|
|
||||||
|
$userRows[] =
|
||||||
|
"<tr data-id=\"$uid\">
|
||||||
|
<td>$name</td>
|
||||||
|
<td>$email</td>
|
||||||
|
<td>$groups</td>
|
||||||
|
<td>$registeredAt</td>
|
||||||
|
</tr>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode("", $userRows);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode() {
|
||||||
|
$html = parent::getCode();
|
||||||
|
|
||||||
|
// Icons
|
||||||
|
$iconRefresh = $this->createIcon("sync");
|
||||||
|
|
||||||
|
// Locale
|
||||||
|
$users = L("Users");
|
||||||
|
$name = L("Name");
|
||||||
|
$email = L("Email");
|
||||||
|
$groups = L("Groups");
|
||||||
|
$registeredAt = L("Registered At");
|
||||||
|
|
||||||
|
// Content
|
||||||
|
$pagination = $this->getPagination();
|
||||||
|
$userRows = $this->getUserRows();
|
||||||
|
|
||||||
|
$html .=
|
||||||
|
"<div class=\"content\">
|
||||||
|
<div class=\"container-fluid\">
|
||||||
|
<div class=\"row\">
|
||||||
|
<div class=\"col-lg-12\">
|
||||||
|
<div class=\"card\">
|
||||||
|
<div class=\"card-header border-0\">
|
||||||
|
<h3 class=\"card-title\">$users</h3>
|
||||||
|
<div class=\"card-tools\">
|
||||||
|
<a href=\"#\" class=\"btn btn-tool btn-sm\" id=\"userTableRefresh\">
|
||||||
|
$iconRefresh
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class=\"card-body table-responsive p-0\">
|
||||||
|
<table class=\"table table-striped table-valign-middle\" id=\"userTable\">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>$name</th>
|
||||||
|
<th>$email</th>
|
||||||
|
<th>$groups</th>
|
||||||
|
<th>$registeredAt</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
$userRows
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
$pagination
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>";
|
||||||
|
|
||||||
|
return $html;
|
||||||
|
}
|
||||||
|
}
|
@ -3,52 +3,58 @@
|
|||||||
namespace Views;
|
namespace Views;
|
||||||
|
|
||||||
use Api\GetLanguages;
|
use Api\GetLanguages;
|
||||||
|
use Elements\View;
|
||||||
|
|
||||||
class LanguageFlags extends \View {
|
class LanguageFlags extends View {
|
||||||
|
|
||||||
|
private array $languageFlags;
|
||||||
|
|
||||||
public function __construct($document) {
|
public function __construct($document) {
|
||||||
parent::__construct($document);
|
parent::__construct($document);
|
||||||
|
$this->languageFlags = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCode() {
|
public function loadView() {
|
||||||
|
parent::loadView();
|
||||||
|
|
||||||
$requestUri = $_SERVER["REQUEST_URI"];
|
|
||||||
$queryString = $_SERVER['QUERY_STRING'];
|
|
||||||
|
|
||||||
$flags = array();
|
|
||||||
$request = new GetLanguages($this->getDocument()->getUser());
|
$request = new 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()) {
|
if($request->execute()) {
|
||||||
|
|
||||||
|
$requestUri = $_SERVER["REQUEST_URI"];
|
||||||
|
$queryString = $_SERVER['QUERY_STRING'];
|
||||||
|
|
||||||
|
$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) . "?";
|
||||||
|
|
||||||
foreach($request->getResult()['languages'] as $lang) {
|
foreach($request->getResult()['languages'] as $lang) {
|
||||||
$langCode = $lang['code'];
|
$langCode = $lang['code'];
|
||||||
$langName = $lang['name'];
|
$langName = $lang['name'];
|
||||||
$query['lang'] = $langCode;
|
$query['lang'] = $langCode;
|
||||||
$queryString = http_build_query($query);
|
$queryString = http_build_query($query);
|
||||||
|
|
||||||
$flags[] = $this->createLink(
|
$this->languageFlags[] = $this->createLink(
|
||||||
"$url$queryString",
|
"$url$queryString",
|
||||||
"<img class=\"p-1\" src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
|
"<img class=\"p-1\" src=\"/img/icons/lang/$langCode.gif\" alt=\"$langName\" title=\"$langName\">"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
$flags[] = $this->createErrorText($request->getLastError());
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return implode('', $flags);
|
public function getCode() {
|
||||||
|
return implode('', $this->languageFlags);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -10,9 +10,13 @@ class LoginBody extends Body {
|
|||||||
parent::__construct($document);
|
parent::__construct($document);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getCode() {
|
public function loadView() {
|
||||||
|
parent::loadView();
|
||||||
$this->getDocument()->getHead()->loadBootstrap();
|
$this->getDocument()->getHead()->loadBootstrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode() {
|
||||||
|
$html = parent::getCode();
|
||||||
|
|
||||||
$username = L("Username");
|
$username = L("Username");
|
||||||
$password = L("Password");
|
$password = L("Password");
|
||||||
@ -25,7 +29,7 @@ class LoginBody extends Body {
|
|||||||
$domain = $_SERVER['HTTP_HOST'];
|
$domain = $_SERVER['HTTP_HOST'];
|
||||||
$protocol = getProtocol();
|
$protocol = getProtocol();
|
||||||
|
|
||||||
$html = "<body>";
|
$html .= "<body>";
|
||||||
|
|
||||||
$accountCreated = "";
|
$accountCreated = "";
|
||||||
if(isset($_GET["accountCreated"])) {
|
if(isset($_GET["accountCreated"])) {
|
||||||
|
13
core/Views/View404.class.php
Normal file
13
core/Views/View404.class.php
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Views;
|
||||||
|
|
||||||
|
use Elements\View;
|
||||||
|
|
||||||
|
class View404 extends View {
|
||||||
|
|
||||||
|
public function getCode() {
|
||||||
|
return parent::getCode() . "<b>Not found</b>";
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
26
js/admin.js
26
js/admin.js
@ -1,4 +1,6 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
|
// Login
|
||||||
$("#username").keypress(function(e) { if(e.which == 13) $("#password").focus(); });
|
$("#username").keypress(function(e) { if(e.which == 13) $("#password").focus(); });
|
||||||
$("#password").keypress(function(e) { if(e.which == 13) $("#btnLogin").click(); });
|
$("#password").keypress(function(e) { if(e.which == 13) $("#btnLogin").click(); });
|
||||||
$("#btnLogin").click(function() {
|
$("#btnLogin").click(function() {
|
||||||
@ -24,8 +26,26 @@ $(document).ready(function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#toggleSidebar").click(function() {
|
$("#userTableRefresh").click(function() {
|
||||||
$(".main-wrapper").toggleClass("sidebar-collapsed");
|
let tbody = $("#userTable > tbody");
|
||||||
$(".main-sidebar").toggleClass("collapsed");
|
let page = parseInt($("#userPageNavigation li.active > a").text().trim());
|
||||||
|
tbody.find("tr").remove();
|
||||||
|
tbody.append("<tr><td colspan=\"4\" class=\"text-center\">Loading… " + createLoadingIcon() + "</td></tr>");
|
||||||
|
|
||||||
|
jsCore.apiCall("/user/fetch", { page: page}, function (data) {
|
||||||
|
let pageCount = data["pages"];
|
||||||
|
let users = data["users"];
|
||||||
|
let userRows = [];
|
||||||
|
|
||||||
|
// TODO: .. maybe use ts instead of plain js?
|
||||||
|
for(let userId in users) {
|
||||||
|
let user = users[userId];
|
||||||
|
userRows.push("<tr><td>" + user.name + "</td><td>" + user.email + "</td><td></td><td></td></tr>");
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.html(userRows.join(""));
|
||||||
|
}, function (err) {
|
||||||
|
alert(err);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -274,3 +274,7 @@ let jsCore = new Core();
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function createLoadingIcon() {
|
||||||
|
return '<i class="fas fa-spin fa-spinner"></i>';
|
||||||
|
}
|
@ -10,7 +10,7 @@ class ApiTestCase(PhpTest):
|
|||||||
# ApiKeys
|
# ApiKeys
|
||||||
"Testing get api keys empty…": self.test_get_api_keys_empty,
|
"Testing get api keys empty…": self.test_get_api_keys_empty,
|
||||||
"Testing create api key…": self.test_create_api_key,
|
"Testing create api key…": self.test_create_api_key,
|
||||||
"Testing referesh api key…": self.test_refresh_api_key,
|
"Testing refresh api key…": self.test_refresh_api_key,
|
||||||
"Testing revoke api key…": self.test_revoke_api_key,
|
"Testing revoke api key…": self.test_revoke_api_key,
|
||||||
|
|
||||||
# Notifications
|
# Notifications
|
||||||
|
@ -11,6 +11,7 @@ class InstallTestCase(PhpTest):
|
|||||||
"Testing invalid usernames…": self.test_invalid_usernames,
|
"Testing invalid usernames…": self.test_invalid_usernames,
|
||||||
"Testing invalid password…": self.test_invalid_password,
|
"Testing invalid password…": self.test_invalid_password,
|
||||||
"Testing not matching password…": self.test_not_matching_passwords,
|
"Testing not matching password…": self.test_not_matching_passwords,
|
||||||
|
"Testing invalid email…": self.test_invalid_email,
|
||||||
"Testing user creation…": self.test_create_user,
|
"Testing user creation…": self.test_create_user,
|
||||||
"Testing skip mail configuration…": self.test_skil_mail_config,
|
"Testing skip mail configuration…": self.test_skil_mail_config,
|
||||||
"Testing complete setup…": self.test_complete_setup,
|
"Testing complete setup…": self.test_complete_setup,
|
||||||
@ -40,8 +41,13 @@ class InstallTestCase(PhpTest):
|
|||||||
self.assertEquals(False, obj["success"])
|
self.assertEquals(False, obj["success"])
|
||||||
self.assertEquals("The given passwords do not match", obj["msg"])
|
self.assertEquals("The given passwords do not match", obj["msg"])
|
||||||
|
|
||||||
|
def test_invalid_email(self):
|
||||||
|
obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD, "confirmPassword": PhpTest.ADMIN_PASSWORD, "email": "123abc" })
|
||||||
|
self.assertEquals(False, obj["success"])
|
||||||
|
self.assertEquals("Invalid email address", obj["msg"])
|
||||||
|
|
||||||
def test_create_user(self):
|
def test_create_user(self):
|
||||||
obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD, "confirmPassword": PhpTest.ADMIN_PASSWORD })
|
obj = self.httpPost(data={ "username": PhpTest.ADMIN_USERNAME, "password": PhpTest.ADMIN_PASSWORD, "confirmPassword": PhpTest.ADMIN_PASSWORD, "email": "test@test.com" })
|
||||||
self.assertEquals(True, obj["success"], obj["msg"])
|
self.assertEquals(True, obj["success"], obj["msg"])
|
||||||
|
|
||||||
def test_skil_mail_config(self):
|
def test_skil_mail_config(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user