Skip to content

Commit

Permalink
Trash manager updates
Browse files Browse the repository at this point in the history
Apply new permissions methods; includes adjustments to base grid class
  • Loading branch information
smg6511 committed Dec 10, 2024
1 parent 06541e3 commit 5702f37
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 87 deletions.
67 changes: 24 additions & 43 deletions core/src/Revolution/Processors/Resource/Trash/GetList.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,21 @@ class GetList extends GetListProcessor

public $permission = 'view';

public $canPurge = false;
public $canUndelete = false;
public $canUPublish = false;

private modManagerDateFormatter $formatter;

public function initialize()
{
$this->formatter = $this->modx->services->get(modManagerDateFormatter::class);

$canChange = $this->modx->hasPermission('save_document') && $this->modx->hasPermission('edit_document');
$this->canPurge = $canChange && $this->modx->hasPermission('purge_deleted');
$this->canUndelete = $canChange && $this->modx->hasPermission('undelete_document');
$this->canUPublish = $canChange && $this->modx->hasPermission('publish_document');

Check warning on line 56 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L53-L56

Added lines #L53 - L56 were not covered by tests

return parent::initialize();
}

Expand Down Expand Up @@ -138,16 +148,22 @@ public function prepareRow(xPDOObject $object)
return [];
}

$permissions = [
'purge' => $this->canPurge && $object->checkPolicy('purge_deleted'),
'undelete' => $this->canUndelete && $object->checkPolicy('undelete_document'),
'publish' => $this->canUPublish && $object->checkPolicy('publish_document')
];

Check warning on line 155 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L151-L155

Added lines #L151 - L155 were not covered by tests

$charset = $this->modx->getOption('modx_charset', null, 'UTF-8');
$objectArray = $object->toArray();
$objectArray['pagetitle'] = htmlentities($objectArray['pagetitle'], ENT_COMPAT, $charset);
$objectArray['content'] = htmlentities($objectArray['content'], ENT_COMPAT, $charset);
$resourceData = $object->toArray();
$resourceData['pagetitle'] = htmlentities($resourceData['pagetitle'], ENT_COMPAT, $charset);
$resourceData['content'] = htmlentities($resourceData['content'], ENT_COMPAT, $charset);

Check warning on line 160 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L158-L160

Added lines #L158 - L160 were not covered by tests

// to enable a better detection of the resource's location, we also construct the
// parent-child path to the resource

$parents = [];
$parent = $objectArray['parent'];
$parent = $resourceData['parent'];

Check warning on line 166 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L166

Added line #L166 was not covered by tests

