Archive for the ‘Content Management Systems’ Category

Duplicate results in Webform module Analysis display

Colin Calnan | Tuesday, June 9th, 2009

I came across a problem with the Webform module recently that seemed initially puzzling but on closer examination was actually quite simple.

The Problem

When viewing the webform results in the analysis page it appeared that there were duplicate values for some fields:

webform-analysis-duplicates

The Cause

TinyMCE. It seems that the webform and it’s components may have been created when TinyMCE was disabled and then edited at a later date when it was enabled. The visibility settings for TinyMCE were as follows:

node/*
user/*
comment/*
admin/build/block/*

This meant that TinyMCE was being attached to textareas in the webform component editing pages, usually at ‘node/xx/edit/components/x’.

“select” form components in webforms use a textarea to allow you to input the options for the select field. Each option value is input on a new line in the textarea. With TinyMCE enabled the options had a <br /> tag appended to them. Whenever the site editor edited the form component values, and had TinyMCE enabled, the value of the option changed from “College” to “College<br />”. So any further submissions to the form resulted in the submitted data being recorded as “College<br />”

When running analysis on this data, the results showed up as duplicates

College 34
College 215

due to the <br /> being rendered out in the HTML, it really should have read

College<br /> 34
College 215

The Solution

Turn off TinyMCE for all webform component editing pages. I did this via the Visibility setting in TinyMCE module:

node/add/*
node/*/edit
user/*
comment/*
admin/build/block/*

Now it only shows up on editing nodes and add nodes, not editing form components.

Now for the data. The first thing to do was to sort out the webform component. I tried to run a find and replace on the database in the ‘webform_components’ table, but this didn’t work, so I resorted to editing and re-saving each form component individually. Then I moved to cleaning up the submitted data. I ran a simple find and replace query on this to remove the unwanted <br /> tags:

UPDATE `webform_submitted_data` set `data` = replace(`data`,'&lt;br /&gt;','') WHERE `nid` = NODE_ID_HERE;

This says; Update my webform_submitted_data table by replacing all instances of <br /> in the data table, with nothing, and do this for all data submitted for the webform whose nid(Node ID) is the id of the webform with the funky data.

This worked a treat and the values reset themselves nicely:

webform-analysis-duplicates-2

Drupal Coda Book

Colin Calnan | Thursday, June 4th, 2009

I’d been itching to try out Coda as my development tool, and their recent 3 day sale meant I got a copy for $45. It’s pretty good so far, I have a couple of issues with it that I’ll cover in a later post. One really nice thing about it is the Books feature. This allows you to add reference material to your Coda development environment and search it without having to go to a browser. I’m always needing the Drupal API, well I actually use this one http://drupalcontrib.org/, which is a brilliant Core and Contrib API, perfect for those tricky Views or Webform moments. Setting up a book is really easy, and here is a screen shot of the settings for a book for Drupal 5 and Drupal 6.

Drupal 5 Book settings
Drupal 5 Book settings
Drupal 6 Book
Drupal 6 Book Settings

I also created a nice little Drupal Book icon from the PSD that I found here which was linked to from here (tips on how to insert coda books).

Drupal Coda Book Icon
Drupal Coda Book Icon

Direct link to book icon

Drupal 6 module’s page unresponsive

Colin Calnan | Thursday, March 26th, 2009

Discovered a very upsetting scenario today while building out a Drupal 6 site. If you have the Update module enabled and drupal.org goes down your module admin page at www.example.com/admin/build/modules stops responding. Why? I think it’s because somewhere in the Update module is a call to updates.drupal.org.

The quick and dirty solution is to temporarily disable the module via the system table in your database.

How do I do that?

  • Using phpMyAdmin or some other GUI tool or via the command link
    1. View the system table contents
    2. Find the row where filename is modules/update/update.module
    3. Edit the row and set it’s status field to 0
    4. Refresh your modules page
  • Via SQL Query/command line

    UPDATE `system` SET `status` = 0 WHERE `filename` = 'modules/update/update.module'

I’m going to submit an issue about this and look into a possible solution. I was halted in my work for over an hour today because of this.
Disclaimer: I am not responsible for those of you who do not backup before making any changes to your database.

Our Theme Toolkit

Colin Calnan | Thursday, March 5th, 2009

So we (Christopher and I) are at DrupalCon, and one of the questions to the floor at The Themer’s Toolkit was “What theme toolkit do you use?”. I thought I’d give a quick overview of our toolkit:

Theme

We’ve been using a custom three column layout that Christopher developed way back when, but recently we’ve been tweaking it considerably, to include new features such as video support, stylesheet switching, custom search form and IE support. It’s now similar to Zen in many ways and provides context/content specific classes and ids for ease of styling. We have plans for the future to create a more flexible theme based on CSS frameworks.

Install Profile

We have our own custom install profile that configures things like TinyMCE and CCK settings as well as setting up an admin user etc.

Coding tools

We are primarily a Mac/Adobe shop, so we have been using Dreamweaver up to now, however we sometimes use TextMate for new projects or projects that don’t require a check-in/check-out capability. With the prospect of version control tools coming in the future we will be reviewing and reassessing. I know there are some hardcode VIM and Emacs users out there, we promise we’ll consider both :)

Version Control

Historically we have been a single developer shop with no need for a version control system as most sites were developed and then rolled out without need to many updates. However with the addition of more custom modules, working on much larger sites and the expansion of our team we now feel it necessary to implement some sort of version control system. Having seen some of these in action at DrupalCon, we will be working to have something running in a few months.

Modules

We use the standard modules such as CCK, Views etc. but also have the following custom modules:

Mobile Detection Module

I’ve recently finished a rough beta version of a mobile redirection module which uses this code to detect the user agent and then redirects to a specified URL. In most case we use a sub-domain folder like /sites/m.example.com and configure the settings.php to use a separate theme. We’ll release this at some point also when it meets drupal’s code and module development standards, not that it doesn’t already but we need to make it less site specific.

Block Class extension

I wrote an extension to the block class module that allows the user to choose from a specified list of block class options, rather than typing one in.

Node title length

In some cases, to stop the design from breaking, we need to ensure that the node title lengths are restricted to a certain maximum. This module allows you to choose the content types to apply the restriction to and does the necessary validation.

CSS Image Replacement

Chris wrote a totally awesome tool that we then roughly ported to a module that uses CSS to do image replacement. For now that’s all I can say, we’ll be working on releasing it in the wild at some point but it rocks and relies on GD libraries and CSS definitions to create images to replace text.

Advanced Menus

Chris built this module to allow us to place any of the available drupal menus in a block and position this anywhere on the site.

We’re constantly working to improve on our workflow, and having recently become a 2 person development team, soon to be 3, we are conscious that improvements need to be made in preparation for future growth. If you have any tips of suggestions for a team the size of ours I’d love to hear them.

What public radio can teach you about fundraising

Lauren Bacon | Wednesday, March 4th, 2009

Slate has a great article up on “The 10 cunning ways public radio stations convince you to give them money” — as I was reading it (and listening to the fabulous audio clips that accompany the article), I reflected on how many of these same smart fundraising techniques apply to good causes everywhere.

Personally, I am a big fan (and longtime supporter) of KEXP, the Seattle-based radio station that also delivers their exceptional music programming online, and one of the things I have always admired about them is their sheer genius when it comes to fundraising. (They’re in the middle of pledge week right now, as a matter of fact, should you feel inclined to give to a great cause.) One year during pledge drive they accepted bids on seemingly random items scattered around the station, so that for, say, $500 (I can’t remember the actual amount), you could get your name on the front of the coffee machine, where staffers would grab a cup of joe. For $1000, you might get your name on a microphone that’s used every day for in-studio performances by some of the best musicians in the world. And so on. The intimacy and immediacy of the items one could sponsor made them incredibly appealing to listeners like me. (I still want my name on that coffee machine.)

I think of all the techniques described in the Slate article, the one that’s most effective at getting my credit card out is this one:

8. Niche marketing

The best of public radio’s weekend shows have distinct personalities: the discursive storytelling of This American Life, the self-deprecating bickering of Car Talk, and the cozy in-jokes of A Prairie Home Companion. All these shows produce special pledge editions, pitching in their signature styles. Ira Glass clearly missed his calling in sales; he is a master of the “ask.” He appeals to his people in their native tongue, sarcasm, calling on them to show their love for the show rather than the station it happens to be playing on: “There is one sure way that you can send a signal to this radio station that you like this program, and that you want them to continue running this program, and that is to call right now. …. Not later, not in an hour, during that other show that comes after us.”

I want to be made to feel special — to feel like I belong to a community of people with whom I share certain common interests and values. If you can make me feel understood and valued, then you will gain my loyalty, and with my loyalty comes a much greater willingness to part with my hard-earned cash.

And you know what didn’t make the list? Fearmongering. (Yes, they did include guilt-tripping, because where would fundraisers be without the ability to tug on heartstrings?) I, for one, would really love it if I never received another fundraising letter that implied the world was on the brink of destruction and that only my dollars could save us from apocalypse, whether the end of the world was coming in the shape of environmental, cultural, or social degradation. I am rarely motivated to action by doom-and-gloom scenarios; what does motivate me is a clearly articulated vision of a better world that I want to live in — even if it’s just a world where The Pixies still get played on the radio every day.

Views random sort not working

Colin Calnan | Wednesday, March 4th, 2009

Recently we developed a simple view that used a random sort to pull one node of a certain content type on each page visit. This is easy to set up and provides satisfactory results. We set it up, logged out, refreshed the page and it didn’t work. The first load was random, but any refresh after that resulted in the same node. Frustrated as to why this was happening I went digging in the view, then in the theming, and after about an hour debugging, realized that site caching was enabled. So that’s a bit of a problem,

“Views random sort does not work the way you would expect with caching enabled “

Pivot Legal – A Drupal breakdown

Colin Calnan | Tuesday, February 24th, 2009

As Melanie mentioned, we just launched a site for Pivot Legal, and a successful launch it was too. I thought I’d take a moment to give a quick breakdown of some of the tricks and wizardry we incorporated on that site.

Valid Code

For starters, we succeeded in getting most of the site to be valid XHTML, of course with Drupal there are parts where client content entry will cause some warnings and Drupals own internals will also do the same, but most of the pages on this site, within our control, are valid.

Lawyer Blogs, Resources and Events

This was the first site that we have implemented multi user blogging and user content categorization, so it was a learning experience for us. Each lawyer has their own page, in this case, that page is the default user profile page. However it’s not just your ordinary user profile page, it’s a themed user profile page. To allow us to theme this page we simply called the following function in template.php

/**
* Catch the theme_profile_profile function, and redirect through the template api
*/
function phptemplate_user_profile($account, $fields) {
	return _phptemplate_callback('user_profile', array('account'=>$account, 'fields'=>$fields));
}

