Property Visibilities
This commit is contained in:
parent
99bfd7e505
commit
d115d8b970
@ -213,8 +213,8 @@ abstract class Request {
|
|||||||
// CSRF Token
|
// CSRF Token
|
||||||
if ($this->csrfTokenRequired && $session) {
|
if ($this->csrfTokenRequired && $session) {
|
||||||
// csrf token required + external call
|
// csrf token required + external call
|
||||||
// if it's not a call with API_KEY, check for csrf_token
|
// if it's not a call with API_KEY, check for csrfToken
|
||||||
$csrfToken = $values["csrf_token"] ?? $_SERVER["HTTP_XSRF_TOKEN"] ?? null;
|
$csrfToken = $values["csrfToken"] ?? $_SERVER["HTTP_XSRF_TOKEN"] ?? null;
|
||||||
if (!$csrfToken || strcmp($csrfToken, $session->getCsrfToken()) !== 0) {
|
if (!$csrfToken || strcmp($csrfToken, $session->getCsrfToken()) !== 0) {
|
||||||
$this->lastError = "CSRF-Token mismatch";
|
$this->lastError = "CSRF-Token mismatch";
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
|
@ -85,7 +85,7 @@ trait Pagination {
|
|||||||
if ($orderBy) {
|
if ($orderBy) {
|
||||||
$handler = $baseQuery->getHandler();
|
$handler = $baseQuery->getHandler();
|
||||||
$baseTable = $handler->getTableName();
|
$baseTable = $handler->getTableName();
|
||||||
$sortColumn = DatabaseEntityHandler::getColumnName($orderBy);
|
$sortColumn = DatabaseEntityHandler::buildColumnName($orderBy);
|
||||||
$fullyQualifiedColumn = "$baseTable.$sortColumn";
|
$fullyQualifiedColumn = "$baseTable.$sortColumn";
|
||||||
$selectedColumns = $baseQuery->getSelectValues();
|
$selectedColumns = $baseQuery->getSelectValues();
|
||||||
|
|
||||||
|
@ -225,7 +225,6 @@ namespace Core\API\User {
|
|||||||
$currentUser->hasGroup(Group::SUPPORT));
|
$currentUser->hasGroup(Group::SUPPORT));
|
||||||
|
|
||||||
$orderBy = $this->getParam("orderBy");
|
$orderBy = $this->getParam("orderBy");
|
||||||
$publicAttributes = ["id", "name", "fullName", "profilePicture", "email"]; // TODO: , "groupNames"];
|
|
||||||
|
|
||||||
$condition = null;
|
$condition = null;
|
||||||
if (!$fullInfo) {
|
if (!$fullInfo) {
|
||||||
@ -234,7 +233,7 @@ namespace Core\API\User {
|
|||||||
new CondBool("User.confirmed")
|
new CondBool("User.confirmed")
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($orderBy && !in_array($orderBy, $publicAttributes)) {
|
if ($orderBy && !$currentUser->canAccess(User::class, $orderBy)) {
|
||||||
return $this->createError("Insufficient permissions for sorting by field '$orderBy'");
|
return $this->createError("Insufficient permissions for sorting by field '$orderBy'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -255,19 +254,8 @@ namespace Core\API\User {
|
|||||||
$users = User::findBy($userQuery);
|
$users = User::findBy($userQuery);
|
||||||
if ($users !== false && $users !== null) {
|
if ($users !== false && $users !== null) {
|
||||||
$this->result["users"] = [];
|
$this->result["users"] = [];
|
||||||
|
foreach ($users as $user) {
|
||||||
foreach ($users as $userId => $user) {
|
$this->result["users"][] = $user->jsonSerialize();
|
||||||
$serialized = $user->jsonSerialize();
|
|
||||||
|
|
||||||
if (!$fullInfo && $userId !== $currentUser->getId()) {
|
|
||||||
foreach (array_keys($serialized) as $attr) {
|
|
||||||
if (!in_array($attr, $publicAttributes)) {
|
|
||||||
unset ($serialized[$attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->result["users"][] = $serialized;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return $this->createError("Error fetching users: " . $sql->getLastError());
|
return $this->createError("Error fetching users: " . $sql->getLastError());
|
||||||
@ -305,20 +293,10 @@ namespace Core\API\User {
|
|||||||
$currentUser->hasGroup(Group::ADMIN) ||
|
$currentUser->hasGroup(Group::ADMIN) ||
|
||||||
$currentUser->hasGroup(Group::SUPPORT));
|
$currentUser->hasGroup(Group::SUPPORT));
|
||||||
|
|
||||||
if (!$fullInfo) {
|
if (!$fullInfo && !$queriedUser["confirmed"]) {
|
||||||
if (!$queriedUser["confirmed"]) {
|
|
||||||
return $this->createError("No permissions to access this user");
|
return $this->createError("No permissions to access this user");
|
||||||
}
|
}
|
||||||
|
|
||||||
$publicAttributes = ["id", "name", "fullName", "profilePicture", "email", "groups"];
|
|
||||||
foreach (array_keys($queriedUser) as $attr) {
|
|
||||||
if (!in_array($attr, $publicAttributes)) {
|
|
||||||
unset($queriedUser[$attr]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($queriedUser["session"]); // strip session information
|
|
||||||
$this->result["user"] = $queriedUser;
|
$this->result["user"] = $queriedUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -358,6 +336,7 @@ namespace Core\API\User {
|
|||||||
|
|
||||||
$this->result["permissions"] = $permissions;
|
$this->result["permissions"] = $permissions;
|
||||||
$this->result["user"] = $currentUser->jsonSerialize();
|
$this->result["user"] = $currentUser->jsonSerialize();
|
||||||
|
$this->result["session"] = $this->context->getSession()->jsonSerialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->success;
|
return $this->success;
|
||||||
@ -575,7 +554,7 @@ namespace Core\API\User {
|
|||||||
$tfaToken = $user->getTwoFactorToken();
|
$tfaToken = $user->getTwoFactorToken();
|
||||||
$this->result["loggedIn"] = true;
|
$this->result["loggedIn"] = true;
|
||||||
$this->result["logoutIn"] = $session->getExpiresSeconds();
|
$this->result["logoutIn"] = $session->getExpiresSeconds();
|
||||||
$this->result["csrf_token"] = $session->getCsrfToken();
|
$this->result["csrfToken"] = $session->getCsrfToken();
|
||||||
if ($tfaToken && $tfaToken->isConfirmed()) {
|
if ($tfaToken && $tfaToken->isConfirmed()) {
|
||||||
$this->result["2fa"] = ["type" => $tfaToken->getType()];
|
$this->result["2fa"] = ["type" => $tfaToken->getType()];
|
||||||
if ($tfaToken instanceof KeyBasedTwoFactorToken) {
|
if ($tfaToken instanceof KeyBasedTwoFactorToken) {
|
||||||
|
@ -4,8 +4,8 @@ namespace Core\Objects;
|
|||||||
|
|
||||||
abstract class ApiObject implements \JsonSerializable {
|
abstract class ApiObject implements \JsonSerializable {
|
||||||
|
|
||||||
public abstract function jsonSerialize(): array;
|
public function __toString() {
|
||||||
|
return json_encode($this->jsonSerialize());
|
||||||
public function __toString() { return json_encode($this->jsonSerialize()); }
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ use Core\Objects\Router\Router;
|
|||||||
|
|
||||||
class Context {
|
class Context {
|
||||||
|
|
||||||
|
private static Context $instance;
|
||||||
|
|
||||||
private ?SQL $sql;
|
private ?SQL $sql;
|
||||||
private ?Session $session;
|
private ?Session $session;
|
||||||
private ?User $user;
|
private ?User $user;
|
||||||
@ -24,7 +26,7 @@ class Context {
|
|||||||
private Language $language;
|
private Language $language;
|
||||||
public ?Router $router;
|
public ?Router $router;
|
||||||
|
|
||||||
public function __construct() {
|
private function __construct() {
|
||||||
|
|
||||||
$this->sql = null;
|
$this->sql = null;
|
||||||
$this->session = null;
|
$this->session = null;
|
||||||
@ -38,6 +40,13 @@ class Context {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function instance(): self {
|
||||||
|
if (!isset(self::$instance)) {
|
||||||
|
self::$instance = new Context();
|
||||||
|
}
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
public function __destruct() {
|
public function __destruct() {
|
||||||
if ($this->sql && $this->sql->isConnected()) {
|
if ($this->sql && $this->sql->isConnected()) {
|
||||||
$this->sql->close();
|
$this->sql->close();
|
||||||
@ -93,12 +102,9 @@ class Context {
|
|||||||
private function loadSession(int $userId, int $sessionId): void {
|
private function loadSession(int $userId, int $sessionId): void {
|
||||||
$this->session = Session::init($this, $userId, $sessionId);
|
$this->session = Session::init($this, $userId, $sessionId);
|
||||||
$this->user = $this->session?->getUser();
|
$this->user = $this->session?->getUser();
|
||||||
if ($this->user) {
|
|
||||||
$this->user->session = $this->session;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function parseCookies() {
|
public function parseCookies(): void {
|
||||||
if (isset($_COOKIE['session']) && is_string($_COOKIE['session']) && !empty($_COOKIE['session'])) {
|
if (isset($_COOKIE['session']) && is_string($_COOKIE['session']) && !empty($_COOKIE['session'])) {
|
||||||
try {
|
try {
|
||||||
$token = $_COOKIE['session'];
|
$token = $_COOKIE['session'];
|
||||||
@ -194,7 +200,6 @@ class Context {
|
|||||||
$this->session = new Session($this, $this->user);
|
$this->session = new Session($this, $this->user);
|
||||||
$this->session->stayLoggedIn = $stayLoggedIn;
|
$this->session->stayLoggedIn = $stayLoggedIn;
|
||||||
if ($this->session->update()) {
|
if ($this->session->update()) {
|
||||||
$user->session = $this->session;
|
|
||||||
return $this->session;
|
return $this->session;
|
||||||
} else {
|
} else {
|
||||||
$this->user = null;
|
$this->user = null;
|
||||||
|
@ -18,15 +18,6 @@ class ApiKey extends DatabaseEntity {
|
|||||||
$this->active = true;
|
$this->active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"active" => $this->active,
|
|
||||||
"apiKey" => $this->apiKey,
|
|
||||||
"validUntil" => $this->validUntil->getTimestamp()
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getValidUntil(): \DateTime {
|
public function getValidUntil(): \DateTime {
|
||||||
return $this->validUntil;
|
return $this->validUntil;
|
||||||
}
|
}
|
||||||
|
27
Core/Objects/DatabaseEntity/Attribute/Visibility.class.php
Normal file
27
Core/Objects/DatabaseEntity/Attribute/Visibility.class.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Core\Objects\DatabaseEntity\Attribute;
|
||||||
|
|
||||||
|
#[\Attribute(\Attribute::TARGET_PROPERTY)] class Visibility {
|
||||||
|
|
||||||
|
// Visibility enum
|
||||||
|
const NONE = 0;
|
||||||
|
const BY_GROUP = 1;
|
||||||
|
const ALL = 2;
|
||||||
|
|
||||||
|
private int $visibility;
|
||||||
|
private array $groups;
|
||||||
|
|
||||||
|
public function __construct(int $visibility, int ...$groups) {
|
||||||
|
$this->visibility = $visibility;
|
||||||
|
$this->groups = $groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType(): int {
|
||||||
|
return $this->visibility;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getGroups(): array {
|
||||||
|
return $this->groups;
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,9 @@ use ArrayAccess;
|
|||||||
use Core\Driver\SQL\Condition\Condition;
|
use Core\Driver\SQL\Condition\Condition;
|
||||||
use Core\Driver\SQL\Expression\Count;
|
use Core\Driver\SQL\Expression\Count;
|
||||||
use Core\Driver\SQL\SQL;
|
use Core\Driver\SQL\SQL;
|
||||||
|
use Core\Objects\Context;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
use Core\Objects\DatabaseEntity\Attribute\Transient;
|
||||||
|
use Core\Objects\DatabaseEntity\Attribute\Visibility;
|
||||||
use JsonSerializable;
|
use JsonSerializable;
|
||||||
|
|
||||||
abstract class DatabaseEntity implements ArrayAccess, JsonSerializable {
|
abstract class DatabaseEntity implements ArrayAccess, JsonSerializable {
|
||||||
@ -52,7 +54,61 @@ abstract class DatabaseEntity implements ArrayAccess, JsonSerializable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract function jsonSerialize(): array;
|
public function jsonSerialize(?array $propertyNames = null): array {
|
||||||
|
$properties = (new \ReflectionClass(get_called_class()))->getProperties();
|
||||||
|
$ignoredProperties = ["entityLogConfig", "customData"];
|
||||||
|
|
||||||
|
$jsonArray = [];
|
||||||
|
foreach ($properties as $property) {
|
||||||
|
$property->setAccessible(true);
|
||||||
|
$propertyName = $property->getName();
|
||||||
|
if (in_array($propertyName, $ignoredProperties)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($property->getAttributes(Transient::class))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$visibility = DatabaseEntityHandler::getAttribute($property, Visibility::class);
|
||||||
|
if ($visibility) {
|
||||||
|
$visibilityType = $visibility->getType();
|
||||||
|
if ($visibilityType === Visibility::NONE) {
|
||||||
|
continue;
|
||||||
|
} else if ($visibilityType === Visibility::BY_GROUP) {
|
||||||
|
$currentUser = Context::instance()->getUser();
|
||||||
|
$groups = $visibility->getGroups();
|
||||||
|
if (!empty($groups)) {
|
||||||
|
if (!$currentUser || empty(array_intersect(array_keys($currentUser->getGroups()), $groups))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($propertyNames === null || isset($propertyNames[$propertyName]) || in_array($propertyName, $propertyNames)) {
|
||||||
|
if ($property->isInitialized($this)) {
|
||||||
|
$value = $property->getValue($this);
|
||||||
|
if ($value instanceof \DateTime) {
|
||||||
|
$value = $value->getTimestamp();
|
||||||
|
} else if ($value instanceof DatabaseEntity) {
|
||||||
|
$subPropertyNames = $propertyNames[$propertyName] ?? null;
|
||||||
|
$value = $value->jsonSerialize($subPropertyNames);
|
||||||
|
}
|
||||||
|
|
||||||
|
$jsonArray[$property->getName()] = $value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $jsonArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function toJsonArray(array $entities, ?array $properties = null): array {
|
||||||
|
return array_map(function ($entity) use ($properties) {
|
||||||
|
return $entity->jsonSerialize($properties);
|
||||||
|
}, $entities);
|
||||||
|
}
|
||||||
|
|
||||||
public function preInsert(array &$row) { }
|
public function preInsert(array &$row) { }
|
||||||
public function postFetch(SQL $sql, array $row) { }
|
public function postFetch(SQL $sql, array $row) { }
|
||||||
|
@ -83,7 +83,7 @@ class DatabaseEntityHandler implements Persistable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$propertyType = $property->getType();
|
$propertyType = $property->getType();
|
||||||
$columnName = self::getColumnName($propertyName);
|
$columnName = self::buildColumnName($propertyName);
|
||||||
if (!($propertyType instanceof \ReflectionNamedType)) {
|
if (!($propertyType instanceof \ReflectionNamedType)) {
|
||||||
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has no valid type");
|
$this->raiseError("Cannot persist class '$className': Property '$propertyName' has no valid type");
|
||||||
}
|
}
|
||||||
@ -206,13 +206,13 @@ class DatabaseEntityHandler implements Persistable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function getAttribute(\ReflectionProperty $property, string $attributeClass): ?object {
|
public static function getAttribute(\ReflectionProperty $property, string $attributeClass): ?object {
|
||||||
$attributes = $property->getAttributes($attributeClass);
|
$attributes = $property->getAttributes($attributeClass);
|
||||||
$attribute = array_shift($attributes);
|
$attribute = array_shift($attributes);
|
||||||
return $attribute?->newInstance();
|
return $attribute?->newInstance();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getColumnName(string $propertyName): string {
|
public static function buildColumnName(string $propertyName): string {
|
||||||
// abcTestLOL => abc_test_lol
|
// abcTestLOL => abc_test_lol
|
||||||
return strtolower(preg_replace_callback("/([a-z])([A-Z]+)/", function ($m) {
|
return strtolower(preg_replace_callback("/([a-z])([A-Z]+)/", function ($m) {
|
||||||
return $m[1] . "_" . strtolower($m[2]);
|
return $m[1] . "_" . strtolower($m[2]);
|
||||||
@ -239,10 +239,18 @@ class DatabaseEntityHandler implements Persistable {
|
|||||||
return $this->nmRelations;
|
return $this->nmRelations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getColumnName(string $property): string {
|
||||||
|
if ($property === "id") {
|
||||||
|
return "$this->tableName.id";
|
||||||
|
} else {
|
||||||
|
return $this->tableName . "." . $this->columns[$property]->getName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getColumnNames(): array {
|
public function getColumnNames(): array {
|
||||||
$columns = ["$this->tableName.id"];
|
$columns = ["$this->tableName.id"];
|
||||||
foreach ($this->columns as $column) {
|
foreach (array_keys($this->columns) as $property) {
|
||||||
$columns[] = $this->tableName . "." . $column->getName();
|
$columns[] = $this->getColumnName($property);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $columns;
|
return $columns;
|
||||||
@ -286,7 +294,7 @@ class DatabaseEntityHandler implements Persistable {
|
|||||||
} else if ($column instanceof JsonColumn) {
|
} else if ($column instanceof JsonColumn) {
|
||||||
$value = json_decode($value);
|
$value = json_decode($value);
|
||||||
} else if (isset($this->relations[$propertyName])) {
|
} else if (isset($this->relations[$propertyName])) {
|
||||||
$relColumnPrefix = self::getColumnName($propertyName) . "_";
|
$relColumnPrefix = self::buildColumnName($propertyName) . "_";
|
||||||
if (array_key_exists($relColumnPrefix . "id", $row)) {
|
if (array_key_exists($relColumnPrefix . "id", $row)) {
|
||||||
$relId = $row[$relColumnPrefix . "id"];
|
$relId = $row[$relColumnPrefix . "id"];
|
||||||
if ($relId !== null) {
|
if ($relId !== null) {
|
||||||
|
@ -42,7 +42,18 @@ class DatabaseEntityQuery extends Select {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addCustomValue(mixed $selectValue): Select {
|
public function only(array $fields): DatabaseEntityQuery {
|
||||||
|
if (!in_array("id", $fields)) {
|
||||||
|
$fields[] = "id";
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->select(array_map(function ($field) {
|
||||||
|
return $this->handler->getColumnName($field);
|
||||||
|
}, $fields));
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function addCustomValue(mixed $selectValue): DatabaseEntityQuery {
|
||||||
if (is_string($selectValue)) {
|
if (is_string($selectValue)) {
|
||||||
$this->additionalColumns[] = $selectValue;
|
$this->additionalColumns[] = $selectValue;
|
||||||
} else if ($selectValue instanceof Alias) {
|
} else if ($selectValue instanceof Alias) {
|
||||||
@ -108,7 +119,7 @@ class DatabaseEntityQuery extends Select {
|
|||||||
$this->innerJoin($referencedTable, "$tableName.$foreignColumnName", "$alias.id", $alias);
|
$this->innerJoin($referencedTable, "$tableName.$foreignColumnName", "$alias.id", $alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
$relationColumnPrefix .= DatabaseEntityHandler::getColumnName($propertyName) . "_";
|
$relationColumnPrefix .= DatabaseEntityHandler::buildColumnName($propertyName) . "_";
|
||||||
$recursiveRelations = $relationHandler->getRelations();
|
$recursiveRelations = $relationHandler->getRelations();
|
||||||
foreach ($relationHandler->getColumns() as $relPropertyName => $relColumn) {
|
foreach ($relationHandler->getColumns() as $relPropertyName => $relColumn) {
|
||||||
$relColumnName = $relColumn->getName();
|
$relColumnName = $relColumn->getName();
|
||||||
|
@ -34,7 +34,7 @@ class NMRelation implements Persistable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getIdColumn(DatabaseEntityHandler $handler): string {
|
public function getIdColumn(DatabaseEntityHandler $handler): string {
|
||||||
return DatabaseEntityHandler::getColumnName($handler->getTableName()) . "_id";
|
return DatabaseEntityHandler::buildColumnName($handler->getTableName()) . "_id";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDataColumns(): array {
|
public function getDataColumns(): array {
|
||||||
@ -56,8 +56,8 @@ class NMRelation implements Persistable {
|
|||||||
foreach ($this->properties as $tableName => $properties) {
|
foreach ($this->properties as $tableName => $properties) {
|
||||||
$columns[$tableName] = [];
|
$columns[$tableName] = [];
|
||||||
foreach ($properties as $property) {
|
foreach ($properties as $property) {
|
||||||
$columnName = DatabaseEntityHandler::getColumnName($tableName) . "_" .
|
$columnName = DatabaseEntityHandler::buildColumnName($tableName) . "_" .
|
||||||
DatabaseEntityHandler::getColumnName($property->getName());
|
DatabaseEntityHandler::buildColumnName($property->getName());
|
||||||
$columns[$tableName][$property->getName()] = $columnName;
|
$columns[$tableName][$property->getName()] = $columnName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,17 +123,6 @@ class GpgKey extends DatabaseEntity {
|
|||||||
return $this->fingerprint;
|
return $this->fingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"fingerprint" => $this->fingerprint,
|
|
||||||
"algorithm" => $this->algorithm,
|
|
||||||
"expires" => $this->expires->getTimestamp(),
|
|
||||||
"added" => $this->added->getTimestamp(),
|
|
||||||
"confirmed" => $this->confirmed
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function confirm(SQL $sql): bool {
|
public function confirm(SQL $sql): bool {
|
||||||
$this->confirmed = true;
|
$this->confirmed = true;
|
||||||
return $this->save($sql, ["confirmed"]);
|
return $this->save($sql, ["confirmed"]);
|
||||||
|
@ -5,7 +5,6 @@ namespace Core\Objects\DatabaseEntity;
|
|||||||
use Core\Driver\SQL\SQL;
|
use Core\Driver\SQL\SQL;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntityHandler;
|
|
||||||
use Core\Objects\DatabaseEntity\Controller\NMRelation;
|
use Core\Objects\DatabaseEntity\Controller\NMRelation;
|
||||||
|
|
||||||
class Group extends DatabaseEntity {
|
class Group extends DatabaseEntity {
|
||||||
@ -29,18 +28,12 @@ class Group extends DatabaseEntity {
|
|||||||
$this->color = $color;
|
$this->color = $color;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"name" => $this->name,
|
|
||||||
"color" => $this->color
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMembers(SQL $sql): array {
|
public function getMembers(SQL $sql): array {
|
||||||
$nmTable = NMRelation::buildTableName(User::class, Group::class);
|
$nmTable = NMRelation::buildTableName(User::class, Group::class);
|
||||||
return User::findBy(User::createBuilder($sql, false)
|
$users = User::findBy(User::createBuilder($sql, false)
|
||||||
->innerJoin($nmTable, "user_id", "User.id")
|
->innerJoin($nmTable, "user_id", "User.id")
|
||||||
->whereEq("group_id", $this->id));
|
->whereEq("group_id", $this->id));
|
||||||
|
|
||||||
|
return User::toJsonArray($users, ["id", "name", "fullName", "profilePicture"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -42,13 +42,14 @@ namespace Core\Objects\DatabaseEntity {
|
|||||||
setcookie('lang', $this->code, 0, "/", $domain, false, false);
|
setcookie('lang', $this->code, 0, "/", $domain, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
public function jsonSerialize(?array $propertyNames = null): array {
|
||||||
return array(
|
$jsonData = parent::jsonSerialize($propertyNames);
|
||||||
'id' => $this->getId(),
|
|
||||||
'code' => $this->code,
|
if ($propertyNames === null || in_array("shortCode", $propertyNames)) {
|
||||||
'shortCode' => explode("_", $this->code)[0],
|
$jsonData["shortCode"] = explode("_", $this->code)[0];
|
||||||
'name' => $this->name,
|
}
|
||||||
);
|
|
||||||
|
return $jsonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function activate() {
|
public function activate() {
|
||||||
|
@ -70,25 +70,6 @@ class MailQueueItem extends DatabaseEntity {
|
|||||||
$this->status = self::STATUS_WAITING;
|
$this->status = self::STATUS_WAITING;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"from" => $this->from,
|
|
||||||
"to" => $this->to,
|
|
||||||
"gpgFingerprint" => $this->gpgFingerprint,
|
|
||||||
"subject" => $this->subject,
|
|
||||||
"message" => $this->body,
|
|
||||||
"status" => $this->status,
|
|
||||||
"reply" => [
|
|
||||||
"to" => $this->replyTo,
|
|
||||||
"name" => $this->replyName,
|
|
||||||
],
|
|
||||||
"retryCount" => $this->retryCount,
|
|
||||||
"nextTry" => $this->nextTry->getTimestamp(),
|
|
||||||
"errorMessage" => $this->errorMessage,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function send(Context $context): bool {
|
public function send(Context $context): bool {
|
||||||
|
|
||||||
$args = [
|
$args = [
|
||||||
|
@ -206,18 +206,6 @@ abstract class Route extends DatabaseEntity {
|
|||||||
return $parameterNames;
|
return $parameterNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"pattern" => $this->pattern,
|
|
||||||
"type" => $this->type,
|
|
||||||
"target" => $this->target,
|
|
||||||
"extra" => $this->extra,
|
|
||||||
"exact" => $this->exact,
|
|
||||||
"active" => $this->active,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setActive(bool $active) {
|
public function setActive(bool $active) {
|
||||||
$this->active = $active;
|
$this->active = $active;
|
||||||
}
|
}
|
||||||
|
@ -93,19 +93,6 @@ class Session extends DatabaseEntity {
|
|||||||
return ($this->stayLoggedIn ? $this->expires->getTimestamp() - time() : -1);
|
return ($this->stayLoggedIn ? $this->expires->getTimestamp() - time() : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return array(
|
|
||||||
'id' => $this->getId(),
|
|
||||||
'active' => $this->active,
|
|
||||||
'expires' => $this->expires->getTimestamp(),
|
|
||||||
'ipAddress' => $this->ipAddress,
|
|
||||||
'os' => $this->os,
|
|
||||||
'browser' => $this->browser,
|
|
||||||
'csrf_token' => $this->csrfToken,
|
|
||||||
'data' => $this->data,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function destroy(): bool {
|
public function destroy(): bool {
|
||||||
session_destroy();
|
session_destroy();
|
||||||
$this->active = false;
|
$this->active = false;
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace Core\Objects\DatabaseEntity;
|
namespace Core\Objects\DatabaseEntity;
|
||||||
|
|
||||||
use Core\API\Parameter\Parameter;
|
|
||||||
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
use Core\Driver\SQL\Expression\CurrentTimeStamp;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\Enum;
|
use Core\Objects\DatabaseEntity\Attribute\Enum;
|
||||||
@ -19,14 +18,4 @@ class SystemLog extends DatabaseEntity {
|
|||||||
public function __construct(?int $id = null) {
|
public function __construct(?int $id = null) {
|
||||||
parent::__construct($id);
|
parent::__construct($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"timestamp" => $this->timestamp->format(Parameter::DATE_TIME_FORMAT),
|
|
||||||
"message" => $this->message,
|
|
||||||
"module" => $this->module,
|
|
||||||
"severity" => $this->severity
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -30,15 +30,6 @@ abstract class TwoFactorToken extends DatabaseEntity {
|
|||||||
$this->data = null;
|
$this->data = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"type" => $this->type,
|
|
||||||
"confirmed" => $this->confirmed,
|
|
||||||
"authenticated" => $this->authenticated,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract function getData(): string;
|
public abstract function getData(): string;
|
||||||
protected abstract function readData(string $data);
|
protected abstract function readData(string $data);
|
||||||
|
|
||||||
|
@ -8,20 +8,45 @@ use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
|||||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\Multiple;
|
use Core\Objects\DatabaseEntity\Attribute\Multiple;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\Unique;
|
use Core\Objects\DatabaseEntity\Attribute\Unique;
|
||||||
|
use Core\Objects\DatabaseEntity\Attribute\Visibility;
|
||||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||||
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntityHandler;
|
||||||
|
|
||||||
class User extends DatabaseEntity {
|
class User extends DatabaseEntity {
|
||||||
|
|
||||||
#[MaxLength(32)] #[Unique] public string $name;
|
#[MaxLength(32)] #[Unique] public string $name;
|
||||||
#[MaxLength(128)] public string $password;
|
|
||||||
#[MaxLength(64)] public string $fullName;
|
#[MaxLength(128)]
|
||||||
#[MaxLength(64)] #[Unique] public ?string $email;
|
#[Visibility(Visibility::NONE)]
|
||||||
#[MaxLength(64)] public ?string $profilePicture;
|
public string $password;
|
||||||
|
|
||||||
|
#[MaxLength(64)]
|
||||||
|
public string $fullName;
|
||||||
|
|
||||||
|
#[MaxLength(64)]
|
||||||
|
#[Unique]
|
||||||
|
public ?string $email;
|
||||||
|
|
||||||
|
#[MaxLength(64)]
|
||||||
|
public ?string $profilePicture;
|
||||||
|
|
||||||
|
#[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)]
|
||||||
private ?\DateTime $lastOnline;
|
private ?\DateTime $lastOnline;
|
||||||
#[DefaultValue(CurrentTimeStamp::class)] public \DateTime $registeredAt;
|
|
||||||
#[DefaultValue(false)] public bool $confirmed;
|
#[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)]
|
||||||
|
#[DefaultValue(CurrentTimeStamp::class)]
|
||||||
|
public \DateTime $registeredAt;
|
||||||
|
|
||||||
|
#[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)]
|
||||||
|
#[DefaultValue(false)]
|
||||||
|
public bool $confirmed;
|
||||||
|
|
||||||
#[DefaultValue(Language::AMERICAN_ENGLISH)] public Language $language;
|
#[DefaultValue(Language::AMERICAN_ENGLISH)] public Language $language;
|
||||||
|
|
||||||
|
#[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)]
|
||||||
public ?GpgKey $gpgKey;
|
public ?GpgKey $gpgKey;
|
||||||
|
|
||||||
|
#[Visibility(Visibility::BY_GROUP, Group::ADMIN, Group::SUPPORT)]
|
||||||
private ?TwoFactorToken $twoFactorToken;
|
private ?TwoFactorToken $twoFactorToken;
|
||||||
|
|
||||||
#[Multiple(Group::class)]
|
#[Multiple(Group::class)]
|
||||||
@ -71,24 +96,6 @@ class User extends DatabaseEntity {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
'id' => $this->getId(),
|
|
||||||
'name' => $this->name,
|
|
||||||
'fullName' => $this->fullName,
|
|
||||||
'profilePicture' => $this->profilePicture,
|
|
||||||
'email' => $this->email,
|
|
||||||
'groups' => $this->groups ?? null,
|
|
||||||
'language' => (isset($this->language) ? $this->language->jsonSerialize() : null),
|
|
||||||
'session' => (isset($this->session) ? $this->session->jsonSerialize() : null),
|
|
||||||
"gpg" => (isset($this->gpgKey) ? $this->gpgKey->jsonSerialize() : null),
|
|
||||||
"2fa" => (isset($this->twoFactorToken) ? $this->twoFactorToken->jsonSerialize() : null),
|
|
||||||
"reqisteredAt" => $this->registeredAt->getTimestamp(),
|
|
||||||
"lastOnline" => $this->lastOnline->getTimestamp(),
|
|
||||||
"confirmed" => $this->confirmed
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(SQL $sql): bool {
|
public function update(SQL $sql): bool {
|
||||||
$this->lastOnline = new \DateTime();
|
$this->lastOnline = new \DateTime();
|
||||||
return $this->save($sql, ["last_online", "language_id"]);
|
return $this->save($sql, ["last_online", "language_id"]);
|
||||||
@ -97,4 +104,37 @@ class User extends DatabaseEntity {
|
|||||||
public function setTwoFactorToken(TwoFactorToken $twoFactorToken) {
|
public function setTwoFactorToken(TwoFactorToken $twoFactorToken) {
|
||||||
$this->twoFactorToken = $twoFactorToken;
|
$this->twoFactorToken = $twoFactorToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canAccess(\ReflectionClass|DatabaseEntity|string $entityOrClass, string $propertyName): bool {
|
||||||
|
try {
|
||||||
|
$reflectionClass = ($entityOrClass instanceof \ReflectionClass
|
||||||
|
? $entityOrClass
|
||||||
|
: new \ReflectionClass($entityOrClass));
|
||||||
|
|
||||||
|
$property = $reflectionClass->getProperty($propertyName);
|
||||||
|
$visibility = DatabaseEntityHandler::getAttribute($property, Visibility::class);
|
||||||
|
if ($visibility === null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$visibilityType = $visibility->getType();
|
||||||
|
if ($visibilityType === Visibility::NONE) {
|
||||||
|
return false;
|
||||||
|
} else if ($visibilityType === Visibility::BY_GROUP) {
|
||||||
|
// allow access to own entity
|
||||||
|
if ($entityOrClass instanceof User && $entityOrClass->getId() === $this->id) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// missing required group
|
||||||
|
if (empty(array_intersect(array_keys($this->groups), $visibility->getGroups()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (\Exception $exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
@ -6,6 +6,7 @@ use Core\Driver\SQL\SQL;
|
|||||||
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
use Core\Objects\DatabaseEntity\Attribute\DefaultValue;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\EnumArr;
|
use Core\Objects\DatabaseEntity\Attribute\EnumArr;
|
||||||
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
use Core\Objects\DatabaseEntity\Attribute\MaxLength;
|
||||||
|
use Core\Objects\DatabaseEntity\Attribute\Visibility;
|
||||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
use Core\Objects\DatabaseEntity\Controller\DatabaseEntity;
|
||||||
|
|
||||||
class UserToken extends DatabaseEntity {
|
class UserToken extends DatabaseEntity {
|
||||||
@ -21,6 +22,7 @@ class UserToken extends DatabaseEntity {
|
|||||||
];
|
];
|
||||||
|
|
||||||
#[MaxLength(36)]
|
#[MaxLength(36)]
|
||||||
|
#[Visibility(Visibility::NONE)]
|
||||||
private string $token;
|
private string $token;
|
||||||
|
|
||||||
#[EnumArr(self::TOKEN_TYPES)]
|
#[EnumArr(self::TOKEN_TYPES)]
|
||||||
@ -41,14 +43,6 @@ class UserToken extends DatabaseEntity {
|
|||||||
$this->used = false;
|
$this->used = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"token" => $this->token,
|
|
||||||
"tokenType" => $this->tokenType
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getType(): string {
|
public function getType(): string {
|
||||||
return $this->tokenType;
|
return $this->tokenType;
|
||||||
}
|
}
|
||||||
|
@ -58,18 +58,18 @@ class KeyBasedTwoFactorToken extends TwoFactorToken {
|
|||||||
return $this->credentialId;
|
return $this->credentialId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
public function jsonSerialize(?array $propertyNames = null): array {
|
||||||
$json = parent::jsonSerialize();
|
$jsonData = parent::jsonSerialize();
|
||||||
|
|
||||||
if (!empty($this->challenge) && !$this->isAuthenticated()) {
|
if (!empty($this->challenge) && !$this->isAuthenticated() && in_array("challenge", $propertyNames)) {
|
||||||
$json["challenge"] = base64_encode($this->challenge);
|
$jsonData["challenge"] = base64_encode($this->challenge);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($this->credentialId)) {
|
if (!empty($this->credentialId) && in_array("credentialID", $propertyNames)) {
|
||||||
$json["credentialID"] = base64_encode($this->credentialId);
|
$jsonData["credentialID"] = base64_encode($this->credentialId);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $json;
|
return $jsonData;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: algorithms, hardcoded values, ...
|
// TODO: algorithms, hardcoded values, ...
|
||||||
|
2
cli.php
2
cli.php
@ -34,7 +34,7 @@ function getDatabaseConfig(): ConnectionData {
|
|||||||
return new $configClass();
|
return new $configClass();
|
||||||
}
|
}
|
||||||
|
|
||||||
$context = new \Core\Objects\Context();
|
$context = \Core\Objects\Context::instance();
|
||||||
if (!$context->isCLI()) {
|
if (!$context->isCLI()) {
|
||||||
_exit("Can only be executed via CLI");
|
_exit("Can only be executed via CLI");
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ if (is_file("MAINTENANCE") && !in_array($_SERVER['REMOTE_ADDR'], ['127.0.0.1', '
|
|||||||
}
|
}
|
||||||
|
|
||||||
use Core\Configuration\Configuration;
|
use Core\Configuration\Configuration;
|
||||||
|
use Core\Objects\Context;
|
||||||
use Core\Objects\Router\Router;
|
use Core\Objects\Router\Router;
|
||||||
|
|
||||||
if (!is_readable(getClassPath(Configuration::class))) {
|
if (!is_readable(getClassPath(Configuration::class))) {
|
||||||
@ -20,7 +21,7 @@ if (!is_readable(getClassPath(Configuration::class))) {
|
|||||||
die(json_encode([ "success" => false, "msg" => "Configuration class is not readable, check permissions before proceeding." ]));
|
die(json_encode([ "success" => false, "msg" => "Configuration class is not readable, check permissions before proceeding." ]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$context = new \Core\Objects\Context();
|
$context = Context::instance();
|
||||||
$sql = $context->initSQL();
|
$sql = $context->initSQL();
|
||||||
$settings = $context->getSettings();
|
$settings = $context->getSettings();
|
||||||
$context->parseCookies();
|
$context->parseCookies();
|
||||||
|
@ -92,9 +92,9 @@ export default function Sidebar(props) {
|
|||||||
<div className={"os-content"} style={{padding: "0px 0px", height: "100%", width: "100%"}}>
|
<div className={"os-content"} style={{padding: "0px 0px", height: "100%", width: "100%"}}>
|
||||||
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
|
<div className="user-panel mt-3 pb-3 mb-3 d-flex">
|
||||||
<div className="info">
|
<div className="info">
|
||||||
<a href="#" className="d-block">{L("account.logged_in_as")}:
|
<span className={"d-block"}>{L("account.logged_in_as")}:
|
||||||
<Link to={"/admin/user/" + api.user.id}>{api.user.name}</Link>
|
<Link to={"/admin/user/" + api.user.id}>{api.user.name}</Link>
|
||||||
</a>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<nav className={"mt-2"}>
|
<nav className={"mt-2"}>
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
export default class API {
|
export default class API {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
this.user = { };
|
this.user = null;
|
||||||
|
this.session = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
csrfToken() {
|
csrfToken() {
|
||||||
return this.loggedIn ? this.user.session.csrf_token : null;
|
return this.loggedIn ? this.session.csrfToken : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async apiCall(method, params) {
|
async apiCall(method, params) {
|
||||||
params = params || { };
|
params = params || { };
|
||||||
params.csrf_token = this.csrfToken();
|
params.csrfToken = this.csrfToken();
|
||||||
let response = await fetch("/api/" + method, {
|
let response = await fetch("/api/" + method, {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
headers: {'Content-Type': 'application/json'},
|
headers: {'Content-Type': 'application/json'},
|
||||||
@ -39,8 +40,14 @@ export default class API {
|
|||||||
let response = await fetch("/api/user/info");
|
let response = await fetch("/api/user/info");
|
||||||
let data = await response.json();
|
let data = await response.json();
|
||||||
if (data) {
|
if (data) {
|
||||||
this.user = data["user"];
|
|
||||||
this.loggedIn = data["loggedIn"];
|
this.loggedIn = data["loggedIn"];
|
||||||
|
if (this.loggedIn) {
|
||||||
|
this.session = data["session"];
|
||||||
|
this.user = data["user"];
|
||||||
|
} else {
|
||||||
|
this.session = null;
|
||||||
|
this.user = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ export function DataTable(props) {
|
|||||||
|
|
||||||
const {currentLocale, requestModules, translate: L} = useContext(LocaleContext);
|
const {currentLocale, requestModules, translate: L} = useContext(LocaleContext);
|
||||||
|
|
||||||
const [doFetchData, setFetchData] = useState(true);
|
const [doFetchData, setFetchData] = useState(false);
|
||||||
const [data, setData] = useState(null);
|
const [data, setData] = useState(null);
|
||||||
const [sortAscending, setSortAscending] = useState(["asc","ascending"].includes(defaultSortOrder?.toLowerCase));
|
const [sortAscending, setSortAscending] = useState(["asc","ascending"].includes(defaultSortOrder?.toLowerCase));
|
||||||
const [sortColumn, setSortColumn] = useState(defaultSortColumn || null);
|
const [sortColumn, setSortColumn] = useState(defaultSortColumn || null);
|
||||||
|
@ -109,16 +109,4 @@ class TestEntity extends DatabaseEntity {
|
|||||||
public float $d;
|
public float $d;
|
||||||
public \DateTime $e;
|
public \DateTime $e;
|
||||||
public ?int $f;
|
public ?int $f;
|
||||||
|
|
||||||
public function jsonSerialize(): array {
|
|
||||||
return [
|
|
||||||
"id" => $this->getId(),
|
|
||||||
"a" => $this->a,
|
|
||||||
"b" => $this->b,
|
|
||||||
"c" => $this->c,
|
|
||||||
"d" => $this->d,
|
|
||||||
"e" => $this->e,
|
|
||||||
"f" => $this->f,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -42,7 +42,7 @@ class RequestTest extends \PHPUnit\Framework\TestCase {
|
|||||||
|
|
||||||
public static function setUpBeforeClass(): void {
|
public static function setUpBeforeClass(): void {
|
||||||
|
|
||||||
RequestTest::$CONTEXT = new Context();
|
RequestTest::$CONTEXT = Context::instance();
|
||||||
if (!RequestTest::$CONTEXT->initSQL()) {
|
if (!RequestTest::$CONTEXT->initSQL()) {
|
||||||
throw new Exception("Could not establish database connection");
|
throw new Exception("Could not establish database connection");
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ class RouterTest extends \PHPUnit\Framework\TestCase {
|
|||||||
private static Context $CONTEXT;
|
private static Context $CONTEXT;
|
||||||
|
|
||||||
public static function setUpBeforeClass(): void {
|
public static function setUpBeforeClass(): void {
|
||||||
RouterTest::$CONTEXT = new Context();
|
RouterTest::$CONTEXT = Context::instance();
|
||||||
RouterTest::$ROUTER = new Router(RouterTest::$CONTEXT);
|
RouterTest::$ROUTER = new Router(RouterTest::$CONTEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user