drupal

Updating the page cache on time

On RVParking.com, we have a detailed directory listing page. It is complex CCK node themed, with an included Google map for location. Also included are two embedded Views. One for photos (a slideshow type photo with AJAX paging) and a View of Reviews (a review node type, with fivestar voting and attached pictures among other things. Both use node references to attach them to the park.

Our Problem: Cached pages with embedded views show stale view data.

The crux on the problem is that a park page would not be rebuilt when a new review or photo was posted. At first I thought it was the view that wasn’t being updated the new photos or reviews. After even turning off the view cache, the problem persisted.

So I looked higher up the cache ladder, and figured out it was the node cache that was the issue. Of course, how would the park node cache know to expire its cache when a review or photo with a reference to this park had been added or updated. But we can tell it to using the nodeapi_hook().

function rvparks_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {
     case 'insert':
     case 'update':
       if ($node->type == 'review' OR $node->type == 'park_photo') {
       $url = url('node/'. $node->field_park_reference[0]['nid'], array('absolute' => TRUE));
       // delete cache entries for that url
       cache_clear_all($url, 'cache_page');
     }
   }
}

This code fragment executes during either the insert (node create) or update (node edit) actions. It checks to see if either a photo or review node has been created. If so, then it form the full URL for the node that is in the node reference field. The Drupal caches uses the full URL to create the cache references, thus the ‘absolute’ => TRUE is needed.

The cache_clear_all() does all the work to delete the cache entry.

Now, when the RV park page is viewed after a new review is posted, the whole node cache is recreated. Problem solved.

The next step in making our site faster will be to add Boost. I am pretty sure this method will still work under Boost. About to find out.

Mashable Mobile Strategies and Drupal

Mashable recently published an HOW TO: Optimize for Mobile. They list a couple create tools that any good Drupal dev should be using if they have a mobile audiences.

But specifically, their fifth step is to use your CMS, with examples of WordPress and Drupal. Specifically they mention Mobile Tools proving they spent a least a little in researching this (or they have quick access to some Drupal talent). I have used Mobile Tools on a a few sites and it definitely a good way to go to present different displays of a website based on the mobile device. With include integration with WURFL for device detection, Mobile Tools is great.

Always glad to just stumble across references to Drupal in general technology based articles.

Views, ImageCache and an iPhone app

When developing a mobile app, be it for the iPhone, an Andriod phone or a Blackberry, having dynamic data sprinkled in the app is important. DogParkUSA.com has recently sponsored the Dog Park Finder iPhone app and they wanted a thumbnail photo in each dog park listing.

One key is that if a park gets its first photo, or a better photo, the editors wanted to be able to promote the photos to the top of the photo page, and to have it show up as the thumbnail in the iPhone app. To do this without updating the app, we used a static URL that points to a View, which displays a Imagecached image.

Imagecache was used to exactly size photos to fit in the frame used in the app, while Views allows us to have a static URL, even though the photo displayed can be changed by the editors (by using the Sticky flag). This flexibility is key to pushing the photos they select into the app without an app update.

Another Module that is key for making a mobile app is Path Redirect. This is needed because many times the URL of a page is need for a web view display. But if a page URL changes then it will break the mobile app, thus being able to use Path Redirect's ability to redirect from an old URL to a new one is great.

I can hear someone say that with the Global Redirect module you could just use 'node/44' type URLs, but we didn't like that solution because in the 'Tell a Friend' email, a URL is embedded that directs to a dog park details page, and we didn't want a 'node/44' type URL in the email.

The next few updates in the app will add in the ability to submit reviews, submit photos and read in live reviews into the app using the services module. I will write about those interesting tasks as they come.

Drupal 6 Search Engine Optimization Review

I recently had the pleasure of reading Drupal 6 Search Engine Optimization by Ben Finklea, published by Packt Publishing. As the lead developer of a small start-up that depends on organic search traffic (as well as Search Engine Marketing, SEM, campaigns) I already have a keen interest and generally thorough understanding of SEO practices in general, and SEO tricks specifically for Drupal. I approached this book wondering how much I may or may not actually learn. Gladly, it was worth the read.

Drupal 6 Search Engine Optimization is a book that any Drupal shop should have on the shelf for reference, and any budding (or experienced) Drupal dev should read to help them add value to any customer site they are building. Search engines many times send the lion's share of traffic to new sites, so building a site that search engines view favorably should always be a priority for any serious site construction project.

In a certain sense, this book is more than a Drupal book as it expounds on a few SEO topics and tools that are completely separate from Drupal. For a few site owners, this is golden as they will not have to plunk down another $20 (or euros, or pounds) for a general SEO book. In the first few chapters general marketing advise, how to set up a Google Analytics account, and even the value of viewing user activities though a click heat map are discussed. All are outside Drupal proper, but are topics that help round out the general knowledge base of the reader. The book assumes a very rudimentary level of Drupal understanding, and assumes no SEO knowledge.