Then create a template file called user_profile.tpl.php, here’s a snippet from that file:

$lawyer_profile = '<div id="lawyer_profile">'. chr(10)
   .'<h2>'. $account->profile_fullname .'</h2>'. chr(10)
   .'<h3>'. $area_of_law .'</h3>'. chr(10)
   .'<p>'. $account->profile_biography .'</p>'. chr(10)
   .'</div>'. chr(10);
// Photo, if any:
  if ($account->picture) {
    $lawyer_photo = theme('image', 	$account->picture, $account->profile_fullname .'\'s photo', $account->profile_fullname);
  }

The Blog, Resource and Events blocks on the lawyer page are simply View blocks that have the username as an argument. To achieve this we used the Usernode module to convert user profiles to nodes so that we could use their properties in views. I believe Views 2 in Drupal 6 will handle all this without the need to this module.

Username funkiness

As there are lots of users creating lots of content Drupal spits out their username attached to the piece of content (e.g “Posted by colin on Feb 26, 2009″). However on this site we wanted to show their full name and link that back to their profile page, again a simple theming function achieved this

function custom_theme_username($object) {
  if ($object->uid && $object->name) {
    // Load user so that we can get the full profile name instead of the short username
    $user = user_load(array('uid' =>$object->uid));
    $name = $user->profile_fullname;
    // If it's the "Pivot Legal LLP" user, go to the 'about-us/' page instead:
    $path = ($name != 'Pivot Legal LLP' ? 'user/'. $object->uid : 'about-us/');
 
    if (user_access('access user profiles')) {
      $output = l($name, $path, array('title' => t('View user profile.')));
    }
    else {
      $output = check_plain($name);
    }
  }
  else if ($object->name) {
    // Sometimes modules display content composed by people who are
    // not registered members of the site (e.g. mailing list or news
    // aggregator modules). This clause enables modules to display
    // the true author of the content.
    if ($object->homepage) {
      $output = l($object->name, $object->homepage);
    }
    else {
      $output = check_plain($object->name);
    }
    $output .= ' ('. t('not verified') .')';
  }
  else {
    $output = variable_get('anonymous', t('Anonymous'));
  }
  return $output;
}

