Incorporate the permanent user deletion sprinkle
This commit is contained in:
@@ -61,6 +61,22 @@
|
|||||||
"userfrosting/js/uf-captcha.js",
|
"userfrosting/js/uf-captcha.js",
|
||||||
"uf-tweaks/js/pages/register.js"
|
"uf-tweaks/js/pages/register.js"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"js/pages/users": {
|
||||||
|
"scripts": [
|
||||||
|
"uf-tweaks/js/pages/users.js"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"sprinkle": {
|
||||||
|
"onCollision": "merge"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"js/pages/deleted-users": {
|
||||||
|
"scripts": [
|
||||||
|
"uf-tweaks/js/widgets/deleted-users.js",
|
||||||
|
"uf-tweaks/js/pages/deleted-users.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
assets/uf-tweaks/js/pages/deleted-users.js
Normal file
32
assets/uf-tweaks/js/pages/deleted-users.js
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
|
||||||
|
* example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
|
||||||
|
*
|
||||||
|
* This script depends on widgets/deleted-users.js, uf-table.js, moment.js, handlebars-helpers.js
|
||||||
|
*
|
||||||
|
* Target page: /deleted-users
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
/**
|
||||||
|
* Navigation buttons
|
||||||
|
*/
|
||||||
|
// Return from the deleted organisations page
|
||||||
|
$("#widget-deletedUsers").find('.js-deletedUsers-return').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
window.location.href = site.uri.public + '/users';
|
||||||
|
});
|
||||||
|
|
||||||
|
// Deleted users table
|
||||||
|
$("#widget-deletedUsers").ufTable({
|
||||||
|
dataUrl: site.uri.public + "/api/users/deleted",
|
||||||
|
useLoadingTransition: site.uf_table.use_loading_transition
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bind table buttons
|
||||||
|
$("#widget-deletedUsers").on("pagerComplete.ufTable", function () {
|
||||||
|
bindDeletedUsersButtons($(this));
|
||||||
|
});
|
||||||
|
});
|
||||||
20
assets/uf-tweaks/js/pages/users.js
Normal file
20
assets/uf-tweaks/js/pages/users.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* Page-specific Javascript file. Should generally be included as a separate asset bundle in your page template.
|
||||||
|
* example: {{ assets.js('js/pages/sign-in-or-register') | raw }}
|
||||||
|
*
|
||||||
|
* This script depends on widgets/users.js, uf-table.js, moment.js, handlebars-helpers.js
|
||||||
|
*
|
||||||
|
* Target page: /users
|
||||||
|
*/
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
/**
|
||||||
|
* Navigation buttons
|
||||||
|
*/
|
||||||
|
// View the deleted organisations page
|
||||||
|
$("#widget-users").find('.js-user-viewDeleted').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
window.location.href = site.uri.public + '/users/deleted';
|
||||||
|
});
|
||||||
|
});
|
||||||
82
assets/uf-tweaks/js/widgets/deleted-users.js
Normal file
82
assets/uf-tweaks/js/widgets/deleted-users.js
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
/**
|
||||||
|
* Users widget. Sets up dropdowns, modals, etc for a table of deleted users.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link delete user action buttons, for example in a table or on a specific users's page.
|
||||||
|
* @param {module:jQuery} el jQuery wrapped element to target.
|
||||||
|
* @param {{delete_redirect: string}} options Options used to modify behaviour of button actions.
|
||||||
|
*/
|
||||||
|
function bindDeletedUsersButtons(el, options) {
|
||||||
|
if (!options) options = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buttons that launch a modal dialog
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Permenetly delete user button
|
||||||
|
el.find('.js-user-permanentDelete').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
$("body").ufModal({
|
||||||
|
sourceUrl: site.uri.public + "/modals/users/confirm-permanent-delete",
|
||||||
|
ajaxParams: {
|
||||||
|
user_name: $(this).data('user_name')
|
||||||
|
},
|
||||||
|
msgTarget: $("#alerts-page")
|
||||||
|
});
|
||||||
|
|
||||||
|
$("body").on('renderSuccess.ufModal', function() {
|
||||||
|
var modal = $(this).ufModal('getModal');
|
||||||
|
var form = modal.find('.js-form');
|
||||||
|
|
||||||
|
form.ufForm()
|
||||||
|
.on("submitSuccess.ufForm", function() {
|
||||||
|
// Navigate or reload page on success
|
||||||
|
if (options.delete_redirect) window.location.href = options.delete_redirect;
|
||||||
|
else window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Restore user button
|
||||||
|
el.find('.js-user-restore').click(function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var data = {};
|
||||||
|
data[site.csrf.keys.name] = site.csrf.name;
|
||||||
|
data[site.csrf.keys.value] = site.csrf.value;
|
||||||
|
|
||||||
|
var url = site.uri.public + '/api/users/u/' + $(this).data('user_name') + '/restore';
|
||||||
|
var debugAjax = (typeof site !== "undefined") && site.debug.ajax;
|
||||||
|
|
||||||
|
return $.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: url,
|
||||||
|
data: data,
|
||||||
|
dataType: debugAjax ? 'html' : 'json',
|
||||||
|
}).fail(function(jqXHR) {
|
||||||
|
// Error messages
|
||||||
|
if (debugAjax && jqXHR.responseText) {
|
||||||
|
document.write(jqXHR.responseText);
|
||||||
|
document.close();
|
||||||
|
} else {
|
||||||
|
console.log("Error (" + jqXHR.status + "): " + jqXHR.responseText);
|
||||||
|
|
||||||
|
// Display errors on failure
|
||||||
|
// TODO: ufAlerts widget should have a 'destroy' method
|
||||||
|
if (!$("#alerts-page").data('ufAlerts')) {
|
||||||
|
$("#alerts-page").ufAlerts();
|
||||||
|
} else {
|
||||||
|
$("#alerts-page").ufAlerts('clear');
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#alerts-page").ufAlerts('fetch').ufAlerts('render');
|
||||||
|
}
|
||||||
|
|
||||||
|
return jqXHR;
|
||||||
|
}).done(function(response) {
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -8,9 +8,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
/*
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* Debug Configuration
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* Turn any of those on to help debug your app
|
||||||
|
*/
|
||||||
'debug' => [
|
'debug' => [
|
||||||
'tokens' => false,
|
'tokens' => false,
|
||||||
],
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ----------------------------------------------------------------------
|
* ----------------------------------------------------------------------
|
||||||
* Database Config
|
* Database Config
|
||||||
@@ -33,6 +40,18 @@ return [
|
|||||||
'minutes' => 540,
|
'minutes' => 540,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* Users Config
|
||||||
|
* ----------------------------------------------------------------------
|
||||||
|
* Allow permanent deletion of users. Default to false.
|
||||||
|
* Installers must turn this on to change default behaviour.
|
||||||
|
*/
|
||||||
|
'users' => [
|
||||||
|
'allow_permanent_delete' => false,
|
||||||
|
'purge_activities' => false,
|
||||||
|
],
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ----------------------------------------------------------------------
|
* ----------------------------------------------------------------------
|
||||||
* PHP global settings
|
* PHP global settings
|
||||||
|
|||||||
@@ -8,5 +8,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'ICON' => 'Icon',
|
'USER' => [
|
||||||
|
'VIEW_DELETED' => 'View deleted users',
|
||||||
|
'DELETED_PAGE_DESCRIPTION' => 'A listing of the deleted users for your site. Provides management tools for restoring and permenently deleting users.',
|
||||||
|
|
||||||
|
'RESTORE_DELETED' => 'Restore user',
|
||||||
|
'RESTORE_SUCCESSFUL' => 'Successfully restored user <strong>{{user_name}}</strong>',
|
||||||
|
|
||||||
|
'PERMANENT_DELETE' => 'Permanetly delete user',
|
||||||
|
'PERMANENT_DELETE_CONFIRM' => 'Are you sure you want to <strong>permanently</strong> delete the user <strong>{{user_name}}</strong>?',
|
||||||
|
'PERMANENT_DELETE_YES' => 'Yes, permanently delete user',
|
||||||
|
'PERMANENT_DELETION_SUCCESSFUL' => 'User <strong>{{user_name}}</strong> has been permanently deleted.',
|
||||||
|
'PERMANENT_DELETE_DISABLED' => 'Permanent deletion of users is currently disabled via the configuration.',
|
||||||
|
],
|
||||||
|
|
||||||
|
'DELETED' => 'Deleted',
|
||||||
|
'ICON' => 'Icon',
|
||||||
|
'RETURN' => 'Return',
|
||||||
];
|
];
|
||||||
29
routes/users.php
Normal file
29
routes/users.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Tweaks (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
use UserFrosting\Sprinkle\Core\Util\NoCache;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Routes for administrative user management.
|
||||||
|
*/
|
||||||
|
$app->group('/users', function () {
|
||||||
|
$this->get('/deleted', 'UserFrosting\Sprinkle\UFTweaks\Controller\DeletedUserController:pageListDeleted');
|
||||||
|
})->add('authGuard')->add(new NoCache());
|
||||||
|
|
||||||
|
$app->group('/api/users', function () {
|
||||||
|
$this->get('/deleted', 'UserFrosting\Sprinkle\UFTweaks\Controller\DeletedUserController:getListDeleted');
|
||||||
|
|
||||||
|
$this->post('/u/{user_name}/restore', 'UserFrosting\Sprinkle\UFTweaks\Controller\DeletedUserController:restore');
|
||||||
|
|
||||||
|
$this->delete('/u/{user_name}/permanent', 'UserFrosting\Sprinkle\UFTweaks\Controller\DeletedUserController:deletePermanent');
|
||||||
|
})->add('authGuard')->add(new NoCache());
|
||||||
|
|
||||||
|
$app->group('/modals/users', function () {
|
||||||
|
$this->get('/confirm-permanent-delete', 'UserFrosting\Sprinkle\UFTweaks\Controller\DeletedUserController:getModalConfirmPermanentDelete');
|
||||||
|
})->add('authGuard')->add(new NoCache());
|
||||||
350
src/Controller/DeletedUserController.php
Normal file
350
src/Controller/DeletedUserController.php
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Tweaks (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\UFTweaks\Controller;
|
||||||
|
|
||||||
|
use Illuminate\Database\Capsule\Manager as Capsule;
|
||||||
|
use Psr\Http\Message\ResponseInterface as Response;
|
||||||
|
use Psr\Http\Message\ServerRequestInterface as Request;
|
||||||
|
use UserFrosting\Fortress\RequestDataTransformer;
|
||||||
|
use UserFrosting\Fortress\RequestSchema;
|
||||||
|
use UserFrosting\Fortress\ServerSideValidator;
|
||||||
|
use UserFrosting\Sprinkle\Core\Controller\SimpleController;
|
||||||
|
use UserFrosting\Support\Exception\BadRequestException;
|
||||||
|
use UserFrosting\Support\Exception\ForbiddenException;
|
||||||
|
use UserFrosting\Support\Exception\NotFoundException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller class for deleted user related requests, including restoring and permanently deleting users.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class DeletedUserController extends SimpleController
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Processes the request to permanently delete a deleted user.
|
||||||
|
*
|
||||||
|
* Permanently deletes the specified user.
|
||||||
|
* Before doing so, checks that:
|
||||||
|
* 1. The current user has permission to delete the requested user;
|
||||||
|
* 2. The submitted data is valid.
|
||||||
|
* This route requires authentication (and should generally be limited to admins or the root user).
|
||||||
|
*
|
||||||
|
* Request type: DELETE
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws NotFoundException If user is not found
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
* @throws BadRequestException
|
||||||
|
*/
|
||||||
|
public function deletePermanent(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Support\Repository\Repository $config */
|
||||||
|
$config = $this->ci->config;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */
|
||||||
|
$ms = $this->ci->alerts;
|
||||||
|
|
||||||
|
|
||||||
|
$user = $this->getDeletedUserFromParams($args);
|
||||||
|
|
||||||
|
// If the user doesn't exist, return 404
|
||||||
|
if (!$user) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'permanently_delete_user', [
|
||||||
|
'user' => $user,
|
||||||
|
])) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config-controlled page
|
||||||
|
if (!$config['users.allow_permanent_delete']) {
|
||||||
|
$ms->addMessageTranslated('danger', 'USER.PERMANENT_DELETE_DISABLED');
|
||||||
|
return $response->withJson([], 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user_name = $user->user_name;
|
||||||
|
|
||||||
|
// Begin transaction - DB will be rolled back if an exception occurs
|
||||||
|
Capsule::transaction(function () use ($user, $user_name, $currentUser, $config) {
|
||||||
|
$user->forceDelete();
|
||||||
|
unset($user);
|
||||||
|
|
||||||
|
// Create activity record
|
||||||
|
$this->ci->userActivityLogger->info("User {$currentUser->user_name} permenetly deleted user {$user_name}.", [
|
||||||
|
'type' => 'user_delete',
|
||||||
|
'user_id' => $currentUser->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$ms->addMessageTranslated('success', 'USER.PERMANENT_DELETION_SUCCESSFUL', [
|
||||||
|
'user_name' => $user_name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response->withJson([], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restores a deleted user
|
||||||
|
*
|
||||||
|
* Before doing so, checks that:
|
||||||
|
* 1. The current user has permission to restore the requested user;
|
||||||
|
* 2. The submitted data is valid.
|
||||||
|
* This route requires authentication (and should generally be limited to admins or the root user).
|
||||||
|
*
|
||||||
|
* Request type: PUT
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws NotFoundException If user is not found
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
* @throws BadRequestException
|
||||||
|
*/
|
||||||
|
public function restore(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */
|
||||||
|
$ms = $this->ci->alerts;
|
||||||
|
|
||||||
|
$user = $this->getDeletedUserFromParams($args);
|
||||||
|
|
||||||
|
// If the user doesn't exist, return 404
|
||||||
|
if (!$user) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'restore_deleted_user', [
|
||||||
|
'user' => $user,
|
||||||
|
])) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin transaction - DB will be rolled back if an exception occurs
|
||||||
|
Capsule::transaction(function () use ($user, $currentUser) {
|
||||||
|
$user->restore();
|
||||||
|
|
||||||
|
// Create activity record
|
||||||
|
$this->ci->userActivityLogger->info("User {$currentUser->user_name} restored deleted user {$user->user_name}.", [
|
||||||
|
'type' => 'user_restore',
|
||||||
|
'user_id' => $currentUser->id,
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
$ms->addMessageTranslated('success', 'USER.RESTORE_SUCCESSFUL', [
|
||||||
|
'user_name' => $user->user_name,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $response->withJson([], 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of deleted users.
|
||||||
|
*
|
||||||
|
* Generates a list of deleted users, optionally paginated, sorted and/or filtered.
|
||||||
|
* This page requires authentication.
|
||||||
|
*
|
||||||
|
* Request type: GET
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
*/
|
||||||
|
public function getListDeleted(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||||
|
$classMapper = $this->ci->classMapper;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'uri_deleted_users')) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// GET parameters
|
||||||
|
$params = $request->getQueryParams();
|
||||||
|
|
||||||
|
$sprunje = $classMapper->createInstance('user_sprunje', $classMapper, $params);
|
||||||
|
$sprunje->extendQuery(function ($query) {
|
||||||
|
return $query->onlyTrashed();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Be careful how you consume this data - it has not been escaped and contains untrusted user-supplied content.
|
||||||
|
// For example, if you plan to insert it into an HTML DOM, you must escape it on the client side (or use client-side templating).
|
||||||
|
return $sprunje->toResponse($response);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get permanent deletion confirmation modal.
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws NotFoundException If user is not found
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
* @throws BadRequestException
|
||||||
|
*/
|
||||||
|
public function getModalConfirmPermanentDelete(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||||
|
$classMapper = $this->ci->classMapper;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Support\Repository\Repository $config */
|
||||||
|
$config = $this->ci->config;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Alert\AlertStream $ms */
|
||||||
|
$ms = $this->ci->alerts;
|
||||||
|
|
||||||
|
|
||||||
|
// GET parameters
|
||||||
|
$params = $request->getQueryParams();
|
||||||
|
|
||||||
|
$user = $this->getDeletedUserFromParams($params);
|
||||||
|
|
||||||
|
// If the user doesn't exist, return not found
|
||||||
|
if (!$user) {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'permanently_delete_user', [
|
||||||
|
'user' => $user,
|
||||||
|
])) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Config-controlled page
|
||||||
|
if (!$config['users.allow_permanent_delete']) {
|
||||||
|
$exception = new BadRequestException();
|
||||||
|
$exception->addUserMessage('USER.PERMANENT_DELETE_DISABLED');
|
||||||
|
throw $exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ci->view->render($response, 'modals/confirm-permanently-delete-user.html.twig', [
|
||||||
|
'user' => $user,
|
||||||
|
'form' => [
|
||||||
|
'action' => "api/users/u/{$user->user_name}/permanent",
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the user listing page for deleted users.
|
||||||
|
*
|
||||||
|
* This page renders a table of delete users, with dropdown menus for admin actions for each user.
|
||||||
|
* Actions typically include: restore user, permanently delete etc.
|
||||||
|
* This page requires authentication.
|
||||||
|
*
|
||||||
|
* Request type: GET
|
||||||
|
*
|
||||||
|
* @param Request $request
|
||||||
|
* @param Response $response
|
||||||
|
* @param array $args
|
||||||
|
*
|
||||||
|
* @throws ForbiddenException If user is not authorized to access page
|
||||||
|
*/
|
||||||
|
public function pageListDeleted(Request $request, Response $response, $args)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Authorize\AuthorizationManager $authorizer */
|
||||||
|
$authorizer = $this->ci->authorizer;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface $currentUser */
|
||||||
|
$currentUser = $this->ci->currentUser;
|
||||||
|
|
||||||
|
// Access-controlled page
|
||||||
|
if (!$authorizer->checkAccess($currentUser, 'uri_deleted_users')) {
|
||||||
|
throw new ForbiddenException();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ci->view->render($response, 'pages/deleted-users.html.twig');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get User instance from params.
|
||||||
|
*
|
||||||
|
* @param string[] $params
|
||||||
|
*
|
||||||
|
* @throws BadRequestException
|
||||||
|
*
|
||||||
|
* @return User|null
|
||||||
|
*/
|
||||||
|
protected function getDeletedUserFromParams(array $params)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||||
|
$classMapper = $this->ci->classMapper;
|
||||||
|
|
||||||
|
|
||||||
|
// Load the request schema
|
||||||
|
$schema = new RequestSchema('schema://requests/user/get-by-username.yaml');
|
||||||
|
|
||||||
|
// Whitelist and set parameter defaults
|
||||||
|
$transformer = new RequestDataTransformer($schema);
|
||||||
|
$data = $transformer->transform($params);
|
||||||
|
|
||||||
|
// Validate, and throw exception on validation errors.
|
||||||
|
$validator = new ServerSideValidator($schema, $this->ci->translator);
|
||||||
|
if (!$validator->validate($data)) {
|
||||||
|
// TODO: encapsulate the communication of error messages from ServerSideValidator to the BadRequestException
|
||||||
|
$e = new BadRequestException();
|
||||||
|
foreach ($validator->errors() as $idx => $field) {
|
||||||
|
foreach ($field as $eidx => $error) {
|
||||||
|
$e->addUserMessage($error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query to get the user
|
||||||
|
$query = $classMapper->getClassMapping('user')::where('user_name', $data['user_name'])->withTrashed();
|
||||||
|
|
||||||
|
// Run the query
|
||||||
|
$user = $query->first();
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
54
src/Database/Migrations/v1_0_0/UpdateActivitiesTable.php
Normal file
54
src/Database/Migrations/v1_0_0/UpdateActivitiesTable.php
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Tweaks (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\UFTweaks\Database\Migrations\v1_0_0;
|
||||||
|
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use UserFrosting\Sprinkle\Core\Database\Migration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Users table migration
|
||||||
|
* Changes `user_id` column properties to nullable to allow users to be deleted without clearing the audit log for that user.
|
||||||
|
* Version 1.0.0.
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class UpdateActivitiesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public static $dependencies = [
|
||||||
|
'UserFrosting\Sprinkle\Account\Database\Migrations\v400\ActivitiesTable',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function up()
|
||||||
|
{
|
||||||
|
if ($this->schema->hasTable('activities')) {
|
||||||
|
$this->schema->table('activities', function (Blueprint $table) {
|
||||||
|
$table->integer('user_id')->unsigned()->nullable()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
|
public function down()
|
||||||
|
{
|
||||||
|
if ($this->schema->hasTable('activities')) {
|
||||||
|
$this->schema->table('activities', function (Blueprint $table) {
|
||||||
|
$table->integer('user_id')->unsigned()->change();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
src/Database/ModelTraits/UserPermanentlyDeletable.php
Normal file
42
src/Database/ModelTraits/UserPermanentlyDeletable.php
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Tweaks (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\UFTweaks\Database\ModelTraits;
|
||||||
|
|
||||||
|
trait UserPermanentlyDeletable {
|
||||||
|
protected function preventActivityPurge() {
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||||
|
$classMapper = static::$ci->classMapper;
|
||||||
|
|
||||||
|
/** @var \UserFrosting\Support\Repository\Repository $config */
|
||||||
|
$config = static::$ci->config;
|
||||||
|
|
||||||
|
if ($this->isForceDeleting() && !$config['users.purge_activities']) {
|
||||||
|
// Keep the activity log, but lose the user field
|
||||||
|
$classMapper->getClassMapping('activity')::query()
|
||||||
|
->where('user_id', $this->id)
|
||||||
|
->update(['user_id' => null]);
|
||||||
|
|
||||||
|
$this->refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bootUserPermanentlyDeletable()
|
||||||
|
{
|
||||||
|
static::deleting(function ($user) {
|
||||||
|
if (!$user->preventActivityPurge()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src/Database/Models/User.php
Normal file
85
src/Database/Models/User.php
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AVSDev UF Tweaks (https://avsdev.uk)
|
||||||
|
*
|
||||||
|
* @link https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks
|
||||||
|
* @license https://git.avsdev.uk/avsdev/sprinkle-uf-tweaks/blob/master/LICENSE.md (LGPL-3.0 License)
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace UserFrosting\Sprinkle\UFTweaks\Database\Models;
|
||||||
|
|
||||||
|
use LogicException;
|
||||||
|
use UserFrosting\Sprinkle\Account\Database\Models\User as UFUser;
|
||||||
|
use UserFrosting\Sprinkle\UFTweaks\Database\ModelTraits\UserPermanentlyDeletable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Class.
|
||||||
|
*
|
||||||
|
* Extends the UF User object by adding the UserPermanentlyDeletable trait
|
||||||
|
*
|
||||||
|
* @author Craig Williams (https://avsdev.uk)
|
||||||
|
*/
|
||||||
|
class User extends UFUser
|
||||||
|
{
|
||||||
|
use UserPermanentlyDeletable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this user from the database, along with any linked roles and activities.
|
||||||
|
*
|
||||||
|
* @return bool true if the deletion was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
protected function deleteUserAssociations()
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Sprinkle\Core\Util\ClassMapper $classMapper */
|
||||||
|
$classMapper = static::$ci->classMapper;
|
||||||
|
|
||||||
|
// Remove all role associations
|
||||||
|
$this->roles()->detach();
|
||||||
|
|
||||||
|
// Remove last activity association
|
||||||
|
$this->lastActivity()->dissociate();
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
// Remove all user tokens
|
||||||
|
$this->activities()->delete();
|
||||||
|
$this->passwordResets()->delete();
|
||||||
|
$classMapper->getClassMapping('verification')::where('user_id', $this->id)->delete();
|
||||||
|
$classMapper->getClassMapping('persistence')::where('user_id', $this->id)->delete();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete this user from the database, along with any linked roles and activities.
|
||||||
|
*
|
||||||
|
* @param bool $hardDelete Trigger a forceDelete()
|
||||||
|
*
|
||||||
|
* @return bool true if the deletion was successful, false otherwise.
|
||||||
|
*/
|
||||||
|
public function delete($hardDelete = false)
|
||||||
|
{
|
||||||
|
/** @var \UserFrosting\Support\Repository\Repository $config */
|
||||||
|
$config = $this->ci->config;
|
||||||
|
|
||||||
|
if ($hardDelete) {
|
||||||
|
return $this->forceDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->isForceDeleting()) {
|
||||||
|
if (!$config['users.allow_permanent_delete']) {
|
||||||
|
throw new LogicException('Permanent deletion is disabled.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->fireModelEvent('deleting') === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->deleteUserAssociations();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = parent::delete();
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ class DefaultPermissions extends UFDefaultPermissions
|
|||||||
*/
|
*/
|
||||||
protected function getPermissions()
|
protected function getPermissions()
|
||||||
{
|
{
|
||||||
$base_permissions = parent::getPermissions();
|
$basePermissions = parent::getPermissions();
|
||||||
|
|
||||||
$defaultRoleIds = [
|
$defaultRoleIds = [
|
||||||
'user' => Role::where('slug', 'user')->first()->id,
|
'user' => Role::where('slug', 'user')->first()->id,
|
||||||
@@ -48,66 +48,84 @@ class DefaultPermissions extends UFDefaultPermissions
|
|||||||
'site-admin' => Role::where('slug', 'site-admin')->first()->id,
|
'site-admin' => Role::where('slug', 'site-admin')->first()->id,
|
||||||
];
|
];
|
||||||
|
|
||||||
return array_merge(
|
$newPermissions = [
|
||||||
$base_permissions,
|
'uri_role' => new Permission([
|
||||||
[
|
'slug' => 'uri_role',
|
||||||
'uri_role' => new Permission([
|
'name' => 'View role',
|
||||||
'slug' => 'uri_role',
|
'conditions' => 'always()',
|
||||||
'name' => 'View role',
|
'description' => 'View the role page of any role.',
|
||||||
'conditions' => 'always()',
|
]),
|
||||||
'description' => 'View the role page of any role.',
|
'uri_roles' => new Permission([
|
||||||
]),
|
'slug' => 'uri_roles',
|
||||||
'uri_roles' => new Permission([
|
'name' => 'Role management page',
|
||||||
'slug' => 'uri_roles',
|
'conditions' => 'always()',
|
||||||
'name' => 'Role management page',
|
'description' => 'View a page containing a table of roles.',
|
||||||
'conditions' => 'always()',
|
]),
|
||||||
'description' => 'View a page containing a table of roles.',
|
'uri_permission' => new Permission([
|
||||||
]),
|
'slug' => 'uri_permission',
|
||||||
'uri_permission' => new Permission([
|
'name' => 'View permission',
|
||||||
'slug' => 'uri_permission',
|
'conditions' => 'always()',
|
||||||
'name' => 'View permission',
|
'description' => 'View the permission page of any permission.',
|
||||||
'conditions' => 'always()',
|
]),
|
||||||
'description' => 'View the permission page of any permission.',
|
'uri_permissions' => new Permission([
|
||||||
]),
|
'slug' => 'uri_permissions',
|
||||||
'uri_permissions' => new Permission([
|
'name' => 'Permission management page',
|
||||||
'slug' => 'uri_permissions',
|
'conditions' => 'always()',
|
||||||
'name' => 'Permission management page',
|
'description' => 'View a page containing a table of permissions.',
|
||||||
'conditions' => 'always()',
|
]),
|
||||||
'description' => 'View a page containing a table of permissions.',
|
|
||||||
]),
|
|
||||||
|
|
||||||
'create_role' => new Permission([
|
'create_role' => new Permission([
|
||||||
'slug' => 'create_role',
|
'slug' => 'create_role',
|
||||||
'name' => 'Create role',
|
'name' => 'Create role',
|
||||||
'conditions' => 'always()',
|
'conditions' => 'always()',
|
||||||
'description' => 'Create a new role.',
|
'description' => 'Create a new role.',
|
||||||
]),
|
]),
|
||||||
'view_role_field' => new Permission([
|
'view_role_field' => new Permission([
|
||||||
'slug' => 'view_role_field',
|
'slug' => 'view_role_field',
|
||||||
'name' => 'View role',
|
'name' => 'View role',
|
||||||
'conditions' => "in(property,['slug','name','description','permissions','users'])",
|
'conditions' => "in(property,['slug','name','description','permissions','users'])",
|
||||||
'description' => 'View certain properties of any role.',
|
'description' => 'View certain properties of any role.',
|
||||||
]),
|
]),
|
||||||
'update_role_field' => new Permission([
|
'update_role_field' => new Permission([
|
||||||
'slug' => 'update_role_field',
|
'slug' => 'update_role_field',
|
||||||
'name' => 'Edit role',
|
'name' => 'Edit role',
|
||||||
'conditions' => "is_master(self.id) || subset(fields,['slug','name','description'])",
|
'conditions' => "is_master(self.id) || subset(fields,['slug','name','description'])",
|
||||||
'description' => 'Edit basic properties of any role.',
|
'description' => 'Edit basic properties of any role.',
|
||||||
]),
|
]),
|
||||||
'update_role_permissions' => new Permission([
|
'update_role_permissions' => new Permission([
|
||||||
'slug' => 'update_role_permissions',
|
'slug' => 'update_role_permissions',
|
||||||
'name' => 'Edit role permissions',
|
'name' => 'Edit role permissions',
|
||||||
'conditions' => "is_master(self.id) || subset(fields,['permissions'])",
|
'conditions' => "is_master(self.id) || subset(fields,['permissions'])",
|
||||||
'description' => 'Edit permissions of any role.',
|
'description' => 'Edit permissions of any role.',
|
||||||
]),
|
]),
|
||||||
'delete_role' => new Permission([
|
'delete_role' => new Permission([
|
||||||
'slug' => 'delete_role',
|
'slug' => 'delete_role',
|
||||||
'name' => 'Delete role',
|
'name' => 'Delete role',
|
||||||
'conditions' => 'always()',
|
'conditions' => 'always()',
|
||||||
'description' => 'Delete a role.',
|
'description' => 'Delete a role.',
|
||||||
]),
|
]),
|
||||||
]
|
|
||||||
);
|
'permanently_delete_user' => new Permission([
|
||||||
|
'slug' => 'permanently_delete_user',
|
||||||
|
'name' => 'Permanently delete user',
|
||||||
|
'conditions' => 'always()',
|
||||||
|
'description' => 'Provides the permission to permanently delete users.',
|
||||||
|
]),
|
||||||
|
'restore_deleted_user' => new Permission([
|
||||||
|
'slug' => 'restore_deleted_user',
|
||||||
|
'name' => 'Restore deleted user',
|
||||||
|
'conditions' => "always()",
|
||||||
|
'description' => 'Provides the permission to restore deleted users.',
|
||||||
|
]),
|
||||||
|
'uri_deleted_users' => new Permission([
|
||||||
|
'slug' => 'uri_deleted_users',
|
||||||
|
'name' => 'Deleted users management page',
|
||||||
|
'conditions' => 'always()',
|
||||||
|
'description' => 'View a page containing a list of deleted users.',
|
||||||
|
]),
|
||||||
|
];
|
||||||
|
|
||||||
|
return array_merge($basePermissions, $newPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -150,6 +168,10 @@ class DefaultPermissions extends UFDefaultPermissions
|
|||||||
$permissions['uri_permissions']->id,
|
$permissions['uri_permissions']->id,
|
||||||
|
|
||||||
$permissions['view_role_field']->id,
|
$permissions['view_role_field']->id,
|
||||||
|
|
||||||
|
$permissions['permanently_delete_user']->id,
|
||||||
|
$permissions['restore_deleted_user']->id,
|
||||||
|
$permissions['uri_deleted_users']->id,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ class ServicesProvider
|
|||||||
*/
|
*/
|
||||||
$container->extend('classMapper', function ($classMapper, $c) {
|
$container->extend('classMapper', function ($classMapper, $c) {
|
||||||
$classMapper->setClassMapping('activity_sprunje', 'UserFrosting\Sprinkle\UFTweaks\Sprunje\ActivitySprunje');
|
$classMapper->setClassMapping('activity_sprunje', 'UserFrosting\Sprinkle\UFTweaks\Sprunje\ActivitySprunje');
|
||||||
|
$classMapper->setClassMapping('user', 'UserFrosting\Sprinkle\UFTweaks\Database\Models\User');
|
||||||
|
|
||||||
return $classMapper;
|
return $classMapper;
|
||||||
});
|
});
|
||||||
|
|||||||
19
templates/modals/confirm-permanently-delete-user.html.twig
Normal file
19
templates/modals/confirm-permanently-delete-user.html.twig
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
{% extends "modals/modal.html.twig" %}
|
||||||
|
|
||||||
|
{% block modal_title %}{{translate("USER.PERMANENT_DELETE")}}{% endblock %}
|
||||||
|
|
||||||
|
{% block modal_body %}
|
||||||
|
<form class="js-form" method="delete" action="{{site.uri.public}}/{{form.action}}">
|
||||||
|
{% include "forms/csrf.html.twig" %}
|
||||||
|
<div class="js-form-alerts">
|
||||||
|
</div>
|
||||||
|
<h4>{{translate("USER.PERMANENT_DELETE_CONFIRM", {user_name: user.user_name})}}</h4>
|
||||||
|
<br>
|
||||||
|
<small>{{translate("ACTION_CANNOT_UNDONE")}}</small>
|
||||||
|
<br>
|
||||||
|
<div class="btn-group-action">
|
||||||
|
<button type="submit" class="btn btn-danger btn-lg btn-block">{{translate("USER.PERMANENT_DELETE_YES")}}</button>
|
||||||
|
<button type="button" class="btn btn-default btn-lg btn-block" data-dismiss="modal">{{translate("CANCEL")}}</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% endblock %}
|
||||||
47
templates/pages/deleted-users.html.twig
Normal file
47
templates/pages/deleted-users.html.twig
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
{% extends "pages/users.html.twig" %}
|
||||||
|
|
||||||
|
{% block page_title %}{{ translate("DELETED") }} {{ translate("USER", 2) }}{% endblock %}
|
||||||
|
|
||||||
|
{% block page_description %}{{ translate("USER.DELETED_PAGE_DESCRIPTION") }}{% endblock %}
|
||||||
|
|
||||||
|
{% block body_matter %}
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div id="widget-deletedUsers" class="box box-primary">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title"><i class="fas fa-fw fa-sitemap"></i> {{ translate("DELETED") }} {{translate('USER', 2)}}</h3>
|
||||||
|
{% include "tables/table-tool-menu.html.twig" %}
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
{% include "tables/deleted-users.html.twig" with {
|
||||||
|
"table" : {
|
||||||
|
"id" : "table-deletedUsers",
|
||||||
|
"columns" : [
|
||||||
|
(checkAccess('view_user_field', { "property" : 'activities' }) ? "last_activity" : "")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
%}
|
||||||
|
</div>
|
||||||
|
<div class="box-footer">
|
||||||
|
<button type="button" class="btn btn-success js-deletedUsers-return">
|
||||||
|
<i class="fas fa-arrow-left"></i> {{translate("RETURN")}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
{% block scripts_page %}
|
||||||
|
<!-- Include validation rules -->
|
||||||
|
<script>
|
||||||
|
{% include "pages/partials/page.js.twig" %}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Include form widgets JS -->
|
||||||
|
{{ assets.js('js/form-widgets') | raw }}
|
||||||
|
|
||||||
|
<!-- Include page-specific JS -->
|
||||||
|
{{ assets.js('js/pages/deleted-users') | raw }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -19,14 +19,19 @@
|
|||||||
}
|
}
|
||||||
%}
|
%}
|
||||||
</div>
|
</div>
|
||||||
{% if checkAccess('create_user') %}
|
|
||||||
<div class="box-footer">
|
<div class="box-footer">
|
||||||
<button type="button" class="btn btn-success js-user-create">
|
{% if checkAccess('create_user') %}
|
||||||
<i class="fas fa-plus-square"></i> {{ translate("USER.CREATE")}}
|
<button type="button" class="btn btn-success js-user-create">
|
||||||
|
<i class="fas fa-plus-square"></i> {{ translate("USER.CREATE")}}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
{% if checkAccess('uri_deleted_users') %}
|
||||||
|
<button type="button" class="btn btn-danger js-user-viewDeleted">
|
||||||
|
<i class="fas fa-minus-square"></i> {{ translate("USER.VIEW_DELETED")}}
|
||||||
</button>
|
</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
24
templates/tables/columns/deleted_users-actions.html.twig
Normal file
24
templates/tables/columns/deleted_users-actions.html.twig
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<script id="{{table.id}}-column-actions" type="text/x-handlebars-template">
|
||||||
|
{% verbatim %}
|
||||||
|
<td class="uf-table-fit-width">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button type="button" class="btn btn-primary dropdown-toggle" data-toggle="dropdown">
|
||||||
|
{% endverbatim %}{{translate("ACTIONS")}}{% verbatim %}
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
<li>
|
||||||
|
<a href="#" data-user_name="{{row.user_name}}" class="js-user-restore">
|
||||||
|
<i class="fas fa-trash-restore"></i> {% endverbatim %}{{translate("USER.RESTORE_DELETED")}}{% verbatim %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" data-user_name="{{row.user_name}}" class="js-user-permanentDelete">
|
||||||
|
<i class="fas fa-trash-alt"></i> {% endverbatim %}{{translate("USER.PERMANENT_DELETE")}}{% verbatim %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
{% endverbatim %}
|
||||||
|
</script>
|
||||||
5
templates/tables/deleted-users.html.twig
Normal file
5
templates/tables/deleted-users.html.twig
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{% extends "@uf-tweaks/tables/users.html.twig" %}
|
||||||
|
|
||||||
|
{% block table_users_column_actions %}
|
||||||
|
{% include "tables/columns/deleted_users-actions.html.twig" %}
|
||||||
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user