Drupal's Proven Productivity Boost Explains it All

Browsing the last day's set of blog posts to Drupal Planet is proof positive that Drupal enhances productivity so much that we have extra time for leisure posting. Of course, my own contribution is equally devoid of 'professional' merit or helpful support (need to finish an SEO book review), but these all contribute to the 'Drupal Rocks!' motif of the Drupal community.

First, there is the Drupal Cube. Drive behind this Nissan Cube and the Drupalicon will be staring you right back in your windshield.

Next, there is the Great Drupalkin hunt of 2009. Check out pingVision's pumpkin/Drupalicon hybrid.

Lastly, there has been a rule change and some fun on IRC.

Seriously, if we have time to post this stuff, Drupal must rock! Hope to see more.

BTW, did you hear that the Whitehouse is using Drupal!!!

Hook on Drush for Windows

So I have heard of Drush for years now, saw my first demo at the Boston DrupalCon but since I do all my dev work on a Windows machine I didn't catch the Drush wave (I kept hearing it was *nix only).

It has always been in the back of my mind to keep looking back into Drush, but somehow I missed the major 2.0 update in June and that it works on Windows now. When I saw Morton's Mac Drush post and revisited my Windows issue, and now I am a convert.

Already there is an install guide written two week ago that I have verified works perfectly for my Vista Business 64 bit machine. Drush is up and running on my dev system now and I am already addicted.

Book Review - Drupal Multimedia

Finally I have found time to read Drupal Multimedia. As an active multimedia developer Aaron Winborn has this topic covered.

Who is it for? You need to read this book if you are want to learn how to make Drupal pop with rich media. Helpful examples and walkthroughs for most of the well used modules are given. If you want to know more about any of these modules, or if you don't even know what these modules can do.

Quick Progression

  • Intro - CCK - Views - Themes
  • Images for admins and editors - Creating Gallery - Teasers - Embedded Images
  • Developing for Images
       Image node - Multiple images - Resizing - Views and slideshows
       Embedded Media
  • Theming Images - Styling Views - Investigating media theming - Overiding image nodes - Image affects

The middle chapters

Lastly Aaron ends the book with a 'Future of Drupal' section which covers what might be next for Drupal in:

  • File Handling
  • Multipurpose
  • UX
  • embedded widgets
  • Mobile web
  • New media - Second life/Wii

I have a few more extensive example that I want to share of how I implemented ideas from this book. But that is for another time. As I said that the start, if you are starting a new Drupal project and you want or need to add rich types of media, pick up this book to assist and enhance your projects.

Dispelling Node Access Fears

