SQL expression rewrite, Pagination, some frontend stuff
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Core\API {
|
||||
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use Core\Objects\Context;
|
||||
|
||||
abstract class GroupsAPI extends Request {
|
||||
@@ -12,7 +13,7 @@ namespace Core\API {
|
||||
|
||||
protected function groupExists($name): bool {
|
||||
$sql = $this->context->getSQL();
|
||||
$res = $sql->select($sql->count())
|
||||
$res = $sql->select(new Count())
|
||||
->from("Group")
|
||||
->whereEq("name", $name)
|
||||
->execute();
|
||||
@@ -29,76 +30,81 @@ namespace Core\API\Groups {
|
||||
use Core\API\GroupsAPI;
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\Traits\Pagination;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
use Core\Driver\SQL\Expression\Alias;
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use Core\Objects\Context;
|
||||
use Core\Objects\DatabaseEntity\Controller\NMRelation;
|
||||
use Core\Objects\DatabaseEntity\Group;
|
||||
|
||||
class Fetch extends GroupsAPI {
|
||||
|
||||
use Pagination;
|
||||
|
||||
private int $groupCount;
|
||||
|
||||
public function __construct(Context $context, $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, array(
|
||||
'page' => new Parameter('page', Parameter::TYPE_INT, true, 1),
|
||||
'count' => new Parameter('count', Parameter::TYPE_INT, true, 20)
|
||||
));
|
||||
parent::__construct($context, $externalCall,
|
||||
self::getPaginationParameters(['id', 'name', 'member_count'])
|
||||
);
|
||||
|
||||
$this->groupCount = 0;
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
$page = $this->getParam("page");
|
||||
if($page < 1) {
|
||||
return $this->createError("Invalid page count");
|
||||
}
|
||||
|
||||
$count = $this->getParam("count");
|
||||
if($count < 1 || $count > 50) {
|
||||
return $this->createError("Invalid fetch count");
|
||||
}
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
$groupCount = Group::count($sql);
|
||||
if ($groupCount === false) {
|
||||
return $this->createError("Error fetching group count: " . $sql->getLastError());
|
||||
if (!$this->initPagination($sql, Group::class)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$groups = Group::findBy(Group::createBuilder($sql, false)
|
||||
->orderBy("id")
|
||||
->ascending()
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count));
|
||||
$memberCount = new Alias($sql->select(new Count())
|
||||
->from(NMRelation::buildTableName("User", "Group"))
|
||||
->whereEq("group_id", new Column("Group.id")), "memberCount");
|
||||
|
||||
if ($groups !== false) {
|
||||
$groupsQuery = $this->createPaginationQuery($sql, [$memberCount]);
|
||||
$groups = $groupsQuery->execute();
|
||||
if ($groups !== false && $groups !== null) {
|
||||
$this->result["groups"] = [];
|
||||
$this->result["pageCount"] = intval(ceil($this->groupCount / $count));
|
||||
$this->result["totalCount"] = $this->groupCount;
|
||||
|
||||
foreach ($groups as $groupId => $group) {
|
||||
$this->result["groups"][$groupId] = $group->jsonSerialize();
|
||||
$this->result["groups"][$groupId]["memberCount"] = 0;
|
||||
}
|
||||
|
||||
$nmTable = NMRelation::buildTableName("User", "Group");
|
||||
$res = $sql->select("group_id", $sql->count("user_id"))
|
||||
->from($nmTable)
|
||||
->groupBy("group_id")
|
||||
->execute();
|
||||
|
||||
if (is_array($res)) {
|
||||
foreach ($res as $row) {
|
||||
list ($groupId, $memberCount) = [$row["group_id"], $row["user_id_count"]];
|
||||
if (isset($this->result["groups"][$groupId])) {
|
||||
$this->result["groups"][$groupId]["memberCount"] = $memberCount;
|
||||
}
|
||||
}
|
||||
foreach ($groups as $group) {
|
||||
$groupData = $group->jsonSerialize();
|
||||
$groupData["memberCount"] = $group["memberCount"];
|
||||
$this->result["groups"][] = $groupData;
|
||||
}
|
||||
} else {
|
||||
return $this->createError("Error fetching groups: " . $sql->getLastError());
|
||||
}
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
}
|
||||
|
||||
class Get extends GroupsAPI {
|
||||
public function __construct(Context $context, bool $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, [
|
||||
"id" => new Parameter("id", Parameter::TYPE_INT)
|
||||
]);
|
||||
}
|
||||
|
||||
protected function _execute(): bool {
|
||||
$sql = $this->context->getSQL();
|
||||
$groupId = $this->getParam("id");
|
||||
$group = Group::find($sql, $groupId);
|
||||
if ($group === false) {
|
||||
return $this->createError("Error fetching group: " . $sql->getLastError());
|
||||
} else if ($group === null) {
|
||||
return $this->createError("Group not found");
|
||||
} else {
|
||||
$this->result["group"] = $group->jsonSerialize();
|
||||
$this->result["group"]["members"] = $group->getMembers($sql);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Create extends GroupsAPI {
|
||||
public function __construct(Context $context, $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, array(
|
||||
|
||||
@@ -5,22 +5,22 @@ namespace Core\API\Parameter;
|
||||
use DateTime;
|
||||
|
||||
class Parameter {
|
||||
const TYPE_INT = 0;
|
||||
const TYPE_FLOAT = 1;
|
||||
const TYPE_BOOLEAN = 2;
|
||||
const TYPE_STRING = 3;
|
||||
const TYPE_DATE = 4;
|
||||
const TYPE_TIME = 5;
|
||||
const TYPE_INT = 0;
|
||||
const TYPE_FLOAT = 1;
|
||||
const TYPE_BOOLEAN = 2;
|
||||
const TYPE_STRING = 3;
|
||||
const TYPE_DATE = 4;
|
||||
const TYPE_TIME = 5;
|
||||
const TYPE_DATE_TIME = 6;
|
||||
const TYPE_EMAIL = 7;
|
||||
const TYPE_EMAIL = 7;
|
||||
|
||||
// only internal access
|
||||
const TYPE_RAW = 8;
|
||||
const TYPE_RAW = 8;
|
||||
|
||||
// only json will work here I guess
|
||||
// nope. also name[]=value
|
||||
const TYPE_ARRAY = 9;
|
||||
const TYPE_MIXED = 10;
|
||||
const TYPE_ARRAY = 9;
|
||||
const TYPE_MIXED = 10;
|
||||
|
||||
const names = array('Integer', 'Float', 'Boolean', 'String', 'Date', 'Time', 'DateTime', 'E-Mail', 'Raw', 'Array', 'Mixed');
|
||||
|
||||
@@ -35,13 +35,15 @@ class Parameter {
|
||||
public bool $optional;
|
||||
public int $type;
|
||||
public string $typeName;
|
||||
public ?array $choices;
|
||||
|
||||
public function __construct(string $name, int $type, bool $optional = FALSE, $defaultValue = NULL) {
|
||||
public function __construct(string $name, int $type, bool $optional = FALSE, $defaultValue = NULL, ?array $choices = NULL) {
|
||||
$this->name = $name;
|
||||
$this->optional = $optional;
|
||||
$this->defaultValue = $defaultValue;
|
||||
$this->value = $defaultValue;
|
||||
$this->type = $type;
|
||||
$this->choices = $choices;
|
||||
$this->typeName = $this->getTypeName();
|
||||
}
|
||||
|
||||
@@ -63,22 +65,22 @@ class Parameter {
|
||||
}
|
||||
|
||||
public function getSwaggerFormat(): ?string {
|
||||
switch ($this->type) {
|
||||
case self::TYPE_DATE:
|
||||
return self::DATE_FORMAT;
|
||||
case self::TYPE_TIME:
|
||||
return self::TIME_FORMAT;
|
||||
case self::TYPE_DATE_TIME:
|
||||
return self::DATE_TIME_FORMAT;
|
||||
case self::TYPE_EMAIL:
|
||||
return "email";
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return match ($this->type) {
|
||||
self::TYPE_DATE => self::DATE_FORMAT,
|
||||
self::TYPE_TIME => self::TIME_FORMAT,
|
||||
self::TYPE_DATE_TIME => self::DATE_TIME_FORMAT,
|
||||
self::TYPE_EMAIL => "email",
|
||||
default => null,
|
||||
};
|
||||
}
|
||||
|
||||
public function getTypeName(): string {
|
||||
return ($this->type >= 0 && $this->type < count(Parameter::names)) ? Parameter::names[$this->type] : "INVALID";
|
||||
$typeName = Parameter::names[$this->type] ?? "INVALID";
|
||||
if ($this->choices) {
|
||||
$typeName .= ", choices: " . json_encode($this->choices);
|
||||
}
|
||||
|
||||
return $typeName;
|
||||
}
|
||||
|
||||
public function toString(): string {
|
||||
@@ -86,7 +88,7 @@ class Parameter {
|
||||
|
||||
$str = "$typeName $this->name";
|
||||
$defaultValue = (is_null($this->value) ? 'NULL' : $this->value);
|
||||
if($this->optional) {
|
||||
if ($this->optional) {
|
||||
$str = "[$str = $defaultValue]";
|
||||
}
|
||||
|
||||
@@ -94,21 +96,21 @@ class Parameter {
|
||||
}
|
||||
|
||||
public static function parseType($value): int {
|
||||
if(is_array($value))
|
||||
if (is_array($value))
|
||||
return Parameter::TYPE_ARRAY;
|
||||
else if(is_numeric($value) && intval($value) == $value)
|
||||
else if (is_numeric($value) && intval($value) == $value)
|
||||
return Parameter::TYPE_INT;
|
||||
else if(is_float($value) || (is_numeric($value) && floatval($value) == $value))
|
||||
else if (is_float($value) || (is_numeric($value) && floatval($value) == $value))
|
||||
return Parameter::TYPE_FLOAT;
|
||||
else if(is_bool($value) || $value == "true" || $value == "false")
|
||||
else if (is_bool($value) || $value == "true" || $value == "false")
|
||||
return Parameter::TYPE_BOOLEAN;
|
||||
else if(is_a($value, 'DateTime'))
|
||||
else if (is_a($value, 'DateTime'))
|
||||
return Parameter::TYPE_DATE_TIME;
|
||||
else if($value !== null && ($d = DateTime::createFromFormat(self::DATE_FORMAT, $value)) && $d->format(self::DATE_FORMAT) === $value)
|
||||
else if ($value !== null && ($d = DateTime::createFromFormat(self::DATE_FORMAT, $value)) && $d->format(self::DATE_FORMAT) === $value)
|
||||
return Parameter::TYPE_DATE;
|
||||
else if($value !== null && ($d = DateTime::createFromFormat(self::TIME_FORMAT, $value)) && $d->format(self::TIME_FORMAT) === $value)
|
||||
else if ($value !== null && ($d = DateTime::createFromFormat(self::TIME_FORMAT, $value)) && $d->format(self::TIME_FORMAT) === $value)
|
||||
return Parameter::TYPE_TIME;
|
||||
else if($value !== null && ($d = DateTime::createFromFormat(self::DATE_TIME_FORMAT, $value)) && $d->format(self::DATE_TIME_FORMAT) === $value)
|
||||
else if ($value !== null && ($d = DateTime::createFromFormat(self::DATE_TIME_FORMAT, $value)) && $d->format(self::DATE_TIME_FORMAT) === $value)
|
||||
return Parameter::TYPE_DATE_TIME;
|
||||
else if (filter_var($value, FILTER_VALIDATE_EMAIL))
|
||||
return Parameter::TYPE_EMAIL;
|
||||
@@ -117,88 +119,90 @@ class Parameter {
|
||||
}
|
||||
|
||||
public function parseParam($value): bool {
|
||||
switch($this->type) {
|
||||
|
||||
$valid = false;
|
||||
switch ($this->type) {
|
||||
case Parameter::TYPE_INT:
|
||||
if(is_numeric($value) && intval($value) == $value) {
|
||||
if (is_numeric($value) && intval($value) == $value) {
|
||||
$this->value = intval($value);
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Parameter::TYPE_FLOAT:
|
||||
if(is_numeric($value) && (floatval($value) == $value || intval($value) == $value)) {
|
||||
if (is_numeric($value) && (floatval($value) == $value || intval($value) == $value)) {
|
||||
$this->value = floatval($value);
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Parameter::TYPE_BOOLEAN:
|
||||
if(strcasecmp($value, 'true') === 0)
|
||||
if (strcasecmp($value, 'true') === 0) {
|
||||
$this->value = true;
|
||||
else if(strcasecmp($value, 'false') === 0)
|
||||
$valid = true;
|
||||
} else if (strcasecmp($value, 'false') === 0) {
|
||||
$this->value = false;
|
||||
else if(is_bool($value))
|
||||
$valid = true;
|
||||
} else if (is_bool($value)) {
|
||||
$this->value = (bool)$value;
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
|
||||
case Parameter::TYPE_DATE:
|
||||
if(is_a($value, "DateTime")) {
|
||||
$this->value = $value;
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
|
||||
$d = DateTime::createFromFormat(self::DATE_FORMAT, $value);
|
||||
if($d && $d->format(self::DATE_FORMAT) === $value) {
|
||||
$this->value = $d;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Parameter::TYPE_TIME:
|
||||
if(is_a($value, "DateTime")) {
|
||||
$this->value = $value;
|
||||
return true;
|
||||
}
|
||||
|
||||
$d = DateTime::createFromFormat(self::TIME_FORMAT, $value);
|
||||
if($d && $d->format(self::TIME_FORMAT) === $value) {
|
||||
$this->value = $d;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
case Parameter::TYPE_DATE:
|
||||
case Parameter::TYPE_DATE_TIME:
|
||||
if(is_a($value, 'DateTime')) {
|
||||
if ($value instanceof DateTime) {
|
||||
$this->value = $value;
|
||||
return true;
|
||||
$valid = true;
|
||||
} else {
|
||||
$d = DateTime::createFromFormat(self::DATE_TIME_FORMAT, $value);
|
||||
if($d && $d->format(self::DATE_TIME_FORMAT) === $value) {
|
||||
$format = $this->getFormat();
|
||||
$d = DateTime::createFromFormat($format, $value);
|
||||
if ($d && $d->format($format) === $value) {
|
||||
$this->value = $d;
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Parameter::TYPE_EMAIL:
|
||||
if (filter_var($value, FILTER_VALIDATE_EMAIL)) {
|
||||
$this->value = $value;
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
case Parameter::TYPE_ARRAY:
|
||||
if(is_array($value)) {
|
||||
if (is_array($value)) {
|
||||
$this->value = $value;
|
||||
return true;
|
||||
$valid = true;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
|
||||
default:
|
||||
$this->value = $value;
|
||||
return true;
|
||||
$valid = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($valid && $this->choices) {
|
||||
if (!in_array($this->value, $this->choices)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $valid;
|
||||
}
|
||||
|
||||
private function getFormat(): ?string {
|
||||
if ($this->type === self::TYPE_TIME) {
|
||||
return self::TIME_FORMAT;
|
||||
} else if ($this->type === self::TYPE_DATE) {
|
||||
return self::DATE_FORMAT;
|
||||
} else if ($this->type === self::TYPE_DATE_TIME) {
|
||||
return self::DATE_TIME_FORMAT;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,9 +5,9 @@ namespace Core\API\Parameter;
|
||||
class StringType extends Parameter {
|
||||
|
||||
public int $maxLength;
|
||||
public function __construct(string $name, int $maxLength = -1, bool $optional = FALSE, ?string $defaultValue = NULL) {
|
||||
public function __construct(string $name, int $maxLength = -1, bool $optional = FALSE, ?string $defaultValue = NULL, ?array $choices = NULL) {
|
||||
$this->maxLength = $maxLength;
|
||||
parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue);
|
||||
parent::__construct($name, Parameter::TYPE_STRING, $optional, $defaultValue, $choices);
|
||||
}
|
||||
|
||||
public function parseParam($value): bool {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
namespace Core\API;
|
||||
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use Core\Driver\SQL\Expression\Distinct;
|
||||
use DateTime;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Condition\CondBool;
|
||||
@@ -16,24 +18,24 @@ class Stats extends Request {
|
||||
parent::__construct($context, $externalCall, array());
|
||||
}
|
||||
|
||||
private function getUserCount() {
|
||||
private function getUserCount(): int {
|
||||
$sql = $this->context->getSQL();
|
||||
$res = $sql->select($sql->count())->from("User")->execute();
|
||||
$res = $sql->select(new Count())->from("User")->execute();
|
||||
$this->success = $this->success && ($res !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
return ($this->success ? $res[0]["count"] : 0);
|
||||
return ($this->success ? intval($res[0]["count"]) : 0);
|
||||
}
|
||||
|
||||
private function getPageCount() {
|
||||
private function getPageCount(): int {
|
||||
$sql = $this->context->getSQL();
|
||||
$res = $sql->select($sql->count())->from("Route")
|
||||
$res = $sql->select(new Count())->from("Route")
|
||||
->where(new CondBool("active"))
|
||||
->execute();
|
||||
$this->success = $this->success && ($res !== FALSE);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
return ($this->success ? $res[0]["count"] : 0);
|
||||
return ($this->success ? intval($res[0]["count"]) : 0);
|
||||
}
|
||||
|
||||
private function checkSettings(): bool {
|
||||
@@ -55,7 +57,7 @@ class Stats extends Request {
|
||||
$date = new DateTime();
|
||||
$monthStart = $date->format("Ym00");
|
||||
$monthEnd = $date->modify("+1 month")->format("Ym00");
|
||||
$res = $sql->select($sql->count($sql->distinct("cookie")))
|
||||
$res = $sql->select(new Count(new Distinct("cookie")))
|
||||
->from("Visitor")
|
||||
->where(new Compare("day", $monthStart, ">="))
|
||||
->where(new Compare("day", $monthEnd, "<"))
|
||||
@@ -92,19 +94,22 @@ class Stats extends Request {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->result["userCount"] = $userCount;
|
||||
$this->result["pageCount"] = $pageCount;
|
||||
$this->result["visitors"] = $visitorStatistics;
|
||||
$this->result["visitorsTotal"] = $visitorCount;
|
||||
$this->result["server"] = array(
|
||||
"version" => WEBBASE_VERSION,
|
||||
"server" => $_SERVER["SERVER_SOFTWARE"] ?? "Unknown",
|
||||
"memory_usage" => memory_get_usage(),
|
||||
"load_avg" => $loadAvg,
|
||||
"database" => $this->context->getSQL()->getStatus(),
|
||||
"mail" => $this->mailConfigured,
|
||||
"reCaptcha" => $this->recaptchaConfigured
|
||||
);
|
||||
$this->result["data"] = [
|
||||
"userCount" => $userCount,
|
||||
"pageCount" => $pageCount,
|
||||
"visitors" => $visitorStatistics,
|
||||
"visitorsTotal" => $visitorCount,
|
||||
"server" => [
|
||||
"version" => WEBBASE_VERSION,
|
||||
"server" => $_SERVER["SERVER_SOFTWARE"] ?? "Unknown",
|
||||
"memory_usage" => memory_get_usage(),
|
||||
"load_avg" => $loadAvg,
|
||||
"database" => $this->context->getSQL()->getStatus(),
|
||||
"mail" => $this->mailConfigured,
|
||||
"reCaptcha" => $this->recaptchaConfigured
|
||||
],
|
||||
];
|
||||
|
||||
return $this->success;
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace Core\API\Template {
|
||||
|
||||
try {
|
||||
$this->result["html"] = $twigEnvironment->render($templateFile, $parameters);
|
||||
} catch (LoaderError | RuntimeError | SyntaxError $e) {
|
||||
} catch (LoaderError | RuntimeError | SyntaxError | \RuntimeException $e) {
|
||||
return $this->createError("Error rendering twig template: " . $e->getMessage());
|
||||
}
|
||||
|
||||
|
||||
109
Core/API/Traits/Pagination.trait.php
Normal file
109
Core/API/Traits/Pagination.trait.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace Core\API\Traits;
|
||||
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\Driver\SQL\Condition\Condition;
|
||||
use Core\Driver\SQL\SQL;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntityHandler;
|
||||
use Core\Objects\DatabaseEntity\Controller\DatabaseEntityQuery;
|
||||
use Core\Objects\DatabaseEntity\User;
|
||||
|
||||
trait Pagination {
|
||||
|
||||
static function getPaginationParameters(array $orderColumns): array {
|
||||
return [
|
||||
'page' => new Parameter('page', Parameter::TYPE_INT, true, 1),
|
||||
'count' => new Parameter('count', Parameter::TYPE_INT, true, 20),
|
||||
'orderBy' => new StringType('orderBy', -1, true, "id", $orderColumns),
|
||||
'sortOrder' => new StringType('sortOrder', -1, true, 'asc', ['asc', 'desc']),
|
||||
];
|
||||
}
|
||||
|
||||
function initPagination(SQL $sql, string $class, ?Condition $condition = null, int $maxPageSize = 100): bool {
|
||||
$this->paginationClass = $class;
|
||||
$this->paginationCondition = $condition;
|
||||
if (!$this->validateParameters($maxPageSize)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->entityCount = call_user_func("$this->paginationClass::count", $sql, $condition);
|
||||
if ($this->entityCount === false) {
|
||||
return $this->createError("Error fetching $this->paginationClass::count: " . $sql->getLastError());
|
||||
}
|
||||
|
||||
$pageCount = intval(ceil($this->entityCount / $this->pageSize));
|
||||
$this->page = min($this->page, $pageCount); // number of pages changed due to pageSize / filter
|
||||
|
||||
$this->result["pagination"] = [
|
||||
"current" => $this->page,
|
||||
"pageSize" => $this->pageSize,
|
||||
"pageCount" => $pageCount,
|
||||
"total" => $this->entityCount
|
||||
];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function validateParameters(int $maxCount = 100): bool {
|
||||
$this->page = $this->getParam("page");
|
||||
if ($this->page < 1) {
|
||||
return $this->createError("Invalid page count");
|
||||
}
|
||||
|
||||
$this->pageSize = $this->getParam("count");
|
||||
if ($this->pageSize < 1 || $this->pageSize > $maxCount) {
|
||||
return $this->createError("Invalid fetch count");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function createPaginationQuery(SQL $sql, array $additionalValues = []): DatabaseEntityQuery {
|
||||
$page = $this->getParam("page");
|
||||
$count = $this->getParam("count");
|
||||
$orderBy = $this->getParam("orderBy");
|
||||
$sortOrder = $this->getParam("sortOrder");
|
||||
|
||||
$baseQuery = call_user_func("$this->paginationClass::createBuilder", $sql, false);
|
||||
$entityQuery = $baseQuery
|
||||
->fetchEntities()
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count);
|
||||
|
||||
if ($this->paginationCondition) {
|
||||
$entityQuery->where($this->paginationCondition);
|
||||
}
|
||||
|
||||
if (!empty($additionalValues)) {
|
||||
foreach ($additionalValues as $additionalValue) {
|
||||
$entityQuery->addCustomValue($additionalValue);
|
||||
}
|
||||
}
|
||||
|
||||
if ($orderBy) {
|
||||
$handler = $baseQuery->getHandler();
|
||||
$baseTable = $handler->getTableName();
|
||||
$sortColumn = DatabaseEntityHandler::getColumnName($orderBy);
|
||||
$fullyQualifiedColumn = "$baseTable.$sortColumn";
|
||||
$selectedColumns = $baseQuery->getSelectValues();
|
||||
|
||||
if (in_array($sortColumn, $selectedColumns)) {
|
||||
$entityQuery->orderBy($sortColumn);
|
||||
} else if (in_array($fullyQualifiedColumn, $selectedColumns)) {
|
||||
$entityQuery->orderBy($fullyQualifiedColumn);
|
||||
} else {
|
||||
$entityQuery->orderBy($orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
if ($sortOrder === "asc") {
|
||||
$entityQuery->ascending();
|
||||
} else {
|
||||
$entityQuery->descending();
|
||||
}
|
||||
|
||||
return $entityQuery;
|
||||
}
|
||||
}
|
||||
@@ -132,11 +132,12 @@ namespace Core\API\User {
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\Template\Render;
|
||||
use Core\API\Traits\Pagination;
|
||||
use Core\API\UserAPI;
|
||||
use Core\API\VerifyCaptcha;
|
||||
use Core\Driver\SQL\Condition\CondBool;
|
||||
use Core\Driver\SQL\Condition\CondNot;
|
||||
use Core\Driver\SQL\Condition\CondOr;
|
||||
use Core\Driver\SQL\Expression\Alias;
|
||||
use Core\Objects\DatabaseEntity\Group;
|
||||
use Core\Objects\DatabaseEntity\UserToken;
|
||||
use Core\Driver\SQL\Column\Column;
|
||||
@@ -209,96 +210,65 @@ namespace Core\API\User {
|
||||
|
||||
class Fetch extends UserAPI {
|
||||
|
||||
use Pagination;
|
||||
|
||||
public function __construct(Context $context, $externalCall = false) {
|
||||
parent::__construct($context, $externalCall, array(
|
||||
'page' => new Parameter('page', Parameter::TYPE_INT, true, 1),
|
||||
'count' => new Parameter('count', Parameter::TYPE_INT, true, 20)
|
||||
));
|
||||
}
|
||||
|
||||
private function selectIds($page, $count): array|bool {
|
||||
$sql = $this->context->getSQL();
|
||||
$res = $sql->select("User.id")
|
||||
->from("User")
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count)
|
||||
->orderBy("User.id")
|
||||
->ascending()
|
||||
->execute();
|
||||
|
||||
$this->success = ($res !== NULL);
|
||||
$this->lastError = $sql->getLastError();
|
||||
|
||||
if ($this->success && is_array($res)) {
|
||||
return array_map(function ($row) {
|
||||
return intval($row["id"]);
|
||||
}, $res);
|
||||
}
|
||||
|
||||
return false;
|
||||
parent::__construct($context, $externalCall,
|
||||
self::getPaginationParameters(['id', 'name', 'email', 'groups', 'registeredAt'])
|
||||
);
|
||||
}
|
||||
|
||||
public function _execute(): bool {
|
||||
|
||||
$page = $this->getParam("page");
|
||||
if ($page < 1) {
|
||||
return $this->createError("Invalid page count");
|
||||
}
|
||||
|
||||
$count = $this->getParam("count");
|
||||
if ($count < 1 || $count > 50) {
|
||||
return $this->createError("Invalid fetch count");
|
||||
}
|
||||
|
||||
$condition = null;
|
||||
$currentUser = $this->context->getUser();
|
||||
$fullInfo = ($currentUser->hasGroup(Group::ADMIN) ||
|
||||
$currentUser->hasGroup(Group::SUPPORT));
|
||||
$currentUser->hasGroup(Group::SUPPORT));
|
||||
|
||||
$orderBy = $this->getParam("orderBy");
|
||||
$publicAttributes = ["id", "name", "fullName", "profilePicture", "email"]; // TODO: , "groupNames"];
|
||||
|
||||
$condition = null;
|
||||
if (!$fullInfo) {
|
||||
$condition = new CondOr(
|
||||
new Compare("User.id", $currentUser->getId()),
|
||||
new CondBool("User.confirmed")
|
||||
);
|
||||
|
||||
if ($orderBy && !in_array($orderBy, $publicAttributes)) {
|
||||
return $this->createError("Insufficient permissions for sorting by field '$orderBy'");
|
||||
}
|
||||
}
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
$userCount = User::count($sql, $condition);
|
||||
if ($userCount === false) {
|
||||
return $this->createError("Error fetching user count: " . $sql->getLastError());
|
||||
if (!$this->initPagination($sql, User::class, $condition)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$userQuery = User::createBuilder($sql, false)
|
||||
->orderBy("id")
|
||||
->ascending()
|
||||
->limit($count)
|
||||
->offset(($page - 1) * $count)
|
||||
->fetchEntities();
|
||||
|
||||
if ($condition) {
|
||||
$userQuery->where($condition);
|
||||
}
|
||||
$groupNames = new Alias(
|
||||
$sql->select(new JsonArrayAgg("name"))->from("Group")
|
||||
->leftJoin("NM_Group_User", "NM_Group_User.group_id", "Group.id")
|
||||
->whereEq("NM_Group_User.user_id", new Column("User.id")),
|
||||
"groups"
|
||||
);
|
||||
|
||||
$userQuery = $this->createPaginationQuery($sql, [$groupNames]);
|
||||
$users = User::findBy($userQuery);
|
||||
if ($users !== false) {
|
||||
if ($users !== false && $users !== null) {
|
||||
$this->result["users"] = [];
|
||||
|
||||
foreach ($users as $userId => $user) {
|
||||
$serialized = $user->jsonSerialize();
|
||||
|
||||
if (!$fullInfo && $userId !== $currentUser->getId()) {
|
||||
$publicAttributes = ["id", "name", "fullName", "profilePicture", "email", "groups"];
|
||||
foreach (array_keys($serialized) as $attr) {
|
||||
if (!in_array($attr, $publicAttributes)) {
|
||||
unset($serialized[$attr]);
|
||||
unset ($serialized[$attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->result["users"][$userId] = $serialized;
|
||||
$this->result["users"][] = $serialized;
|
||||
}
|
||||
|
||||
$this->result["pageCount"] = intval(ceil($userCount / $count));
|
||||
$this->result["totalCount"] = $userCount;
|
||||
} else {
|
||||
return $this->createError("Error fetching users: " . $sql->getLastError());
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Core\API\Visitors {
|
||||
use Core\API\Parameter\Parameter;
|
||||
use Core\API\Parameter\StringType;
|
||||
use Core\API\VisitorsAPI;
|
||||
use Core\Driver\SQL\Expression\Count;
|
||||
use DateTime;
|
||||
use Core\Driver\SQL\Condition\Compare;
|
||||
use Core\Driver\SQL\Expression\Add;
|
||||
@@ -82,7 +83,7 @@ namespace Core\API\Visitors {
|
||||
$type = $this->getParam("type");
|
||||
|
||||
$sql = $this->context->getSQL();
|
||||
$query = $sql->select($sql->count(), "day")
|
||||
$query = $sql->select(new Count(), "day")
|
||||
->from("Visitor")
|
||||
->whereGt("count", 1)
|
||||
->groupBy("day")
|
||||
|
||||
Reference in New Issue
Block a user