mirror of
https://github.com/processwire/processwire.git
synced 2025-08-24 15:23:11 +02:00
Update ProcessPageList to have module configuration setting as to whether or not Trash and Restore is available to non-superusers. Plus add new Restore tab to ProcessPageEdit that appears when editing a page in the trash. Previously you could only restore by using the PageList "restore" action.
This commit is contained in:
@@ -607,44 +607,36 @@ class PagePermissions extends WireData implements Module {
|
|||||||
*/
|
*/
|
||||||
public function trashListable($page = null) {
|
public function trashListable($page = null) {
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
static $showTrashUsers = array();
|
|
||||||
$user = $this->wire('user');
|
$user = $this->wire('user');
|
||||||
|
|
||||||
|
// trash and anything in it always visible to superuser
|
||||||
|
if($user->isSuperuser()) return true;
|
||||||
|
|
||||||
if(isset($showTrashUsers[$user->id])) {
|
// determine if system has page-edit-trash-created permission installed
|
||||||
$showTrash = $showTrashUsers[$user->id];
|
$petc = 'page-edit-trash-created';
|
||||||
|
if(!$this->wire('permissions')->has($petc)) $petc = false;
|
||||||
|
|
||||||
|
if($user->hasPermission('page-delete')) {
|
||||||
|
// has page-delete globally
|
||||||
|
} else if($petc && $user->hasPermission($petc)) {
|
||||||
|
// has page-edit-trash-created globally
|
||||||
|
} else if($user->hasPermission('page-delete', true)) {
|
||||||
|
// has page-delete added specifically at a template
|
||||||
|
} else if($petc && $user->hasPermission($petc, true)) {
|
||||||
|
// has page-edit-trash-created added specifically at a template
|
||||||
} else {
|
} else {
|
||||||
$petc = 'page-edit-trash-created';
|
// user does not have any of the permissions above, so trash is not listable
|
||||||
if(!$this->wire('permissions')->has($petc)) $petc = false;
|
return false;
|
||||||
if($user->isSuperuser()) {
|
|
||||||
// superuser
|
|
||||||
$showTrash = true;
|
|
||||||
} else if($user->hasPermission('page-delete')) {
|
|
||||||
// has page-delete globally
|
|
||||||
$showTrash = true;
|
|
||||||
} else if($petc && $user->hasPermission($petc)) {
|
|
||||||
// has page-edit-trash-created globally
|
|
||||||
$showTrash = true;
|
|
||||||
} else if($user->hasPermission('page-delete', true)) {
|
|
||||||
// has page-delete added specifically at a template
|
|
||||||
$showTrash = true;
|
|
||||||
} else if($petc && $user->hasPermission($petc, true)) {
|
|
||||||
// has page-edit-trash-created added specifically at a template
|
|
||||||
$showTrash = true;
|
|
||||||
} else {
|
|
||||||
$showTrash = false;
|
|
||||||
}
|
|
||||||
$showTrashUsers[$user->id] = $showTrash;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!$showTrash) return false;
|
// if request not asking about specific page, return general "trash is listable?" request
|
||||||
if($page === null || !$page->id) return $showTrash;
|
if($page === null || !$page->id) return true;
|
||||||
|
|
||||||
$trashPageID = $this->wire('config')->trashPageID;
|
|
||||||
if($page->id == $trashPageID) return $showTrash;
|
|
||||||
|
|
||||||
$listable = $this->pageEditable($page);
|
// if request is for the actual Trash page, consider this to be a general request
|
||||||
|
if($page->id == $this->wire('config')->trashPageID) return true;
|
||||||
return $listable;
|
|
||||||
|
// page is listable in the trash only if it is also editable
|
||||||
|
return $this->pageEditable($page);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -859,14 +851,24 @@ class PagePermissions extends WireData implements Module {
|
|||||||
*/
|
*/
|
||||||
public function moveable($event) {
|
public function moveable($event) {
|
||||||
/** @var Page $page */
|
/** @var Page $page */
|
||||||
$page = $event->object;
|
$page = $event->object;
|
||||||
$moveable = $page->editable('parent');
|
|
||||||
if($moveable && count($event->arguments) && $event->arguments[0] instanceof Page) {
|
/** @var Page|null $parent */
|
||||||
/** @var Page $parent */
|
$parent = $event->arguments(0);
|
||||||
$parent = $event->arguments[0];
|
if(!$parent || !$parent instanceof Page || !$parent->id) $parent = null;
|
||||||
$moveable = $parent->addable($page);
|
|
||||||
|
if($page->id == 1) {
|
||||||
|
$moveable = false;
|
||||||
|
} else {
|
||||||
|
$moveable = $page->editable('parent');
|
||||||
}
|
}
|
||||||
if($page->id == 1) $moveable = false;
|
|
||||||
|
if($moveable && $parent) {
|
||||||
|
$moveable = $parent->addable($page);
|
||||||
|
} else if($parent && $parent->isTrash() && $parent->id == $this->wire('config')->trashPageID) {
|
||||||
|
$moveable = $page->deletable();
|
||||||
|
}
|
||||||
|
|
||||||
$event->return = $moveable;
|
$event->return = $moveable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -893,7 +893,11 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
|
|||||||
|
|
||||||
if($this->page->addable() || $this->page->numChildren) $form->append($this->buildFormChildren());
|
if($this->page->addable() || $this->page->numChildren) $form->append($this->buildFormChildren());
|
||||||
if(!$this->page->template->noSettings && $this->useSettings) $form->append($this->buildFormSettings());
|
if(!$this->page->template->noSettings && $this->useSettings) $form->append($this->buildFormSettings());
|
||||||
if($this->isTrash) $this->message($this->_("This page is in the Trash"));
|
if($this->isTrash && !$this->isPost) {
|
||||||
|
$this->message($this->_("This page is in the Trash"));
|
||||||
|
$tabRestore = $this->buildFormRestore();
|
||||||
|
if($tabRestore) $form->append($tabRestore);
|
||||||
|
}
|
||||||
$tabDelete = $this->buildFormDelete();
|
$tabDelete = $this->buildFormDelete();
|
||||||
if($tabDelete->children()->count()) $form->append($tabDelete);
|
if($tabDelete->children()->count()) $form->append($tabDelete);
|
||||||
if($this->page->viewable() && !$this->requestModal) $this->buildFormView($this->getViewUrl());
|
if($this->page->viewable() && !$this->requestModal) $this->buildFormView($this->getViewUrl());
|
||||||
@@ -1427,6 +1431,47 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
|
|||||||
return $wrapper;
|
return $wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the 'restore' tab shown for pages in the trash
|
||||||
|
*
|
||||||
|
* Returns boolean false if restore not possible.
|
||||||
|
*
|
||||||
|
* @return InputfieldWrapper|bool
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected function buildFormRestore() {
|
||||||
|
|
||||||
|
if(!$this->page->isTrash()) return false;
|
||||||
|
/** @var InputfieldWrapper $wrapper */
|
||||||
|
$wrapper = $this->wire(new InputfieldWrapper());
|
||||||
|
$id = $this->className() . 'Restore';
|
||||||
|
$restoreLabel = $this->_('Restore'); // Tab Label: Restore
|
||||||
|
$restoreLabel2 = $this->_('Move out of trash and restore to original location');
|
||||||
|
$wrapper->attr('id', $id);
|
||||||
|
$wrapper->attr('title', $restoreLabel);
|
||||||
|
$this->addTab($id, $restoreLabel);
|
||||||
|
|
||||||
|
if(!preg_match('/^(\d+)\.(\d+)\.(\d+)_(.+)$/', $this->page->name, $matches)) return false;
|
||||||
|
$parentID = (int) $matches[2];
|
||||||
|
$parent = $parentID ? $this->wire('pages')->get($parentID) : null;
|
||||||
|
|
||||||
|
if(!$parent || !$parent->id) return false;
|
||||||
|
|
||||||
|
/** @var InputfieldCheckbox $field */
|
||||||
|
$field = $this->modules->get('InputfieldCheckbox');
|
||||||
|
$field->attr('id+name', 'restore_page');
|
||||||
|
$field->attr('value', $this->page->id);
|
||||||
|
|
||||||
|
$field->icon = 'trash-o';
|
||||||
|
$field->label = $restoreLabel2;
|
||||||
|
$field->description = $this->_('Check the box to confirm that you want to restore this page.'); // Restore page confirmation instruction
|
||||||
|
$field->notes = sprintf($this->_('The page will be restored into parent: **%s**'), $parent->get('path'));
|
||||||
|
$field->label2 = $restoreLabel;
|
||||||
|
$wrapper->append($field);
|
||||||
|
|
||||||
|
return $wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the 'view' tab on the Page Edit form
|
* Build the 'view' tab on the Page Edit form
|
||||||
*
|
*
|
||||||
@@ -1632,6 +1677,16 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
|
|||||||
$message .= ' - ' . $this->_('Cannot be published until errors are corrected');
|
$message .= ' - ' . $this->_('Cannot be published until errors are corrected');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if($this->input->post('restore_page') && $this->page->isTrash() && $this->page->restorable()) {
|
||||||
|
if($formErrors) {
|
||||||
|
$this->warning($this->_('Page cannot be restored while errors are present'));
|
||||||
|
} else if($this->wire('pages')->restore($this->page, false)) {
|
||||||
|
$message = sprintf($this->_('Restored Page: %s'), '{path}') . $numChanges;
|
||||||
|
} else {
|
||||||
|
$this->warning($this->_('Error restoring page'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->wire('pages')->save($this->page, $options);
|
$this->wire('pages')->save($this->page, $options);
|
||||||
$message = str_replace('{path}', $this->page->path, $message);
|
$message = str_replace('{path}', $this->page->path, $message);
|
||||||
@@ -1942,14 +1997,15 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
|
|||||||
|
|
||||||
$afterDeleteRedirect = $this->config->urls->admin . "page/?open={$this->parent->id}";
|
$afterDeleteRedirect = $this->config->urls->admin . "page/?open={$this->parent->id}";
|
||||||
if($this->wire('page')->process != $this->className()) $afterDeleteRedirect = "../";
|
if($this->wire('page')->process != $this->className()) $afterDeleteRedirect = "../";
|
||||||
|
$pagePath = $this->page->path();
|
||||||
|
|
||||||
if(($this->isTrash || $this->page->template->noTrash) && $this->page->deleteable()) {
|
if(($this->isTrash || $this->page->template->noTrash) && $this->page->deleteable()) {
|
||||||
$this->session->message(sprintf($this->_('Deleted page: %s'), $this->page->url)); // Page deleted message
|
$this->session->message(sprintf($this->_('Deleted page: %s'), $pagePath)); // Page deleted message
|
||||||
$this->pages->delete($this->page, true);
|
$this->pages->delete($this->page, true);
|
||||||
$this->session->redirect($afterDeleteRedirect);
|
$this->session->redirect($afterDeleteRedirect);
|
||||||
|
|
||||||
} else if($this->pages->trash($this->page)) {
|
} else if($this->pages->trash($this->page)) {
|
||||||
$this->session->message(sprintf($this->_('Moved page to trash: %s'), $this->page->url)); // Page moved to trash message
|
$this->session->message(sprintf($this->_('Moved page to trash: %s'), $pagePath)); // Page moved to trash message
|
||||||
$this->session->redirect($afterDeleteRedirect);
|
$this->session->redirect($afterDeleteRedirect);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
* @property int $hoverActionDelay Milliseconds delay between hover and showing of actions.
|
* @property int $hoverActionDelay Milliseconds delay between hover and showing of actions.
|
||||||
* @property int $hoverActionFade Milliseconds to spend fading in or out actions.
|
* @property int $hoverActionFade Milliseconds to spend fading in or out actions.
|
||||||
* @property bool|int $useBookmarks Allow use of PageList bookmarks?
|
* @property bool|int $useBookmarks Allow use of PageList bookmarks?
|
||||||
|
* @property bool|int $useTrash Allow non-superusers to use Trash?
|
||||||
*
|
*
|
||||||
* @method string ajaxAction($action)
|
* @method string ajaxAction($action)
|
||||||
* @method PageArray find($selectorString, Page $page)
|
* @method PageArray find($selectorString, Page $page)
|
||||||
@@ -37,7 +38,7 @@ class ProcessPageList extends Process implements ConfigurableModule {
|
|||||||
return array(
|
return array(
|
||||||
'title' => 'Page List',
|
'title' => 'Page List',
|
||||||
'summary' => 'List pages in a hierarchal tree structure',
|
'summary' => 'List pages in a hierarchal tree structure',
|
||||||
'version' => 120,
|
'version' => 121,
|
||||||
'permanent' => true,
|
'permanent' => true,
|
||||||
'permission' => 'page-edit',
|
'permission' => 'page-edit',
|
||||||
'icon' => 'sitemap',
|
'icon' => 'sitemap',
|
||||||
@@ -113,6 +114,7 @@ class ProcessPageList extends Process implements ConfigurableModule {
|
|||||||
$this->set('limit', self::defaultLimit);
|
$this->set('limit', self::defaultLimit);
|
||||||
$this->set('useHoverActions', false);
|
$this->set('useHoverActions', false);
|
||||||
$this->set('useBookmarks', false);
|
$this->set('useBookmarks', false);
|
||||||
|
$this->set('useTrash', false);
|
||||||
$this->set('bookmarks', array());
|
$this->set('bookmarks', array());
|
||||||
|
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
@@ -349,6 +351,7 @@ class ProcessPageList extends Process implements ConfigurableModule {
|
|||||||
$renderer->setLimit($limit);
|
$renderer->setLimit($limit);
|
||||||
$renderer->setPageLabelField($this->getPageLabelField());
|
$renderer->setPageLabelField($this->getPageLabelField());
|
||||||
$renderer->setLabel('trash', $this->trashLabel);
|
$renderer->setLabel('trash', $this->trashLabel);
|
||||||
|
$renderer->setUseTrash($this->useTrash || $this->wire('user')->isSuperuser());
|
||||||
|
|
||||||
return $renderer;
|
return $renderer;
|
||||||
}
|
}
|
||||||
@@ -634,6 +637,17 @@ class ProcessPageList extends Process implements ConfigurableModule {
|
|||||||
$fields = $this->wire(new InputfieldWrapper());
|
$fields = $this->wire(new InputfieldWrapper());
|
||||||
/** @var Modules $modules */
|
/** @var Modules $modules */
|
||||||
$modules = $this->wire('modules');
|
$modules = $this->wire('modules');
|
||||||
|
|
||||||
|
/** @var InputfieldCheckbox $field */
|
||||||
|
$field = $modules->get('InputfieldCheckbox');
|
||||||
|
$field->attr('name', 'useTrash');
|
||||||
|
$field->label = $this->_('Allow non-superuser editors to use Trash?');
|
||||||
|
$field->icon = 'trash-o';
|
||||||
|
$field->description =
|
||||||
|
$this->_('When checked, users will be able to see pages in the trash (only pages they have access to).') . ' ' .
|
||||||
|
$this->_('This will also enable the “Trash” and “Restore” actions, where access control allows.');
|
||||||
|
if(!empty($data['useTrash'])) $field->attr('checked', 'checked');
|
||||||
|
$fields->append($field);
|
||||||
|
|
||||||
/** @var InputfieldText $field */
|
/** @var InputfieldText $field */
|
||||||
$field = $modules->get("InputfieldText");
|
$field = $modules->get("InputfieldText");
|
||||||
|
@@ -1,8 +1,15 @@
|
|||||||
<?php namespace ProcessWire;
|
<?php namespace ProcessWire;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ProcessPageListActions
|
||||||
|
*
|
||||||
|
* @method array getExtraActions(Page $page)
|
||||||
|
*
|
||||||
|
*/
|
||||||
class ProcessPageListActions extends Wire {
|
class ProcessPageListActions extends Wire {
|
||||||
|
|
||||||
protected $superuser = false;
|
protected $superuser = false;
|
||||||
|
protected $useTrash = false;
|
||||||
|
|
||||||
protected $actionLabels = array(
|
protected $actionLabels = array(
|
||||||
'edit' => 'Edit',
|
'edit' => 'Edit',
|
||||||
@@ -33,6 +40,10 @@ class ProcessPageListActions extends Wire {
|
|||||||
$this->actionLabels = array_merge($this->actionLabels, $actionLabels);
|
$this->actionLabels = array_merge($this->actionLabels, $actionLabels);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUseTrash($useTrash) {
|
||||||
|
$this->useTrash = (bool) $useTrash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an array of available Page actions, indexed by $label => $url
|
* Get an array of available Page actions, indexed by $label => $url
|
||||||
*
|
*
|
||||||
@@ -167,7 +178,7 @@ class ProcessPageListActions extends Wire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$trashable = $page->trashable();
|
$trashable = $this->useTrash && $page->trashable();
|
||||||
$trashIcon = "<i class='fa fa-trash-o'></i> ";
|
$trashIcon = "<i class='fa fa-trash-o'></i> ";
|
||||||
if($trashable && !$user->isSuperuser()) {
|
if($trashable && !$user->isSuperuser()) {
|
||||||
// do not allow non-superuser ability to trash branches of pages, only individual pages
|
// do not allow non-superuser ability to trash branches of pages, only individual pages
|
||||||
|
@@ -19,6 +19,7 @@ abstract class ProcessPageListRender extends Wire {
|
|||||||
protected $superuser = false;
|
protected $superuser = false;
|
||||||
protected $actions = null;
|
protected $actions = null;
|
||||||
protected $options = array();
|
protected $options = array();
|
||||||
|
protected $useTrash = false;
|
||||||
|
|
||||||
public function __construct(Page $page, PageArray $children) {
|
public function __construct(Page $page, PageArray $children) {
|
||||||
$this->page = $page;
|
$this->page = $page;
|
||||||
@@ -66,6 +67,11 @@ abstract class ProcessPageListRender extends Wire {
|
|||||||
public function setLabel($key, $value) {
|
public function setLabel($key, $value) {
|
||||||
$this->actionLabels[$key] = $value;
|
$this->actionLabels[$key] = $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setUseTrash($useTrash) {
|
||||||
|
$this->useTrash = (bool) $useTrash;
|
||||||
|
$this->actions->setUseTrash($this->getUseTrash());
|
||||||
|
}
|
||||||
|
|
||||||
public function setPageLabelField($pageLabelField) {
|
public function setPageLabelField($pageLabelField) {
|
||||||
$this->pageLabelField = $pageLabelField;
|
$this->pageLabelField = $pageLabelField;
|
||||||
@@ -201,6 +207,10 @@ abstract class ProcessPageListRender extends Wire {
|
|||||||
public function getChildren() {
|
public function getChildren() {
|
||||||
return $this->children;
|
return $this->children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUseTrash() {
|
||||||
|
return $this->useTrash;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -10,8 +10,6 @@ class ProcessPageListRenderJSON extends ProcessPageListRender {
|
|||||||
|
|
||||||
protected $systemIDs = array();
|
protected $systemIDs = array();
|
||||||
|
|
||||||
protected $allowTrash = null;
|
|
||||||
|
|
||||||
public function __construct(Page $page, PageArray $children) {
|
public function __construct(Page $page, PageArray $children) {
|
||||||
|
|
||||||
parent::__construct($page, $children);
|
parent::__construct($page, $children);
|
||||||
@@ -24,44 +22,6 @@ class ProcessPageListRenderJSON extends ProcessPageListRender {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Are we allowed to display the Trash page?
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
protected function allowTrash() {
|
|
||||||
|
|
||||||
if($this->allowTrash !== null) return $this->allowTrash;
|
|
||||||
|
|
||||||
/** @var User $user */
|
|
||||||
$user = $this->wire('user');
|
|
||||||
|
|
||||||
$petc = 'page-edit-trash-created';
|
|
||||||
if(!$this->wire('permissions')->has($petc)) $petc = false;
|
|
||||||
|
|
||||||
if($user->isSuperuser()) {
|
|
||||||
// superuser
|
|
||||||
$this->allowTrash = true;
|
|
||||||
} else if($user->hasPermission('page-delete')) {
|
|
||||||
// has page-delete globally
|
|
||||||
$this->allowTrash = true;
|
|
||||||
} else if($petc && $user->hasPermission($petc)) {
|
|
||||||
// has page-edit-trash-created globally
|
|
||||||
$this->allowTrash = true;
|
|
||||||
} else if($user->hasPermission('page-delete', true)) {
|
|
||||||
// has page-delete added specifically at a template
|
|
||||||
$this->allowTrash = true;
|
|
||||||
} else if($petc && $user->hasPermission($petc, true)) {
|
|
||||||
// has page-edit-trash-created added specifically at a template
|
|
||||||
$this->allowTrash = true;
|
|
||||||
} else {
|
|
||||||
$this->allowTrash = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->allowTrash;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderChild(Page $page) {
|
public function renderChild(Page $page) {
|
||||||
|
|
||||||
$outputFormatting = $page->outputFormatting;
|
$outputFormatting = $page->outputFormatting;
|
||||||
@@ -173,7 +133,9 @@ class ProcessPageListRenderJSON extends ProcessPageListRender {
|
|||||||
|
|
||||||
if(!$this->superuser && $page404 && $page404->parent_id == 1 && !isset($extraPages[$idTrash])) {
|
if(!$this->superuser && $page404 && $page404->parent_id == 1 && !isset($extraPages[$idTrash])) {
|
||||||
$pageTrash = $this->wire('pages')->get($idTrash);
|
$pageTrash = $this->wire('pages')->get($idTrash);
|
||||||
if($pageTrash->id && $pageTrash->listable()) $extraPages[$pageTrash->id] = $pageTrash;
|
if($pageTrash->id && $this->getUseTrash() && $pageTrash->listable()) {
|
||||||
|
$extraPages[$pageTrash->id] = $pageTrash;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($extraPages as $page) {
|
foreach($extraPages as $page) {
|
||||||
|
Reference in New Issue
Block a user