feat(admin): preserve list return target for back and close actions
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
use MintyPHP\Http\Input\FormErrors;
|
||||
use MintyPHP\Http\Input\RequestInput;
|
||||
use MintyPHP\Http\Input\RequestInputFactory;
|
||||
use MintyPHP\Http\Request;
|
||||
use MintyPHP\Support\Flash;
|
||||
|
||||
function requestInput(): RequestInput
|
||||
@@ -36,3 +37,160 @@ function flashFormErrors(FormErrors $errors, ?string $scope = null, string $keyP
|
||||
$index++;
|
||||
}
|
||||
}
|
||||
|
||||
function requestExtractNestedReturnValue(mixed $value): string
|
||||
{
|
||||
if (is_string($value)) {
|
||||
return trim($value);
|
||||
}
|
||||
if (!is_array($value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for ($i = count($value) - 1; $i >= 0; $i--) {
|
||||
$item = $value[$i] ?? null;
|
||||
if (is_string($item) && trim($item) !== '') {
|
||||
return trim($item);
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
function requestLooksLikeDetailPath(string $path): bool
|
||||
{
|
||||
$path = trim($path, '/');
|
||||
if ($path === '') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$segments = array_values(array_filter(explode('/', $path), static fn (string $segment): bool => $segment !== ''));
|
||||
return in_array('edit', $segments, true) || in_array('create', $segments, true);
|
||||
}
|
||||
|
||||
function requestNormalizeSafeInternalTarget(string $target, int $maxDepth = 5): string
|
||||
{
|
||||
$target = trim($target);
|
||||
if ($target === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$parts = parse_url($target);
|
||||
if (
|
||||
!is_array($parts)
|
||||
|| ($parts['scheme'] ?? '') !== ''
|
||||
|| ($parts['host'] ?? '') !== ''
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$safeTarget = Request::safeReturnTarget($target);
|
||||
if ($safeTarget === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$safeParts = parse_url($safeTarget);
|
||||
if (!is_array($safeParts)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$path = Request::stripLocale(Request::stripBasePath((string) ($safeParts['path'] ?? '')));
|
||||
if ($path === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$queryParams = [];
|
||||
parse_str((string) ($safeParts['query'] ?? ''), $queryParams);
|
||||
if (!is_array($queryParams)) {
|
||||
$queryParams = [];
|
||||
}
|
||||
$nestedReturnTarget = requestExtractNestedReturnValue($queryParams['return'] ?? null);
|
||||
unset($queryParams['return']);
|
||||
$query = http_build_query($queryParams);
|
||||
$normalizedOuterTarget = $path . ($query !== '' ? '?' . $query : '');
|
||||
|
||||
if ($maxDepth > 0 && $nestedReturnTarget !== '' && requestLooksLikeDetailPath($path)) {
|
||||
$resolvedNestedTarget = requestNormalizeSafeInternalTarget($nestedReturnTarget, $maxDepth - 1);
|
||||
if ($resolvedNestedTarget !== '') {
|
||||
return $resolvedNestedTarget;
|
||||
}
|
||||
}
|
||||
|
||||
return $normalizedOuterTarget;
|
||||
}
|
||||
|
||||
function requestResolveReturnTargetFromInput(RequestInput $input, string $fallback = ''): string
|
||||
{
|
||||
$normalizedFallback = requestNormalizeSafeInternalTarget($fallback);
|
||||
|
||||
$candidate = $input->body('return', '');
|
||||
if (!is_string($candidate)) {
|
||||
$candidate = '';
|
||||
}
|
||||
$candidate = trim($candidate);
|
||||
|
||||
if ($candidate === '') {
|
||||
$candidate = $input->query('return', '');
|
||||
if (!is_string($candidate)) {
|
||||
$candidate = '';
|
||||
}
|
||||
$candidate = trim($candidate);
|
||||
}
|
||||
|
||||
if ($candidate !== '') {
|
||||
$resolved = requestNormalizeSafeInternalTarget($candidate);
|
||||
if ($resolved !== '') {
|
||||
if ($normalizedFallback === '') {
|
||||
return $resolved;
|
||||
}
|
||||
$resolvedPath = (string) parse_url($resolved, PHP_URL_PATH);
|
||||
if ($resolvedPath !== '' && !requestLooksLikeDetailPath($resolvedPath)) {
|
||||
return $resolved;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $normalizedFallback;
|
||||
}
|
||||
|
||||
function requestResolveReturnTarget(string $fallback = ''): string
|
||||
{
|
||||
return requestResolveReturnTargetFromInput(requestInput(), $fallback);
|
||||
}
|
||||
|
||||
function requestPathWithReturnTarget(string $path, string $returnTarget = ''): string
|
||||
{
|
||||
$resolvedPath = requestNormalizeSafeInternalTarget($path);
|
||||
if ($resolvedPath === '') {
|
||||
return '';
|
||||
}
|
||||
|
||||
$returnTarget = trim($returnTarget);
|
||||
if ($returnTarget === '') {
|
||||
return $resolvedPath;
|
||||
}
|
||||
|
||||
$normalizedReturnTarget = requestNormalizeSafeInternalTarget($returnTarget);
|
||||
if ($normalizedReturnTarget === '') {
|
||||
return $resolvedPath;
|
||||
}
|
||||
|
||||
$parts = parse_url($resolvedPath);
|
||||
if (!is_array($parts)) {
|
||||
return '';
|
||||
}
|
||||
$pathValue = Request::stripLocale(Request::stripBasePath((string) ($parts['path'] ?? '')));
|
||||
if ($pathValue === '') {
|
||||
return '';
|
||||
}
|
||||
$queryParams = [];
|
||||
parse_str((string) ($parts['query'] ?? ''), $queryParams);
|
||||
if (!is_array($queryParams)) {
|
||||
$queryParams = [];
|
||||
}
|
||||
unset($queryParams['return']);
|
||||
$queryParams['return'] = $normalizedReturnTarget;
|
||||
$query = http_build_query($queryParams);
|
||||
|
||||
return $pathValue . ($query !== '' ? '?' . $query : '');
|
||||
}
|
||||
|
||||
@@ -11,6 +11,9 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/departments');
|
||||
$createTarget = requestPathWithReturnTarget('admin/departments/create', $returnTarget);
|
||||
$tenantService = app(\MintyPHP\Service\Tenant\TenantService::class);
|
||||
$directoryScopeGateway = app(\MintyPHP\Service\Directory\DirectoryScopeGateway::class);
|
||||
$departmentService = app(\MintyPHP\Service\Org\DepartmentService::class);
|
||||
@@ -48,8 +51,8 @@ if ($allowedTenantIds) {
|
||||
}
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), 'admin/departments/create', 'csrf_expired');
|
||||
Router::redirect('admin/departments/create');
|
||||
Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired');
|
||||
Router::redirect($createTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -70,14 +73,14 @@ if ($request->hasBody('description')) {
|
||||
$action = (string) $request->body('action', 'create');
|
||||
if ($action === 'create_close') {
|
||||
if ($warnings) {
|
||||
Flash::info(implode(' ', $warnings), 'admin/departments', 'department_warning');
|
||||
Flash::info(implode(' ', $warnings), $closeTarget, 'department_warning');
|
||||
}
|
||||
Flash::success('Department created', 'admin/departments', 'department_created');
|
||||
Router::redirect('admin/departments');
|
||||
Flash::success('Department created', $closeTarget, 'department_created');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
$uuid = (string) ($result['uuid'] ?? '');
|
||||
if ($uuid !== '') {
|
||||
$target = "admin/departments/edit/{$uuid}";
|
||||
$target = requestPathWithReturnTarget("admin/departments/edit/{$uuid}", $returnTarget);
|
||||
if ($warnings) {
|
||||
Flash::info(implode(' ', $warnings), $target, 'department_warning');
|
||||
}
|
||||
@@ -85,10 +88,10 @@ if ($request->hasBody('description')) {
|
||||
Router::redirect($target);
|
||||
} else {
|
||||
if ($warnings) {
|
||||
Flash::info(implode(' ', $warnings), 'admin/departments', 'department_warning');
|
||||
Flash::info(implode(' ', $warnings), $closeTarget, 'department_warning');
|
||||
}
|
||||
Flash::success('Department created', 'admin/departments', 'department_created');
|
||||
Router::redirect('admin/departments');
|
||||
Flash::success('Department created', $closeTarget, 'department_created');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/departments');
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
@@ -23,10 +25,11 @@ $tenantService = app(\MintyPHP\Service\Tenant\TenantService::class);
|
||||
$directoryScopeGateway = app(\MintyPHP\Service\Directory\DirectoryScopeGateway::class);
|
||||
|
||||
$uuid = trim((string) ($id ?? ''));
|
||||
$editTarget = requestPathWithReturnTarget("admin/departments/edit/{$uuid}", $returnTarget);
|
||||
$department = $uuid !== '' ? $departmentService->findByUuid($uuid) : null;
|
||||
if (!$department) {
|
||||
Flash::error('Department not found', 'admin/departments', 'department_not_found');
|
||||
Router::redirect('admin/departments');
|
||||
Flash::error('Department not found', $closeTarget, 'department_not_found');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
|
||||
$departmentId = (int) ($department['id'] ?? 0);
|
||||
@@ -95,8 +98,8 @@ if ($allowedTenantIds) {
|
||||
}
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), "admin/departments/edit/{$uuid}", 'csrf_expired');
|
||||
Router::redirect("admin/departments/edit/{$uuid}");
|
||||
Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');
|
||||
Router::redirect($editTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,20 +145,20 @@ if ($request->hasBody('description')) {
|
||||
$cleaned = $departmentService->cleanupUserAssignments($departmentId);
|
||||
$action = (string) $request->body('action', 'save');
|
||||
if ($cleaned > 0) {
|
||||
Flash::info(t('Department assignments cleaned: %d', $cleaned), "admin/departments/edit/{$uuid}", 'department_assignments_cleaned');
|
||||
Flash::info(t('Department assignments cleaned: %d', $cleaned), $editTarget, 'department_assignments_cleaned');
|
||||
}
|
||||
if ($action === 'save_close') {
|
||||
if ($warnings) {
|
||||
Flash::info(implode(' ', $warnings), 'admin/departments', 'department_warning');
|
||||
Flash::info(implode(' ', $warnings), $closeTarget, 'department_warning');
|
||||
}
|
||||
Flash::success('Department updated', 'admin/departments', 'department_updated');
|
||||
Router::redirect('admin/departments');
|
||||
Flash::success('Department updated', $closeTarget, 'department_updated');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
if ($warnings) {
|
||||
Flash::info(implode(' ', $warnings), "admin/departments/edit/{$uuid}", 'department_warning');
|
||||
Flash::info(implode(' ', $warnings), $editTarget, 'department_warning');
|
||||
}
|
||||
Flash::success('Department updated', "admin/departments/edit/{$uuid}", 'department_updated');
|
||||
Router::redirect("admin/departments/edit/{$uuid}");
|
||||
Flash::success('Department updated', $editTarget, 'department_updated');
|
||||
Router::redirect($editTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Http\Request;
|
||||
|
||||
$canCreateDepartments = (bool) ($canCreateDepartments ?? false);
|
||||
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
|
||||
@@ -24,7 +25,7 @@ $listTitle = t('Departments');
|
||||
ob_start();
|
||||
?>
|
||||
<?php if ($canCreateDepartments): ?>
|
||||
<a role="button" class="primary" href="admin/departments/create">
|
||||
<a role="button" class="primary" href="<?php e(requestPathWithReturnTarget('admin/departments/create', Request::pathWithQuery())); ?>">
|
||||
<i class="bi bi-plus"></i> <?php e(t('Create department')); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -11,6 +11,9 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/permissions');
|
||||
$createTarget = requestPathWithReturnTarget('admin/permissions/create', $returnTarget);
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
@@ -31,8 +34,8 @@ $form = [
|
||||
];
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), 'admin/permissions/create', 'csrf_expired');
|
||||
Router::redirect('admin/permissions/create');
|
||||
Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired');
|
||||
Router::redirect($createTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -44,17 +47,17 @@ if ($request->hasBody('key')) {
|
||||
if (($result['ok'] ?? false) && !$errorBag->hasAny()) {
|
||||
$action = (string) $request->body('action', 'create');
|
||||
if ($action === 'create_close') {
|
||||
Flash::success('Permission created', 'admin/permissions', 'permission_created');
|
||||
Router::redirect('admin/permissions');
|
||||
Flash::success('Permission created', $closeTarget, 'permission_created');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
$id = (int) ($result['id'] ?? 0);
|
||||
if ($id > 0) {
|
||||
$target = "admin/permissions/edit/{$id}";
|
||||
$target = requestPathWithReturnTarget("admin/permissions/edit/{$id}", $returnTarget);
|
||||
Flash::success('Permission created', $target, 'permission_created');
|
||||
Router::redirect($target);
|
||||
} else {
|
||||
Flash::success('Permission created', 'admin/permissions', 'permission_created');
|
||||
Router::redirect('admin/permissions');
|
||||
Flash::success('Permission created', $closeTarget, 'permission_created');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,18 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/permissions');
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
|
||||
$id = (int) ($id ?? 0);
|
||||
$editTarget = requestPathWithReturnTarget("admin/permissions/edit/{$id}", $returnTarget);
|
||||
$permission = $id > 0 ? app(\MintyPHP\Service\Access\PermissionGateway::class)->find($id) : null;
|
||||
if (!$permission) {
|
||||
Flash::error('Permission not found', 'admin/permissions', 'permission_not_found');
|
||||
Router::redirect('admin/permissions');
|
||||
Flash::error('Permission not found', $closeTarget, 'permission_not_found');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
$isSystemPermission = (int) ($permission['is_system'] ?? 0) === 1;
|
||||
$contextDecision = $authorizationService->authorize(PermissionAuthorizationPolicy::ABILITY_ADMIN_PERMISSIONS_EDIT_CONTEXT, [
|
||||
@@ -46,8 +49,8 @@ $selectedRoleIds = $rolePermissionRepository->listRoleIdsByPermissionId($id);
|
||||
$previousRoleIds = $selectedRoleIds;
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), "admin/permissions/edit/{$id}", 'csrf_expired');
|
||||
Router::redirect("admin/permissions/edit/{$id}");
|
||||
Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');
|
||||
Router::redirect($editTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -97,11 +100,11 @@ if ($request->hasBody('key')) {
|
||||
if ($syncSucceeded) {
|
||||
$action = (string) $request->body('action', 'save');
|
||||
if ($action === 'save_close') {
|
||||
Flash::success('Permission updated', 'admin/permissions', 'permission_updated');
|
||||
Router::redirect('admin/permissions');
|
||||
Flash::success('Permission updated', $closeTarget, 'permission_updated');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
Flash::success('Permission updated', "admin/permissions/edit/{$id}", 'permission_updated');
|
||||
Router::redirect("admin/permissions/edit/{$id}");
|
||||
Flash::success('Permission updated', $editTarget, 'permission_updated');
|
||||
Router::redirect($editTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Http\Request;
|
||||
|
||||
$canCreatePermissions = (bool) ($canCreatePermissions ?? false);
|
||||
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
|
||||
@@ -22,7 +23,7 @@ $listTitle = t('Permissions');
|
||||
ob_start();
|
||||
?>
|
||||
<?php if ($canCreatePermissions): ?>
|
||||
<a role="button" class="primary" href="admin/permissions/create">
|
||||
<a role="button" class="primary" href="<?php e(requestPathWithReturnTarget('admin/permissions/create', Request::pathWithQuery())); ?>">
|
||||
<i class="bi bi-plus"></i> <?php e(t('Create permission')); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -11,6 +11,9 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/roles');
|
||||
$createTarget = requestPathWithReturnTarget('admin/roles/create', $returnTarget);
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
@@ -34,8 +37,8 @@ $permissions = $permissionRepository->listActive();
|
||||
$selectedPermissionIds = [];
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), 'admin/roles/create', 'csrf_expired');
|
||||
Router::redirect('admin/roles/create');
|
||||
Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired');
|
||||
Router::redirect($createTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -72,17 +75,17 @@ if ($request->hasBody('description')) {
|
||||
|
||||
$action = (string) $request->body('action', 'create');
|
||||
if ($action === 'create_close') {
|
||||
Flash::success('Role created', 'admin/roles', 'role_created');
|
||||
Router::redirect('admin/roles');
|
||||
Flash::success('Role created', $closeTarget, 'role_created');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
$uuid = (string) ($result['uuid'] ?? '');
|
||||
if ($uuid !== '') {
|
||||
$target = "admin/roles/edit/{$uuid}";
|
||||
$target = requestPathWithReturnTarget("admin/roles/edit/{$uuid}", $returnTarget);
|
||||
Flash::success('Role created', $target, 'role_created');
|
||||
Router::redirect($target);
|
||||
} else {
|
||||
Flash::success('Role created', 'admin/roles', 'role_created');
|
||||
Router::redirect('admin/roles');
|
||||
Flash::success('Role created', $closeTarget, 'role_created');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/roles');
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
@@ -21,10 +23,11 @@ $userWriteRepository = app(\MintyPHP\Repository\User\UserWriteRepository::class)
|
||||
$userAccountService = app(\MintyPHP\Service\User\UserAccountService::class);
|
||||
|
||||
$uuid = trim((string) ($id ?? ''));
|
||||
$editTarget = requestPathWithReturnTarget("admin/roles/edit/{$uuid}", $returnTarget);
|
||||
$role = $uuid !== '' ? app(\MintyPHP\Service\Access\RoleService::class)->findByUuid($uuid) : null;
|
||||
if (!$role) {
|
||||
Flash::error('Role not found', 'admin/roles', 'role_not_found');
|
||||
Router::redirect('admin/roles');
|
||||
Flash::error('Role not found', $closeTarget, 'role_not_found');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
|
||||
$roleId = (int) ($role['id'] ?? 0);
|
||||
@@ -65,8 +68,8 @@ $permissions = $permissionRepository->listActive();
|
||||
$selectedPermissionIds = $rolePermissionRepository->listPermissionIdsByRoleId($roleId);
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), "admin/roles/edit/{$uuid}", 'csrf_expired');
|
||||
Router::redirect("admin/roles/edit/{$uuid}");
|
||||
Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');
|
||||
Router::redirect($editTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,11 +117,11 @@ if ($request->hasBody('description')) {
|
||||
if ($syncSucceeded) {
|
||||
$action = (string) $request->body('action', 'save');
|
||||
if ($action === 'save_close') {
|
||||
Flash::success('Role updated', 'admin/roles', 'role_updated');
|
||||
Router::redirect('admin/roles');
|
||||
Flash::success('Role updated', $closeTarget, 'role_updated');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
Flash::success('Role updated', "admin/roles/edit/{$uuid}", 'role_updated');
|
||||
Router::redirect("admin/roles/edit/{$uuid}");
|
||||
Flash::success('Role updated', $editTarget, 'role_updated');
|
||||
Router::redirect($editTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Http\Request;
|
||||
|
||||
$canCreateRoles = (bool) ($canCreateRoles ?? false);
|
||||
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
|
||||
@@ -22,7 +23,7 @@ $listTitle = t('Roles');
|
||||
ob_start();
|
||||
?>
|
||||
<?php if ($canCreateRoles): ?>
|
||||
<a role="button" class="primary" href="admin/roles/create">
|
||||
<a role="button" class="primary" href="<?php e(requestPathWithReturnTarget('admin/roles/create', Request::pathWithQuery())); ?>">
|
||||
<i class="bi bi-plus"></i> <?php e(t('Create role')); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -11,6 +11,9 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/tenants');
|
||||
$createTarget = requestPathWithReturnTarget('admin/tenants/create', $returnTarget);
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
@@ -54,8 +57,8 @@ $form = [
|
||||
$ssoUiState = $canManageSso ? $tenantSsoService->buildMicrosoftUiState(0, $form) : [];
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), 'admin/tenants/create', 'csrf_expired');
|
||||
Router::redirect('admin/tenants/create');
|
||||
Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired');
|
||||
Router::redirect($createTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ if ($request->hasBody('description')) {
|
||||
if (!($ssoSaveResult['ok'] ?? false)) {
|
||||
Flash::error(
|
||||
(string) (($ssoSaveResult['errors'][0] ?? t('Tenant SSO settings could not be saved'))),
|
||||
'admin/tenants',
|
||||
$closeTarget,
|
||||
'tenant_sso_create_failed'
|
||||
);
|
||||
}
|
||||
@@ -113,17 +116,17 @@ if ($request->hasBody('description')) {
|
||||
}
|
||||
$action = (string) ($post['action'] ?? 'create');
|
||||
if ($action === 'create_close') {
|
||||
Flash::success('Tenant created', 'admin/tenants', 'tenant_created');
|
||||
Router::redirect('admin/tenants');
|
||||
Flash::success('Tenant created', $closeTarget, 'tenant_created');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
$uuid = (string) ($result['uuid'] ?? '');
|
||||
if ($uuid !== '') {
|
||||
$target = "admin/tenants/edit/{$uuid}";
|
||||
$target = requestPathWithReturnTarget("admin/tenants/edit/{$uuid}", $returnTarget);
|
||||
Flash::success('Tenant created', $target, 'tenant_created');
|
||||
Router::redirect($target);
|
||||
} else {
|
||||
Flash::success('Tenant created', 'admin/tenants', 'tenant_created');
|
||||
Router::redirect('admin/tenants');
|
||||
Flash::success('Tenant created', $closeTarget, 'tenant_created');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,8 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/tenants');
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
if ($currentUserId > 0) {
|
||||
@@ -20,10 +22,11 @@ if ($currentUserId > 0) {
|
||||
$userAccountService = app(\MintyPHP\Service\User\UserAccountService::class);
|
||||
|
||||
$uuid = trim((string) ($id ?? ''));
|
||||
$editTarget = requestPathWithReturnTarget("admin/tenants/edit/{$uuid}", $returnTarget);
|
||||
$tenant = $uuid !== '' ? app(\MintyPHP\Service\Tenant\TenantService::class)->findByUuid($uuid) : null;
|
||||
if (!$tenant) {
|
||||
Flash::error('Tenant not found', 'admin/tenants', 'tenant_not_found');
|
||||
Router::redirect('admin/tenants');
|
||||
Flash::error('Tenant not found', $closeTarget, 'tenant_not_found');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
|
||||
$tenantId = (int) ($tenant['id'] ?? 0);
|
||||
@@ -97,8 +100,8 @@ $errors = [];
|
||||
$form = array_merge($tenant, $ssoConfig);
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), "admin/tenants/edit/{$uuid}", 'csrf_expired');
|
||||
Router::redirect("admin/tenants/edit/{$uuid}");
|
||||
Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');
|
||||
Router::redirect($editTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -149,11 +152,11 @@ if ($request->hasBody('description')) {
|
||||
}
|
||||
$action = (string) ($post['action'] ?? 'save');
|
||||
if ($action === 'save_close') {
|
||||
Flash::success('Tenant updated', 'admin/tenants', 'tenant_updated');
|
||||
Router::redirect('admin/tenants');
|
||||
Flash::success('Tenant updated', $closeTarget, 'tenant_updated');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
Flash::success('Tenant updated', "admin/tenants/edit/{$uuid}", 'tenant_updated');
|
||||
Router::redirect("admin/tenants/edit/{$uuid}");
|
||||
Flash::success('Tenant updated', $editTarget, 'tenant_updated');
|
||||
Router::redirect($editTarget);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Http\Request;
|
||||
|
||||
$canCreateTenants = (bool) ($canCreateTenants ?? false);
|
||||
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
|
||||
@@ -22,7 +23,7 @@ $listTitle = t('Tenants');
|
||||
ob_start();
|
||||
?>
|
||||
<?php if ($canCreateTenants): ?>
|
||||
<a role="button" class="primary" href="admin/tenants/create">
|
||||
<a role="button" class="primary" href="<?php e(requestPathWithReturnTarget('admin/tenants/create', Request::pathWithQuery())); ?>">
|
||||
<i class="bi bi-plus"></i> <?php e(t('Create tenant')); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -16,6 +16,9 @@ use MintyPHP\Support\Guard;
|
||||
$session = app(SessionStoreInterface::class)->all();
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/users');
|
||||
$createTarget = requestPathWithReturnTarget('admin/users/create', $returnTarget);
|
||||
$authorizationService = app(\MintyPHP\Service\Access\AuthorizationService::class);
|
||||
$decision = $authorizationService->authorize(UserAuthorizationPolicy::ABILITY_ADMIN_USERS_CREATE, [
|
||||
'actor_user_id' => (int) ($session['user']['id'] ?? 0),
|
||||
@@ -93,8 +96,8 @@ $form = [
|
||||
];
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), 'admin/users/create', 'csrf_expired');
|
||||
Router::redirect('admin/users/create');
|
||||
Flash::error(t('Form expired, please try again'), $createTarget, 'csrf_expired');
|
||||
Router::redirect($createTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,7 +175,7 @@ if ($request->hasBody('email')) {
|
||||
if ($syncErrors) {
|
||||
Flash::error(
|
||||
implode(' | ', $syncErrors),
|
||||
"admin/users/edit/{$createdUuid}",
|
||||
requestPathWithReturnTarget("admin/users/edit/{$createdUuid}", $returnTarget),
|
||||
'user_custom_field_sync_failed'
|
||||
);
|
||||
}
|
||||
@@ -181,17 +184,17 @@ if ($request->hasBody('email')) {
|
||||
}
|
||||
$action = (string) ($post['action'] ?? 'create');
|
||||
if ($action === 'create_close') {
|
||||
Flash::success('User created', 'admin/users', 'user_created');
|
||||
Router::redirect('admin/users');
|
||||
Flash::success('User created', $closeTarget, 'user_created');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
$uuid = $createdUuid;
|
||||
if ($uuid !== '') {
|
||||
$target = "admin/users/edit/{$uuid}";
|
||||
$target = requestPathWithReturnTarget("admin/users/edit/{$uuid}", $returnTarget);
|
||||
Flash::success('User created', $target, 'user_created');
|
||||
Router::redirect($target);
|
||||
} else {
|
||||
Flash::success('User created', 'admin/users', 'user_created');
|
||||
Router::redirect('admin/users');
|
||||
Flash::success('User created', $closeTarget, 'user_created');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ $session = $sessionStore->all();
|
||||
|
||||
Guard::requireLogin();
|
||||
$request = requestInput();
|
||||
$returnTarget = requestResolveReturnTarget();
|
||||
$closeTarget = requestResolveReturnTarget('admin/users');
|
||||
$userAccountService = app(\MintyPHP\Service\User\UserAccountService::class);
|
||||
$userAssignmentService = app(\MintyPHP\Service\User\UserAssignmentService::class);
|
||||
$userTenantRepository = app(\MintyPHP\Repository\Tenant\UserTenantRepository::class);
|
||||
@@ -38,10 +40,11 @@ $passwordHints = $userPasswordPolicyService->hints();
|
||||
|
||||
$currentUserId = (int) ($session['user']['id'] ?? 0);
|
||||
$uuid = trim((string) ($id ?? ''));
|
||||
$editTarget = requestPathWithReturnTarget("admin/users/edit/{$uuid}", $returnTarget);
|
||||
$user = $uuid !== '' ? $userAccountService->findByUuid($uuid) : null;
|
||||
if (!$user) {
|
||||
Flash::error('User not found', 'admin/users', 'user_not_found');
|
||||
Router::redirect('admin/users');
|
||||
Flash::error('User not found', $closeTarget, 'user_not_found');
|
||||
Router::redirect($closeTarget);
|
||||
}
|
||||
|
||||
$userId = (int) ($user['id'] ?? 0);
|
||||
@@ -175,8 +178,8 @@ $canManageApiTokens = $canManageApiTokens && $canViewSecurityArtifacts;
|
||||
$showApiTokens = $canViewSecurityArtifacts && (!empty($apiTokens) || $canManageApiTokens);
|
||||
|
||||
if ($request->isMethod('POST') && !Session::checkCsrfToken()) {
|
||||
Flash::error(t('Form expired, please try again'), "admin/users/edit/{$uuid}", 'csrf_expired');
|
||||
Router::redirect("admin/users/edit/{$uuid}");
|
||||
Flash::error(t('Form expired, please try again'), $editTarget, 'csrf_expired');
|
||||
Router::redirect($editTarget);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -297,11 +300,11 @@ if ($request->hasBody('email')) {
|
||||
if (!$errorBag->hasAny()) {
|
||||
$action = (string) ($post['action'] ?? 'save');
|
||||
if ($action === 'save_close') {
|
||||
Flash::success('User updated', 'admin/users', 'user_updated');
|
||||
Router::redirect('admin/users');
|
||||
Flash::success('User updated', $closeTarget, 'user_updated');
|
||||
Router::redirect($closeTarget);
|
||||
} else {
|
||||
Flash::success('User updated', "admin/users/edit/{$uuid}", 'user_updated');
|
||||
Router::redirect("admin/users/edit/{$uuid}");
|
||||
Flash::success('User updated', $editTarget, 'user_updated');
|
||||
Router::redirect($editTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
use MintyPHP\Router;
|
||||
use MintyPHP\Session;
|
||||
use MintyPHP\Http\Request;
|
||||
|
||||
$toolbarFilterSchema = is_array($toolbarFilterSchema ?? null) ? $toolbarFilterSchema : [];
|
||||
$searchToolbarFilterSchema = is_array($searchToolbarFilterSchema ?? null) ? $searchToolbarFilterSchema : [];
|
||||
@@ -64,7 +65,7 @@ ob_start();
|
||||
</details>
|
||||
|
||||
<?php if ($canCreateUser): ?>
|
||||
<a role="button" class="primary" href="admin/users/create">
|
||||
<a role="button" class="primary" href="<?php e(requestPathWithReturnTarget('admin/users/create', Request::pathWithQuery())); ?>">
|
||||
<i class="bi bi-plus"></i> <?php e(t('Create user')); ?>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -11,6 +11,10 @@ use MintyPHP\Session;
|
||||
$titlebar = is_array($titlebar ?? null) ? $titlebar : [];
|
||||
$title = (string) ($titlebar['title'] ?? '');
|
||||
$backHref = (string) ($titlebar['backHref'] ?? '');
|
||||
$backReturn = requestResolveReturnTarget();
|
||||
if ($backReturn !== '') {
|
||||
$backHref = $backReturn;
|
||||
}
|
||||
$backTitle = (string) ($titlebar['backTitle'] ?? t('Cancel'));
|
||||
$unsavedMessage = (string) ($titlebar['unsavedMessage'] ?? t('You have unsaved changes. Leave without saving?'));
|
||||
$unsavedIndicatorLabel = (string) ($titlebar['unsavedIndicatorLabel'] ?? t('Unsaved changes'));
|
||||
|
||||
147
tests/Support/RequestReturnTargetHelperTest.php
Normal file
147
tests/Support/RequestReturnTargetHelperTest.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace MintyPHP\Tests\Support;
|
||||
|
||||
use MintyPHP\Http\Input\RequestInput;
|
||||
use MintyPHP\I18n;
|
||||
use MintyPHP\Router;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class RequestReturnTargetHelperTest extends TestCase
|
||||
{
|
||||
private array $serverBackup = [];
|
||||
private string $defaultLocaleBackup = 'en';
|
||||
private string $localeBackup = '';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->serverBackup = $_SERVER;
|
||||
$this->defaultLocaleBackup = (string) I18n::$defaultLocale;
|
||||
$this->localeBackup = (string) I18n::$locale;
|
||||
I18n::$defaultLocale = 'de';
|
||||
I18n::$locale = 'de';
|
||||
Router::$baseUrl = '/';
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$_SERVER = $this->serverBackup;
|
||||
I18n::$defaultLocale = $this->defaultLocaleBackup;
|
||||
I18n::$locale = $this->localeBackup;
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetPrefersBodyValue(): void
|
||||
{
|
||||
$input = new RequestInput('POST', ['return' => 'admin/roles'], ['return' => 'admin/users?active=1'], []);
|
||||
|
||||
$this->assertSame('admin/users?active=1', \requestResolveReturnTargetFromInput($input));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetFallsBackToQueryValue(): void
|
||||
{
|
||||
$input = new RequestInput('GET', ['return' => 'admin/departments?tenant=abc'], [], []);
|
||||
|
||||
$this->assertSame('admin/departments?tenant=abc', \requestResolveReturnTargetFromInput($input));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetFallsBackToStaticPathForInvalidReturn(): void
|
||||
{
|
||||
$input = new RequestInput('POST', ['return' => 'https://evil.example/path'], [], []);
|
||||
|
||||
$this->assertSame('admin/users', \requestResolveReturnTargetFromInput($input, 'admin/users'));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetIgnoresRefererWhenReturnIsInvalid(): void
|
||||
{
|
||||
$_SERVER['HTTP_REFERER'] = 'https://app.example/admin/roles';
|
||||
$_SERVER['HTTP_HOST'] = 'app.example';
|
||||
|
||||
$input = new RequestInput('POST', ['return' => 'https://evil.example/path'], [], []);
|
||||
|
||||
$this->assertSame('admin/users', \requestResolveReturnTargetFromInput($input, 'admin/users'));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetFallsBackToStaticPathForEmptyReturn(): void
|
||||
{
|
||||
$input = new RequestInput('GET', [], [], []);
|
||||
|
||||
$this->assertSame('admin/users', \requestResolveReturnTargetFromInput($input, 'admin/users'));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetStripsLocalePrefixAndNestedReturn(): void
|
||||
{
|
||||
$input = new RequestInput('GET', ['return' => '/de/admin/users?active=1&return=admin/old'], [], []);
|
||||
|
||||
$this->assertSame('admin/users?active=1', \requestResolveReturnTargetFromInput($input));
|
||||
}
|
||||
|
||||
public function testPathWithReturnTargetAppendsReturnQuery(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'admin/users/edit/abc?return=admin%2Fusers%3Factive%3D1',
|
||||
\requestPathWithReturnTarget('admin/users/edit/abc', 'admin/users?active=1')
|
||||
);
|
||||
}
|
||||
|
||||
public function testPathWithReturnTargetPreservesExistingQueryAndRemovesNestedReturn(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'admin/users/edit/abc?tab=profile&return=admin%2Fusers%3Factive%3D1',
|
||||
\requestPathWithReturnTarget(
|
||||
'admin/users/edit/abc?tab=profile',
|
||||
'admin/users?active=1&return=admin/old'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function testPathWithReturnTargetIgnoresExternalReturnTarget(): void
|
||||
{
|
||||
$_SERVER['HTTP_REFERER'] = 'https://app.example/admin/roles';
|
||||
$_SERVER['HTTP_HOST'] = 'app.example';
|
||||
|
||||
$this->assertSame(
|
||||
'admin/users/edit/abc',
|
||||
\requestPathWithReturnTarget('admin/users/edit/abc', 'https://evil.example')
|
||||
);
|
||||
}
|
||||
|
||||
public function testPathWithReturnTargetNormalizesLocalePrefixedReturnTarget(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'admin/users/edit/abc?return=admin%2Fusers%3Factive%3D1',
|
||||
\requestPathWithReturnTarget('admin/users/edit/abc', '/de/admin/users?active=1')
|
||||
);
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetUsesDeepestNestedReturnTarget(): void
|
||||
{
|
||||
$input = new RequestInput(
|
||||
'GET',
|
||||
['return' => 'admin/users/edit/abc?return=admin/users?active=1'],
|
||||
[],
|
||||
[]
|
||||
);
|
||||
|
||||
$this->assertSame('admin/users?active=1', \requestResolveReturnTargetFromInput($input, 'admin/users'));
|
||||
}
|
||||
|
||||
public function testResolveReturnTargetFallsBackToListWhenReturnPointsToDetailPath(): void
|
||||
{
|
||||
$input = new RequestInput('GET', ['return' => 'admin/users/edit/abc'], [], []);
|
||||
|
||||
$this->assertSame('admin/users', \requestResolveReturnTargetFromInput($input, 'admin/users'));
|
||||
}
|
||||
|
||||
public function testPathWithReturnTargetFlattensNestedEditReturnTargetToList(): void
|
||||
{
|
||||
$this->assertSame(
|
||||
'admin/users/edit/abc?return=admin%2Fusers%3Factive%3D1',
|
||||
\requestPathWithReturnTarget(
|
||||
'admin/users/edit/abc',
|
||||
'admin/users/edit/abc?return=admin/users?active=1'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -152,6 +152,10 @@ const readSubmitLockMode = (submitter, form) => normalizeSubmitLock(
|
||||
const disableSubmitControlsForForm = (form, submitter, options = {}) => {
|
||||
const controls = findSubmitControlsForForm(form);
|
||||
controls.forEach((control) => {
|
||||
if (submitter instanceof HTMLElement && control === submitter) {
|
||||
// Keep the clicked submitter enabled so its name/value stays part of form submission.
|
||||
return;
|
||||
}
|
||||
control.disabled = true;
|
||||
});
|
||||
if (submitter instanceof HTMLElement) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { warnOnce } from './app-dom.js';
|
||||
import { confirmDialog } from './app-confirm-dialog.js';
|
||||
import { gridFiltersFromSchema } from '../pages/app-list-utils.js';
|
||||
import { gridFiltersFromSchema, withCurrentListReturn } from '../pages/app-list-utils.js';
|
||||
import { initListFilterExperience } from '../pages/app-list-filter-experience.js';
|
||||
import { buildChipsFromMeta, removeChipFromMetaState, clearMetaState } from '../pages/app-list-filter-state.js';
|
||||
|
||||
@@ -294,7 +294,11 @@ export function createServerGrid(options) {
|
||||
return '';
|
||||
}
|
||||
const url = rowDblClick.getUrl(rowData, rowIndex);
|
||||
return String(url || '').trim();
|
||||
const normalized = String(url || '').trim();
|
||||
if (normalized === '') {
|
||||
return '';
|
||||
}
|
||||
return withCurrentListReturn(normalized);
|
||||
};
|
||||
|
||||
const getInput = (input) => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { initStandardListPage } from '../core/app-grid-factory.js';
|
||||
import { readPageConfig } from '../core/app-page-config.js';
|
||||
import { warnOnce } from '../core/app-dom.js';
|
||||
import { getAppBase } from './app-list-utils.js';
|
||||
import { getAppBase, withCurrentListReturn } from './app-list-utils.js';
|
||||
|
||||
const config = readPageConfig('admin-api-audit-index');
|
||||
if (config) {
|
||||
@@ -60,7 +60,7 @@ if (config) {
|
||||
if (!cell.uuid) {
|
||||
return gridjs.html(label);
|
||||
}
|
||||
const url = new URL(`admin/users/edit/${cell.uuid}`, appBase).toString();
|
||||
const url = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
|
||||
return gridjs.html(`<a href="${url}">${label}</a>`);
|
||||
},
|
||||
},
|
||||
@@ -75,7 +75,7 @@ if (config) {
|
||||
if (!cell.uuid) {
|
||||
return gridjs.html(label);
|
||||
}
|
||||
const url = new URL(`admin/tenants/edit/${cell.uuid}`, appBase).toString();
|
||||
const url = withCurrentListReturn(new URL(`admin/tenants/edit/${cell.uuid}`, appBase).toString());
|
||||
return gridjs.html(`<a href="${url}">${label}</a>`);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { initStandardListPage } from '../core/app-grid-factory.js';
|
||||
import { readPageConfig } from '../core/app-page-config.js';
|
||||
import { warnOnce } from '../core/app-dom.js';
|
||||
import { getAppBase } from './app-list-utils.js';
|
||||
import { getAppBase, withCurrentListReturn } from './app-list-utils.js';
|
||||
|
||||
const config = readPageConfig('admin-import-audit-index');
|
||||
if (config) {
|
||||
@@ -56,7 +56,7 @@ if (config) {
|
||||
if (!cell.uuid) {
|
||||
return gridjs.html(label);
|
||||
}
|
||||
const url = new URL(`admin/users/edit/${cell.uuid}`, appBase).toString();
|
||||
const url = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
|
||||
return gridjs.html(`<a href="${url}">${label}</a>`);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { initStandardListPage } from '../core/app-grid-factory.js';
|
||||
import { readPageConfig } from '../core/app-page-config.js';
|
||||
import { warnOnce } from '../core/app-dom.js';
|
||||
import { getAppBase } from './app-list-utils.js';
|
||||
import { getAppBase, withCurrentListReturn } from './app-list-utils.js';
|
||||
|
||||
const config = readPageConfig('admin-system-audit-index');
|
||||
if (config) {
|
||||
@@ -42,7 +42,7 @@ if (config) {
|
||||
if (!cell.uuid) {
|
||||
return gridjs.html(label);
|
||||
}
|
||||
const url = new URL(`admin/users/edit/${cell.uuid}`, appBase).toString();
|
||||
const url = withCurrentListReturn(new URL(`admin/users/edit/${cell.uuid}`, appBase).toString());
|
||||
return gridjs.html(`<a href="${url}">${label}</a>`);
|
||||
},
|
||||
},
|
||||
|
||||
@@ -8,6 +8,83 @@ export const getAppBase = () => {
|
||||
return `${window.location.origin}/`;
|
||||
};
|
||||
|
||||
const resolveAppBaseUrl = () => {
|
||||
try {
|
||||
return new URL(getAppBase(), `${window.location.origin}/`);
|
||||
} catch {
|
||||
return new URL(`${window.location.origin}/`);
|
||||
}
|
||||
};
|
||||
|
||||
const normalizeBasePathname = (pathname) => {
|
||||
const trimmed = String(pathname ?? '').trim();
|
||||
if (trimmed === '' || trimmed === '/') {
|
||||
return '/';
|
||||
}
|
||||
return `/${trimmed.replace(/^\/+|\/+$/g, '')}/`;
|
||||
};
|
||||
|
||||
const toAppRelativePath = (pathname, appBasePathname) => {
|
||||
const normalizedPath = `/${String(pathname ?? '').replace(/^\/+/, '')}`;
|
||||
const normalizedBasePath = normalizeBasePathname(appBasePathname);
|
||||
let relativePath = normalizedPath;
|
||||
|
||||
if (normalizedBasePath !== '/' && relativePath.startsWith(normalizedBasePath)) {
|
||||
relativePath = relativePath.slice(normalizedBasePath.length - 1);
|
||||
} else if (normalizedBasePath !== '/' && relativePath === normalizedBasePath.slice(0, -1)) {
|
||||
relativePath = '/';
|
||||
}
|
||||
|
||||
return relativePath.replace(/^\/+/, '');
|
||||
};
|
||||
|
||||
const parseAppUrl = (value, baseUrl = resolveAppBaseUrl()) => {
|
||||
const raw = String(value ?? '').trim();
|
||||
if (raw === '') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return new URL(raw, baseUrl);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const normalizeListReturnTarget = (sourceUrl = window.location.href) => {
|
||||
const appBaseUrl = resolveAppBaseUrl();
|
||||
const parsed = parseAppUrl(sourceUrl, appBaseUrl);
|
||||
if (!parsed) {
|
||||
return '';
|
||||
}
|
||||
const relativePath = toAppRelativePath(parsed.pathname, appBaseUrl.pathname);
|
||||
if (relativePath === '') {
|
||||
return '';
|
||||
}
|
||||
const query = new URLSearchParams(parsed.search);
|
||||
query.delete('return');
|
||||
const queryString = query.toString();
|
||||
return `${relativePath}${queryString ? `?${queryString}` : ''}`;
|
||||
};
|
||||
|
||||
export const withReturnTarget = (targetUrl, returnTarget = '') => {
|
||||
const appBaseUrl = resolveAppBaseUrl();
|
||||
const parsedTarget = parseAppUrl(targetUrl, appBaseUrl);
|
||||
if (!parsedTarget) {
|
||||
return String(targetUrl ?? '');
|
||||
}
|
||||
if (parsedTarget.origin !== window.location.origin) {
|
||||
return String(targetUrl ?? '');
|
||||
}
|
||||
const normalizedReturn = normalizeListReturnTarget(returnTarget);
|
||||
parsedTarget.searchParams.delete('return');
|
||||
if (normalizedReturn !== '') {
|
||||
parsedTarget.searchParams.set('return', normalizedReturn);
|
||||
}
|
||||
return parsedTarget.toString();
|
||||
};
|
||||
|
||||
export const withCurrentListReturn = (targetUrl) => withReturnTarget(targetUrl, window.location.href);
|
||||
|
||||
export const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
|
||||
Reference in New Issue
Block a user