Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
May 29 2018
May 29

Part 3: The Final Installment

This is the final installment of Drupal Taxonomy that we feel is important for one unfamiliar with Drupal to know! At this point, hopefully, you understand some of the key language that is regularly used in the Drupal Community.  For reference, our first two blogs, Part 1 and Part 2, should provide you any background you may not already have.  Mobomo is the team that is behind NASA, the solar eclipse with NASA, the USGS store, and NOAA Fisheries, all of which are Drupal sites.  Similar to these organizations, Drupal is the CMS system for you if your needs are more complex, you are developing an e-commerce portal, or if you have a large amount of content to maintain.  If you have a Drupal project in the works or are about to migrate versions or CMS systems, Mobomo has some of the best and brightest Drupal developers in the DC area.

Key Terms:

  1. Permissions – This is a tool for controlling access to content creation, modification, and site administration at the application level.
    1. Administrators can assign permissions to roles, then assign users to these roles.
    2. The first user receives all permissions.
  2. Template – This refers to a file to express presentation
    1.  These are mostly written with a markup language that has variables representing data provided to the template.
  3. Theme Engine – This is a set of scripts that interprets code and makes theming a site easier. These scripts take the dynamically generated content and output it to HTML.
    1. The default theme engine is PHPTemplate
  4. Theme Hook – This is an identifier used by the calls to the theme() function to delegate rendering to a theme function or theme template.  Modules which extend Drupal may declare their own theme hooks to allow editors to control the markup of that module in their theme.
  5. Trigger – These typically result from a characteristic change in an entity maintained by a module.
    1. Types:
      1. Deleting content
      2. Adding a comment that a user has logged in
      3. Adding a term
  6. Triage – A new issue is assigned a priority based on its severity, frequency, risk, and other predetermined factors.
  7. Zebra Striping – This refers to the to the alternating colors between rows of data. It is most common for rows of data to alternate background colors between white and gray.
  8. Testbot – A continuous integration service for testing patches submitted to project issues on Drupal.org.
  9. Roles – A name for a group of users, to whom you can collectively assign permissions. There are two predetermined, locked roles for every new Drupal installation:
    1. Authenticated User – anyone with an account on the site.
    2. Anonymous User – those who haven’t yet created accounts or are not logged in.
    3. Additional roles can be added and users can belong to multiple roles.
  10. Path – This is the final portion of the URL that refers to a specific function or a piece of content.

Please reference Drupal.org for more information!

Apr 26 2018
Apr 26

Part 2:

In our previous blog post, we gave a brief intro to some terms that we believe are necessary to understand the basics of Drupal.   Here we have what we believe to be the next round of terms that we consider necessary to understanding those basics. Recently, we had the opportunity to assist Matrix AMC in migrating from Drupal 6 to Drupal 8.  They were unable to use their website because of the version of Drupal that their website was hosted on was out of date and no longer supported by the Drupal community. While these specific terms are consistent across Drupal versions, they are crucial to understanding the importance of being up to date in with your version of Drupal.

Key Terms:     

  1. Block – the boxes visible in the regions of a Drupal website.
    1. Most blocks (e.g. recent forum topics) are generated on-the-fly by various Drupal modules, but they can be created in the administer blocks area of a Drupal site.
  2. Region – defined areas of a page where content can be placed. Different themes can define different regions so the options are often different per-site. Basic regions include:
    1. Header
    2. Footer
    3. Content
    4. Left sidebar
    5. Right Sidebar
  3. Roles – a name for a group of users, to whom you can collectively assign permissions. There are two predefined, locked roles for every new Drupal installation:
    1. Authenticated User- anyone with an account on the site.
    2. Anonymous User- those who haven’t yet created accounts or are not logged in.
  4. WYSIWYG – What You See Is What You Get; An acronym used in computing to describe a method in which content is edited and formatted by interacting with an interface that closely resembles the final product.
  5. Book – a set of pages tied together in a hierarchical sequence, perhaps with chapters, sections, or subsections.  Books can be used for manuals, site resource guides, Frequently Asked Questions (FAQs), etc.
  6. Breadcrumbs – the set of links, usually near the top of the page, that shows the path you followed to locate the current page.
    1. The term is borrowed from Hansel and Gretel, who left crumbs of bread along their path so they could find their way back out of the forest.
  7. Form mode – this is a way to customize the layout of an entity’s edit form.
  8. Multisite – a feature of Drupal that allows one to run multiple websites from the same Drupal codebase.
  9. Patch – a small piece of software designed to update or fix problems with a computer program or its supporting data.
    1. This includes fixing bugs, replacing graphics and improving the usability or performance.
  10. User – the user interacting with Drupal. This user is either anonymous or logged into Drupal through its account.

Refer to Drupal.org for any other questions!

Mar 28 2018
Mar 28

Key Drupal Taxonomy

Part 1:

When it comes to considering what is the best CMS for a website, most don’t know up from down or Drupal from WordPress.  At Mobomo, we consider ourselves Drupal experts and have guided many of our clients through a Drupal migration. Drupal is a content management system that is at the core of many websites.  Drupal defines itself as “an open source platform for building amazing digital experiences.” These simple Drupal terms, or taxonomies, make it sound easy, but it can, in fact, be very confusing. Listed below are some popular terms defined to help make the start of the migration process what it should be, simple and easy:

Key Terms:

  1. Taxonomy – this is the classification system used by Drupal. This classification system is very similar to the Categories system you’ll find in WordPress.
  2. Vocabularies – a category, or a collection of terms.
  3. Terms – items that go into a vocabulary.
  4. Tags – this is a generic way to classify your content and this is also the default setting when you first begin.
  5. Menus – these refer both to the clickable navigation elements on a page, as well as to Drupal’s internal system for handling requests. When a request is sent to Drupal, the menu system uses the provided URL to determine what functions to call.  
    • There are 4 types:
      • Main
      • Management
      • Navigation
      • User
  6. Theme – this refers to the look and feel of a site and it is determined by a combined collection of template files, in addition to configuration and asset files.  Drupal modules define themeable functions which can be overridden by the theme file.  The header, icons, and block layout are all contained within a theme
  7. Content-Type – Every node, see below for definition, belongs to a content type.  This defines many different default settings for nodes of that type.  Content Types may have different fields, as well as modules may define their own content types.
  8. Fields – These are elements that can be attached to a node or other Drupal entities. Fields typically have text, image, or terms.
  9. Node – A piece of content in Drupal that has a title, an optional body, and perhaps other fields. Every node belongs to a particular content type (see above), and can be classified using the taxonomy system. Examples of nodes are polls and images.
  10. Views – This refers to a module that allows you to click and configure the interface for running database queries. It can give the results in many formats.
  11. Views Display – A views display is created inside of a view to show the objects fetched by the view in a variety of ways.
  12. Module – A code that extends Drupal features and functionality. Drupal core comes with required modules, some of which are optional. A large number of “contrib,” or non-core, modules are listed in the project directory.
    • Core- has features that are available within Drupal by default
    • Custom- a module that is custom developed for a purpose that may not be available within the core system.  
    • Contributed- A module that is made available to others within the Drupal community after it was created as a custom module. There are more than 40,000 modules available today.

