1
0
mirror of https://github.com/processwire/processwire.git synced 2025-08-09 00:06:55 +02:00

Refactoring of the PagesTrash class and related, plus some minor additions to PagesNames class

This commit is contained in:
Ryan Cramer
2018-08-24 10:41:47 -04:00
parent dfa8cc7b74
commit cc53b835b6
6 changed files with 439 additions and 110 deletions

View File

@@ -29,7 +29,7 @@
* @method bool saveField(Page $page, $field, array $options = array()) Save just the named field from $page. Same as: $page->save('field') #pw-group-manipulation
* @method bool trash(Page $page, $save = true) Move a page to the trash. If you have already set the parent to somewhere in the trash, then this method won't attempt to set it again. #pw-group-manipulation
* @method bool restore(Page $page, $save = true) Restore a trashed page to its original location. #pw-group-manipulation
* @method int emptyTrash() Empty the trash and return number of pages deleted. #pw-group-manipulation
* @method int|array emptyTrash(array $options = array()) Empty the trash and return number of pages deleted. #pw-group-manipulation
* @method bool delete(Page $page, $recursive = false, array $options = array()) Permanently delete a page and it's fields. Unlike trash(), pages deleted here are not restorable. If you attempt to delete a page with children, and don't specifically set the $recursive param to True, then this method will throw an exception. If a recursive delete fails for any reason, an exception will be thrown. #pw-group-manipulation
* @method Page|NullPage clone(Page $page, Page $parent = null, $recursive = true, $options = array()) Clone an entire page, it's assets and children and return it. #pw-group-manipulation
* @method Page|NullPage add($template, $parent, $name = '', array $values = array()) #pw-group-manipulation
@@ -619,13 +619,14 @@ class Pages extends Wire {
*
* #pw-group-manipulation
*
* @return int Returns total number of pages deleted from trash.
* @param array $options See PagesTrash::emptyTrash() for advanced options
* @return int|array Returns total number of pages deleted from trash, or array if verbose option specified.
* This number is negative or 0 if not all pages could be deleted and error notices may be present.
* @see Pages::trash(), Pages::restore()
*
*/
public function ___emptyTrash() {
return $this->trasher()->emptyTrash();
public function ___emptyTrash(array $options = array()) {
return $this->trasher()->emptyTrash($options);
}
/**

View File

@@ -135,17 +135,41 @@ class PagesNames extends Wire {
return array(implode($delimiter, $parts), (int) $suffix);
}
/**
* Does the given name or Page have a number suffix? Returns the number if yes, or false if not
*
* @param string|Page $name
* @param bool $getNamePrefix Return the name prefix rather than the number suffix? (default=false)
* @return int|bool|string Returns false if no number suffix, or int for number suffix or string for name prefix (if requested)
*
*/
public function hasNumberSuffix($name, $getNamePrefix = false) {
if($name instanceof Page) $name = $name->name;
list($namePrefix, $numberSuffix) = $this->nameAndNumber($name);
if(!$numberSuffix) return false;
return $getNamePrefix ? $namePrefix : $numberSuffix;
}
/**
* Get the name format string that should be used for given $page if no name was assigned
*
* @param Page $page
* @param array $options
* - `fallbackFormat` (string): Fallback format if another cannot be determined (default='untitled-time').
* - `parent` (Page|null): Optional parent page to use instead of $page->parent (default=null).
* @return string
*
*/
public function defaultPageNameFormat(Page $page) {
public function defaultPageNameFormat(Page $page, array $options = array()) {
$format = 'untitled-time'; // default fallback format
$parent = $page->parent();
$defaults = array(
'fallbackFormat' => 'untitled-time',
'parent' => null,
);
$options = array_merge($defaults, $options);
$parent = $options['parent'] ? $options['parent'] : $page->parent();
$format = '';
if($parent && $parent->id && $parent->template->childNameFormat) {
// if format specified with parent template, use that
@@ -162,6 +186,14 @@ class PagesNames extends Wire {
if(strlen($pageTitle->getDefaultValue())) $format = 'title';
}
if(empty($format)) {
if($page->id && $options['fallbackFormat']) {
$format = $options['fallbackFormat'];
} else {
$format = 'untitled-time';
}
}
return $format;
}
@@ -273,13 +305,23 @@ class PagesNames extends Wire {
*
* @param string|Page $name Name to make unique, or Page to pull it from.
* @param Page||string|null You may optionally specify Page or name in this argument if not in the first.
*
* Note that specifying a Page here or in the first argument is important if the page already exists, as it is used
* as the page to exclude when checking for name collisions, and we want to exclude $page from that check.
* @param array $options
* - `parent` (Page|null): Optionally specify a different parent if $page does not currently have the parent you want to use.
* - `language` (Language|int): Get unique for this language (if multi-language page names active).
* @return string Returns unique name
*
*/
public function uniquePageName($name = '', $page = null) {
public function uniquePageName($name = '', $page = null, array $options = array()) {
$options = array();
$defaults = array(
'page' => null,
'parent' => null,
'language' => null
);
$options = array_merge($defaults, $options);
if($name instanceof Page) {
$_name = is_string($page) ? $page : '';
@@ -288,14 +330,22 @@ class PagesNames extends Wire {
}
if($page) {
$parent = $page->parent();
if($options['parent'] === null) $options['parent'] = $page->parent();
if(!strlen($name)) $name = $page->name;
$options['parent'] = $parent;
$options['page'] = $page;
}
if(!strlen($name)) {
$name = $this->uniqueRandomPageName();
// no name currently present, so we need to determine what kind of name it should have
if($page) {
$format = $this->defaultPageNameFormat($page, array(
'fallbackFormat' => $page->id ? 'random' : 'untitled-time',
'parent' => $options['parent']
));
$name = $this->pageNameFromFormat($page, $format);
} else {
$name = $this->uniqueRandomPageName();
}
}
while($this->pageNameExists($name, $options)) {
@@ -389,7 +439,7 @@ class PagesNames extends Wire {
* - `page` (Page|int): Ignore this Page or page ID
* - `parent` (Page|int): Limit search to only this parent.
* - `multilang` (bool): Check other languages if multi-language page names supported? (default=false)
* - `language` (Language|int): Limit check to only this language (default=null)
* - `language` (Language|int): Limit check to only this language [also implies multilang option] (default=null)
*
* @return int Returns quantity of pages using name, or 0 if name not in use.
*

View File

@@ -5,7 +5,7 @@
*
* Implements page trash/restore/empty methods of the $pages API variable
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
*/
@@ -18,6 +18,12 @@ class PagesTrash extends Wire {
*/
protected $pages;
/**
* Construct
*
* @param Pages $pages
*
*/
public function __construct(Pages $pages) {
$this->pages = $pages;
}
@@ -34,13 +40,17 @@ class PagesTrash extends Wire {
*
*/
public function trash(Page $page, $save = true) {
if(!$this->pages->isDeleteable($page) || $page->template->noTrash) {
throw new WireException("This page may not be placed in the trash");
}
if(!$trash = $this->pages->get($this->config->trashPageID)) {
throw new WireException("Unable to load trash page defined by config::trashPageID");
}
$page->addStatus(Page::statusTrash);
if(!$page->parent->isTrash()) {
$parentPrevious = $page->parent;
$page->parent = $trash;
@@ -49,7 +59,10 @@ class PagesTrash extends Wire {
} else {
$parentPrevious = null;
}
if(!preg_match('/^' . $page->id . '(\.\d+\.\d+)?_.+/', $page->name)) {
$nameInfo = $this->parseTrashPageName($page->name);
if(!$nameInfo || $nameInfo['id'] != $page->id) {
// make the name unique when in trash, to avoid namespace collision and maintain parent restore info
$name = $page->id;
if($parentPrevious && $parentPrevious->id) {
@@ -68,12 +81,13 @@ class PagesTrash extends Wire {
$page->set("name$language->id", $name . "_" . $langName);
}
}
}
if($save) $this->pages->save($page);
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, false);
if($save) $this->pages->trashed($page);
$this->pages->debugLog('trash', $page, true);
return true;
}
@@ -90,110 +104,365 @@ class PagesTrash extends Wire {
*/
public function restore(Page $page, $save = true) {
if(preg_match('/^(' . $page->id . ')((?:\.\d+\.\d+)?)_(.+)$/', $page->name, $matches)) {
$info = $this->getRestoreInfo($page, true);
if(!$info['restorable']) return false;
if($matches[2]) {
/** @noinspection PhpUnusedLocalVariableInspection */
list($unused, $parentID, $sort) = explode('.', $matches[2]);
$parentID = (int) $parentID;
$sort = (int) $sort;
} else {
$parentID = 0;
$sort = 0;
}
$prefix = $matches[1] . $matches[2] . '_';
$name = $matches[3];
if($parentID && $page->parent->isTrash() && !$page->parentPrevious) {
// no new parent was defined, so use the one in the page name
$newParent = $this->pages->get($parentID);
if($newParent->id && $newParent->id != $page->id) {
$page->parent = $newParent;
$page->sort = $sort;
}
}
if(!count($page->parent->children("name=$name, include=all"))) {
$page->name = $name; // remove namespace collision info if no collision
// do the same for other languages, when applicable
if($this->wire('languages') && $this->wire('modules')->isInstalled('LanguageSupportPageNames')) {
foreach($this->wire('languages') as $language) {
if($language->isDefault()) continue;
$langName = $page->get("name$language->id");
if(strpos($langName, $prefix) !== 0) continue;
$langName = str_replace($prefix, '', $langName);
$page->set("name$language->id", $langName);
}
}
}
}
if(!$page->parent->isTrash()) {
if($page->parent->isTrash()) {
if($save) $page->save();
} else {
$page->removeStatus(Page::statusTrash);
if($save) $page->save();
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, true);
if($save) $this->pages->restored($page);
$this->pages->debugLog('restore', $page, true);
} else {
if($save) $page->save();
}
return true;
}
/**
* Get info needed to restore a Page that is in the trash
*
* Returns array with the following info:
* - `restorable` (bool): Is the page restorable to a previous known/existing parent?
* - `notes` (array): Any additional notes to explain restore info (like reason why not restorable, or why name changed, etc.)
* - `parent` (Page|NullPage): Parent page that it should restore to
* - `parent_id` (int): ID of parent page that it should restore to
* - `sort` (int): Sort order that should be restored to page
* - `name` (string): Name that should be restored to pages “name” property.
* - `namePrevious` (string): Previous name, if we had to modify the original name to make it restorable.
* - `name{id}` (string): Name that should be restored to language where {id} is language ID (if appliable).
*
* @param Page $page Page to restore
* @param bool $populateToPage Populate this information to given page? (default=false)
* @return array
*
*/
public function getRestoreInfo(Page $page, $populateToPage = false) {
$info = array(
'restorable' => false,
'notes' => array(),
'parent' => $this->pages->newNullPage(),
'parent_id' => 0,
'sort' => 0,
'name' => '',
'namePrevious' => '',
);
/** @var Languages|array $languages */
$languages = $this->wire('languages');
if(!$languages || !$this->wire('modules')->isInstalled('LanguageSupportPageNames')) $languages = array();
// initialize name properties in $info for each language
foreach($languages as $language) {
$info["name$language->id"] = '';
}
$result = $this->parseTrashPageName($page->name);
if(!$result || $result['id'] !== $page->id) {
// page does not have restore info
$info['notes'][] = 'Page name does not contain restore information';
return $info;
}
$name = $result['name'];
$trashPrefix = $result['prefix']; // pageID.parentID.sort_ prefix for testing other language names later
$newParent = null;
$parentID = $result['parent_id'];
$sort = $result['sort'];
if($parentID && $parentID != $page->id) {
if($page->rootParent()->isTrash()) {
// no new parent was defined, so use the one in the page name
$newParent = $this->pages->get($parentID);
if(!$newParent->id) {
$newParent = null;
$info['notes'][] = 'Original parent no longer exists';
}
} else {
$info['notes'][] = 'Page root parent is not trash';
}
} else if($parentID) {
$info['notes'][] = "Invalid parent ID: $parentID";
} else {
// page was likely trashed a long time ago, before this info was stored
$info['notes'][] = 'Page name does not contain previous parent or sort info';
}
$info['parent'] = $newParent ? $newParent : $this->pages->newNullPage();
$info['parent_id'] = $parentID;
$info['sort'] = $sort;
// if we have no new parent available we can exit now
if(!$newParent) {
$info['notes'][] = 'Unable to determine parent to restore to';
return $info;
}
// check if there is already a page at the restore location with the same name
$namePrevious = $name;
$name = $this->pages->names()->uniquePageName($name, $page, array('parent' => $newParent));
if($name !== $namePrevious) {
$info['notes'][] = "Name changed from '$namePrevious' to '$name' to be unique in new parent";
$info['namePrevious'] = $namePrevious;
}
$info['name'] = $name;
$info['restorable'] = true;
if($populateToPage) {
$page->name = $name;
$page->parent = $newParent;
$page->sort = $sort;
}
// do the same for other languages, when applicable
foreach($languages as $language) {
/** @var Language $language */
if($language->isDefault()) continue;
$langName = $page->get("name$language->id");
if(!strlen($langName)) continue;
if(strpos($langName, $trashPrefix) === 0) {
list(,$langName) = explode('_', $langName);
}
$langNamePrevious = $langName;
$langName = $this->pages->names()->uniquePageName($langName, $page, array(
'parent' => $newParent,
'language' => $language
));
if($populateToPage) $page->set("name$language->id", $langName);
$info["name$language->id"] = $langName;
if($langName !== $langNamePrevious) {
$info['notes'][] = $language->get('title|name') . ' ' .
"name changed from '$langNamePrevious' to '$langName' to be unique in new parent";
}
}
return $info;
}
/**
* Parse a trashed page name into an array of its components
*
* @param string $name
* @return array|bool Returns array of info if name is a trash/restore name, or boolean false if not
*
*/
public function parseTrashPageName($name) {
$info = array(
'id' => 0,
'parent_id' => 0,
'sort' => 0,
'name' => $name,
'prefix' => '',
'note' => '',
);
// match "pageID.parentID.sort_name" in page name (1).(2.2)_3
if(!preg_match('/^(\d+)((?:\.\d+\.\d+)?)_(.+)$/', $name, $matches)) return false;
$info['id'] = (int) $matches[1];
$info['name'] = $matches[3];
if($matches[2]) {
// matches[2] contains ".parentID.sort"
list(, $parentID, $sort) = explode('.', $matches[2]);
$info['parent_id'] = (int) $parentID;
$info['sort'] = (int) $sort;
} else {
// page was likely trashed a long time ago, before this info was stored
$info['note'] = 'Page name does not contain previous parent or sort info';
}
// pageID.parentID.sort_ prefix that can be used with other language names
$info['prefix'] = $matches[1] . $matches[2] . '_';
return $info;
}
/**
* Delete all pages in the trash
*
* Populates error notices when there are errors deleting specific pages.
*
* @return int Returns total number of pages deleted from trash.
* This number is negative or 0 if not all pages could be deleted and error notices may be present.
* @param array $options
* - `chunkSize` (int): Pages will be deleted in chunks of this many pages per chunk (default=100).
* - `chunkTimeLimit` (int): Maximum seconds allowed to process deletion of each chunk (default=600).
* - `chunkLimit' (int): Maximum chunks to process in an emptyTrash() call (default=1000);
* - `pageLimit` (int): Maximum pages to delete per emptyTrash() call (default=0, no limit).
* - `timeLimit` (int): Maximum time (in seconds) to allow for trash empty (default=3600).
* - `pass2` (bool): Perform a secondary pass using alternate method as a backup? (default=true)
* Note: pass2 is always disabled when a pageLimit is in use or timeLimit has been exceeded.
* - `verbose` (bool): Return verbose array of information about the trash empty process? For debug/dev purposes (default=false)
* @return int|array Returns integer (default) or array in verbose mode.
* - By default, returns total number of pages deleted from trash. This number is negative or 0 if not
* all pages could be deleted and error notices may be present.
* - Returns associative array with verbose information if verbose option is chosen.
*
*/
public function emptyTrash() {
public function emptyTrash(array $options = array()) {
$trashPage = $this->pages->get($this->wire('config')->trashPageID);
$selector = "include=all, has_parent=$trashPage, children.count=0, status=" . Page::statusTrash;
$defaults = array(
'chunkSize' => 100,
'chunkTimeLimit' => 600,
'chunkLimit' => 100,
'pageLimit' => 0,
'timeLimit' => 3600,
'pass2' => true,
'verbose' => false,
);
$options = array_merge($defaults, $options);
$trashPageID = $this->wire('config')->trashPageID;
$masterSelector = "include=all, children.count=0, status=" . Page::statusTrash;
$totalDeleted = 0;
$lastTotalInTrash = 0;
$numBatches = 0;
$chunkCnt = 0;
$errorCnt = 0;
$nonTrashIDs = array(); // page IDs that had trash status but did not have trash parent
$result = array();
$timer = $options['verbose'] ? Debug::timer() : null;
$startTime = time();
$stopTime = $options['timeLimit'] ? $startTime + $options['timeLimit'] : false;
$stopNow = false;
$options['stopTime'] = $stopTime; // for pass2
// Empty trash pass1:
// Operates by finding pages in trash using Page::statusTrash that have no children
do {
set_time_limit(60 * 10);
$selector = $masterSelector;
if($options['chunkTimeLimit']) {
set_time_limit($options['chunkTimeLimit']);
}
if(count($nonTrashIDs)) {
$selector .= ", id!=" . implode('|', $nonTrashIDs);
}
$totalInTrash = $this->pages->count($selector);
if(!$totalInTrash || $totalInTrash == $lastTotalInTrash) break;
$lastTotalInTrash = $totalInTrash;
$items = $this->pages->find("$selector, limit=100");
if($options['chunkSize'] > 0) $selector .= ", limit=$options[chunkSize]";
$items = $this->pages->find($selector);
$cnt = $items->count();
foreach($items as $item) {
// determine if any limits have been reached
if($stopTime && time() > $stopTime) $stopNow = true;
if($options['pageLimit'] && $totalDeleted >= $options['pageLimit']) $stopNow = true;
if($stopNow) break;
// if page does not have trash as a parent, then this is a page with trash status
// that is somewhere else in the page tree (not likely)
if($item->rootParent()->id !== $trashPageID) {
$nonTrashIDs[$item->id] = $item->id;
$errorCnt++;
continue;
}
// delete the page
try {
$totalDeleted += $this->pages->delete($item, true);
} catch(\Exception $e) {
$this->error($e->getMessage());
$errorCnt++;
}
}
$this->pages->uncacheAll();
$numBatches++;
} while($cnt);
// just in case anything left in the trash, use a backup method
$trashPage = $this->pages->get($trashPage->id); // fresh copy
$trashPages = $trashPage->children("include=all");
foreach($trashPages as $t) {
try {
$totalDeleted += $this->pages->delete($t, true);
} catch(\Exception $e) {
$this->error($e->getMessage());
}
$this->pages->uncacheAll();
$chunkCnt++;
if($options['chunkLimit'] && $chunkCnt >= $options['chunkLimit']) break;
} while($cnt && !$stopNow);
// if recording verbose info, populate it for pass1 now
if($options['verbose']) {
$result['pass1_cnt'] = $chunkCnt;
$result['pass1_numDeleted'] = $totalDeleted;
$result['pass1_numErrors'] = $errorCnt;
$result['pass1_elapsedTime'] = Debug::timer($timer);
}
$this->pages->uncacheAll();
if($totalDeleted) {
$totalInTrash = $this->pages->count("has_parent=$trashPage, include=all, status=" . Page::statusTrash);
if($totalInTrash) $totalDeleted = $totalDeleted * -1;
// Empty trash pass2:
// Operates by finding pages that are children of the Trash and performing recursive delete upon them
if($options['pass2'] && !$stopNow && !$options['pageLimit']) {
$totalDeleted += $this->emptyTrashPass2($options, $result);
}
if($totalDeleted || $options['verbose']) {
$numTrashChildren = $this->wire('pages')->count("parent_id=$trashPageID, include=all");
// return a negative number if pages still remain in trash
if($numTrashChildren && !$options['verbose']) $totalDeleted = $totalDeleted * -1;
} else {
$numTrashChildren = 0;
}
if($options['verbose']) {
$result['startTime'] = $startTime;
$result['elapsedTime'] = Debug::timer($timer);
$result['numDeleted'] = $totalDeleted;
$result['numRemain'] = $numTrashChildren;
$result['numErrors'] = $errorCnt;
$result['numMispaced'] = count($nonTrashIDs);
$result['idsMisplaced'] = $nonTrashIDs;
$result['options'] = $options;
return $result;
}
return $totalDeleted;
}
/**
* Secondary pass for trash deletion
*
* This works by finding the children of the trash page and performing a recursive delete on them.
*
* @param array $options Options passed to emptyTrash() method
* @param array $result Verbose array, modified directly
* @return int
*
*/
protected function emptyTrashPass2(array $options, &$result) {
if($options['chunkTimeLimit']) {
set_time_limit($options['chunkTimeLimit']);
}
$timer = $options['verbose'] ? Debug::timer() : null;
$numErrors = 0;
$numDeleted = 0;
$trashPage = $this->pages->get($this->wire('config')->trashPageID);
$trashPages = $trashPage->children("include=all");
foreach($trashPages as $t) {
try {
// perform recursive delete
$numDeleted += $this->pages->delete($t, true);
} catch(\Exception $e) {
$this->error($e->getMessage());
$numErrors++;
}
if($options['stopTime'] && time() > $options['stopTime']) break;
}
$this->pages->uncacheAll();
if($options['verbose']) {
$result['pass2_numDeleted'] = $numDeleted;
$result['pass2_numErrors'] = $numErrors;
$result['pass2_elapsedTime'] = Debug::timer($timer);
}
return $numDeleted;
}
}

View File

@@ -746,25 +746,19 @@ class PagePermissions extends WireData implements Module {
*
*/
public function restorable($event) {
$event->return = true;
/** @var Page $page */
$page = $event->object;
/** @var User $user */
$user = $this->wire('user');
$event->return = false;
if($page->isLocked()) return;
if(!$page->isTrash()) return;
if(!$page->isTrash() && !$page->rootParent()->isTrash()) return;
if(!$user->isSuperuser() && !$page->editable()) return;
$parts = explode('.', $page->name, 4);
if(count($parts) < 3) return;
list($pageID, $parentID, $rest) = explode('.', $page->name);
list($sort, $name) = explode('_', $rest, 2);
if($pageID || $sort) {} // ignore
$parent = $this->wire('pages')->get((int) $parentID);
$info = $this->wire('pages')->trasher()->getRestoreInfo($page);
if(!$info['restorable']) return;
$parent = $info['parent'];
// check if parent does not allow this user to add pages here
if(!$parent->addable($page)) return;
// check if parent already has a page with the same name
if($parent->numChildren("name=$name, include=all")) return;
if(!$parent->id || !$parent->addable($page)) return;
$event->return = true;
}

View File

@@ -1443,6 +1443,9 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
if(!$this->page->isTrash()) return false;
if(!$this->page->restorable()) return false;
$info = $this->wire('pages')->trasher()->getRestoreInfo($this->page);
if(!$info['restorable']) return false;
/** @var InputfieldWrapper $wrapper */
$wrapper = $this->wire(new InputfieldWrapper());
$id = $this->className() . 'Restore';
@@ -1451,12 +1454,7 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
$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;
$newPath = $info['parent']->path() . $info['name'] . '/';
/** @var InputfieldCheckbox $field */
$field = $this->modules->get('InputfieldCheckbox');
@@ -1466,7 +1464,9 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
$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->notes = sprintf($this->_('The page will be restored to: **%s**.'), $newPath);
if($info['namePrevious']) $field->notes .= ' ' .
sprintf($this->_('Original name will be adjusted from **%1$s** to **%2$s** to be unique.'), $info['namePrevious'], $info['name']);
$field->label2 = $restoreLabel;
$wrapper->append($field);

View File

@@ -8,13 +8,15 @@
* For more details about how Process modules work, please see:
* /wire/core/Process.php
*
* ProcessWire 3.x, Copyright 2016 by Ryan Cramer
* ProcessWire 3.x, Copyright 2018 by Ryan Cramer
* https://processwire.com
*
*/
class ProcessPageTrash extends Process {
const debug = false;
public static function getModuleInfo() {
return array(
'title' => __('Page Trash', __FILE__), // getModuleInfo title
@@ -31,16 +33,23 @@ class ProcessPageTrash extends Process {
public function ___execute() {
if(!$this->wire('user')->isSuperuser()) throw new WirePermissionException();
$input = $this->wire('input');
if(isset($_POST['submit_empty']) && !empty($_POST['confirm_empty'])) {
if($input->post('submit_empty') && $input->post('confirm_empty')) {
$this->session->CSRF->validate();
$totalDeleted = $this->wire('pages')->emptyTrash();
$result = $this->wire('pages')->emptyTrash(array(
'verbose' => true
));
if(self::debug) $this->warning($result);
$message = $this->_('Emptied the trash') . ' ' .
sprintf($this->_n('(%d page)', '(%d pages)', abs($totalDeleted)), abs($totalDeleted));
if($totalDeleted < 0) $message .= ' - ' . $this->_('Not all pages could be deleted');
sprintf($this->_n('(%d page)', '(%d pages)', $result['numDeleted']), $result['numDeleted']);
if($result['numRemain'] > 0) {
$message .= ' - ' . $this->_('Not all pages could be deleted');
}
$this->session->message($message);
// redirect to admin root after emptying trash
$this->session->redirect($this->config->urls->admin);
$this->session->redirect($this->wire('config')->urls('admin'));
return '';
} else {
// render a form showing what pages are in the trash and confirming they want to empty it
return $this->render();
@@ -55,20 +64,24 @@ class ProcessPageTrash extends Process {
$trashPages = $this->pages->get($this->config->trashPageID)->children("limit=2, status<" . Page::statusMax);
/** @var InputfieldForm $form */
$form = $this->modules->get("InputfieldForm");
$form->attr('action', './');
$form->attr('method', 'post');
if(!count($trashPages)) return "<h2>" . $this->_("The trash is empty") . "</h2>";
/** @var InputfieldMarkup $field */
$field = $this->modules->get("InputfieldMarkup");
$field->label = $this->_("The following pages are in the trash");
/** @var ProcessPageList $pageList */
$pageList = $this->modules->get('ProcessPageList');
$pageList->set('id', $this->config->trashPageID);
$pageList->set('showRootPage', false);
$field->value = $pageList->execute();
$form->add($field);
/** @var InputfieldCheckbox $field */
$field = $this->modules->get("InputfieldCheckbox");
$field->attr('name', 'confirm_empty');
$field->attr('value', 1);
@@ -77,13 +90,15 @@ class ProcessPageTrash extends Process {
$field->notes = $this->_("If there are too many items in the trash, you may have to empty it multiple times.");
$form->add($field);
/** @var InputfieldSubmit $field */
$field = $this->modules->get("InputfieldSubmit");
$field->attr('name', 'submit_empty');
$form->add($field);
return $form->render();
}
public function ___executeForce() {
}
}