Searching

It was a requirement to have a searchable directory of free legal resources on this site. We built this using a content type and Views, along with the Views Fast Search module. This allows you to create a view which acts like a search but uses view Filters to restrict the search. Robert Douglass, one of the famed Lullabots has written a great tutorial on how to set it up.

How did you get the search box on the home page?

This is pretty easy. Go ahead and create your Fast Search that we mentioned above. Then create a block and paste the following code into the body of the block, making sure to include the php tags and to set the input format to PHP:

  < ?php
  // Get the view
  $view = views_get_view('directory_search');
  // Get the filters ( in this case the search box) - this is returned as a form
  $form = drupal_retrieve_form('views_filters', $view);
  // When you submit the form make sure we go to the view page
  $form['#action'] = url($view->url);
  // Set your form up correctly in drupal
  drupal_process_form('views_filters', $form);
  // Spit the form out to screen
  return drupal_render_form('views_filters', $form);
  ?>

Of course this method can be applied to any view filters.

Views, views and more views

The rest of the site is mostly views and view blocks with some serious argument handling. The rotating images on the homepage are achieved using a simple content type for the image and then using a view to randomly load one of those pieces of content.

If you have any questions about any of the other functionality on the site or on anything I’ve menitoned above please feel free to post a comment.

Displaying Webform results in a block

