Robin S Posted December 21, 2020 Share Posted December 21, 2020 If you have the core ProcessPageClone module installed you can copy a page via Page List: But sometimes I don't want to hunt around through Page List to find the page I want to copy, and instead I have found the page via the admin search and have it open in Page Edit. So I used this hook in /site/ready.php to add a "Clone this page" button to the bottom of the Settings tab. // Add a clone button to the Settings tab of Page Edit if this page is allowed to be cloned $wire->addHookAfter('ProcessPageEdit::buildFormSettings', function(HookEvent $event) { $wrapper = $event->return; $modules = $event->wire()->modules; $page = $event->process->getPage(); /** @var ProcessPageClone $ppc */ $ppc = $modules->get('ProcessPageClone'); if($page && $ppc->hasPermission($page)) { /** @var InputfieldButton $f */ $f = $modules->get('InputfieldButton'); $f->value = 'Clone this page'; $f->href = $event->wire()->config->urls->admin . 'page/clone/?id=' . $page->id; $wrapper->add($f); } }); Maybe somebody else has this need and finds this useful. 12 1 Link to comment Share on other sites More sharing options...
Robin S Posted December 21, 2020 Author Share Posted December 21, 2020 Also, I want to open the page in Page Edit after it is cloned. Here is a hook for that: // Edit the cloned page after it is created $wire->addHookBefore('ProcessPageClone::execute', function(HookEvent $event) { $event->wire()->addHookBefore('Session::redirect', function(HookEvent $event) { $url = $event->arguments(0); $id = (int) filter_var($url, FILTER_SANITIZE_NUMBER_INT); $redirect_url = $event->wire()->config->urls->admin . 'page/edit/?id=' . $id; $event->arguments(0, $redirect_url); }); }); 7 Link to comment Share on other sites More sharing options...
bernhard Posted April 29, 2021 Share Posted April 29, 2021 This is what I am using: $this->wire->addHookAfter('ProcessPageEdit::getSubmitActions', function($event) { $page = $event->process->getPage(); if($page->template != "foo") return; $actions = $event->return; unset($actions['next']); $actions['clone'] = [ 'value' => 'clone', 'icon' => 'clone', 'label' => 'Save + create copy', ]; $event->return = $actions; }); $this->wire->addHookAfter('ProcessPageEdit::processSubmitAction', function($event) { $action = $event->arguments(0); // action name, i.e. 'hello' $page = $event->process->getPage(); // Page that was edited/saved if($page->template != 'foo') return; if($action === 'clone') { $copy = $this->wire->pages->clone($page); $copy->title .= ' (copy ' . uniqid() . ')'; $copy->save(); $this->wire->session->redirect($copy->editUrl); } }); 7 1 Link to comment Share on other sites More sharing options...
Cybermano Posted June 15, 2023 Share Posted June 15, 2023 Thanks a lot! Do you know if someone has ever thought about to insert those kind of features into the core? *edit* If sombody may need the same behaviour of Pagelist cloning (with unpublished status), I made a little hack to the @bernhard code: ... if($action === 'clone') { // get original page status $status = $page->status(); // set unpublished $page->setStatus(2049); $page->save(); // clone $copy = $this->wire->pages->clone($page); // get original name and store in an array $nameArr = explode('-',$page->name); // save cloned name (microtime style + 1) $copy->setAndSave('name', $nameArr[0] . '-' . ($nameArr[1] + 1)); // add ' (copy ... + incremented timestamp)' to title $copy->setAndSave('title', $page->title . ' (copy ' . ($nameArr[1] + 1) . ')'); // set unpublished status $copy->setStatus(2049); $copy->save(); // restore original status to original page $page->setStatus($status); $page->save(); $this->wire->session->redirect($copy->editUrl); } ... N.B. I used this trick (unpublishing the original page) due a personal hook that checks pages with same title (and warn if found one or more dupicates). Without the unpublished status for the original page, it seems that at first saving the title is the same also for cloned item... The same results is obtained with the @Robin S hook, but flagging "Keep page unpublished" in the cloning mask. Link to comment Share on other sites More sharing options...
bernhard Posted June 15, 2023 Share Posted June 15, 2023 Hey @Cybermano what exactly are you trying to do? If you only want to make the clone unpublished you can add this line before $copy->save(): $copy->addStatus(Page::statusUnpublished); I've some questions and suggestions for improvements on your code (assuming that the only difference to my old post is that the cloned page should be unpublished): Why do you set $page to unpublished during clone and then restore the previous state? That's 2x unnecessary page saves. You are using setAndSave for name + title, but you save() the $copy later anyhow. That means you produce 3 save operations instead of one. Just use $copy->set('name', ...) and same for title and changes will be stored on save() You are using setStatus(). It should be fine but I think it's better to use addStatus() in general unless you really want to force a single status. For example pages can have a "corrupted" status and that will be removed on setStatus() but will be kept when using addStatus() - please anybody correct me if I'm wrong ? 1 1 Link to comment Share on other sites More sharing options...
Cybermano Posted June 16, 2023 Share Posted June 16, 2023 Hi @bernhard, thank you for your suggestions and for your time too. You are right, there are unecessary $page->save(), but: let's say that I have a hook that check if there are pages with the same title (on page saved). Spoiler // CHECK TITLE DUPLICATES - ONLY FOR CURRENT YEAR if (wire('user')->isLoggedin()){ $wire->addHookBefore('Pages::saved(template=foo)', function ($event){ $page = $event->arguments[0]; $alreadyPresent = []; $message = ''; // get items to check $items = wire('pages')->find('template=foo, parent.title='.date('Y')); foreach ($items as $item){ // store in array if not already present if ( !array_key_exists(date('Y').'_'.$item->title, $alreadyPresent) ){ $alreadyPresent[date('Y').'_'.$item->title] = $item->title; } else { // for SuperUsers create links for id (to open page for editing) if($item->editable()) { $message .= '<b>'.$item->title.'</b> already exists for this year ('.date('Y').')'; $duplicates = wire('pages')->find('template=foo, title='.$item->title.', parent.title='.date('Y')); $links = ''; $counter = 1; foreach ($duplicates as $d){ if ($d->editable()){ $links .= '<a href="'.$d->editUrl.'" target="_blank">'.$d->id.'</a>'; } if ($counter < count($duplicates)){ $links .= ' | '; } $counter++; } $message .= ' [ page ids: '.$links.' ] '; } else { // for safe-editors (without editing permission) $message .= '<b>'.$item->title.'</b> already exists for this year ('.date('Y').') [ page ids: '.wire('pages')->find('template=foo, title='.$item->title.', parent.title='.date('Y')).' ] '; } } } if ($message != '') { $page->warning($message, Notice::allowMarkup); } }); } We have a site where guests can subscribe themselve on each year as art performers for yearly event, but only once. The subscription form is a FormBuilder form with manual send to page (to double check some contents filled in by guests, such as descriptions, social profile links or loaded images). We have hundreds of requests every year, and we can't remember the name of all; so I made the previous hook that works fine. Well, this hook warns every time on cloning despite the title changing. The only way that I have found to not be warned is to set the original page as unpublished and, after cloning, to restore the original status. Otherwise with RobinS's hook, but activating "Make the new page unpublished" on cloning mask (but this is a one more passage respect your hook). Maybe I missing something or I making any mistake... Link to comment Share on other sites More sharing options...
Cybermano Posted June 16, 2023 Share Posted June 16, 2023 I'm trying to upload two screencasts to better explain... You can see those screencasts here:https://drive.google.com/drive/folders/15JgYJDwTKowOX2zUw14yy1S9RcF4YAwg?usp=sharing Link to comment Share on other sites More sharing options...
bernhard Posted June 16, 2023 Share Posted June 16, 2023 @Cybermano It should be quite easy to ignore cloned pages in your hook: $wire->addHookAfter('Pages::saved', function ($event) { $page = $event->arguments(0); if ($page->_cloning) return; bd($page); }); Use tracy debugger to inspect the saved page objects and you can see if there are any helpful properties (like _added for example). 1 Link to comment Share on other sites More sharing options...
Cybermano Posted June 16, 2023 Share Posted June 16, 2023 10 hours ago, bernhard said: @Cybermano It should be quite easy to ignore cloned pages in your hook: $wire->addHookAfter('Pages::saved', function ($event) { $page = $event->arguments(0); if ($page->_cloning) return; bd($page); }); Use tracy debugger to inspect the saved page objects and you can see if there are any helpful properties (like _added for example). I will surely do. Many thanks. Thanks again @bernhard: perfect as always, of course. I will indagate the _added prop as suggested (maybe there is the reason that fires my hook without the _cloning return). Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now