MarcC Posted February 27, 2013 Share Posted February 27, 2013 I would like to make a cron job that moves pages from one parent to another parent, if a given page field has ID == n. It's kind of a cleanup function--somebody can mark a "project" page as "complete" and it is auto-archived. I'm new to this sort of scripting, so I looked at the API to see if I could figure out how to move a page. Would I clone the page with the different parent, then delete the page? That seems like one option that stood out as I browsed the cheat sheet. Thanks for any tips. Link to comment Share on other sites More sharing options...
apeisa Posted February 27, 2013 Share Posted February 27, 2013 You change page parent and save it. Link to comment Share on other sites More sharing options...
Soma Posted February 27, 2013 Share Posted February 27, 2013 I would add a checkbox 'archived'. No cronjob no url change, much simpler. Link to comment Share on other sites More sharing options...
Pete Posted February 27, 2013 Share Posted February 27, 2013 Basically do what these chaps are saying - add a checkbox field like Soma says, then as Apeisa says have a module look for the checkbox before the page is saved and if it's checked then change the parent and continue saving as normal. If I had more time this morning there actually is very little code to this but I've got to go to work - I'm 100% certain someone will be here with some code soon though The actual code that does the page "moving" in amongst the other module code is actually as simple as this: // ...Module code above to intercept page save, check for correct template and a ticked checkbox $page->parent = $pages->get('/path/to/archive/page/'); // ...Rest of module code. No need to $page->save(); since we're going to hook before page save so the save routine will continue as normal after this Link to comment Share on other sites More sharing options...
MarcC Posted February 27, 2013 Author Share Posted February 27, 2013 Thanks guys. Do I really have to use the checkbox? (And write a module?) My client is already using a drop-down menu for "In Progress," "Waiting for Customer," "Completed," etc. So a checkbox would actually make the design more complex for the client. They just want to set the item to "Completed" and see it archived sometime in the next day. They are using a front end that already hides an item if it is marked as Completed. $page->parent = $pages->get('/path/to/archive/page/'); Ah, of course. Seems so simple compared to what I was thinking. Link to comment Share on other sites More sharing options...
Soma Posted February 27, 2013 Share Posted February 27, 2013 I just meant some option to mark it like you have. Why move page to an archive at all as it will change url which is not good maybe. Just saying 'archives' are always a little weird. Link to comment Share on other sites More sharing options...
MarcC Posted February 27, 2013 Author Share Posted February 27, 2013 They want to archive it so it doesn't appear in the same list as all the other projects in the back end. First I said, "just unpublish it" but now they have items that are crossed out next to all the normal items. Since some staff are more comfortable using the back end, this is kind of a visual clutter problem. They even offered to move items back out of the archive by themselves if they ever need to reuse. What do I need to know to make this work in a cron job? Do I need to do $page->save after I change the parent? Link to comment Share on other sites More sharing options...
apeisa Posted February 27, 2013 Share Posted February 27, 2013 You could do this via LazyCron, but since you probably want to archive it right away you save the page, I think most logical place would be just hook into page save process. This should get you pretty close (written in browser): class MovePage extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Move page after saving it with certain value', 'summary' => 'Modules are easy and fun.', 'version' => 1 'autoload' => true, ); } public function init() { $this->pages->addHookAfter('save', $this, 'hookSave'); } public function hookSave(HookEvent $event) { $page = $event->object; // No need to to anything, if field isn't archived if ($page->my_page_field->id != 1234) return; // If it is archived, then we remove the archived (this avoids pagesave loop $page->my_page_field->remove(1234); $page->parent = $this->pages->get("/archived/"); $page->save(); } } Link to comment Share on other sites More sharing options...
MarcC Posted February 27, 2013 Author Share Posted February 27, 2013 Wow, thanks a lot apeisa. I guess I'll try that. I don't understand why this is necessary: $page = $event->object; ...but the rest makes a lot of sense. Link to comment Share on other sites More sharing options...
apeisa Posted February 27, 2013 Share Posted February 27, 2013 It gives you the page that is being saved. wire('page') would give you the admin page (page edit -page). That event stuff feels little mysterious when you're starting with autoload modules, and it you will grok it soon. Lots of information here on forums about it. 1 Link to comment Share on other sites More sharing options...
MarcC Posted February 27, 2013 Author Share Posted February 27, 2013 Thanks man. Gonna try the module today. BTW what happens if the page field is required, and we remove the 1234 selected ID? 1 Link to comment Share on other sites More sharing options...
apeisa Posted February 27, 2013 Share Posted February 27, 2013 I don't know, but would guess that it would just throw error when you save archived page in admin. You don't have to remove it though, you might check the parent also (if page is already in archive, don't move and save). 1 Link to comment Share on other sites More sharing options...
MarcC Posted March 12, 2013 Author Share Posted March 12, 2013 Hey apeisa, I installed the module but it's not archiving. Any ideas? Error logs are clean and I've adjusted the code to fit the corresponding field. <?php class AutoArchive extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Move page to archive after saving it with certain value', 'summary' => 'It can\'t be this simple.', 'version' => 1, 'autoload' => true ); } public function init() { $this->pages->addHookAfter('save', $this, 'hookSave'); } public function hookSave(HookEvent $event) { $page = $event->object; // No need to to anything if project is not marked as complete. // I checked my sample project and its ID returns 1157. // But it doesn't get archived (placed under the projects-archive parent) when saved. if ($page->project_status->id != 1157) return; // If it is marked as complete / 1157, then we remove the marked as complete part (this avoids pagesave loop) $page->project_status->remove(1157); $page->parent = $this->pages->get("/projects-archive/"); $page->save(); } } ?> Not sure how to use breakpoints or something similar for modules, or I would have tried that. Thanks. Link to comment Share on other sites More sharing options...
apeisa Posted March 13, 2013 Share Posted March 13, 2013 1157 should be the id of the status page, not the project page. Assuming that project_status is page field. Link to comment Share on other sites More sharing options...
MarcC Posted March 13, 2013 Author Share Posted March 13, 2013 Right, page 1157 is titled "Completed"--does that make sense? project_status is the page field. I hope I understood what you're saying. Link to comment Share on other sites More sharing options...
Wanze Posted March 13, 2013 Share Posted March 13, 2013 Is project_status an Array if there can be only one page selected? Can you try this: $statusPage = wire('pages')->get(1157); $page->project_status->remove($statusPage); Also for testing if your hook really gets executed: if ($page->project_status->id == 1157) die("Hook gets executed..."); This should give you an ugly white page if you save a project marked as 'complete'. Link to comment Share on other sites More sharing options...
MarcC Posted March 13, 2013 Author Share Posted March 13, 2013 Thanks Wanze--that results in: Error Call to a member function remove() on a non-object And no white page. So it does seem like it is trying to work. Field type is single page or boolean false when none selected. Code so far: <?php class AutoArchive extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Move page to archive after saving it with certain value', 'summary' => 'It can\'t be this simple.', 'version' => 1, 'autoload' => true ); } public function init() { $this->pages->addHookAfter('save', $this, 'hookSave'); } public function hookSave(HookEvent $event) { $page = $event->object; // No need to to anything if project is not marked as complete. // I checked my sample project and its ID returns 1157. if ($page->project_status->id == 1157) die("Hook gets executed..."); // If it is marked as complete / 1157, then we remove the marked as complete part (this avoids pagesave loop) $statusPage = wire('pages')->get(1157); $page->project_status->remove($statusPage); $page->parent = $this->pages->get("/projects-archive/"); $page->save(); } } ?> Link to comment Share on other sites More sharing options...
MarcC Posted March 13, 2013 Author Share Posted March 13, 2013 Actually it looks like the test is failing: if ($page->project_status->id == 1157) ...is returning false. But that is the ID that the field contains. Weird. And when I echo that variable elsewhere (just as a test), it comes up as 1157. Help! Link to comment Share on other sites More sharing options...
MarcC Posted March 13, 2013 Author Share Posted March 13, 2013 Well, solved it by trying something I found in Ryan's docs. I noticed that $event->object didn't seem to be storing any ID or name data. So instead of using $page = $event->object, I tried $page = $event->arguments[0] and it worked. ProcessWire's save message still says it's saved under the old parent, which is kind of weird since it's just been moved under a different parent, so if anybody knows a workaround, let me know. (Edit: Added a $this->message, which helps but they still see 2 separate save messages) Final changes: (Would love any feedback you have) $page = $event->arguments[0]; // No need to to anything if project is not marked as complete. // I checked my sample project and its ID returns 1157. if ($page->project_status->id != 1157 && $page->parent->name == 'projects-archive') { //not completed and it's in the archive. Get it back to the projects page. $page->project_status->remove(); $page->parent = $this->pages->get(1131); //projects page $page->save(); } elseif ($page->project_status->id != 1157) { //just a normal page. return; } // If it is marked as complete / 1157, then we remove the marked as complete part (this avoids pagesave loop) if ($page->project_status->id == 1157 && $page->parent->name != 'projects-archive') { //completed and not in archive. move to archive. $page->project_status->remove(1157); $page->parent = $this->pages->get(1322); //projects archive page $page->save(); } Link to comment Share on other sites More sharing options...
ryan Posted March 14, 2013 Share Posted March 14, 2013 At the top, I'm thinking you want to add something to avoid acting on pages that aren't of your defined type. Something like this: if(!$page->template != 'project') return; Because this is a single-page reference field (from what I can tell above), I don't think this line would work: $page->project_status->remove(1157); That's calling the remove() method of $page, which just unset a value. Since the page likely doesn't have a variable named "1157", the line above essentially does nothing. What you would want to do instead is set project_status to be a blank value, or another page. $page->project_status = null; // this $page->project_status = $pages->get('some other page'); // or this 1 Link to comment Share on other sites More sharing options...
MarcC Posted March 14, 2013 Author Share Posted March 14, 2013 Ha ha, awesome Ryan. I wondered about both of those things. Actually I think I should probably just remove the remove() part anyway. I can't see why it's necessary now to null out the page field, when I have added new logic to test whether the page is already in the archive or not. //This has nothing to do with you, non-project pages. if(!$page->template != 'project') return; // No need to to anything if project is not marked as complete. // I checked my sample project and its ID returns 1157. if ($page->project_status->id != 1157 && $page->parent->name == 'projects-archive') { //marked as incomplete and it's in the archive. Get it back to the projects page. //$page->project_status = null; // remove the status $page->parent = $this->pages->get(1131); //projects page $page->save(); $this->message("Moved out of archive. New page location: {$page->url}"); } // If it is marked as complete / 1157, then we remove the marked as complete part (this avoids pagesave loop) if ($page->project_status->id == 1157 && $page->parent->name != 'projects-archive') { //completed and not in archive. move to archive. //$page->project_status = null; // remove the status $page->parent = $this->pages->get(1322); //projects archive page $page->save(); $this->message("Archived. New page location: {$page->url}"); } Is there something I can (or should) do about the "Saved" message that shows an incorrect parent after the module just changed the parent? I have two messages now, the Saved Page message and my own message saying there's a new parent. Thanks! Link to comment Share on other sites More sharing options...
MarcC Posted March 14, 2013 Author Share Posted March 14, 2013 This is the current module code after more changes: <?php class AutoArchive extends WireData implements Module { public static function getModuleInfo() { return array( 'title' => 'Project Auto-Archiver', 'summary' => 'If the page is saved and the project_status field is set to completed, archive it. If the project is in the archive and has its status changed away from completed, unarchive it.', 'version' => 1, 'autoload' => true ); } public function init() { $this->pages->addHookAfter('save', $this, 'hookSave'); } public function hookSave(HookEvent $event) { $page = $event->arguments[0]; //This has nothing to do with you, non-project pages. if($page->template->name != 'projects') return; // If the project is not marked as complete and it's already archived, move it back to the normal projects area. if ($page->project_status->id != 1157 && $page->parent->name == 'projects-archive') { $page->parent = $this->pages->get(1131); //projects page $page->save(); $this->message("Moved out of archive. New page location: {$page->url}"); } // If it is marked as complete, and not in the archive yet, archive it. if ($page->project_status->id == 1157 && $page->parent->name != 'projects-archive') { $page->parent = $this->pages->get(1322); //move under projects archive page $page->save(); $this->message("Archived. New page location: {$page->url}"); } } } ?> Link to comment Share on other sites More sharing options...
ryan Posted March 16, 2013 Share Posted March 16, 2013 Marc, you may want to change your hook from "save" to "saveReady" or "saved". If you use "saveReady", then you won't have to perform your own $page->save(); since you'd be hooking in right before $pages does it's own save. This is the same as a before "save" hook, except that it's been confirmed that a save will definitely occur, so a little safer. However, you'll probably want to add something at the top of your hookSave() function like: if(!$page->id) return; so that it doesn't get involved with pages that are about to be saved for the first time. If you use the "saved" hook (available in the dev branch of PW), this is essentially the same as what you are already doing, just a little safer, since it wouldn't get called if an error occurred in save(). Though for most practical purposes it's equivalent. I would probably look into using "saveReady" just because that could reduce what's currently happening in 2 saves to just 1. 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