mirror of
https://github.com/processwire/processwire.git
synced 2025-08-09 16:26:59 +02:00
Add new $pages moveReady(), restoreReady(), and renameReady() hooks. Add option for callback hook on $pages->save(). Improvements to PagesTrash class. Update $pages class so restored() hook does not ever need to be called manually, and update ProcessPageEdit to reflect that.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
* This is the most used object in the ProcessWire API.
|
* This is the most used object in the ProcessWire API.
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2022 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2024 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
* @link http://processwire.com/api/variables/pages/ Offical $pages Documentation
|
* @link http://processwire.com/api/variables/pages/ Offical $pages Documentation
|
||||||
@@ -62,10 +62,12 @@
|
|||||||
* @method saveReady(Page $page) Hook called just before a page is saved.
|
* @method saveReady(Page $page) Hook called just before a page is saved.
|
||||||
* @method saved(Page $page, array $changes = array(), $values = array()) Hook called after a page is successfully saved.
|
* @method saved(Page $page, array $changes = array(), $values = array()) Hook called after a page is successfully saved.
|
||||||
* @method added(Page $page) Hook called when a new page has been added.
|
* @method added(Page $page) Hook called when a new page has been added.
|
||||||
|
* @method moveReady(Page $page) Hook called when a page is about to be moved to another parent.
|
||||||
* @method moved(Page $page) Hook called when a page has been moved from one parent to another.
|
* @method moved(Page $page) Hook called when a page has been moved from one parent to another.
|
||||||
* @method templateChanged(Page $page) Hook called when a page template has been changed.
|
* @method templateChanged(Page $page) Hook called when a page template has been changed.
|
||||||
* @method trashReady(Page $page) Hook called when a page is about to be moved to the trash.
|
* @method trashReady(Page $page) Hook called when a page is about to be moved to the trash.
|
||||||
* @method trashed(Page $page) Hook called when a page has been moved to the trash.
|
* @method trashed(Page $page) Hook called when a page has been moved to the trash.
|
||||||
|
* @method restoreReady(Page $page) Hook called when a page is about to be restored out of the trash.
|
||||||
* @method restored(Page $page) Hook called when a page has been moved OUT of the trash.
|
* @method restored(Page $page) Hook called when a page has been moved OUT of the trash.
|
||||||
* @method deleteReady(Page $page, array $options) Hook called just before a page is deleted.
|
* @method deleteReady(Page $page, array $options) Hook called just before a page is deleted.
|
||||||
* @method deleted(Page $page, array $options) Hook called after a page has been deleted.
|
* @method deleted(Page $page, array $options) Hook called after a page has been deleted.
|
||||||
@@ -73,6 +75,7 @@
|
|||||||
* @method deletedBranch(Page $page, array $options, $numDeleted) Hook called after branch of pages deleted, on initiating page only.
|
* @method deletedBranch(Page $page, array $options, $numDeleted) Hook called after branch of pages deleted, on initiating page only.
|
||||||
* @method cloneReady(Page $page, Page $copy) Hook called just before a page is cloned.
|
* @method cloneReady(Page $page, Page $copy) Hook called just before a page is cloned.
|
||||||
* @method cloned(Page $page, Page $copy) Hook called after a page has been successfully cloned.
|
* @method cloned(Page $page, Page $copy) Hook called after a page has been successfully cloned.
|
||||||
|
* @method renameReady(Page $page) Hook called when a page is about to be renamed.
|
||||||
* @method renamed(Page $page) Hook called after a page has been successfully renamed.
|
* @method renamed(Page $page) Hook called after a page has been successfully renamed.
|
||||||
* @method sorted(Page $page, $children = false, $total = 0) Hook called after $page has been sorted.
|
* @method sorted(Page $page, $children = false, $total = 0) Hook called after $page has been sorted.
|
||||||
* @method statusChangeReady(Page $page) Hook called when a page's status has changed and is about to be saved.
|
* @method statusChangeReady(Page $page) Hook called when a page's status has changed and is about to be saved.
|
||||||
@@ -2198,6 +2201,20 @@ class Pages extends Wire {
|
|||||||
$page->setQuietly('_added', true);
|
$page->setQuietly('_added', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook called when a page is about to be moved to another parent
|
||||||
|
*
|
||||||
|
* Note the previous parent is accessible in the `$page->parentPrevious` property.
|
||||||
|
*
|
||||||
|
* #pw-hooker
|
||||||
|
*
|
||||||
|
* @param Page $page Page that is about to be moved.
|
||||||
|
* @since 3.0.235
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function ___moveReady(Page $page) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook called when a page has been moved from one parent to another
|
* Hook called when a page has been moved from one parent to another
|
||||||
*
|
*
|
||||||
@@ -2258,6 +2275,18 @@ class Pages extends Wire {
|
|||||||
public function ___trashed(Page $page) {
|
public function ___trashed(Page $page) {
|
||||||
$this->log("Trashed page", $page);
|
$this->log("Trashed page", $page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook called when a page is about to be moved OUT of the trash (restored)
|
||||||
|
*
|
||||||
|
* #pw-hooker
|
||||||
|
*
|
||||||
|
* @param Page $page Page that is about to be restored
|
||||||
|
* @since 3.0.235
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function ___restoreReady(Page $page) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook called when a page has been moved OUT of the trash (restored)
|
* Hook called when a page has been moved OUT of the trash (restored)
|
||||||
@@ -2387,6 +2416,29 @@ class Pages extends Wire {
|
|||||||
public function ___cloned(Page $page, Page $copy) {
|
public function ___cloned(Page $page, Page $copy) {
|
||||||
$this->log("Cloned page to $copy->path", $page);
|
$this->log("Cloned page to $copy->path", $page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hook called when a page is about to be renamed i.e. had its name field change)
|
||||||
|
*
|
||||||
|
* The previous name can be accessed at `$page->namePrevious`.
|
||||||
|
* The new name can be accessed at `$page->name`.
|
||||||
|
*
|
||||||
|
* This hook is only called when a page's name changes. It is not called when
|
||||||
|
* a page is moved unless the name was changed at the same time.
|
||||||
|
*
|
||||||
|
* **Multi-language note:**
|
||||||
|
* Also note this hook may be called if a page's multi-language name changes.
|
||||||
|
* In those cases the language-specific name is stored in "name123" while the
|
||||||
|
* previous value is stored in "-name123" (where 123 is the language ID).
|
||||||
|
*
|
||||||
|
* #pw-hooker
|
||||||
|
*
|
||||||
|
* @param Page $page The $page that was renamed
|
||||||
|
* @since 3.0.235
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function ___renameReady(Page $page) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook called when a page has been renamed (i.e. had its name field change)
|
* Hook called when a page has been renamed (i.e. had its name field change)
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Implements page manipulation methods of the $pages API variable
|
* Implements page manipulation methods of the $pages API variable
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2021 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2024 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -424,6 +424,9 @@ class PagesEditor extends Wire {
|
|||||||
* - `ignoreFamily` (boolean): Bypass check of allowed family/parent settings when saving (default=false)
|
* - `ignoreFamily` (boolean): Bypass check of allowed family/parent settings when saving (default=false)
|
||||||
* - `noHooks` (boolean): Prevent before/after save hooks from being called (default=false)
|
* - `noHooks` (boolean): Prevent before/after save hooks from being called (default=false)
|
||||||
* - `noFields` (boolean): Bypass saving of custom fields (default=false)
|
* - `noFields` (boolean): Bypass saving of custom fields (default=false)
|
||||||
|
* - `caller` (string): Optional name of calling function (i.e. 'pages.trash'), for internal use (default='') 3.0.235+
|
||||||
|
* - `callback` (string|callable): Hook method name from $pages or callable to trigger after save.
|
||||||
|
* It receives a single $page argument. For internal use. (default='') 3.0.235+
|
||||||
* @return bool True on success, false on failure
|
* @return bool True on success, false on failure
|
||||||
* @throws WireException
|
* @throws WireException
|
||||||
*
|
*
|
||||||
@@ -438,6 +441,8 @@ class PagesEditor extends Wire {
|
|||||||
'ignoreFamily' => false,
|
'ignoreFamily' => false,
|
||||||
'noHooks' => false,
|
'noHooks' => false,
|
||||||
'noFields' => false,
|
'noFields' => false,
|
||||||
|
'caller' => '',
|
||||||
|
'callback' => '',
|
||||||
);
|
);
|
||||||
|
|
||||||
if(is_string($options)) $options = Selectors::keyValueStringToArray($options);
|
if(is_string($options)) $options = Selectors::keyValueStringToArray($options);
|
||||||
@@ -445,6 +450,10 @@ class PagesEditor extends Wire {
|
|||||||
$user = $this->wire()->user;
|
$user = $this->wire()->user;
|
||||||
$languages = $this->wire()->languages;
|
$languages = $this->wire()->languages;
|
||||||
$language = null;
|
$language = null;
|
||||||
|
$parentPrevious = $page->parentPrevious;
|
||||||
|
$caller = $options['caller'];
|
||||||
|
$callback = $options['callback'];
|
||||||
|
$useHooks = empty($options['noHooks']);
|
||||||
|
|
||||||
// if language support active, switch to default language so that saved fields and hooks don't need to be aware of language
|
// if language support active, switch to default language so that saved fields and hooks don't need to be aware of language
|
||||||
if($languages && $page->id != $user->id && "$user->language") {
|
if($languages && $page->id != $user->id && "$user->language") {
|
||||||
@@ -465,19 +474,35 @@ class PagesEditor extends Wire {
|
|||||||
$page->removeStatus(Page::statusUnpublished);
|
$page->removeStatus(Page::statusUnpublished);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($page->parentPrevious && !$isNew) {
|
if($parentPrevious && !$isNew) {
|
||||||
if($page->isTrash() && !$page->parentPrevious->isTrash()) {
|
if($useHooks) $this->pages->moveReady($page);
|
||||||
$this->pages->trash($page, false);
|
if($caller !== 'pages.trash' && $caller !== 'pages.restore') {
|
||||||
} else if($page->parentPrevious->isTrash() && !$page->parent->isTrash()) {
|
if($page->isTrash() && !$parentPrevious->isTrash()) {
|
||||||
$this->pages->restore($page, false);
|
if($this->pages->trash($page, false)) $callback = 'trashed';
|
||||||
|
} else if($parentPrevious->isTrash() && !$page->parent->isTrash()) {
|
||||||
|
if($this->pages->restore($page, false)) $callback = 'restored';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($options['adjustName']) $this->pages->names()->checkNameConflicts($page);
|
if($options['adjustName']) $this->pages->names()->checkNameConflicts($page);
|
||||||
if(!$this->savePageQuery($page, $options)) return false;
|
|
||||||
$result = $this->savePageFinish($page, $isNew, $options);
|
if($page->namePrevious && !$isNew && $page->namePrevious != $page->name) {
|
||||||
|
if($useHooks) $this->pages->renameReady($page);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->savePageQuery($page, $options);
|
||||||
|
if($result) $result = $this->savePageFinish($page, $isNew, $options);
|
||||||
if($language) $user->setLanguage($language); // restore language
|
if($language) $user->setLanguage($language); // restore language
|
||||||
|
|
||||||
|
if($result && !empty($callback) && $useHooks) {
|
||||||
|
if(is_string($callback) && ctype_alnum($callback)) {
|
||||||
|
$this->pages->$callback($page); // hook method name in $pages
|
||||||
|
} else if(is_callable($callback)) {
|
||||||
|
$callback($page); // user defined callback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
*
|
*
|
||||||
* Implements page trash/restore/empty methods of the $pages API variable
|
* Implements page trash/restore/empty methods of the $pages API variable
|
||||||
*
|
*
|
||||||
* ProcessWire 3.x, Copyright 2023 by Ryan Cramer
|
* ProcessWire 3.x, Copyright 2024 by Ryan Cramer
|
||||||
* https://processwire.com
|
* https://processwire.com
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
@@ -18,6 +18,14 @@ class PagesTrash extends Wire {
|
|||||||
*/
|
*/
|
||||||
protected $pages;
|
protected $pages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Last action, i.e. "restore:1234"
|
||||||
|
*
|
||||||
|
* @var int
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
protected $lastAction = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct
|
* Construct
|
||||||
*
|
*
|
||||||
@@ -41,17 +49,18 @@ class PagesTrash extends Wire {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function trash(Page $page, $save = true) {
|
public function trash(Page $page, $save = true) {
|
||||||
|
|
||||||
if(!$this->pages->isDeleteable($page) || $page->template->noTrash) {
|
if(!$this->pages->isDeleteable($page) || $page->template->noTrash) {
|
||||||
throw new WireException("This page (id=$page->id) may not be placed in the trash");
|
throw new WireException("This page (id=$page->id) may not be placed in the trash");
|
||||||
}
|
}
|
||||||
|
|
||||||
$trash = $this->pages->get($this->config->trashPageID);
|
$trash = $this->pages->get($this->wire()->config->trashPageID);
|
||||||
|
|
||||||
if(!$trash->id) {
|
if(!$trash->id) {
|
||||||
throw new WireException("Unable to load trash page defined by config::trashPageID");
|
throw new WireException("Unable to load trash page defined by config::trashPageID");
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->pages->trashReady($page);
|
if($this->lastAction != "trash:$page") $this->pages->trashReady($page);
|
||||||
|
|
||||||
$page->addStatus(Page::statusTrash);
|
$page->addStatus(Page::statusTrash);
|
||||||
|
|
||||||
@@ -70,10 +79,10 @@ class PagesTrash extends Wire {
|
|||||||
// make the name unique when in trash, to avoid namespace collision and maintain parent restore info
|
// make the name unique when in trash, to avoid namespace collision and maintain parent restore info
|
||||||
$name = $page->id;
|
$name = $page->id;
|
||||||
if($parentPrevious && $parentPrevious->id) {
|
if($parentPrevious && $parentPrevious->id) {
|
||||||
$name .= "." . $parentPrevious->id;
|
$sort = $page->get('sortPrevious|sort');
|
||||||
$name .= "." . $page->sort;
|
$name .= ".$parentPrevious->id.$sort";
|
||||||
}
|
}
|
||||||
$page->name = ($name . "_" . $page->name);
|
$page->name = ($name . '_' . $page->name);
|
||||||
|
|
||||||
// do the same for other languages, if present
|
// do the same for other languages, if present
|
||||||
$languages = $this->wire()->languages;
|
$languages = $this->wire()->languages;
|
||||||
@@ -87,9 +96,13 @@ class PagesTrash extends Wire {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($save) $this->pages->save($page);
|
$this->lastAction = "trash:$page";
|
||||||
|
|
||||||
|
if($save) {
|
||||||
|
$this->pages->save($page, array('caller' => 'pages.trash', 'callback' => 'trashed'));
|
||||||
|
}
|
||||||
|
|
||||||
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, false);
|
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, false);
|
||||||
if($save) $this->pages->trashed($page);
|
|
||||||
$this->pages->debugLog('trash', $page, true);
|
$this->pages->debugLog('trash', $page, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -107,24 +120,30 @@ class PagesTrash extends Wire {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public function restore(Page $page, $save = true) {
|
public function restore(Page $page, $save = true) {
|
||||||
|
|
||||||
$info = $this->getRestoreInfo($page, true);
|
$info = $this->getRestoreInfo($page, true);
|
||||||
|
|
||||||
if($info['restorable']) {
|
if($info['restorable']) {
|
||||||
// we detected original parent
|
// we detected original parent
|
||||||
if($save) $page->save();
|
if($this->lastAction !== "restore:$page") $this->pages->restoreReady($page);
|
||||||
|
|
||||||
} else if(!$page->parent->isTrash()) {
|
} else if(!$page->parent->isTrash()) {
|
||||||
// page has had new parent already set
|
// page has had new parent already set
|
||||||
|
if($this->lastAction !== "restore:$page") $this->pages->restoreReady($page);
|
||||||
$page->removeStatus(Page::statusTrash);
|
$page->removeStatus(Page::statusTrash);
|
||||||
if($save) $page->save();
|
|
||||||
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, true);
|
$this->pages->editor()->savePageStatus($page->id, Page::statusTrash, true, true);
|
||||||
if($save) $this->pages->restored($page);
|
|
||||||
$this->pages->debugLog('restore', $page, true);
|
$this->pages->debugLog('restore', $page, true);
|
||||||
} else {
|
} else {
|
||||||
// page is in trash and we cannot detect new parent
|
// page is in trash and we cannot detect new parent
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->lastAction = "restore:$page";
|
||||||
|
|
||||||
|
if($save) {
|
||||||
|
$this->pages->save($page, array('caller' => 'pages.restore', 'callback' => 'restored'));
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +240,7 @@ class PagesTrash extends Wire {
|
|||||||
|
|
||||||
if($populateToPage) {
|
if($populateToPage) {
|
||||||
$page->name = $name;
|
$page->name = $name;
|
||||||
|
$page->removeStatus(Page::statusTrash);
|
||||||
if($newParent) {
|
if($newParent) {
|
||||||
$page->sort = $sort;
|
$page->sort = $sort;
|
||||||
$page->parent = $newParent;
|
$page->parent = $newParent;
|
||||||
|
@@ -2157,20 +2157,17 @@ class ProcessPageEdit extends Process implements WirePageEditor, ConfigurableMod
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$restored = false;
|
|
||||||
if($input->post('restore_page') && $page->isTrash() && $page->restorable()) {
|
if($input->post('restore_page') && $page->isTrash() && $page->restorable()) {
|
||||||
if($formErrors) {
|
if($formErrors) {
|
||||||
$this->warning($this->_('Page cannot be restored while errors are present'));
|
$this->warning($this->_('Page cannot be restored while errors are present'));
|
||||||
} else if($pages->restore($page, false)) {
|
} else if($pages->restore($page, false)) {
|
||||||
$message = sprintf($this->_('Restored Page: %s'), '{path}') . $numChanges;
|
$message = sprintf($this->_('Restored Page: %s'), '{path}') . $numChanges;
|
||||||
$restored = true;
|
|
||||||
} else {
|
} else {
|
||||||
$this->warning($this->_('Error restoring page'));
|
$this->warning($this->_('Error restoring page'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$pages->save($page, $options);
|
$pages->save($page, $options);
|
||||||
if($restored) $pages->restored($page);
|
|
||||||
$message = str_replace('{path}', $page->path, $message);
|
$message = str_replace('{path}', $page->path, $message);
|
||||||
$this->message($message);
|
$this->message($message);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user