*/ ?>

Monday 2012-10-22 - Learn something new every day

Today was a long day of coding today at work.  I learned a couple new things, nothing I major but some that are good to know.  I also got a lot of stuff done.  Things are looking pretty good for our check-in with the client tomorrow afternoon.

The big application that I've been working on for the last couple of weeks that spurred on my advance in knowledge on using lists and models is hopefully pretty close to done as far as functionality goes.  There are probably a couple more things to fine tune, but for the most part everything is working.  Today I got the last couple of things done that I needed to do.

First off was changing how the application creates pages.  You can create two types of main objects using the dashboard interface, a basic and an advanced version.  Each one was creating a different type of page object, then stores the cID in the custom table to join across the CollectionSearchIndexAttributes table so that you can search on attributes of the page.  The form to create the objects has a ton of attributes that are saved against the page when it's created, too. 

The thing was, when it made a new page of either type, it was just creating the page using $parentPage->add($data) - this wasn't going to be good enough for this application.  For the simple pages, just creating a new page was enough, but for the advanced, it's actually supposed to be a page tree with a parent page and five sub pages of different types.  So instead of just using the add function, I was using duplicate.  But duplicate only goes one deep, so I had to do some digging to find out another function.  Turns out that there is a duplicateAll() function that you can use.  That copies recursively. 

  1. // adding a new page under a parent page
  2.  
  3. $parent = Page::getByPath("/page-path");
  4. $ct = CollectionType::getByHandle("your_page_type");
  5. $data = array(
  6. 'cName' => $name,
  7. 'cDescription' => $description,
  8. 'uID' => $uID,
  9. 'cHandle' => $cHandle,
  10. 'cDatePublic' => date(strtotime("now"), 'Y-m-d H:i:s')
  11. );
  12. $newPage = $parent->add($ct, $data);
  13.  
  14. // duplicating a page (one level)
  15.  
  16. $parent = Page::getByPath("/page-path");
  17. $newPageMaster = Page::getByPath('/path-to-page-to-duplicate');
  18. $cnewPage = $newPageMaster->duplicate($parent);
  19.  
  20. // duplicating a page (all levels)
  21.  
  22. $parent = Page::getByPath("/page-path");
  23. $newPageMaster = Page::getByPath('/path-to-page-to-duplicate');
  24. $cnewPage = $newPageMaster->duplicateAll($parent);

As you can see, the syntax is a bit different between the two different ways of adding a page.  I didn't know about the duplicate all method before, or I'd forgotten it.  After duplicating the pages, I update the permissions to be assigned to the new editor.  Pretty simple, but it took me awhile to figure out how to get it all working properly.

Then I had to expand it a bit - you are able to change the class of object after it's created, so I had to set it up to delete the sub pages if it's going from advanced to basic and then re-copy the sub-pages of the advanced page master if you go the other way.  That was pretty easy to set up.

What it was that took awhile was trying to figure out how to update the page paths.  Not sure why I drew a blank on this one.  I first of did some digging into how to pass in the ppURL array to update the page paths, but that was just updating the secondary paths.  I managed to remove the canonical URL a couple times that way.  Oops.  Then I remembered that cHandle is an argument you can pass to add or update, so I tried that.  It seemed to be working, stepping through the debugger was showing the correct value being passed to a save function for the collectionVersions table.  The table was getting updated.  So figuring out what was going on was really confusing me.

Some googling and searching on concrete5.org finally led me to the answer.  It turns out that when you add a page, it calls a page function rescanCollectionPath() that updates the canonical page path.  Once I added that to my page duplication function, the page paths actually updated.

The other thing that I had to get working was integration with Pro Events.  Just adding the package to the site is pretty easy, but this system has a bunch of different administrators that will all be managing their own pages.  By default, PE will simply show anyone with access to the dashboard edit pages every section that is marked as an 'event section' in the entire site.  And actually, I think with the way it's written, permissions aren't checked for adding sub-pages, so they could probably see and edit every event for every calendar throughout the site.  Not really what you want in this case.

The lists that I've created for the main objects that people are allowed to edit came in really handy here again.  If the user is just a regular object admin and not a object super admin, then I pull up the list, filter it by userID, then loop over it.  If it's an 'advanced' type, then it will have a calendar page underneath it, I pull up that page object by it's path and get the cID from it.  Where the code in ProEvents pulls up the sections array for the dropdown to filter events in the list event page, or in the Event List block where you pick which calendar you are displaying, or where it gets the list of events not filtered by parent page I updated the code to use the array of IDs I got from the object list to filter by parent ID. So if you aren't a super admin, when you use ProEvents it only shows you the event sections that you are allowed to post to.

It's a kind of undocumented feature of the PageList class - you can pass in an array of cIDs instead of just one parent ID.  None of the page list blocks in the marketplace do it, but it's there in the core class.  I actually think I should make an add on that allows you to add as many parent pages as you want to a page list - that would probably be a pretty popular add on, especially if it had a couple good templates. 

The other problem with it was that the list also showed all the events with the same name - the page paths and page titles were the same for all of the event sections, since they are duplicated.  So I modified the display to show the parent page name then the event page.  So it looked like "Parent Page :: Events Calendar" in the dropdowns instead of just "Events Calendar" four times.  That was pretty easy to set up. 

