I figured out the last piece of the puzzle for user blogs and calendar integration

Keep reading if you like php code.  I go into saving a new page and creating aliases for it against a page list of other pages of a particular type.  It's geektastic.

screenshot8.pngThe basic premise of user blogs is pretty simple.  You have a single page called "Create User Blog Post" that has a form on it. On that form you have a couple of rich text areas and a sidebar with some buttons in it.  Those buttons are made up from attributes of the page we're about to create, as are the text areas.  In the sidebar I'm allowing people to include the page in items calendar.  On the surface, this seems simple enough.  However, when you look at it closer it's a lot harder than it seems.

The thing is that items calendar and user blogs use a totally different way of interacting with the page lists.  For user blogs, you have a custom page list block, and in that block there's an array of check boxes - which categories do you want to display in the list.  You display pages from everywhere, the page will actually live under a url of www.site.com/user_blogs/<user_blog_name_format>/blog-post-url.  With Items Calendar you can choose to display from all pages of a particular type site wide, but not filter by category like you can with user blogs.  I guess I could have tried to build this functionality in, but the code involved in Items Calendar is pretty involved, so I don't want to mess with it too much more than I already am. 

So what can I do?  Well, Items Calendar offers another way of listing pages which is to display all pages underneath a particular page.  This sounds more like what I need.  When I create my user blogs layout, the people have three choices when they publish a new page.  They can publish it on their own personal blog only, no broader sharing at all.  They can't make it private to themselves, though, all posts are public.  Or they can choose to share it with all, meaning it will show up on any blog list that chooses 'All Categories' for filtering.  Or they can choose "Selected <category>" where category is the handle of a collection attribute set in the dashboard.  If they choose to share with selected category, then it will show up on all blog list blocks where that category is selected for sharing.

This makes things come together a little bit for me - there is a 1:1 correlation between sharing and the calendar.  In this case, it's a Location.  There are three Locations on the site that I'm building, and each one has it's own calendar and blog on a detail page.  The contents of the blog are output virtually by category filtering, but we don't have that option for items calendar.  We need to add a copy of the page underneath each one of these Location detail pages.  So when we add or edit the post, we need to first remove all existing aliases and then create an alias underneath every page that has the same collection attribute value set.  So if the location is "Northeast" then the Location page that has "Northeast" set will receive an alias.  I haven't tried it with multiple selections, I only needed one layer of depth and didn't think that publishing posts to multiple blogs would work all that well.  You could do it as long as you only ever had one selection for the Location / List view and then multiples on the Blog Post view.  Otherwise you could end up with two aliases underneath the page instead of just one.

I had to make a patch to items calendar to make sure that it wouldn't output pages before their date public but other than that the code below for adding a page alias worked great.

  1. /// Inside of Edit Page Function
  2. $db = Loader::db();
  3. $aliasedPages = $db->getAll('SELECT cID FROM Pages WHERE cPointerID=?', array($p->getCollectionID()));
  4. foreach ($aliasedPages as $cID){
  5. Page::getByID($cID)->removeThisAlias();
  6. }
  7. $this->saveData($p);
  8. Loader::model('page_list');
  9. $pl = new PageList();
  10. $pl->filterByCollectionTypeHandle('location');
  11. $p_location = $p->getAttribute('greaserag_location');
  12. foreach ($pl->get() as $locationPage){
  13. $locationPageLocation = $locationPage->getAttribute('greaserag_location');
  14. foreach($locationPageLocation as $location){
  15. if ($p_location->contains($location)){
  16. $aliasCID = $p->addCollectionAlias($locationPage);
  17. $aliasPage = Page::getByID($aliasCID);
  18. $aliasPage->setAttribute("exclude_nav", true);
  19. }
  20. }
  21. }
  22.  
  23. // Inside of Add Function
  24.  
  25. $this->saveData($p);
  26.  
  27. Loader::model('page_list');
  28. $pl = new PageList();
  29. $pl->filterByCollectionTypeHandle('location');
  30. $ak = CollectionAttributeKey::getByHandle('greaserag_location');
  31.  
  32. $location_id = $_POST['akID']['36']['atSelectOptionID']['0'];
  33. foreach ($pl->get() as $locationPage){
  34. $locationPageLocation = $locationPage->getAttribute('greaserag_location');
  35. foreach($locationPageLocation as $location){
  36. if ($location->ID == $_POST['akID']['36']['atSelectOptionID']['0']){
  37. $aliasCID = $p->addCollectionAlias($locationPage);
  38. $aliasPage = Page::getByID($aliasCID);
  39. $aliasPage->setAttribute("exclude_nav", true);
  40. }
  41. }
  42. }

I have to treat the add function a little differently than the edit function.  Because it's a newly created page we don't have access to the full page object until the script is done executing.  This means that although we are able to save attribute values against the page object and the submitted form values, we are not able to then turn around and call getAttribute on the page object and get the correct value back.  The version object for the page has an empty array for attributes.

So instead I'm checking the form field value explicitly.  I know it's not very clean and there's no way that this would work for marketplace code because I'd be requiring you to inspect your for fields and the value really could be anything.  I had to look at the value in the post array in xdebug in order to get the string right for the attribute's form field.  So it's not in any way clean, but it makes everything work once you put that one value in.  The alternative would be to not publish calendar data until edit mode but that seems clunky and stupid and I didn't want to do that.