Colin Calnan | Wednesday, January 14th, 2009

We use the webform module for most of our form needs on our client sites. It’s a pretty good module that provides most of the functionality required. The most important part of the webforms module is the submission data. The reason for the form in the first place is to gather data that can be analyzed and then acted on. Webform has a results area where you can view all the submitted data, analyze it, download it and export it. It does this using some useful built in functions such as webform_get_submissions.

However, say you want to get the number of submissions to a particular field in a form and then display the most popular choices in a block, a little similar to a poll but the data is collected as part of a webform. For our example, the form is collecting a list of reasons for donating and then displaying the top 5 reasons in a block on the site.

One way to achieve this is via a module. We create a block using hook_block and then display the top 5 reasons in it (explanations are in comments in code):

function mymodule_block($op = 'list', $delta = 0, $edit = array()) {
  switch($op){
    // Create the block listing in the block admin page
    case 'list':
      $blocks[0]['info'] = t('Top 5 Reasons for donating');
      return $blocks;
      break;
 
    case 'view':
      switch($delta) {
        case 0:
          if (module_exists('webform')) {
 
            // Set the node id to the webform we require the results of
            $nid = 999;
 
            // Load the webform node
            $node = node_load($nid);
 
            // Retrieve the list of reason choices from the webform field, split at line breaks 
            // The choices are entered in a textarea when creating a form 
            // in the format "i_have_money|I have lots of money"
            $reason_key_pair = explode('', $node->;webform['components'][1]['extra']['items']);
 
            // Loop through the textarea to create an array in the form 
            // $submission['i_have_money']=>;I have lots of money.
            foreach($reason_key_pair as $key =>; $value) {
              $new_array = explode('|', $value);
              $submissions[trim($new_array[0])] = $new_array[1];
            }
 
            // Execute the query to return the top 5 reasons for donating from the webform_submitted_data table
            $result = db_query("SELECT sd.data reason, COUNT(sd.data) total
              FROM {webform_submissions} s
              LEFT JOIN webform_submitted_data sd ON sd.sid = s.sid
              WHERE sd.nid = %d AND sd.data != '%s'
              GROUP BY sd.data
              ORDER BY COUNT(sd.data) DESC
              LIMIT 0,5", $node-&gt;nid, '');          
 
              // Create an array which will hold the reasons - which we will then display in the block
              $reasons = array();
 
              // We run through the submissions and match them to the choices from the webform textarea values
              while ($row = db_fetch_array($result)) {
                $safe_key = $row['reason'];
 
                // If a user chosen option actually exists in the current options available, then display it
                if(array_key_exists($safe_key, $submissions)) {
                  $reasons[] = $submissions[$safe_key];
                }
              }
 
              // If there are no results we need to display a message
              if (count($reasons) == 0) {
                $reasons[] = array('data' =&gt; t('There are no reasons'));
              }
 
             // Theme the results as a list to be styled with CSS
             $block['content'] = theme('item_list', $reasons, NULL, 'ol', $attributes = NULL);
          }
        	break;
        }
      return $block;
      break;
  }
}

Enable the module, then go to the blocks admin page and your block will now be available. Simply set the title and position it where you want it.

An example of this block can be seen in the right sidebar on the BCNDP website.

When is a view not a view? When it’s a page

Colin Calnan | Monday, December 8th, 2008

We’ve come across an issue quite a few times during the development/build of a site where a client wants to include some content above or below a view and wants to have the ability to edit that content. There are a number of ways to achieve this functionality:

  1. You could simply use the “Header” field in the create/edit view screen and place some text in there. However, that does not have a nice WYSIWYG editor, one could be assigned but it’s not ideal.
  2. You could create a block and place it in a region above the view. Again, unless you have a WYSIWYG Editor set up for blocks, this is not perfect.
  3. You could, in some way, add a view to the bottom of a page – ah!, that’s it.

The caveat with 1 and 2 above is that it requires giving the site editor/client access to views and blocks, in a lot of cases this is way too much control and will usually result in problems. 3 is the ideal solution. Once I figured this out, I began to develop the functionality: I created a Select CCK select field for all page content types which pulled a list of all available views by using the views_get_all_views() function. This field stored the name of the view, I then used the name of this view in page.tpl.php to load the view via the function views_get_view(). It started to get a little messy, so I decided to start building a module, only to find that one already existed.

It’s called Viewfield and does exactly what it says on the tin. It allows you to add a CCK field which is a list of views to choose from. The view will then display on a page in whatever position and format you choose. It works great.

You can see it in action on this page from the BCNDP website.

Search Engine Friendly URL’s and Drupal – Part 2

Colin Calnan | Wednesday, December 3rd, 2008

In my previous post I showed you how to set the friendly URL for each page/piece of content on your site. Hand curating can be fun, but it’s also time consuming and requires a little thought. It can also be a little cumbersome when you have a long title, like the title of this post. In steps the Path Auto module to save the day. In conjunction with the Token module, Path Auto automates the task of creating and setting these URL’s. How does it work? It’s pretty simple really.

  • Using Drupals internal URL alias functionality, Path Auto automatically(automagically) creates URLs for you when you create a new piece of content.
  • The structure of this URL is determined by you, the user, via the Path Auto administration page (http://www.yoursite.com/admin/settings/pathauto).
  • Each individual content type (via CCK module) can have it’s own settings
  • The path can be hard coded to anything you like, or it can use “tokens” to determine how the page is made up, for instance:
    • The path can be made up of the title of the content (http://www.example.com/the-title-of-the-content)
    • The path can be made up of CCK types and CCK fields (http://www.example.com/albums/rock)
    • The path can be made up of vocabularies and taxonomy terms (http://www.example.com/2008/10)
    • The name of the author (http://www.example.com/colin/drupal-search-friendly-url)
    • There is a comprehensive list of the options (replacement patterns) available for creating URLs /aliases on the path auto settings page, some examples:
      • [nid] – Node Id
      • [title-raw] – Unfiltered node title
      • [yyyy]- Node creation year (four digit)
      • [term] – Name of top taxonomy term
      • [author-name] – Node author’s user name
  • Once your paths have been setup you can then select the “Bulk generate aliases for nodes that are not aliased” for those paths you have configured. This will create new URLs/aliases for your existing content. Also note that this will generate aliases for all existing nodes which do not already have aliases.
  • In the future, when creating new pieces of content, the URL/alias will be created for you based on the criteria you have chosen in the path auto settings.

So that’s it, not that difficult really. There are a few issues I’m come across when using path auto, and my one piece of advice is to decide on your alias structure before you start entering content. Or, if you have entered content and are running a bulk update, Think carefully about your alias structure. If you do mess it up there’s an easy way to re-run the alias update. You can do this via Update area of the “Content” management page. Select the content you want to update and choose “Update path alias” from the drop down options.

 


t. 604.684.2498 | f. 604.721.4007 | e. turningheads [at] raisedeyebrow.com