Venturing into the node access realm of hooks and functionality for some reason always felt complex, so I have avoided it complete thus far, relying on various node or taxonomy access based modules (http://drupal.org/taxonomy/term/74), to handle any access issues I needed. This usually resulted in systems that were more complex or more burdensome to maintain than desired. Well, I was wrong, the node access system isn't complex, and it is powerfully efficient and optimized.

Recently, I needed to implement a system to allow editing access to a node (an organization node) by multiple users, but we wanted the original node author to dole out this access instead of sending requests to the administrators. But beyond that we needed them to be able to edit all the 'product' nodes that referenced the 'organization' node.

A couple modules were close to what I needed (http://drupal.org/project/nodeaccess_autoreference, http://drupal.org/project/nodeaccess_userreference, http://drupal.org/project/nodeaccess_nodereference) but they were a bit more featured loaded than I wanted, and I used this as an opportunity to learn about the node access hooks (check out the node access example for a general example.).

To tackle my issue, I started with a user reference CCK item on the organization node, so the node author can assign other members to the node, and also added a node reference item to the product nodes to reference an organization node.

Now that the links were in place, I had to implement the node access permissions. Even time a node is checked for 'edit' permissions the node grants hook is run, so my module needed to implement this hook.

function example_node_grants($account, $op) {
   //Keyed array with realm as key
   $grants['referenced'] = array($account->uid);
   return $grants;
}

From the API guide, I just needed to create an array with the key as the realm of access permissions (should not conflict with any other node access module you have, if any). I called the realm 'referenced' since I was using node and user references, and since it was user based, I just use the user ID as the item to look for.

In simple terms, this tells the node access system to look for an entry that links the user ID, with the requested node ID in the 'referenced' realm of entries.

But the check is useless unless we have already added grants to the system, so the node access record hook is needed.

Checking the API guide, I just needed to pass an array of arrays with the grants that needed to be saved.

function example_node_access_records($node) {
   if ($node->type == 'org') {
   //For each user referenced in the CCK item, add a grant entry.
   foreach($node->field_members as $u) {
     if (!empty($u['uid'])) {
       $r_user = user_load($u['uid']);
       $grants[] = array(
         'realm' => 'referenced',
         'gid' => $u['uid'],
         'grant_view' => TRUE,
         'grant_update' => TRUE,
         'grant_delete' => user_access('administer org', $r_user) ? 1 : 0,
         );
       }
     }
   }
   elseif ($node->type = 'product') {
     //For each 'org' node referenced, check for all user references to add grants.
     foreach($node->field_org_ref as $o) {
       if (!empty($o['nid'])) {
         $o_node = node_load($o['nid']);
       //For each user referenced in the CCK item, add a grant entry.
       foreach($o_node->field_members as $u) {
       if (!empty($u['uid'])) {
         $r_user = user_load($u['uid']);
         $grants[] = array(
         'realm' => 'referenced',
         'gid' => $u['uid'],
         'grant_view' => TRUE,
         'grant_update' => TRUE,
         'grant_delete' => user_access('administer org', $r_user) ? 1 : 0,
         );
         }
       }
       }
     }
   }

   return $grants;
}

When a node is saved, all the grants for that node are re-calculated. But I had an issue where I needed to also resave all the referenced 'product' nodes with new permissions each time the 'org' node was updated. So I needed to also make a nodeapi hook entry to save new grants for any referenced 'product' nodes.

function example_nodeapi(&$node, $op) {
if ($op == 'update' or $op == 'insert') {
//Update node access table with grants of the author and members of an org
if ($node->type == 'org') {
//Add grants for all products that reference this node.

$qry = db_query("SELECT DISTINCT(nid) FROM content_type_product WHERE field_org_ref_nid = %d",$node->nid);
while ($result = db_fetch_object($qry)) {
$grants = array();
foreach($node->field_members as $u) {
if (!empty($u['uid'])) {
$r_user = user_load($u['uid']);
$grants[] = array(
'realm' => 'referenced',
'gid' => $u['uid'],
'grant_view' => TRUE,
'grant_update' => TRUE,
'grant_delete' => user_access('administer org', $r_user) ? 1 : 0,
);
}
}
node_access_write_grants($result, $grants, 'referenced');
}
}
}
}

The node_access_write_grants function takes the same array entries like the node access records hook. It isn't needed to be called oin the node access records hook because the grants are written later through the hook loop, but in the nodeapi() implementation, we have to explicitly call the write grants function to make sure they are saved.

To round out the permissions carefully, I turned on the CCK permissions modules and protected both the user reference and node reference fields so that members with this new delegated power can't add or remove other people from the editing powers. Only the node author can do that.

Now that I have done it once, I won't be nearly as fearful regarding the node access system, and I have found how optimized this system is. Great job Drupal core developers.

YSlow Small site review

I always check YSlow before a new site launch, but I don't remember when they updated the look and functionality. Maybe it is the FF 3.5 version. But regardless it is great to see that the look resembles the vertical tabs from the new content editing form in Drupal 7.


Click for large image.

Beyond the surface change, the have also added a new Small Site or Blog setting that changes what is evaluated, CDN, Etags, Cookie Domains and AJAX compression are not used in the scoring.

This is encouraging since the new site now has a score of 89. Then when I turn on CCS and JS aggregation the score jumps to 98. I imagine reducing background images and moving a few more JS scripts to the bottom would get close to a perfect 100, but I am not reaching for that number anyway.

But the new scoring is great since it focuses on what small to mid-level sites can actually work on, since CDN and JS/AJAX compression is just not really workable for such sites. A score of 98 lets me know that I am really done with the stuff that should concern a smaller sized site.

Embedded Video How To - Start Youtube mid-video

Here is a simple trick that I learned while creating a small, fun video site on a whim. The main problem is that we wanted to start some Youtube videos at a specific interval in the video, say 30 seconds in. We were already use the Embedded Media Field set of modules of CCK (http://drupal.org/project/emfield, with the video/youtube addon modules), but mid-video starts aren't supported (yet as I will submit a patch shortly to add this functionality to the options array).

I will write more about the full site build at a later time, but here is what I did to tackle this issue. I am assuming that you are familiar with CCK, EM Field modules and a bit of knowledge to theme a node template.

Here is what I did:

1) Create the embedded video field for youtube ('field_song_video' in my implementation).
2) Create am integer field to hold the number of seconds into the video to start ('field_song_start' in my implementation).
3) Remove from Display on the node both of these fields.
4) Updated my node-songs.tpl.php (replace with your appropriate node template file).
5) Where I wanted my video to be embedded I placed the following code:

<?php print theme('emvideo_youtube_flash', $node->field_song_video[0]['value'] .'&start='.$node->field_song_start[0]['value'], 450, 350, 0); ?>

The main part to notice is the '&start=...' part. The theming function for the youtube player will add the start variable to the Youtube request URL which will start the video at a later place than the beginning. The other arguments are the size of the player (450px by 350px) and the auto-start flag.

NOTE: The API has changed a bit since my site implementation so the new code would look like this (replace 'emvideo_youtube_flash' with 'video_cck_youtube_flash').

<?php print theme('video_cck_youtube_flash', $node->field_song_video[0]['value'] .'&start='.$node->field_song_start[0]['value'], 450, 350, 0); ?>

Syndicate content