See Part 2 and Part 3 for more.  Any other questions? Check out Drupal.org!

Jul 12 2013
Jul 12

It is sometimes necessary to test a piece of functionality which involves a taxonomy term. For example, we often use taxonomy terms to classify content. Thankfully, it is possible to attach taxonomy terms to nodes in Behat, if we are careful.

A Simple Use Case

Let's assume a simple example, with a content type called "Document". This content type has a file field to attach a downloadable document, and it also has a taxonomy term reference field, which is used to classify the document. For example, we may mark this document as a "Brochure", or an "Ebook". These two taxonomy terms are part of the same vocabulary called "document_type."

Now, it is useful for our users to visit a page with a view listing all of the document nodes, with an exposed filter on the taxonomy term. This way, users can click to sort the document nodes, and see only nodes tagged with "Ebook" or "Brochure".

Implementing the Test

The trick lies in explicitly creating the taxonomy terms before we create the nodes in our tests. If we try to create the taxonomy terms by simply populating the field with the term value on the node, it will not effectively be attached. We must do both:

  Given "document_type" terms:
    | name     |   
    | eBook    |   
    | Brochure |

  Given "document" nodes:
    | title                   | body                     | field_document_type |
    | An Interesting eBook    | This is a great book!    | eBook               |   
    | An Interesting Brochure | This is a nice brochure! | Brochure            |   

Scenario: Filter resources on documents
    Given I am on "document-view-page"
    When I select "Brochure" from "Select the Type of Document"
    And I press "Search"
    Then I should see the link "An Interesting Brochure"
    And I should not see the link "An Interesting eBook"

If we were to only create the document nodes, the test would throw no error, but our taxonomy terms would not actually be created. Therefore, when we try to use our filters, the term will not be in place, and the test will fail.

May 17 2013
May 17

Episode Number: 


In this episode we continue learning about Drupal Commerce and begin learning how to set up Drupal Commerce Product Attributes using Drupal Taxonomy.

In this episode you will learn:

  • How to set up Drupal taxonomy vocabularies and taxonomy terms to use as product attributes.
  • How to add Taxonomy term reference fields to Product types to create Drupal Commerce product attributes.

DDoD Video: 

Nov 28 2012
Nov 28

In this lesson, we use the taxonomy that we have created for our videos to create a view of our videos, with an exposed filter that lets users limit the view by taxonomy terms.

Ooyala is a paid video delivery service, which manages your videos and handles video delivery to your site. You will need to have an Ooyala account in order to use this module on your Drupal site.

Nov 28 2012
Nov 28

In this lesson, Andrew will walk you through how to use Drupal taxonomy terms with your videos, and to also, importantly have that information synced with your videos in the Ooyala Backlot.

Ooyala is a paid video delivery service, which manages your videos and handles video delivery to your site. You will need to have an Ooyala account in order to use this module on your Drupal site.

Oct 01 2012
Oct 01

Posted Oct 1, 2012 // 0 comments

Recently, I revisited the publishing system we built for Thomson-Reuters' London Olympics coverage, one of the features I reviewed was the taxonomy processing aspects of the content ingestion engine. we built this to take in content feeds from Reuters' wire service content management and routing system. When you are in the weeds of building out a system, it's hard to appreciate the complexities of the systems that you are building. It was illustrative to return to the site months after we launched it and gain a deeper appreciation for the challenges we faced in building out the publishing engines that processed thousands of assets per day throughout the duration of the games.

The application of the taxonomies was a multi-layered process that progressively applied terms to the article nodes in several distinct steps:

  • Sports codes (example: "Athletics", or "Basketball") were parsed out of a series of tags in the article XML and matched against Sport records pulled from the third-party Olympic data provider.  When the Sport records were imported during development and the database populated with Sports and Events, the standard Olympic codes were included, and it was these that were mapped to.
  • In some cases, the codes were mapped instead against a local table of alternative Sport codes used internally by photographers to ensure that these alternative publishing paths would result in equivalent mappings.
  • Events also included in the tags within the XML, but not always.
  • The slugline was crafted to include sport, event, and match information, although only the match information was parsed out.
  • Athlete associations were applied by passing the text elements - title, caption, article body, summaries - through Thomson-Reuters' OpenCalais semantic tagging engine, and pulling 'people' terms from their library of terms. If there were any matches between the person references returned and the Athlete records created from the Olympics data associations with the Athletes, then they were applied.
  • Countries were NOT pulled using OpenCalais, although those mappings were available - the concern was that there would be far too many false-positives applied for Great Britain, given that nearly every article contained references to the host country.  Instead, if Athlete associations were obtained, we queried the Athlete record for the Country with which they were affiliated, and applied that reference to the article.

Although there were aspects of this process that were worked out as requirements changed and evolved, (in particular, it was discovered relatively late that photographers were using an alternate standard for sports tagging,) the system was ultimately successful because we had mapped out the process well before beginning development. We understood the complexities inherent in Reuters' content model.

It seems elementary that these things would be worked out ahead of time, but requirements evolve, and sometimes you just have to roll with the changes in order to ensure the success of the project.  What makes this process successful is a successful content strategy.

Data Informs Process

We had many sessions where we discussed potential ways of mapping data into the system, and there were a number of alternatives that were rejected because there were potentially too many holes in the processes of managing the data.  Make sure you get a look at production-level data as soon as possible in the project, and make sure your technical leads have a chance to work through any potential issues with decoding and processing the data.  If you can see ahead of time, that there are basic compatibility issues between what should be relatable data points in different third-party data feeds, then there is still time to get alterations made to the data, and, failing that, devising work-arounds, alternative mappings, or transformations using contextual clues in the data.  

Additional processing steps can be applied to handle systemic issues - as we did by using OpenCalais to gain athlete association before using athlete to create country associations.  Semantic tagging can be used to handle other cases where you know that a key piece of information might be missing from the original article, but an educated guess can be made as to what it is by seeing what subjects and terms are pulled through the parsing.  For example, if a set of articles are missing top-line mappings to sections within a larger news site, using OpenCalais or a similar technology can tell you that topically, it produces the strongest associations within particular vocabularies. References to sports teams and athletes would indicate that it should be a sports article, and references to members of Congress would place it within a politics vertical. 

Sometimes it's simpler to accept that weaknesses in the data, can be more easily handled by empowering the client with smart tools or smarter business processes.  If the problems can be isolated to an easily identifiable subset of content, these particular articles might be routed to a group of editors whose purview would include handling remapping the missing meta data.  If you know that there are systemic weaknesses in how taxonomies are applied in general - you know that a certain percentage of articles will as a matter of course, be missing terms. You can work the creation of more sophisticated taxonomy management tools into the budget to allow editors more immediate access to the taxonomy.  If your stakeholders decide that the incidence of bad data can be best solved by leaning on their editors and writers to use their internal taxonomies consistently and correctly, they'll start laying the law down as soon as you determine with them that this is the most efficient and promising route to better online content.