while ($parent != 0) {
$parentObject = $this->modx->getObject(modResource::class, $parent);
Expand All @@ -163,46 +179,11 @@ public function prepareRow(xPDOObject $object)
foreach ($parents as $parent) {
$parentPath = $parent->get('pagetitle') . ' (' . $parent->get('id') . ') > ' . $parentPath;
}
$objectArray['parentPath'] = '[' . $objectArray['context_key'] . '] ' . $parentPath;

// TODO implement permission checks for every resource and return only resources user is allowed to see

// show the permissions for the context
$canView = $this->modx->hasPermission('view_document');
$canPurge = $this->modx->hasPermission('purge_deleted');
$canUndelete = $this->modx->hasPermission('undelete_document');
$canPublish = $this->modx->hasPermission('publish_document');
$canSave = $this->modx->hasPermission('save_document');
$canEdit = $this->modx->hasPermission('edit_document');
$canList = $this->modx->hasPermission('list');
$canLoad = $this->modx->hasPermission('load');

$objectArray['iconCls'] = $this->modx->getOption('mgr_source_icon', null, 'icon-folder-open-o');

$cls = [];
$cls[] = 'restore';
$cls[] = 'purge';
$cls[] = 'undelete_document';

$cls = [];
if ($object->checkPolicy('purge_deleted') && $canSave && $canEdit && $canPurge) {
$cls[] = 'trashpurge';
}
if ($object->checkPolicy('undelete_document') && $canSave && $canEdit) {
$cls[] = 'trashundelete';
}
if ($object->checkPolicy('save') && $canSave && $canEdit) {
$cls[] = 'trashsave';
}
if ($object->checkPolicy('edit') && $canSave && $canEdit) {
$cls[] = 'trashedit';
}
$cls[] = 'trashrow';

$objectArray['cls'] = implode(' ', $cls);
$resourceData['parentPath'] = '[' . $resourceData['context_key'] . '] ' . $parentPath;

Check warning on line 182 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L182

Added line #L182 was not covered by tests

$objectArray['deletedon'] = $this->formatter->formatDateTime($objectArray['deletedon']);
$resourceData['deletedon'] = $this->formatter->formatDateTime($resourceData['deletedon']);
$resourceData['permissions'] = $permissions;

Check warning on line 185 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L184-L185

Added lines #L184 - L185 were not covered by tests

return $objectArray;
return $resourceData;

Check warning on line 187 in core/src/Revolution/Processors/Resource/Trash/GetList.php

View check run for this annotation

Codecov / codecov/patch

core/src/Revolution/Processors/Resource/Trash/GetList.php#L187

Added line #L187 was not covered by tests
}
}
17 changes: 10 additions & 7 deletions manager/assets/modext/widgets/core/modx.grid.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,13 +386,15 @@ Ext.extend(MODx.grid.GridBase, Ext.grid.EditorGridPanel, {
if (!this.userHasSavePermissions && isProtected) {
return;
}
// Checking record-level permissions; this block checking for 'cls' can be removed once all grids are updated
if (Object.hasOwn(record.data, 'cls')) {
const hasPermissionsProp = Object.hasOwn(record[this.permissionsProviderProp], 'permissions');
// Checking record-level permissions
/** @todo This block checking for 'cls' can be removed once all grids are updated */
if (!hasPermissionsProp && Object.hasOwn(record.data, 'cls')) {
if (Ext.isEmpty(record.data.cls)) {
return;
}
}
if (Object.hasOwn(record[this.permissionsProviderProp], 'permissions')) {
if (hasPermissionsProp) {
if (
Ext.isEmpty(record[this.permissionsProviderProp].permissions)
|| Object.values(record[this.permissionsProviderProp].permissions).every(permission => !permission)
Expand Down Expand Up @@ -448,6 +450,7 @@ Ext.extend(MODx.grid.GridBase, Ext.grid.EditorGridPanel, {
const
{ options } = item,
{ id } = this.menu.record,
// eslint-disable-next-line no-shadow
doAction = (id, options) => {
const
action = Ext.urlEncode(options.params || { action: options.action }),
Expand Down Expand Up @@ -703,9 +706,9 @@ Ext.extend(MODx.grid.GridBase, Ext.grid.EditorGridPanel, {
return records.some(record => this.userCanDeleteRecord(record));
},

userCanDeleteRecord: function(record) {
userCanDeleteRecord: function(record, action = 'delete') {
const objPermissions = record[this.permissionsProviderProp].permissions;
return !Ext.isEmpty(objPermissions) && !record[this.permissionsProviderProp].isProtected && objPermissions.delete === true;
return !Ext.isEmpty(objPermissions) && !record[this.permissionsProviderProp].isProtected && objPermissions[action] === true;
},

userCanDuplicateRecord: function(record) {
Expand Down Expand Up @@ -1048,13 +1051,13 @@ Ext.extend(MODx.grid.GridBase, Ext.grid.EditorGridPanel, {
case true:
case 'true':
case 1:
metaData.css = 'green';
metaData.css += ' green';
return _('yes');
case false:
case 'false':
case '':
case 0:
metaData.css = 'red';
metaData.css += ' red';
return _('no');
// no default
}
Expand Down
137 changes: 100 additions & 37 deletions manager/assets/modext/widgets/resource/modx.grid.trash.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ MODx.grid.Trash = function(config = {}) {
'parentPath',
'deletedon',
'deletedby',
'deletedby_name',
'cls'
'deletedby_name'
],
paging: true,
autosave: true,
Expand Down Expand Up @@ -47,8 +46,21 @@ MODx.grid.Trash = function(config = {}) {
width: 40,
sortable: true,
editor: {
xtype: 'combo-boolean',
renderer: 'boolean'
xtype: 'combo-boolean'
},
renderer: {
fn: function(value, metaData, record) {
/*
This field depends on permission other than the typicaledit,
thus not using the base setEditableCellClasses() method here
*/
if (!record.json.permissions.publish) {
// eslint-disable-next-line no-param-reassign
metaData.css = 'editor-disabled';
}
return this.rendYesNo(value, metaData);
},
scope: this
}
}, {
header: _('trash.deletedon_title'),
Expand All @@ -64,39 +76,57 @@ MODx.grid.Trash = function(config = {}) {
return record.data.deletedby_name;
}
}],

tbar: [
/*
Not using base getBulkActionsButton() method here, as this menu utilizes
methods/actions specific to this class not supported by that method
*/
{
text: _('bulk_actions'),
id: 'modx-btn-bulk-actions',
menu: [{
text: _('trash.selected_purge'),
itemId: 'modx-bulk-menu-opt-purge',
handler: this.purgeSelected,
scope: this
}, {
text: _('trash.selected_restore'),
itemId: 'modx-bulk-menu-opt-restore',
handler: this.restoreSelected,
scope: this
}]
}, {
text: _('trash.purge_all'),
id: 'modx-purge-all',
cls: 'x-btn-purge-all',
}],
listeners: {
click: {
fn: this.purgeAll,
fn: function(btn) {
const
menuOptPurge = btn.menu.getComponent('modx-bulk-menu-opt-purge'),
menuOptUndelete = btn.menu.getComponent('modx-bulk-menu-opt-restore')
;
if (this.getSelectionModel().getCount() === 0) {
menuOptPurge.disable();
menuOptUndelete.disable();
} else {
if (this.userCanPurge) {
menuOptPurge.enable();
}
if (this.userCanUndelete) {
menuOptUndelete.enable();
}
}
},
scope: this
}
}
}, {
text: _('trash.purge_all'),
id: 'modx-btn-purge-all',
cls: 'x-btn-purge-all',
handler: this.purgeAll
}, {
text: _('trash.restore_all'),
id: 'modx-restore-all',
id: 'modx-btn-restore-all',
cls: 'x-btn-restore-all',
listeners: {
click: {
fn: this.restoreAll,
scope: this
}
}
handler: this.restoreAll
},
'->',
{
Expand Down Expand Up @@ -125,6 +155,38 @@ MODx.grid.Trash = function(config = {}) {
});

MODx.grid.Trash.superclass.constructor.call(this, config);

this.gridMenuActions = ['purge', 'undelete'];
this.setUserHasPermissions('purge', ['purge_deleted']);
this.setUserHasPermissions('undelete', ['undelete_document']);
this.setShowActionsMenu();

this.on({
render: grid => {
const buttonsToHide = [];
if (!this.userCanPurge && !this.userCanUndelete) {
buttonsToHide.push('modx-btn-bulk-actions', 'modx-btn-purge-all', 'modx-btn-restore-all');
} else {
const bulkMenu = Ext.getCmp('modx-btn-bulk-actions').menu;
if (!this.userCanPurge) {
buttonsToHide.push('modx-btn-purge-all');
bulkMenu.getComponent('modx-bulk-menu-opt-purge').disable();
}
if (!this.userCanUndelete) {
buttonsToHide.push('modx-btn-restore-all');
bulkMenu.getComponent('modx-bulk-menu-opt-restore').disable();
}
}
if (buttonsToHide.length > 0) {
buttonsToHide.forEach(btnId => Ext.getCmp(btnId)?.hide());
}
},
beforeedit: function(e) {
if (e.field === 'published' && !this.userCanEditRecord(e.record, 'publish')) {
return false;
}
}
});
};

Ext.extend(MODx.grid.Trash, MODx.grid.Grid, {
Expand All @@ -133,37 +195,40 @@ Ext.extend(MODx.grid.Trash, MODx.grid.Grid, {
const
model = this.getSelectionModel(),
record = model.getSelected(),
p = record.data.cls,
canPurge = this.userCanPurge && this.userCanDeleteRecord(record, 'purge'),
canUndelete = this.userCanUndelete && this.userCanEditRecord(record, 'undelete'),
menu = []
;
if (model.getCount() > 1) {
menu.push({
text: _('trash.selected_purge'),
handler: this.purgeSelected,
scope: this
});
menu.push({
text: _('trash.selected_restore'),
handler: this.restoreSelected,
scope: this
});
if (canPurge) {
menu.push({
text: _('trash.selected_purge'),
handler: this.purgeSelected,
scope: this
});
}
if (canUndelete) {
menu.push({
text: _('trash.selected_restore'),
handler: this.restoreSelected,
scope: this
});
}
} else {
if (p.indexOf('trashpurge') !== -1) {
if (canPurge) {
menu.push({
text: _('trash.purge'),
handler: this.purgeResource
});
}
if (p.indexOf('trashundelete') !== -1) {
if (canUndelete) {
menu.push({
text: _('trash.restore'),
handler: this.restoreResource
});
}
}
if (menu.length > 0) {
this.addContextMenuItem(menu);
}
return menu;
},

purgeResource: function() {
Expand Down Expand Up @@ -428,9 +493,7 @@ Ext.extend(MODx.grid.Trash, MODx.grid.Grid, {
Ext.getCmp('modx-trash-link')?.updateState(+total);
},

listResources: function(separator) {
separator = separator || '';

listResources: function(separator = '') {
// creates a textual representation of the selected resources
// we create a textlist of the resources here to show them again in the confirmation box
const
Expand Down

0 comments on commit 5702f37

Please sign in to comment.