CLI: API templates
This commit is contained in:
parent
a7dc4c0d2f
commit
10f7025569
@ -248,7 +248,7 @@ abstract class Request {
|
|||||||
|
|
||||||
// Check for permission
|
// Check for permission
|
||||||
$req = new \Core\API\Permission\Check($this->context);
|
$req = new \Core\API\Permission\Check($this->context);
|
||||||
$this->success = $req->execute(array("method" => self::getEndpoint()));
|
$this->success = $req->execute(["method" => self::getEndpoint()]);
|
||||||
$this->lastError = $req->getLastError();
|
$this->lastError = $req->getLastError();
|
||||||
if (!$this->success) {
|
if (!$this->success) {
|
||||||
return false;
|
return false;
|
||||||
@ -336,11 +336,19 @@ abstract class Request {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$isNestedAPI = $reflectionClass->getParentClass()->getName() !== Request::class;
|
$parentClass = $reflectionClass->getParentClass();
|
||||||
|
if ($parentClass === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$isNestedAPI = $parentClass->getName() !== Request::class;
|
||||||
if (!$isNestedAPI) {
|
if (!$isNestedAPI) {
|
||||||
# e.g. /api/stats or /api/info
|
# e.g. /api/stats or /api/info
|
||||||
$methodName = $reflectionClass->getShortName();
|
$methodName = $reflectionClass->getShortName();
|
||||||
return $prefix . lcfirst($methodName);
|
return $prefix . lcfirst($methodName);
|
||||||
|
} else if ($parentClass->getName() === \TestRequest::class) {
|
||||||
|
$methodName = $reflectionClass->getShortName();
|
||||||
|
return $prefix . "/e2e-test/" . lcfirst($methodName);
|
||||||
} else {
|
} else {
|
||||||
# e.g. /api/user/login
|
# e.g. /api/user/login
|
||||||
$methodClass = $reflectionClass;
|
$methodClass = $reflectionClass;
|
||||||
@ -348,6 +356,10 @@ abstract class Request {
|
|||||||
while (!endsWith($nestedClass->getName(), "API")) {
|
while (!endsWith($nestedClass->getName(), "API")) {
|
||||||
$methodClass = $nestedClass;
|
$methodClass = $nestedClass;
|
||||||
$nestedClass = $nestedClass->getParentClass();
|
$nestedClass = $nestedClass->getParentClass();
|
||||||
|
|
||||||
|
if (!$nestedClass) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$nestedAPI = substr(lcfirst($nestedClass->getShortName()), 0, -3);
|
$nestedAPI = substr(lcfirst($nestedClass->getShortName()), 0, -3);
|
||||||
|
@ -102,7 +102,7 @@ namespace Core\API {
|
|||||||
if ($count === 1) {
|
if ($count === 1) {
|
||||||
return $string;
|
return $string;
|
||||||
} else {
|
} else {
|
||||||
return "the next $count ${string}s";
|
return "the next $count {$string}s";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,7 +222,7 @@ namespace Core\API\User {
|
|||||||
|
|
||||||
public function __construct(Context $context, $externalCall = false) {
|
public function __construct(Context $context, $externalCall = false) {
|
||||||
parent::__construct($context, $externalCall,
|
parent::__construct($context, $externalCall,
|
||||||
self::getPaginationParameters(['id', 'name', 'fullName', 'email', 'groups', 'registeredAt', 'confirmed'],
|
self::getPaginationParameters(['id', 'name', 'fullName', 'email', 'groups', 'registeredAt', 'active', 'confirmed'],
|
||||||
'id', 'asc')
|
'id', 'asc')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -510,7 +510,7 @@ class RowIteratorMySQL extends RowIterator {
|
|||||||
return $row;
|
return $row;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function rewind() {
|
public function rewind(): void {
|
||||||
if ($this->useCache) {
|
if ($this->useCache) {
|
||||||
$this->rowIndex = 0;
|
$this->rowIndex = 0;
|
||||||
} else if ($this->rowIndex !== 0) {
|
} else if ($this->rowIndex !== 0) {
|
||||||
|
@ -21,15 +21,15 @@ abstract class RowIterator implements \Iterator {
|
|||||||
protected abstract function getNumRows(): int;
|
protected abstract function getNumRows(): int;
|
||||||
protected abstract function fetchRow(int $index): array;
|
protected abstract function fetchRow(int $index): array;
|
||||||
|
|
||||||
public function current() {
|
public function current(): array {
|
||||||
return $this->fetchRow($this->rowIndex);
|
return $this->fetchRow($this->rowIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function next() {
|
public function next(): void {
|
||||||
$this->rowIndex++;
|
$this->rowIndex++;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function key() {
|
public function key(): int {
|
||||||
return $this->rowIndex;
|
return $this->rowIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,4 +68,20 @@ return [
|
|||||||
"color" => "Farbe",
|
"color" => "Farbe",
|
||||||
"logged_in_as" => "Eingeloggt als",
|
"logged_in_as" => "Eingeloggt als",
|
||||||
"active" => "Aktiv",
|
"active" => "Aktiv",
|
||||||
|
"group" => "Gruppe",
|
||||||
|
|
||||||
|
# dialogs
|
||||||
|
"fetch_group_members_error" => "Fehler beim Holen der Gruppenmitglieder",
|
||||||
|
"remove_group_member_error" => "Fehler beim Entfernen des Gruppenmitglieds",
|
||||||
|
"add_group_member_error" => "Fehler beim Hinzufügen des Gruppenmitglieds",
|
||||||
|
"create_group_error" => "Fehler beim Erstellen der Gruppe",
|
||||||
|
"update_group_error" => "Error beim Aktualisieren der Gruppe",
|
||||||
|
"delete_group_error" => "Error beim Löschen der Gruppe",
|
||||||
|
"search_users_error" => "Fehler beim Suchen des Benutzers",
|
||||||
|
"delete_group_title" => "Gruppe löschen",
|
||||||
|
"delete_group_text" => "Möchten Sie diese Gruppe wirklich löschen? Dies kann nicht rückgängig gemacht werden.",
|
||||||
|
"remove_group_member_title" => "Mitglied entfernen",
|
||||||
|
"remove_group_member_text" => "Möchten Sie wirklich den Benutzer '%s' von dieser Gruppe entfernen?",
|
||||||
|
"add_group_member_title" => "Mitglied hinzufügen",
|
||||||
|
"add_group_member_text" => "Einen Benutzer suchen um ihn der Gruppe hinzuzufügen",
|
||||||
];
|
];
|
@ -38,4 +38,6 @@ return [
|
|||||||
"remove_route_error" => "Fehler beim Entfernen der Route",
|
"remove_route_error" => "Fehler beim Entfernen der Route",
|
||||||
"regenerate_router_cache_error" => "Fehler beim Erzeugen des Router Caches",
|
"regenerate_router_cache_error" => "Fehler beim Erzeugen des Router Caches",
|
||||||
"regenerate_router_cache_success" => "Router Cache erfolgreich erzeugt",
|
"regenerate_router_cache_success" => "Router Cache erfolgreich erzeugt",
|
||||||
|
"delete_route_title" => "Route löschen",
|
||||||
|
"delete_route_text" => "Möchten Sie wirklich die folgende Route löschen?",
|
||||||
];
|
];
|
@ -68,4 +68,20 @@ return [
|
|||||||
"color" => "Color",
|
"color" => "Color",
|
||||||
"logged_in_as" => "Logged in as",
|
"logged_in_as" => "Logged in as",
|
||||||
"active" => "Active",
|
"active" => "Active",
|
||||||
|
"group" => "Group",
|
||||||
|
|
||||||
|
# dialogs
|
||||||
|
"fetch_group_members_error" => "Error fetching group members",
|
||||||
|
"remove_group_member_error" => "Error removing group member",
|
||||||
|
"add_group_member_error" => "Error adding member",
|
||||||
|
"create_group_error" => "Error creating group",
|
||||||
|
"update_group_error" => "Error updating group",
|
||||||
|
"delete_group_error" => "Error deleting group",
|
||||||
|
"search_users_error" => "Error searching users",
|
||||||
|
"delete_group_title" => "Delete Group",
|
||||||
|
"delete_group_text" => "Do you really want to delete this group? This action cannot be undone.",
|
||||||
|
"remove_group_member_title" => "Remove member",
|
||||||
|
"remove_group_member_text" => "Do you really want to remove user '%s' from this group?",
|
||||||
|
"add_group_member_title" => "Add member",
|
||||||
|
"add_group_member_text" => "Search a user to add to the group",
|
||||||
];
|
];
|
@ -38,4 +38,6 @@ return [
|
|||||||
"remove_route_error" => "Error removing route",
|
"remove_route_error" => "Error removing route",
|
||||||
"regenerate_router_cache_error" => "Error regenerating router cache",
|
"regenerate_router_cache_error" => "Error regenerating router cache",
|
||||||
"regenerate_router_cache_success" => "Router cache successfully regenerated",
|
"regenerate_router_cache_success" => "Router cache successfully regenerated",
|
||||||
|
"delete_route_title" => "Delete Route",
|
||||||
|
"delete_route_text" => "Do you really want to delete the following route?",
|
||||||
];
|
];
|
138
cli.php
138
cli.php
@ -7,6 +7,7 @@ include_once 'Core/core.php';
|
|||||||
require_once 'Core/datetime.php';
|
require_once 'Core/datetime.php';
|
||||||
include_once 'Core/constants.php';
|
include_once 'Core/constants.php';
|
||||||
|
|
||||||
|
use Core\API\Request;
|
||||||
use Core\Configuration\DatabaseScript;
|
use Core\Configuration\DatabaseScript;
|
||||||
use Core\Driver\SQL\Column\Column;
|
use Core\Driver\SQL\Column\Column;
|
||||||
use Core\Driver\SQL\Condition\Compare;
|
use Core\Driver\SQL\Condition\Compare;
|
||||||
@ -16,6 +17,7 @@ use Core\Driver\SQL\SQL;
|
|||||||
use Core\Objects\ConnectionData;
|
use Core\Objects\ConnectionData;
|
||||||
|
|
||||||
// TODO: is this available in all installations?
|
// TODO: is this available in all installations?
|
||||||
|
use Core\Objects\Context;
|
||||||
use JetBrains\PhpStorm\NoReturn;
|
use JetBrains\PhpStorm\NoReturn;
|
||||||
|
|
||||||
function printLine(string $line = ""): void {
|
function printLine(string $line = ""): void {
|
||||||
@ -68,8 +70,13 @@ function connectSQL(): ?SQL {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function printHelp(array $argv): void {
|
function printHelp(array $argv): void {
|
||||||
|
global $registeredCommands;
|
||||||
printLine("=== WebBase CLI tool ===");
|
printLine("=== WebBase CLI tool ===");
|
||||||
printLine("Usage: " . $argv[0]);
|
printLine("Usage: " . $argv[0] . " [action] <args>");
|
||||||
|
foreach ($registeredCommands as $command => $data) {
|
||||||
|
$description = $data["description"] ?? "";
|
||||||
|
printLine(" - $command: $description");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyPatch(\Core\Driver\SQL\SQL $sql, string $patchName): bool {
|
function applyPatch(\Core\Driver\SQL\SQL $sql, string $patchName): bool {
|
||||||
@ -660,7 +667,7 @@ function onImpersonate($argv): void {
|
|||||||
|
|
||||||
function onFrontend(array $argv): void {
|
function onFrontend(array $argv): void {
|
||||||
if (count($argv) < 3) {
|
if (count($argv) < 3) {
|
||||||
_exit("Usage: cli.php frontend <build|add|ls> [options...]");
|
_exit("Usage: cli.php frontend <build|add|rm|ls> [options...]");
|
||||||
}
|
}
|
||||||
|
|
||||||
$reactRoot = realpath(WEBROOT . "/react/");
|
$reactRoot = realpath(WEBROOT . "/react/");
|
||||||
@ -800,17 +807,126 @@ function onFrontend(array $argv): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onAPI(array $argv): void {
|
||||||
|
if (count($argv) < 3) {
|
||||||
|
_exit("Usage: cli.php api <ls|add> [options...]");
|
||||||
|
}
|
||||||
|
|
||||||
|
$action = $argv[2] ?? null;
|
||||||
|
if ($action === "ls") {
|
||||||
|
$endpoints = Request::getApiEndpoints();
|
||||||
|
foreach ($endpoints as $endpoint => $class) {
|
||||||
|
$className = $class->getName();
|
||||||
|
printLine(" - $className: $endpoint");
|
||||||
|
}
|
||||||
|
// var_dump($endpoints);
|
||||||
|
} else if ($action === "add") {
|
||||||
|
echo "API-Name: ";
|
||||||
|
$methodNames = [];
|
||||||
|
$apiName = ucfirst(trim(fgets(STDIN)));
|
||||||
|
if (!preg_match("/[a-zA-Z_-]/", $apiName)) {
|
||||||
|
_exit("Invalid API-Name, should be [a-zA-Z_-]");
|
||||||
|
}
|
||||||
|
|
||||||
|
printLine("Do you want to add nested methods? Leave blank to skip.");
|
||||||
|
while (true) {
|
||||||
|
echo "Method name: ";
|
||||||
|
$methodName = ucfirst(trim(fgets(STDIN)));
|
||||||
|
if ($methodName) {
|
||||||
|
if (!preg_match("/[a-zA-Z_-]/", $methodName)) {
|
||||||
|
printLine("Invalid method name, should be [a-zA-Z_-]");
|
||||||
|
} else if (in_array($methodName, $methodNames)) {
|
||||||
|
printLine("You already added this method.");
|
||||||
|
} else {
|
||||||
|
$methodNames[] = $methodName;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($methodNames)) {
|
||||||
|
$fileName = "{$apiName}API.class.php";
|
||||||
|
$methods = implode("\n\n", array_map(function ($methodName) use ($apiName) {
|
||||||
|
return " class $methodName extends {$apiName}API {
|
||||||
|
|
||||||
|
public function __construct(Context \$context, bool \$externalCall = false) {
|
||||||
|
parent::__construct(\$context, \$externalCall, []);
|
||||||
|
// TODO: auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _execute(): bool {
|
||||||
|
// TODO: auto-generated method stub
|
||||||
|
return \$this->success;
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
}, $methodNames));
|
||||||
|
$content = "<?php
|
||||||
|
|
||||||
|
namespace Site\API {
|
||||||
|
|
||||||
|
use Core\API\Request;
|
||||||
|
use Core\Objects\Context;
|
||||||
|
|
||||||
|
abstract class {$apiName}API extends Request {
|
||||||
|
public function __construct(Context \$context, bool \$externalCall = false, array \$params = []) {
|
||||||
|
parent::__construct(\$context, \$externalCall, \$params);
|
||||||
|
// TODO: auto-generated method stub
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Site\API\\$apiName {
|
||||||
|
|
||||||
|
use Core\Objects\Context;
|
||||||
|
use Site\API\TestAPI;
|
||||||
|
|
||||||
|
$methods
|
||||||
|
}";
|
||||||
|
} else {
|
||||||
|
$fileName = "$apiName.class.php";
|
||||||
|
$content = "<?php
|
||||||
|
|
||||||
|
namespace Site\API;
|
||||||
|
|
||||||
|
use Core\API\Request;
|
||||||
|
use Core\Objects\Context;
|
||||||
|
|
||||||
|
class $apiName extends Request {
|
||||||
|
|
||||||
|
public function __construct(Context \$context, bool \$externalCall = false) {
|
||||||
|
parent::__construct(\$context, \$externalCall, []);
|
||||||
|
// TODO: auto-generated method stub
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function _execute(): bool {
|
||||||
|
// TODO: auto-generated method stub
|
||||||
|
return \$this->success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
";
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = implode(DIRECTORY_SEPARATOR, [WEBROOT, "Site", "API", $fileName]);
|
||||||
|
file_put_contents($path, $content);
|
||||||
|
printLine("Successfully created API-template: $path");
|
||||||
|
} else {
|
||||||
|
_exit("Usage: cli.php api <ls|add> [options...]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$argv = $_SERVER['argv'];
|
$argv = $_SERVER['argv'];
|
||||||
$registeredCommands = [
|
$registeredCommands = [
|
||||||
"help" => ["handler" => "printHelp"],
|
"help" => ["handler" => "printHelp", "description" => "prints this help page"],
|
||||||
"db" => ["handler" => "handleDatabase"],
|
"db" => ["handler" => "handleDatabase", "description" => "database actions like importing, exporting and shell"],
|
||||||
"routes" => ["handler" => "onRoutes"],
|
"routes" => ["handler" => "onRoutes", "description" => "view and modify routes"],
|
||||||
"maintenance" => ["handler" => "onMaintenance"],
|
"maintenance" => ["handler" => "onMaintenance", "description" => "toggle maintenance mode"],
|
||||||
"test" => ["handler" => "onTest"],
|
"test" => ["handler" => "onTest", "description" => "run unit and integration tests", "requiresDocker" => true],
|
||||||
"mail" => ["handler" => "onMail"],
|
"mail" => ["handler" => "onMail", "description" => "send mails and process the pipeline"],
|
||||||
"settings" => ["handler" => "onSettings"],
|
"settings" => ["handler" => "onSettings", "description" => "change and view settings"],
|
||||||
"impersonate" => ["handler" => "onImpersonate", "requiresDocker" => true],
|
"impersonate" => ["handler" => "onImpersonate", "description" => "create a session and print cookies and csrf tokens", "requiresDocker" => true],
|
||||||
"frontend" => ["handler" => "onFrontend"],
|
"frontend" => ["handler" => "onFrontend", "description" => "build and manage frontend modules"],
|
||||||
|
"api" => ["handler" => "onAPI", "description" => "view and create API endpoints"],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
@ -73,7 +73,7 @@ export default function EditGroupView(props) {
|
|||||||
setMembers(res.users);
|
setMembers(res.users);
|
||||||
pagination.update(res.pagination);
|
pagination.update(res.pagination);
|
||||||
} else {
|
} else {
|
||||||
props.showDialog(res.msg, "Error fetching group members");
|
props.showDialog(res.msg, L("account.fetch_group_members_error"));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -85,47 +85,16 @@ export default function EditGroupView(props) {
|
|||||||
let newMembers = members.filter(u => u.id !== userId);
|
let newMembers = members.filter(u => u.id !== userId);
|
||||||
setMembers(newMembers);
|
setMembers(newMembers);
|
||||||
} else {
|
} else {
|
||||||
props.showDialog(data.msg, "Error removing group member");
|
props.showDialog(data.msg, L("account.remove_group_member_error"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [api, groupId, members]);
|
}, [api, groupId, members]);
|
||||||
|
|
||||||
const onSave = useCallback(() => {
|
|
||||||
setSaving(true);
|
|
||||||
if (isNewGroup) {
|
|
||||||
api.createGroup(group.name, group.color).then(data => {
|
|
||||||
setSaving(false);
|
|
||||||
if (!data.success) {
|
|
||||||
props.showDialog(data.msg, "Error creating group");
|
|
||||||
} else {
|
|
||||||
navigate(`/admin/group/${data.id}`)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
api.updateGroup(groupId, group.name, group.color).then(data => {
|
|
||||||
setSaving(false);
|
|
||||||
if (!data.success) {
|
|
||||||
props.showDialog(data.msg, "Error updating group");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, [api, groupId, isNewGroup, group]);
|
|
||||||
|
|
||||||
const onSearchUser = useCallback((async (query) => {
|
|
||||||
let data = await api.searchUser(query);
|
|
||||||
if (!data.success) {
|
|
||||||
props.showDialog(data.msg, "Error searching users");
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return data.users;
|
|
||||||
}), [api]);
|
|
||||||
|
|
||||||
const onAddMember = useCallback(() => {
|
const onAddMember = useCallback(() => {
|
||||||
if (selectedUser) {
|
if (selectedUser) {
|
||||||
api.addGroupMember(groupId, selectedUser.id).then(data => {
|
api.addGroupMember(groupId, selectedUser.id).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
props.showDialog(data.msg, "Error adding member");
|
props.showDialog(data.msg, L("account.add_group_member_error"));
|
||||||
} else {
|
} else {
|
||||||
let newMembers = [...members];
|
let newMembers = [...members];
|
||||||
newMembers.push(selectedUser);
|
newMembers.push(selectedUser);
|
||||||
@ -136,10 +105,41 @@ export default function EditGroupView(props) {
|
|||||||
}
|
}
|
||||||
}, [api, groupId, selectedUser])
|
}, [api, groupId, selectedUser])
|
||||||
|
|
||||||
|
const onSave = useCallback(() => {
|
||||||
|
setSaving(true);
|
||||||
|
if (isNewGroup) {
|
||||||
|
api.createGroup(group.name, group.color).then(data => {
|
||||||
|
setSaving(false);
|
||||||
|
if (!data.success) {
|
||||||
|
props.showDialog(data.msg, L("account.create_group_error"));
|
||||||
|
} else {
|
||||||
|
navigate(`/admin/group/${data.id}`)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
api.updateGroup(groupId, group.name, group.color).then(data => {
|
||||||
|
setSaving(false);
|
||||||
|
if (!data.success) {
|
||||||
|
props.showDialog(data.msg, L("account.update_group_error"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [api, groupId, isNewGroup, group]);
|
||||||
|
|
||||||
|
const onSearchUser = useCallback((async (query) => {
|
||||||
|
let data = await api.searchUser(query);
|
||||||
|
if (!data.success) {
|
||||||
|
props.showDialog(data.msg, L("account.search_users_error"));
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return data.users;
|
||||||
|
}), [api]);
|
||||||
|
|
||||||
const onDeleteGroup = useCallback(() => {
|
const onDeleteGroup = useCallback(() => {
|
||||||
api.deleteGroup(groupId).then(data => {
|
api.deleteGroup(groupId).then(data => {
|
||||||
if (!data.success) {
|
if (!data.success) {
|
||||||
props.showDialog(data.msg, "Error deleting group");
|
props.showDialog(data.msg, L("account.delete_group_error"));
|
||||||
} else {
|
} else {
|
||||||
navigate("/admin/groups");
|
navigate("/admin/groups");
|
||||||
}
|
}
|
||||||
@ -150,6 +150,15 @@ export default function EditGroupView(props) {
|
|||||||
onFetchGroup();
|
onFetchGroup();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const complementaryColor = (color) => {
|
||||||
|
if (color.startsWith("#")) {
|
||||||
|
color = color.substring(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let numericValue = parseInt(color, 16);
|
||||||
|
return "#" + (0xFFFFFF - numericValue).toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
if (group === null) {
|
if (group === null) {
|
||||||
return <CircularProgress />
|
return <CircularProgress />
|
||||||
}
|
}
|
||||||
@ -221,8 +230,8 @@ export default function EditGroupView(props) {
|
|||||||
variant={"outlined"} color={"secondary"}
|
variant={"outlined"} color={"secondary"}
|
||||||
onClick={() => setDialogData({
|
onClick={() => setDialogData({
|
||||||
open: true,
|
open: true,
|
||||||
title: L("Delete Group"),
|
title: L("account.delete_group_title"),
|
||||||
message: L("Do you really want to delete this group? This action cannot be undone."),
|
message: L("account.delete_group_text"),
|
||||||
onOption: option => option === 0 && onDeleteGroup()
|
onOption: option => option === 0 && onDeleteGroup()
|
||||||
})}>
|
})}>
|
||||||
{L("general.delete")}
|
{L("general.delete")}
|
||||||
@ -258,8 +267,8 @@ export default function EditGroupView(props) {
|
|||||||
disabled: !api.hasPermission("groups/removeMember"),
|
disabled: !api.hasPermission("groups/removeMember"),
|
||||||
onClick: (entry) => setDialogData({
|
onClick: (entry) => setDialogData({
|
||||||
open: true,
|
open: true,
|
||||||
title: L("Remove member"),
|
title: L("account.remove_group_member_title"),
|
||||||
message: sprintf(L("Do you really want to remove user '%s' from this group?"), entry.fullName || entry.name),
|
message: sprintf(L("account.remove_group_member_text"), entry.fullName || entry.name),
|
||||||
onOption: (option) => option === 0 && onRemoveMember(entry.id)
|
onOption: (option) => option === 0 && onRemoveMember(entry.id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -270,8 +279,8 @@ export default function EditGroupView(props) {
|
|||||||
variant={"outlined"} disabled={!api.hasPermission("groups/addMember")}
|
variant={"outlined"} disabled={!api.hasPermission("groups/addMember")}
|
||||||
onClick={() => setDialogData({
|
onClick={() => setDialogData({
|
||||||
open: true,
|
open: true,
|
||||||
title: L("Add member"),
|
title: L("account.add_group_member_title"),
|
||||||
message: "Search a user to add to the group",
|
message: L("account.add_group_member_text"),
|
||||||
inputs: [
|
inputs: [
|
||||||
{
|
{
|
||||||
type: "custom", name: "search", element: SearchField,
|
type: "custom", name: "search", element: SearchField,
|
||||||
|
@ -193,10 +193,10 @@ export default function RouteListView(props) {
|
|||||||
color={"secondary"}
|
color={"secondary"}
|
||||||
onClick={() => setDialogData({
|
onClick={() => setDialogData({
|
||||||
open: true,
|
open: true,
|
||||||
title: L("Delete Route"),
|
title: L("routes.delete_route_title"),
|
||||||
message: L("Do you really want to delete the following route?"),
|
message: L("routes.delete_route_text"),
|
||||||
inputs: [
|
inputs: [
|
||||||
{ type: "text", value: route.pattern, disabled: true}
|
{ type: "text", name: "pattern", value: route.pattern, disabled: true}
|
||||||
],
|
],
|
||||||
options: [L("general.ok"), L("general.cancel")],
|
options: [L("general.ok"), L("general.cancel")],
|
||||||
onOption: btn => btn === 0 && onDeleteRoute(route.id)
|
onOption: btn => btn === 0 && onDeleteRoute(route.id)
|
||||||
|
@ -56,8 +56,8 @@ class RequestTest extends \PHPUnit\Framework\TestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
||||||
runkit7_function_rename($functionName, "__orig_${functionName}_impl");
|
runkit7_function_rename($functionName, "__orig_{$functionName}_impl");
|
||||||
runkit7_function_rename("__new_${functionName}_impl", $functionName);
|
runkit7_function_rename("__new_{$functionName}_impl", $functionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ class RequestTest extends \PHPUnit\Framework\TestCase {
|
|||||||
RequestTest::$CONTEXT->getSQL()?->close();
|
RequestTest::$CONTEXT->getSQL()?->close();
|
||||||
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
foreach (self::FUNCTION_OVERRIDES as $functionName) {
|
||||||
runkit7_function_remove($functionName);
|
runkit7_function_remove($functionName);
|
||||||
runkit7_function_rename("__orig_${functionName}_impl", $functionName);
|
runkit7_function_rename("__orig_{$functionName}_impl", $functionName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,6 +99,7 @@ class RequestTest extends \PHPUnit\Framework\TestCase {
|
|||||||
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "POST"), $allMethodsAllowed->getLastError());
|
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "POST"), $allMethodsAllowed->getLastError());
|
||||||
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "PUT"), $allMethodsAllowed->getLastError());
|
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "PUT"), $allMethodsAllowed->getLastError());
|
||||||
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "DELETE"), $allMethodsAllowed->getLastError());
|
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "DELETE"), $allMethodsAllowed->getLastError());
|
||||||
|
$this->assertFalse($this->simulateRequest($allMethodsAllowed, "NONEXISTENT"), $allMethodsAllowed->getLastError());
|
||||||
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "OPTIONS"), $allMethodsAllowed->getLastError());
|
$this->assertTrue($this->simulateRequest($allMethodsAllowed, "OPTIONS"), $allMethodsAllowed->getLastError());
|
||||||
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
$this->assertEquals(204, self::$SENT_STATUS_CODE);
|
||||||
$this->assertEquals(["Allow" => "OPTIONS, GET, POST"], self::$SENT_HEADERS);
|
$this->assertEquals(["Allow" => "OPTIONS, GET, POST"], self::$SENT_HEADERS);
|
||||||
@ -156,6 +157,10 @@ abstract class TestRequest extends Request {
|
|||||||
protected function _execute(): bool {
|
protected function _execute(): bool {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getEndpoint(string $prefix = ""): ?string {
|
||||||
|
return "test";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RequestAllMethods extends TestRequest {
|
class RequestAllMethods extends TestRequest {
|
||||||
|
Loading…
Reference in New Issue
Block a user