There was one other little thing that needed to be adjusted for the events calendar was updating the Event List blocks when you copy the page.  This is something that I've run into on a lot of different sites - when you create a new page and use page lists to do something like display pages underneath that page, or to use the value for $cParentID to filter a select attribute by usage to get a bunch of tag links.  So when you add the new page, the page list or event list on the page isn't updated with the new parentCID.  A lot of times what I do is override the controller for the Page List or other list, and add a new function to it.  That looks a bit like this:

  1. public function updateParentID($cID) {
  2. $db = Loader::db();
  3. $q = "update bPageList set cParentID = ?, cThis = 0 where bID = ?";
  4. $b = $this->getBlockObject();
  5. $bID = $b->getBlockID();
  6. $v = array($cID, $bID);
  7. $res = $db->query($q, $v);
  8. }

So in your on_page_add and on_page_update functions, you get the cID from the passed in page, then loop over the blocks in the page and update them all with the new ID.  This is really good if you have a lot of blogs or something on a site, and want to display an archive or tag cloud that's specific to that parent page and not everywhere in the site.

  1. <?php
  2.  
  3. defined('C5_EXECUTE') or die("Access Denied.");
  4.  
  5. class YourPagePostPageTypeController extends Controller {
  6.  
  7. public function on_page_add($c) {
  8.  
  9. $parentCID = $c->getCollectionParentID();
  10.  
  11. $aBlocks = $c->getBlocks("Sidebar");
  12. foreach ($aBlocks as $block) {
  13. if ($block->getBlockTypeHandle() == "page_list"){
  14. $newBlock = $block->duplicate($c);
  15. $cnt = Loader::controller($newBlock);
  16. $cnt->updateParentID($parentCID);
  17. $block->delete();
  18. }
  19. }
  20.  
  21. $aBlocks = $c->getBlocks("Comments");
  22. foreach ($aBlocks as $block) {
  23. if ($block->getBlockTypeHandle() == "advanced_comments"){
  24. $newBlock = $block->duplicate($c);
  25. $block->delete();
  26. }
  27. }
  28. }
  29.  
  30. public function on_page_update($c) {
  31.  
  32. $parentCID = $c->getCollectionParentID();
  33.  
  34. $aBlocks = $c->getBlocks("Sidebar");
  35. foreach ($aBlocks as $block) {
  36. if ($block->getBlockTypeHandle() == "user_blog_list"){
  37. $newBlock = $block->duplicate($c);
  38. $cnt = Loader::controller($newBlock);
  39. $cnt->updateParentID($parentCID);
  40. $block->delete();
  41. }
  42. }
  43. }
  44.  
  45. public function on_page_move($c, $oldParent, $newParent){
  46.  
  47. $parentCID = $newParent->getCollectionID();
  48.  
  49. $aBlocks = $c->getBlocks("Sidebar");
  50. foreach ($aBlocks as $block) {
  51. if ($block->getBlockTypeHandle() == "user_blog_list"){
  52. $newBlock = $block->duplicate($c);
  53. $cnt = Loader::controller($newBlock);
  54. $cnt->updateParentID($parentCID);
  55. $block->delete();
  56. }
  57. }
  58. }
  59. }
  60. ?>

In this example, I'm also copying and then deleting a comment block on page add - the reason for this is because comment blocks in older versions of concrete5 would have problems when added from page type defaults.  They store the collection ID as well, but that's not updated when you make a new page.  So when you add comments, the comments are tagged with bID and cID in the comments secondary table.  So when you edit the block, it changes the bID and then none of the comments are displayed.  This code prevents that.

To run the code in the page events, you'd need to add something like this to /config/site_events.php or your package controller:

  1. // package controller
  2.  
  3. public function on_start(){
  4. Events::extendPageType('your_page_type', 'on_page_add');
  5. Events::extendPageType('your_page_type', 'on_page_update');
  6. Events::extendPageType('your_page_type', 'on_page_move');
  7. }
  8.  
  9. // site_events.php
  10.  
  11. Events::extendPageType(''your_page_type', 'on_page_add');
  12. Events::extendPageType('your_page_type', 'on_page_update');
  13. Events::extendPageType(''your_page_type', 'on_page_move');

That was about it for today, really.  A lot of little things to file off of the application, but it's getting closer.  I think the next thing is going to be integrating everything with whatever theme they supply, that should take a bit of work, too, but hopefully not too much.

Oh, I guess the other thing I did was changing how my map list filtered pages.  I was using google maps to geocode the location for the objects / pages, then used a mySQL table to look for the center of the requested zip code and compare that to the geocoded location for the object.  It wasn't too accurate, so what I ended up doing was changing it to look for the requested zip code center by geocoding it with google instead.  That was a lot more accurate.

Not a lot else happened, I didn't manage to get anything done on any outside projects after work which kind of sucked.  Maybe wed.  Tomorrow I'm working out in Golden Valley instead of working from home so I'll be in the office when we check in with our client on the large project.  I'm not sure if we're going to actually demo anything at that point or not, but it's a lot easier to be in the office instead of at home when we have the conference calls.

blog comments powered by Disqus