Try to Break the Content

Key to all of this is is the conversations you have with the client, where you work through their publishing workflows, sources of data, and the intersections between the two.  This needs to go beyond gathering requirements and documenting user stories - you need to try to break the system.  Brainstorm about worst-case scenarios.  Let the client talk about their worst fears regarding the system.  Poke holes in their ideas.  Let them challenge you on yours, and be prepared to walk through any implementations you have in mind.  You'll be much better prepared for the unexpected if you try to narrow down the possibilities for what that might be.   

Solutions Architect Joshua Lieb has a deep knowledge of web development, content management, and programming thanks to his more than than 15 years of web development experience in the government, publishing, and NGO sectors. His proven ...

Jun 25 2012
Jun 25

Posted Jun 25, 2012 // 0 comments

Apache Solr is one of the great solutions for providing search functionality to one's site and there are many modules for Drupal that provide the functionality. The Search API and ApacheSolr modules are two great examples of such. I became a huge fan of the options that Search API provided until I ran into one major roadblock... the site(s) I was implementing search for, was hosted on Acquia and using Acquia's Solr service, which only supports the ApacheSolr module.

So, what's the drawback? Well, out of the box ApacheSolr only supports nodes for indexing. Taxonomy and entities are ignored, which for many of the requirements for my sites in progress was necessary. Was it the end of the world? Not at all. After a little research I found that taxonomy and other entities could be added for indexing by the simple implementation of hook_apachesolr_entity_info_alter.

Though I'm not directly demonstrating it, taxonomy can be included as well as Drupal (for the most part.) The taxonomy is treated like an entity.

Here's an example of using the hook to add some custom photo and text entities for indexing:

 * Implements hook_apachesolr_entity_info_alter().
 * Adds Text and Photo items to the search index.