The nice thing is that concrete 5 has this nice flexible api for adding and removing pages in different locations.  It's a relatively simple matter to bring up a list of all pages that meet my criteria for page type, knowing that all of those page types will have a blog list and a calendar like I need.  Then I can further narrow that list to only the ones that share a particular metadata attribute in common with my newly created page, and add the alias there.  The API is flexible enough that it even allows for different metadata to be applied to the aliased page vs the original page - I set the aliased page to be hidden from autonav so that it doesn't create a third level of navigation underneath on the Location page in the dropdowns.

I guess in a way this means that user blogs now functions differently for how sharing is handled if you want it to - you could go back to using regular page lists and simply displaying aliased pages, then alias the page to whatever pages meet the criteria.  That might be another way to handle creating the blog posts, maybe not.  It certainly works for what I need which was to integrate with items calendar. 

I could take it one step further - I have code from a site I worked on several months ago at Hutman that automates the creation of calendars and sets them to display underneath a newly created page on page add.  I'm not using that on the blog I'm working on now but might set that to work on the site for the Occupy the Neighborhood website.  I'm really liking how this application is coming together as a collaborative blogging platform, it's really functional.  The integration with Items Calendar is pretty nice, too.  I don't get everything that is going on in the calendar code but it makes adding calendars and associating pages with calendars really pretty simple and easy. For the occupy website I might make it so that you can add a new neighborhood even if you're not an administrator, if I do this then I will for sure need to automate adding the calendar.

I really like the idea behind user blogs, allowing anyone to publish blog posts on a website.  Especially paired up with something like the Locations that I'm using on this site and on Occupy The Neighborhood it offers a way for people to offer up relevant, local information to other people in a simple and easy to use format.  It doesn't require administrator access or anything complicated, there are no tools to learn, it's simple enough that anyone can figure it out. 

User Blog Add/Edit Form

I'm debating if I should keep it totally simple like you see it here or update the form to include forms for meta title, description and keywords.  I think most people would get the meta data fields, and hopefully would use them so that you'd get good SEO coming on the blog posts, too.  The items calendar actually links back to the original collection and not to the aliased collection so that's a nice bonus, no duplicate pages to worry about for google's spiders. 

I also learned how to update share this to display multiple share this instances on a page with different urls for each one.  In my page list, I'm doing something like this:

  1. <?php
  2. $title = $cobj->getCollectionName();
  3. $stTitle = urlencode($title);
  4. $stURL = $nh->getLinkToCollection($cobj, true);
  5. ?>
  6.  
  7. <div class="sharing">
  8. <span class='st_sharethis_hcount'
  9. displayText='ShareThis'
  10. st_url="<?php echo $stURL?>"
  11. st_title="<?php echo $stTitle;?>"></span>
  12. <span class='st_email_hcount'
  13. displayText='Email'
  14. st_url="<?php echo $stURL?>"
  15. st_title="<?php echo $stTitle;?>"></span>
  16. <span class='st_twitter_hcount'
  17. displayText='Tweet'
  18. st_url="<?php echo $stURL?>"
  19. st_title="<?php echo $stTitle;?>"></span>
  20. <span class='st_facebook_hcount'
  21. displayText='Facebook'
  22. st_url="<?php echo $stURL?>"
  23. st_title="<?php echo $stTitle;?>"></span>
  24. <span class='st_delicious_hcount'
  25. displayText='Delicious'
  26. st_url="<?php echo $stURL?>"
  27. st_title="<?php echo $stTitle;?>"></span>
  28. <span class='st_linkedin_hcount'
  29. displayText='LinkedIn'
  30. st_url="<?php echo $stURL?>"
  31. st_title="<?php echo $stTitle;?>"></span>
  32. <span class='st_plusone_hcount'
  33. st_url="<?php echo $stURL?>"
  34. st_title="<?php echo $stTitle;?>"></span>
  35. </div>

By manually specifying the url and title for each sharing link you can display a page list where each sharing icon set shares that individual page.  I had actually asked about this on a totally random awhile back, but they didn't actually specify how to do it. I found the information on the share this website, and also found out how to integrate with google analytics.  Unfortunately I also found out that the data that I'm saving from werstnet.com is not complete, it appears to not be logging data for the www.werstnet.com domain, only for werstnet.com and local.werstnet.com.  I'm not sure why this is or how to fix it, I have a support ticket open with ShareThis so it's my first experience with their customer support.  So far they replied in under 24 hours but I haven't heard back after clarifying a couple of issues. 

I tried implementing the sharing on the views for this blog but the buttons just looked way too big and out of place for my liking, so no sharing buttons on the blog view.  There are still buttons if you want to share the blog page itself, just not individual page sharing links.  You have to actually visit the page and read the post before I let you share the post.  Maybe that's wrong, maybe I should make it even easier, so that people could share more than one post without even reloading their page.  I didn't really like that for this site, though, so I left it out.

I wish more people had of taken advantage of the new login features on werstnet to request access to extra content like this stuff.  So far there have only been two people and they were both coders from the IRC channel about concrete 5.  So I guess when I post stuff up like this I'm really just posting it up for myself because nobody else actually wants to know this kind of stuff, or rather they don't want to have me decide which portions are shared with them.  If I just post up some train wreck of a depressing post because I'm having a bad day everyone and their dog will share it and send it all over the world and it will get lots of hits but that's not really what I want.  I want more a intimate connection with the people that are reading the stuff that I write, and to start sculpting the content that I create for more than one audience.  To me it seems like a natural progression of how I've been using text, I have the tools so why not use them.

A large portion of this post is available to people in the Lovers group only.

blog comments powered by Disqus