function tr_search_apachesolr_entity_info_alter(&$entity_info) {
  $entity_info['text_item']['indexable'] = TRUE;
  $entity_info['text_item']['status callback'] = 'tr_search_status_callback';
  $entity_info['text_item']['document callback'][] = 'tr_search_solr_document';
  $entity_info['text_item']['reindex callback'] = 'tr_search_solr_reindex_text_item';
  $entity_info['text_item']['index_table'] = 'apachesolr_index_entities_text_item';
  $entity_info['photo_item']['indexable'] = TRUE;
  $entity_info['photo_item']['status callback'] = 'tr_search_status_callback';
  $entity_info['photo_item']['document callback'][] = 'tr_search_solr_document';
  $entity_info['photo_item']['reindex callback'] = 'tr_search_solr_reindex_photo_item';
  $entity_info['photo_item']['index_table'] = 'apachesolr_index_entities_photo_item';  

Within that hook we are saying that we wish the two new entities to be included for indexing.

The structure of the $entity_info array is a series of sub-arrays keyed by the entity bundle name followed by a series values for the appropriate callbacks for each of the new entities.

For those elements, let's step through what each means with an example of a callback function.

  • "Indexable" - indicates to ApacheSolr that the entity can be indexed and exposes the entity within the ApacheSolr configuration pages in Drupal's admin.
  • "status callback" - indicates that the entity is enabled and available in the system
     * Callback for the search status. Since all text and photo items are 'published', this is always true.
    function tr_search_status_callback($term, $type) {
      return TRUE;

    In this case, we are just returning TRUE, but more advanced logic could be implemented to indicate the status pending advanced needs.
  • "document callback" - callback to provide the content/values that will be sent to Solr server for indexing.
     * Builds the information for a Solr document.
     * @param ApacheSolrDocument $document
     *   The Solr document we are building up.
     * @param stdClass $entity
     *   The entity we are indexing.
     * @param string $entity_type
     *   The type of entity we're dealing with.
    function tr_search_solr_document(ApacheSolrDocument $document, $entity, $entity_type) {
      // Headline
      $document->label = apachesolr_clean_text((!empty($entity->field_tr_headline[LANGUAGE_NONE][0])) ? $entity->field_tr_headline[LANGUAGE_NONE][0]['value'] : '');
      // Credit
      $document->ss_credit = (!empty($entity->field_tr_credit[LANGUAGE_NONE][0])) ? $entity->field_tr_credit[LANGUAGE_NONE][0]['value'] : '';
      // Slug
      $document->ss_slug = (!empty($entity->field_tr_slug[LANGUAGE_NONE][0])) ? $entity->field_tr_slug[LANGUAGE_NONE][0]['value'] : '';
      $document->tus_slug = (!empty($entity->field_tr_slug[LANGUAGE_NONE][0])) ? $entity->field_tr_slug[LANGUAGE_NONE][0]['value'] : '';
      // Content
      if ($entity_type == 'photo_item') {
        $document->content = apachesolr_clean_text((!empty($entity->field_tr_caption[LANGUAGE_NONE][0])) ? $entity->field_tr_caption[LANGUAGE_NONE][0]['value'] : '');
      else {
        $document->content = apachesolr_clean_text((!empty($entity->field_tr_text_body[LANGUAGE_NONE][0])) ? $entity->field_tr_text_body[LANGUAGE_NONE][0]['value'] : '');
      $documents = array();
      $documents[] = $document;
      return $documents;

    Pretty much checking the entity fields that we want to send we capture their values as properties of the solr document object and return
  • "reindex callback" - callback to provide instructions for when the content is being re-indexed in the system
     * Reindexing callback for ApacheSolr, for text items
    function tr_search_solr_reindex_text_item() {
      $indexer_table = apachesolr_get_indexer_table('text_item');
      $transaction = db_transaction();
      $env_id = apachesolr_default_environment();
      try {
          ->condition('entity_type', 'text_item')
        $select = db_select('text_item', 't');
        $select->addField('t', 'eid', 'entity_id');     
        $select->addExpression(REQUEST_TIME, 'changed');
        $insert = db_insert($indexer_table)
          ->fields(array('entity_id', 'changed'))
      catch (Exception $e) {
        watchdog_exception('Apache Solr', $e);
        return FALSE;
      return TRUE;
  • "index_table" - the name of the database table that ApacheSolr will locally store references for.
    This table is one that you define in a *.install file using drupal's schema api. The table structure should mimic the same structure that ApacheSolr already defines for nodes.
     * Implements hook_schema().
    function tr_search_schema() {
      $types = array(
          'text_item' => 'apachesolr_index_entities_text_item',
          'photo_item' => 'apachesolr_index_entities_photo_item',
      foreach ($types as $key => $type) {
        $schema[$type] = array(
          'description' => t('Stores a record of when an entity changed to determine if it needs indexing by Solr.'),
          'fields' => array(
            'entity_type' => array(
              'description' => t('The type of entity.'),
              'type' => 'varchar',
              'length' => 128,
              'not null' => TRUE,
              'default' => $key,          
            'entity_id' => array(
              'description' => t('The primary identifier for an entity.'),
              'type' => 'int',
              'unsigned' => TRUE,
              'not null' => TRUE,
            'bundle' => array(
              'description' => t('The bundle to which this entity belongs.'),
              'type' => 'varchar',
              'length' => 128,
              'not null' => TRUE,
              'default' => $key,
            'status' => array(
              'description' => t('Boolean indicating whether the entity is visible to non-administrators (eg, published for nodes).'),
              'type' => 'int',
              'not null' => TRUE,
              'default' => 1,
            'changed' => array(
              'description' => t('The Unix timestamp when an entity was changed.'),
              'type' => 'int',
              'not null' => TRUE,
              'default' => 0,
          'indexes' => array(
            'changed' => array('changed', 'status'),
          'primary key' => array('entity_id'),
      return $schema;

Using this base set of code you can be able to add any drupal entity to ApacheSolr for indexing. Not everything demonstrated may conform to ones exact needs but, the premise remains the same in allowing entities not supported out of the box to be added to search indexes.

(A hand to Brad Blake for contribution in the code as well)

Geoff is a developer at Phase2 who originally studied computer science in college before switching to culinary arts. Thus began a career that led Geoff to become an executive chef for different restaurants in Columbia, SC.

After close ...

Apr 13 2012
Apr 13

New Feature in Canopy - Taxonomy

Posted on: Friday, April 13th 2012 by Ken So

Do you know what Canopy is? If not click here to find out more.

Canopy by Appnovation integrates a world class Open Source document repository application with the highly scalable and popular Open Source website development tool allowing for the best of both worlds. Alfresco compliments Drupal by providing a feature rich UI for managing web assets, as well as providing full document and knowledge management for entire organizations. Drupal as the frontend provides Alfresco with a highly flexible presentation layer that can be used to build websites that would be more or less impossible with Alfresco alone.

We have been expanding Canopy's features and today I will be talking about categorisation.

Both Drupal and Alfresco are great individually with categorisation, but together they're even better. We needed a way to sync taxonomy terms for Drupal and Categories in Alfresco. We achieve this by providing RESTful API between the applications so that each will know when there are new terms to be added in the system.

In Drupal, we provided hooks to pre-existing taxonomy functions by adding submit handlers that, when triggered, will send the data to a custom Alfresco webscript.

Here is a small snapshot of the code


 * Implementation of hook_form_alter
function alfresco_taxonomy_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'taxonomy_form_vocabulary':
    $form['#submit'][] = 'alfresco_taxonomy_form_vocabulary';
  case 'taxonomy_vocabulary_confirm_delete':
    $form['#submit'][] = 'alfresco_taxonomy_vocabulary_confirm_delete_submit';
  case 'taxonomy_form_term':
    $form['#submit'][] = 'alfresco_taxonomy_form_term';
 * Alfresco taxonomy submit function
function alfresco_taxonomy_form_term(&$form, $form_state = array()) {
  module_load_include('inc', 'alfresco_connector', 'includes/alfresco_connector.alfresco_api');
  module_load_include('inc', 'alfresco_connector', 'includes/alfresco_connector.helpers');
  $values = $form_state['values'];
  $alfresco_endpoint = variable_get(AFRESCO_CONNECTOR_ENDPOINT_KEY, '');
  $alf_ticket = alfresco_connector__alfresco_api_get_api_ticket();
  if(empty($alf_ticket)) {
    return NULL;
  switch ($values['op']) {
    case 'Save':
  $url = $alfresco_endpoint . $path . "?alf_ticket=$alf_ticket";
  $boundary = 'A0sFSD';
  $method = 'POST';
  $headers = array(
    "Content-Type" => "multipart/form-data; boundary=$boundary",
  $params = array(
    'tagName' => $values['name'],
  $data = wl_alfresco_connector__helpers_multipart_encode($boundary, $params);
  $response = drupal_http_request($url, $headers, $method, $data);


// See if the tag already exists
tagExists = taxonomyCheckTagExist(store, tagName);
if (!tagExists) {
  tag = taggingService.createTag(store, tagName);
  if (tag == null) {
    model.result = FAILURE_MESSAGE;
} else {
  tag = tagName;
  model.response = ERROR_1000_DUPLICATE;
// Put the created tag into the model
model.tag = tag;
model.tagExists = (tagExists) ? "TRUE" : "FALSE";
model.tags = taggingService.getTags(store);

This just one of the neat features that we are working on with Appnovation's Canopy. We are always updating and creating new features that will benefit our clients.

Happy Coding!

Dec 19 2011
Dec 19

Sometimes it is helpful to add custom information to a taxonomy term.

In our case we want to have a unique homepage for each taxonomy term instead of the default “/taxonomy/term/blahblahblah”  so we use the “Term Fields” module which allows us to add fields to taxonomies much like cck with nodes.  It also has the advantage of working with drupal views.. so its kind of a no brainer… here’s a quick tutorial on how to add links to our content taxonomies here at the Health Sciences library

Jan 21 2011
Jan 21

I'm working on a major Drupal (6) site migration now, and one of the components that needs to be automatically migrated is taxonomies, mapping old vocabularies and terms to new ones. Core taxonomy stores node terms in the term_node table, which is fairly easy to work with. However, two taxonomy supplements are making the process a little more complicated:

First, we're using Content Taxonomy to generate prettier/smarter taxonomy fields on node forms. The module allows partial vocabularies to be displayed, in nicer formats than the standard multi-select field. The problem with Content Taxonomy for the migration, however, is that it duplicates the term_node links into its own CCK tables. If a node is mapped to a term in term_node, but not in the Content Taxonomy table, when the taxonomy list appears on the node form, the link isn't set.

Ideally, the module would have the ability to re-sync from node_term built in. There's an issue thread related to this - Keep core taxonomy & CCK taxonomy synced - but it's not resolved.

So I wrote a Drush command to do this. To run it, rename "MODULE" to your custom module, backup your database, read the code so you understand what it does, and run drush sync-content-taxonomy --verbose.

Warning: This code only works properly on *shared* CCK fields, that is, fields with their own tables (content_field_X tables, not a common content_type_Y table). Don't use this if your fields are only used by one content type.

The other taxonomy supplement that needs to be migrated is Primary Term. I'll be writing a similar Drush script for this in the next few days.

Update 1/27: There was a bug in the way it cleared the tables before rebuilding, should be good now. (Make sure to download the latest Gist.)

Aug 11 2010
Aug 11
Drupal Blog - Tags and Categories

My old blog used Wordpress, and I wanted to carry over Wordpress's categorization methods into Drupal.

There are two kinds of taxonomies I wanted to add:

  1. A heirarchy of categories. This is like a table of contents.  It lays out the subject matter of my blog.
  2. General tags.  I want to describe each blog post with tags I make up on-the-fly.

To cover both, I created two vocabularies.  (A vocabulary is a set of terms).  To create a vocabulary:

  1. Click the Administer link.
  2. Content management.
  3. Taxonomy.
  4. Click the Add vocabulary tab.

I called the first vocabulary "Categories" and applied them to my blog posts under Content Types.  Click save.

Click "Add terms."  Try to create a good hierarchy of terms.  Since my blog only has a few Drupal-related posts at the time of this writing, I went with;

Web Development
--> Drupal 6

Remember that you can click and drag items to create subcategories, one under another.  As the blog grows, I'll probably post other articles on subjects such as PHP, Javascript and MySQL.

To create the tags taxonomy, return to the main Taxonomy page and follow the same steps.  But this time, under settings, select Tags.

Instead of adding terms under Tags, I chose to edit my previous blog posts and add tags for each.

Jun 29 2010
Jun 29
Printer-friendly versionPDF version

Recently I had to plan the unenviable task of adding terms from a new taxonomy category to dozens of nodes that had previously been created on a site. As I pondered my options (including a temporary Caribbean vacation) I remembered module I had seen pop up on Drupal.org called Views Bulk Operations or VBO for short. 

What VBO allows you to do create a view that will apply an action to many nodes at one time. So you must have the Views module installed and it helps to know your way around views a little bit because after installing and enabling VBO you'll need to create a Node view that will allow you to create a list of the nodes that you want to apply actions to.

The view that I created to make the taxonomy updates to the nodes in question had a pair of fields that allowed me to identify the nodes to update. Those fields for Title and Type, as in node type. This was just enough info to know for sure whether or not I wanted to make a change to the node. I added to the view a couple of exposed filters that would allow me to narrow down the list of the nodes by selecting the node type and a string of text from the title. The image below shows you what the finished product looks like.

So with this layout using exposed filters I can then make a couple of choices to filter the list down to the nodes I want to change. Then in the actions to take section I can indicate what I want to do by selecting the appropriate radio button. Then I can select one or more of the vocabularies in order to make that change. In my case I had added a whole new vocabulary with new terms so I chose Add the selected terms and then selected the vocabulary term that I wanted to apply (in this case from the Node Type vocabulary) before selecting the nodes (either one by one or by clicking the box next to Title to select all) then clicking the button (not shown in the image) at the bottom of the page to apply the update. At the end of the process you should see a confirmation that the nodes were updated. At that point you should double check a few of the nodes to make sure the updates were applied as you intended them.

Here's a little more about how I got to the end described above. The image below shows where you would select the style of Bulk Operations which is required for your view to offer you the correct options. Take note that the type of display you're looking for is one of Page. One more important thing to notice is the Access setting. Since this is the kind of function you would only want available to special users make sure you have Access set in a way that limits the ability to get to the page. In my case the user must have the permission to administer content types.


The image below shows the many options for the Bulk Operations style choice. Notice that I chose Modify node taxonomy terms as the only option. I don't know enough about the module yet to choose multiple options at once so I just stuck with the one.

The image below is what the preview looked like before I saved and attempted to use the view for the first time. Take note once again of the three elements. 

  1. Exposed filters which allowed me to narrow down the list of nodes
  2. The actions to take which should be consistent with my Bulk Operations settings choices
  3. The fields, Title and Type, which allow me to identify whether or not these are the correct nodes to take action on

Okay, so now you're ready to do some damage (in a good way of course) and make big changes to your nodes in a very short period of time. A final word of caution though. I did all of this on a pre-production site so if I did make a mistake it wouldn't affect site users. If you are trying VBO out for the first time I recommend doing so on a test site so that you can get the hang of what you need to do without having to back out major changes. Of course, if you make the wrong changes using VBO you can always back those changes out....using VBO.

Video Links

YouTube Version

Flash Version

Quicktime Version

Mar 19 2010
Mar 19

A recent request from a client was to improve the Views 2 exposed taxonomy filter so that it intelligently hides taxonomy terms which aren't related to any of the content returned by the View query. As anybody who has spent time poking about in Views vast configuration panel will know, this isn't possible through the existing Taxonomy handler.

Stackoverflow.com had a few posts from users requesting the same functionality, and replies from others saying it wasn't possible with Views 2. Well the good news is: it is possible, right now, without hacking Views.

For our specific project then taxonomy list was to only list the terms which related to the nodes returned by the initial (un-filtered) View display query. Once taxonomy terms are selected and the filter applied the taxonomy term list still needs to show a list of terms from the original query of nodes, not the currently filtered list. This prevents the taxonomy term filter from slowly shrinking down without providing a means to go back to other taxonomy terms.

The solution was to take the default taxonomy filter handler in Views core and modify it as a replacement handler.

To make the traditional multiple select list more user friendly (no CTRL click rubbish) we added a layer of jquery thanks to the Simple Multi Select plugin and some CSS in the sites theme to show tick box images next to the selected items.

The developed code is now available on drupal.org. Be sure to pay attention to the side effects of using this filter.

Mar 10 2010
Mar 10

One of the issues I encountered when migrating nodes to Drupal, using the migrate module, was that I couldn't associate nodes with more than one taxonomy term. Actually in this example, I'm migrating content from one Drupal database to another, so I'm going to assume everyone is already familiar with the database structure, specifically the node and term_node tables.

When I first started using the migrate module, I ran into a similar problem with migrating a user's roles. It's not possible to just create a Views relationship (aka LEFT JOIN) between the node and term_node tables using the node id. This will produce one row for each node and taxonomy combination, but the migrate module is only able to handle data sets that contain one row for each entity. With the above solution, I have more than one row for each node, which causes the migrate module to import the same node more than once, causing all sorts of problems.

Like with the user roles example before, we can overcome this by implementing a migrate hook, specifically hook_migrate_prepare_node().

* Implements hook_migrate_prepare_node().
function mymodule_migrate_prepare_node(&$node, $tblinfo, &$row) {
$vocabs, $source_vocabs;
$errors = array(); // Get a list of vocabs in our target database.
if (empty($vocabs)) {
$result = db_query("SELECT vid, name FROM {vocabulary}");
    while (
$vrow = db_fetch_object($result)) {
$vocabs[$vrow->name] = $vrow->vid;
// Set up per-node type specific stuff.
$node_vocabs = array();
  switch (
$node->type) {
// Here the 1 and 0 identify which are free-tagging vocabs and which aren't.
$node_vocabs = array('Regions' => 0, 'Keywords' => 1, 'Topics' => 0);
$node_vocabs = array('Keywords' => 1, 'Topics' => 0);
// I have 2 database connections defined in my settings.php.
  // This statement allows me to use the source Drupal database for subsequent queries.
db_set_active('old_drupal_db'); // Get vocabs from our source database.
if (empty($source_vocabs)) {
$result = db_query("SELECT vid, name FROM {vocabulary}");
    while (
$vrow = db_fetch_object($result)) {
$source_vocabs[$vrow->name] = $vrow->vid;
// Map each node to its taxonomy terms.
if (!empty($node_vocabs)) {
    foreach (
$node_vocabs as $vname => $tags) {
$terms = array();
$result = db_query("SELECT d.name FROM {term_data} d, {term_node} n WHERE n.tid = d.tid AND n.nid = %d AND d.vid = %d", $row->nid, $source_vocabs[$vname]);
      while (
$term = db_fetch_object($result)) {
$terms[] = $term->name;
// Depending on whether it's a free-tagging vocabulary or not, the terms are stored slightly differently.
$vid = $vocabs["$vname"];
$vid_key = 'migrate_taxonomy_' . $vid;
      if (!empty(
$terms)) {
        if (
$tags) {
$node->$vid_key = '"' . implode('"' . $tblinfo->multiple_separator . '"', $terms) . '"';
        else {
$node->$vid_key = implode($tblinfo->multiple_separator, $terms);
// Switch back to using the default, aka target, Drupal database.



Note, for each vocab for a node type, I identify whether or not it's a free-tagging one or not. This is because I handle them slightly differently because of the way taxonomy module treats them, and to avoid problems with commas and quotes within terms, etc. In addition, there can be problems if you use commas as your separator, so when creating the content set I set the multiple separator ($tblinfo->multiple_separator) to be a pipe |.

Nov 11 2009
Nov 11

Having built two fairly robust and strongly interlinked taxonomies to aid in categorization, SEO and navigability of my site, I decided I wanted one of those (old school) tag clouds to display my lexicon. After waiting for the enormous drupal modules page to load, I decided the best route to follow would be to use Tagadelic, though it appears that I may have to use helper modules to get the fully customizable effect I am looking for.

According to the module description found on the module page of your site: Tagadelic makes weighted tag clouds from your taxonomy terms.

For this tutorial I will be running through the installation, setup and general access to the Tagadelic module (version 6.x-1.2). I am assuming that you have the Core - optional module Taxonomy installed and that you have at least one multi-word taxonomy in place and that it has been used to tag a handful of nodes.

Here we go:

  1. Check your Status Report

    You should have a clear status report (Administer > Reports > Status report) before continuing with these installation instructions.

  2. Download the Module

    Make sure that you download the latest version of the Tagadelic module for your distribution: Tagadelic - http://drupal.org/project/tagadelic

    Once you have downloaded the module, move it to the appropriate modules folder for your install and extract it in place.

  3. Read the README

    Tagadelic is a small module, without any databases, or configuration, that generates pages with weighted tags. Tagadelic is both an out of the box, ready to use module, if you want simple weighted tag clouds. With, or without some small CSS moderations this will probably suit most cases. But tagadelic is an API system too. You can pass your queries to the APIs and get weighted clouds of virtually anything: clouds by amout of views, clouds for a certain user, etceteras. The module does not do all this, but you can make it do that, with quite little coding.

  4. Enable the Module

    From the Modules page (Administer > Site building > Modules), enable the tagadelic module. As a site administrator, run update.php.

  5. Configure Tagadelic Module

    Configure the Tagadelic module (Administer > Site configuration > Tagadelic configuration) to set defaults for the tagadelic views. The following values can be set:

    Tagadelic sort order: This will be the sort order used in all Tagadelic displays. The options are as follows: 'by weight, ascending'; 'by weight, descending'; 'by title, ascending'; 'by title, descending'; and 'random'. The default value for this setting is 'by title, ascending'.

    Amount of tags on the pages: The amount of tags that will show up in a cloud on the Tagadelic page. Amount of tags in blocks must be configured in the block settings of the various cloud blocks. The default value for this setting is 60 tags.

    Number of levels: The number of levels between the least popular tags and the most popular ones. Different levels will be assigned a different class to be themed in tagadelic.css. The default value for this setting is 6 levels.

    Choose to 'Save the configuration' or 'Reset to defaults' if mistakes have been made.

  6. Review Pages and Blocks

    After installation and configuration, you can review the results of your labors which is a page and two blocks that approach the idea of a tag cloud.

    Pages: First off is to go to the Tagadelic page found at yoursite/tagadelic (http://baxwrds.com/tagadelic/). This page lists all the tags in all of your vocabularies. Through the use of URL arguments you can also list the tags only in single vocabulary (http://baxwrds.com/tagadelic/chunk/1) or from multiple vocabularies (http://baxwrds.com/tagadelic/chunk/2,1) additionally vocabularies can be listed in groups (http://baxwrds.com/tagadelic/2,1).

    Blocks: Two types of blocks are created by the Tagadelic module. Firstly a block is created for each of your vocabularies and will be named: Tags in vocab name. Inside the block configuration you can choose the amount of tags to show. If not all tags in the vocabulary are visible a more link will also be present to take the user to the main tagadelic page. An example of these blocks can be seen on the right. The second type of block is 'Tags in the current post' and lists the tags of the current post. Due to the horrible formatting of this block I have chosen not to display it.

There you have it, examples and all. It is a descent module and does what it says, but it does lack documentation. Two resources I found useful were: http://www.webschuur.com/modules/tagadelic and http://drupal.org/handbook/modules/Tagadelic. Peace.

Nov 03 2009
Nov 03

Play Video

Movie link (right-click to download) Problem with this video? Contact me

While trying to think of what to write, regarding Drupal taxonomy, it was my kids’ story
last night that inspired this posting about this video covering how the taxonomy module works and what it means to your Drupal site.

Taxonomy, taxidermy, category, road kill
let’s play a matching game,
and then increase our Drupal skill.

Spend some time with synonyms,
take a break, eat M-n-M’s,
return to Content Taxonomy,
help Drupal fix the economy.

We want a killer Drupal site,
without the pain and UI blight
of something ugly, hard to use,
so let’s all learn Taxonomy and Views!

I’ve got this video
right here for you,
it covers Vocabularies
and then Terms too.

It’s great to learn what can be done,
now let’s all go and have some fun,
being addicted to Drupal stuff,
which really isn’t all that tough.

Wow! that’s not something I normally do when writing about some silly Drupal video tutorial designed to help you learn more about Drupal and how Drupal Taxonomy works. But, hey, it was fun!

Oct 20 2009
Oct 20

What is the semantic Web?

I seem to be asked this question on a daily basis mostly in reference to Google now using RDFA and Drupal 7 efforts to put RDFA in Drupal Core (iO1 is sponsoring ;-). The answer is not a simple one liner so I am constantly struggling to come up with a concise unifying statement that allows me to explain it to anyone. No luck yet so here's my current best effort at it.

This is a VERY top level and simplified explanation that is not written as a proper technical introduction but merely as something that will help non technical people get their head around the concepts. It is missing chunks of the semantic web on purpose.

The Semantic Web
Lets start with trying to get you thinking in a way that will help drive your interest deeper Have a look at this list http://sindice.com/map and imagine all the ways in which you could use all the data from these sites if you could just link together it in a useful manner and query it in an intelligent manner. The solution to how to create whatever your mind just came up with is the semantic web.

At the moment we have a web of documents that are linked via hyperlinks. While this has been great in getting the web started it extremely limiting in terms of being able to develop intelligent computing solutions to common problems. This happens because all the data ends up in silo's with no way of properly expressing the links between the information.

The semantic web allows us to adds meaning to links between "stuff" on the web, (stuff can include the documents themselves or people) and add additional machine processable data to "stuff". Critical to the concept of the semantic web is the notion of being able to run a query against the web in the way that you would run a query on a Database in house. As you can see from the list above we are moving towards a web of data with more and more people publishing there information is usable formats.

Obvious problems that this approach can solve:

Scenario 1)

  • The BBC run a front page news article linking to a site www.we-are-scammers.me saying that this site is known to be scammer and no-one should use them.
  • Obviously other people will now run with the story and start to post similar links to www.we-are-scammers.me.
  • Google then gives a massive boost to the credibility level of site www.we-are-scammers.me because of all the links and now this site can rank for almost any keyword it wants in Google serps.

Result - Users being put in danger

Solution : Give the BBC ways of expressing in a computer readable manner that it thinks this is a bad link (and before anyone asks nofollow does not work in this scenario)

Scenario 2)

  • A search engine reads this paragraph:
    • I go out for a stroll on a breezy day and as I wind my way along I feel the wind in my face. This sets my mind thinking about how to improve the way that people wind up companies when they fall on there face? Recently I was involved in the wind up of a company that was involved in making gears that make it easier to wind up clocks. I felt that one of the directors was trying to wind the rest of the board of directors up so that they would quit and that this was the real reason why communications failed.
  • How does it work out what the six instances of "wind" mean?
    • If you google "wind" the entire first page (for me at least) is make up of 8 separate links to "wind" in the weather context and two links to products/services that are named "wind".

Result : Search engines return your article to the wrong people Solution: Use a semantically enabled cms that allows you to define what each instance of "wind" means using ontological references so that you help the search engines to understand what you meant.


  • Twitter becomes massively popular
  • I follow a few hundred people
  • Some of these users "occasionally" tweet something useful but in the main publish links to crap or Re tweet people I am not interested in listening to

Result : I stop following them as there is no way of simply filtering the tweets that are directly relevant to what I am interested in and I feel that I am missing genuinely interesting information due to the being swamped with to many useless tweets

Solution: Using RDFA, FOAF and SIOC I can create a client that intelligently drops stuff that is obviously not of interest to me and auto filters based on:
a) Topic of tweet (RDFA)
b) Relationship between me and the person and / or the person they are retweeting (FOAF)
c) The relationship between that person and the content (SIOC)

There are millions of incredibly useful semantic applications that are suddenly becoming feasible as commercial projects due to the vast amounts of data that are now starting to be published. Just using dbpedia itself has huge commercial opportunities. I think that we are at the beginnning of the end of the beginning for the semantic web and that next year will be a year when it really starts to flourish.
Some Links I hope you will find use full:

Intro to semantic web

Tim Berners Lee talking about the semantic web

Tim Berners Lee on ted

RDFA Intro

Just a great site on whats happening ihe he sntintic world:

Great list of videos on the semantic web

Interesting Article on cross pollinan

Bookmark/Search this post with

Oct 06 2009
Oct 06

Lately I've been helping out a web development team at BYU develop a new calendaring system in Drupal to replace our existing all-in-one calendar. It's an ambitious project that's leveraging quite a few different modules from the Drupalsphere.

Some of the features include:

  • Import ical feeds.
  • Filter events for display by event category
  • Download customized ical feeds by category (i.e. Student wants to import just sport and academic events into his personal calendar).
  • Hovering on event titles exposes tooltip (we're using Beautytips) with full event info

Here's a screenshot of the UI as of last night:

I wanted to blog about how we created our ical exports by category as I couldn't find any documentation about this and it doesn't work out of the box.

The default view from Calendar includes an ical display. This, by default, filters ical feeds by date -- e.g. only include events in the ical feed after a certain date. This works fine.

The problem we ran into is how to filter the ical feed by taxonomy terms. In Views we created a taxonomy term filter and exposed that to users so they can created custom calendar views of different event categories. That worked wonderfully. The problem was that when you downloaded the ical feed from the filtered calendar view, it included all events, not just events from the categories you'd selected.

After examining the url for the ical feed we found the problem.

The default ical URL that's generated looks like this:


Views takes each url segment separated by a forward slash as arguments. calendar/ical tells views to return an ical representation of the calendar. 2009-10 tells views to only include events from October 2009 onward. But the next bit, ?tid[2][2]=2&tid[1][1]=1&tid[3][3]=3&tid[4][4]=4&tid[5]=5&tid[6][5]=6&tid[7][6]=7, a list of term IDs that were currently active on the calendar view, weren't working at helping Views produced a filtered ical feed.

Digging into the taxonomy term argument, I discovered it expected arguments in the form of 1+2+3 or 1,2,3. 1+2+3 meaning Views would return events that had terms 1 OR 2 OR 3 and 1,2,3 meaning Views would return events that had terms 1 AND 2 AND 3. For example, aiocalendar/calendar/ical/2009-10/1+4+6 would return an ical feed of all events from October 2009 onward that had term ids of 1 or 4 or 6.

I jumped to the command line and tested my theory by using wget and some handcrafted urls and it worked! The ical feeds I was getting only included events from categories I included as arguments in the URL.

Now the trick was to override the ical url generated to replace it with one in the correct format. I found the theme function in the Calendar module where the the ical feed icon was generated, copied that to my module, and rewrote the url using some regex kungfu (read more on overriding theme functions in the Drupal handbook).


function phptemplate_calendar_ical_icon($url) {
  $search = preg_match_all("/tid\[([0-9])*\]/", $url, $matches);
  $tids = $matches[1];

  $search = preg_match("/(.*ical\/)20[0-9]{2}-.*/", $url, $url_stem);

  $url = $url_stem[1];

  foreach($tids as $tid) {
    $url .= $tid . "+";

  $url = trim($url, "+");

  if ($image = theme('image', drupal_get_path('module', 'date_api') .'/images/ical16x16.gif', t('Add to calendar'), t('Add to calendar'))) {
    return '<div style="text-align:right"><a href="http://kyle.mathews2000.com/blog/2009/10/06/how-create-custom-ical-exports-category-using-drupal-and-views/'. check_url($url) .'" class="ical-icon" title="ical">'. $image .'</a></div>';

We'll be releasing our calendar, custom code and all, as a feature on BYU's own feature server once the new calendar is released to production. But you can grab a development copy of it now at https://island.byu.edu/content/first-upload-calendar-feature

The BYU Drupal community is collaborating to improve our calendar feature as it solves nicely a common need on the many department web sites deployed on campus. If you'd like to join the Drupal Calendar group to participate, contact me, and I'll create an account for you on our groups site.

Jul 06 2009
Jul 06

The Workhabit blog is chock full of updates to our products, videos from our crew, and discussion about our design, development, events, business strategies, and observations.

Jun 20 2008
Jun 20

In Wordpress, unlike in Drupal, terms are not lumped together in posts. Each Wordpress vocabulary has its own “template tag”, and the ones that come out-of-the box are: the_tags(), and the_category(). The following theming tweak is about putting order in Drupal terms before they're output to screen. It you need to break up your terms by vocabulary before you display them, read on.

Here's a Wordpress blog entry:

I will present an all-purpose solution that will print all terms by vocabulary. Each vocabulary list will be wrapped in its own HTML element, and I will use the vocabulary name as a 'label' for each list — a label you will be able to edit in the Administration Section of your Drupal site.


  1. Edit template.php to rebuild your $node->taxonomy array...

  2. ... and re-theme that array into a new $terms variable... and then...

  3. style your terms as needed in style.css.

Say you have two vocabularies, one for “free tagging”, and another for filing posts under sections, like so:

You may not like your free tags to be lumped together with your “Filed under” terms.

You may prefer to see something like this:

Let's get to it.

You will use a prepocess function for your node template. You will add this function to template.php if it has not already been defined. Open your theme template.php file in a text editor, and add the following code (please read the comments):

* Override or insert PHPTemplate variables into the node template.
function phptemplate_preprocess_node(&$vars) {
  // If we have any terms...
  if ($vars['node']->taxonomy) {
    // Let's iterate through each term.
    foreach ($vars['node']->taxonomy as $term) {
      // We will build a new array where there will be as many
      // nested arrays as there are vocabularies
      // The key for each nested array is the vocabulary ID.     
      $vocabulary[$term->vid]['taxonomy_term_'. $term->tid]  = array(
        'title' => $term->name,
        'href' => taxonomy_term_path($term),
        'attributes' => array(
          'rel' => 'tag', 
          'title' => strip_tags($term->description),
    // Making sure vocabularies appear in the same order.
    ksort($vocabulary, SORT_NUMERIC);
    // We will get rid of the old $terms variable.
    // And build a new $terms.
    foreach ($vocabulary as $vid => $terms) {
      // Getting the name of the vocabulary.
      $name = taxonomy_vocabulary_load($vid)->name;
      // Using the theme('links', ...) function to theme terms list.
      $terms = theme('links', $terms, array('class' => 'links inline'));
      // Wrapping the terms list.
      $vars['terms'] .= '<div class="vocabulary taxonomy_vid_';
      $vars['terms'] .= $vid;
      $vars['terms'] .= '">';
      $vars['terms'] .= $name;
      $vars['terms'] .= ':&nbsp;';
      $vars['terms'] .= $terms;
      $vars['terms'] .= '</div>';

Here is what the preprocess function does essentially:

The new HTML generated from print $terms (in node.tpl.php) is shown in this Firebug screen capture:

Creating PHPTemplate variables to pass on to your node.tpl.php template

As Opusoid recommends in a comment below, you can add each vocabulary to a new variable, that you pass on to your node template. In node.tpl.php, you can output each vocabulary variable wherever you wish. It gets super easy to place each vocabulary list in a precise location in the node: above it, below it, wherever you want. Brilliant, Opusoid, thank you!

Ordering vocabularies by weight — Improved version by manuee

Manuee modified the code snippet above to order vocabularies by weight. This is something you'll most likely prefer to do. After all, that's what weight is all about: presentation order. His code snippet is in his comment below. Thanks manuee!

Does that work in Drupal 5?

This solution will work in Drupal 6 only. Of course, there's an equivalent method for Drupal 5, and if someone asks for it I will provide it.

In Drupal 5, there was a function called taxonomy_get_vocabulary($vid) .This function has been renamed in Drupal 6 to taxonomy_vocabulary_load($vid). It has been renamed probably to bring naming consistency between functions that load objects, such as node_load() and user_load(). The function returns the vocabulary object matching the vocabulary ID $vid. The vocabulary object contains 'name' and 'description' properties, both of which you can use in your theme.

CSS styling

You may need to style your terms if you want them to appear on the same line, like so (these are rules added to the Garland theme style.css file):

 * Terms styling rules
.vocabulary {
  display: inline-block;
  padding-right: 1.5em;
.terms {
  float: none;

Last edited by Caroline Schnapp about 4 years ago.

Oct 06 2007
Oct 06

Being fallible (not being the pope) I learned some things from my Drupal installation. Dries asked me to keep a list of things, and this is a record of what I still remember.

Beware though that this list is focused on using Drupal for a personal blog.

You do not need the Blog module !
If you just want a personal blog, you really don't. I consider this vital advise, especially because not using Blog seems counter-intuitive and because it is described as:

Blog: Enables keeping easily and regularly updated user web pages or blogs.

You can simply use the Story content type for writing your blog articles and it makes your Drupal structure much more simple. The Blog module is really meant to allow individual users to have their own Blog, which is definitely not what I wanted. If you however made the same mistake as I did, here is the medicine:

mysql> UPDATE node SET type = 'story' WHERE type = 'blog';

Don't create Vocabularies or Terms individually
Don't do like I did and create your "tags" in advance, you really do not have to. And definitely do not start to put them in different categories and all that. Just create one Vocabulary called "Tags", enable free-tagging and be done with it. Every article you can simply add your tags in a comma seperated list.

The whole Taxonomy idea is a nice and abstract idea that covers all possible uses that far outreach the goals you have for a personal blog, so don't bother. You can always start using it if you are ready for it and if there is a *real* use.

Get used to and remember the Administer menu
I had a hard time finding exactly where some things resided in the menu and literally searched for more than 30 minutes to find where I could re-enable the Menu block for Anonymous users. This was not a pleasant experience, especially since I disabled it myself and couldn't remember where or how.

Maybe I have to add that late night hours are not the best times to contemplate something like this :)

Play with it and then start over
This is something I should have done, but time-constraints (a night only has 8 hours!) and other responsibilities of course get in the way. It is so easy to set op a second Drupal instance once you want to do it properly that you shouldn't care of breaking things.

Select a good set of modules
Some of the shortcomings I felt after I finished my setup actually were taken care of by powerful modules, only I didn't know of them. Of course I browsed the list by module name, but if you're not looking for something in particular you won't notice the interesting ones.

I ended up with the following list of additional modules, but I appreciate if you fill in some you use for a personal Drupal blog:

  • Captcha: prevent spam, but don't set it too difficult !
  • Event: nice if you want to tell visitors where they can meet you
  • Pathauto: important if you want a link that contains keywords (your title)
  • Recent Blocks: by default Drupal only has a Recent Comments block, which I didn't find that useful for my bog
  • Update status: this one is a *must-have* module, it can mail you a report when Drupal or modules get outdated (or have security related fixes)

I played with much more modules, but none of them survived the test of time :) Let me remind you that this article is about using Drupal for a personal blog. There are many many more modules that are useful, but less so if you only want a personal blog.

Disable what you don't need
I noticed that there was a lot included that I didn't need and I probably enabled stuff that I thought was useful. However I ended up with the following short list of non-core modules:

Comment, Menu, Path, Search, Statistics, Taxonomy, Tracker

Do you still remember your mistakes with Drupal ? Let us hear them !

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web