Lesson #4 Class Notes wiki -- Module Mashups!

You are viewing a wiki page. You are welcome to join the group and then edit it. Be bold!

Contribution Module Rock Star Roundup! CCK, views, panels... oh my!

Today we will be seeing what we can do, creating rich content layouts with modules such as CCK, views, panels, and how to make use of some of the core modules, such as taxonomy. And as we shall see, you can use views within panels...

Comment: It would be helpful to have an abstract of what will be accomplished, or even a demo of the end results for those who get scared browsing half way down and seeing all that code.

First step is to install the relevant modules.

  • Content Construction Kit
  • Views
  • Views Bonus Pack
  • Panels

Note: make sure you are working with the latest released versions of both Drupal and the modules!

Starting with Drupal 5, we can install non-core modules in ./sites/all/modules, leaving the ./modules directory to include only core modules which can thereby be easily overritten with a future updated version of Drupal. For Drupal 4.7.x, you can similar benefits by installing modules in ./sites/default/modules (for a standard, single-site install) or in ./sites/example.com/modules if you are doing multi-sites or moved your settings.php file to the directory corresponding to you site name. See also: http://drupal.org/node/70151

Most basic use case for Views

Views are basically a way of generating lists, a way of generating queries on your node content tables for your site.

It comes with a series of default views that you can enable and you can also build your own.

A very basic view which comes with the views series of modules is the frontpage view. This supplants the default frontpage generated by the node module. But you can accomplish the same thing by saying, give me a list of teasers, use a pager, don't put Home as an additional breadcrumb (in the case of the front page it is already there), show ten per page, let's have an RSS feed, the filter is to show anything promoted to front page; and if it's sticky put them towards the top, otherwise put the most recent one there.

That would be your basic frontpage. The interesting thing about using views to accomplish that is that you can then add other things to this; for example, add a header and a footer, add a page for use with empty lists (no nodes promoted to front page). This would allow you to add special graphics to the top, whatever you like.

Try editing the frontpage view and adding a header.

Our first Dojo view

So let's make our first view. The name is "dojo", a simple name, appropriate given the fact that this will get turned into a CSS id to work with.

Note: if you are not watching the screencast, in order to follow the discussion of the "dojo" view editing options, go ahead and cut and paste the following code into the "Import View Code"  textarea after hitting the views import tab (it assumes you have the Dojo drupal sandbox up-to-date from previous modules, with the Dojo module and vocabulary, and so forth); after submitting the import views code, if the parsing is successful (and it was in this writer's case), you will be taken to the views edit window. Press submit and you will have successfully imported the view, and if you have any dojo nodes created on your system, you can execute the view from administer > views (click on the "dojoclass" link) and see how it works. Then edit the view to follow the discussion.


  $view = new stdClass();
  $view->name = 'dojo';
  $view->description = 'Dojo example view';
  $view->access = array (
  $view->view_args_php = '';
  $view->page = TRUE;
  $view->page_title = 'Dojo Class';
  $view->page_header = '';
  $view->page_header_format = '1';
  $view->page_footer = '';
  $view->page_footer_format = '1';
  $view->page_empty = '';
  $view->page_empty_format = '1';
  $view->page_type = 'table';
  $view->url = 'dojoclass';
  $view->use_pager = TRUE;
  $view->nodes_per_page = '25';
  $view->sort = array (
  $view->argument = array (
  $view->field = array (
    array (
      'tablename' => 'node',
      'field' => 'title',
      'label' => 'Name',
      'handler' => 'views_handler_field_nodelink',
      'sortable' => '1',
      'defaultsort' => 'ASC',
      'options' => 'link',
    array (
      'tablename' => 'node',
      'field' => 'created',
      'label' => 'Joined',
      'handler' => 'views_handler_field_date_small',
      'sortable' => '1',
    array (
      'tablename' => 'node_data_field_belt_color',
      'field' => 'field_belt_color_value',
      'label' => 'Belt Level',
      'sortable' => '1',
      'options' => 'plain',
    array (
      'tablename' => 'node',
      'field' => 'edit',
      'label' => '',
  $view->filter = array (
    array (
      'tablename' => 'node',
      'field' => 'status',
      'operator' => '=',
      'options' => '',
      'value' => '1',
    array (
      'tablename' => 'node',
      'field' => 'type',
      'operator' => 'OR',
      'options' => '',
      'value' => array (
  0 => 'dojo',
    array (
      'tablename' => 'node_data_field_belt_color',
      'field' => 'field_belt_color_value_default',
      'operator' => 'OR',
      'options' => '',
      'value' => array (
  $view->exposed_filter = array (
    array (
      'tablename' => 'node_data_field_belt_color',
      'field' => 'field_belt_color_value_default',
      'label' => '',
      'optional' => '1',
      'is_default' => '0',
      'operator' => '1',
      'single' => '1',
  $view->requires = array(node, node_data_field_belt_color);
  $views[$view->name] = $view;

Note: here is the content code which you can import via CCK with administer > content types > import:

$content[type]  = array (
'name' => 'Dojo',
'type' => 'dojo',
'description' => 'dojo',
'title_label' => 'Title',
'body_label' => 'Body',
'min_word_count' => '0',
'help' => '',
'node_options' =>
array (
'status' => true,
'promote' => true,
'sticky' => false,
'revision' => false,
'comment' => '2',
'upload' => 1,
'old_type' => 'dojo',
'orig_type' => '',
'module' => 'node',
'custom' => '1',
'modified' => '1',
'locked' => '0',
$content[fields] = array (
0 =>
array (
'widget_type' => 'options_select',
'label' => 'Belt Color',
'weight' => '0',
'description' => '',
'group' => false,
'required' => '0',
'multiple' => '0',
'text_processing' => '0',
'max_length' => '',
'allowed_values' => 'white
'allowed_values_php' => '',
'field_name' => 'field_belt_color',
'field_type' => 'text',
'module' => 'text, optionwidgets',

It is also included in the attached pastebin.txt

Editing our new view

If you don't want to restrict access to the view, you leave all the access checkboxes unchecked under "Access".

The Page section provides a place to enable a page view (some views only have blocks, others only have pages, others have both).

The Fields section is for specifying which fields you want to be displayed in your view (in our frontpage view we displayed a Teaser list, the default mode for the front page). There is also table view, for seeing tables of data.

The Arguments section provides a way for users to pass parameters through the URL of the requested page into the view. For example, we have given our view the URL of 'dojoclass' and if some hits 'dojoclass/white' that might affect the nodes selected for the list. For example, if you specify RSS feed as an argument, if the URL is 'dojoclass/feed' then that might invoke an RSS feed.

The Filters section provides a way to filter your query, and the Exposed Filters make these filters available to the user.

The Sort Criteria are the "ORDER BY" specification for the view, which is, after all, a query.

So, in the Page section, Provide Page View is checked, and the url is specified as "dojoclass".  The View Type is "Table View" and the Title to be displayed is "Dojo Class List". We will use a pager and since we are using a table, we can show more than 10 nodes a page, 25 are specified.

In the Fields section, we specify Node Title, Node Created Time, Belt Color and an edit link for the node we are listing.

We provide labels for the fields. Belt Color is to displayed in plain text, and the column made sortable.

In the Filters section we specify that we only want to list Dojo nodes that are published (two separate filters). We also add a filter to specify the color of the belt color, and then "expose" it, making it available as a filter widget to users.

If we go ahead and save the view, and invoke it (./dojousers) we can see our table view of published Dojo nodes. The "Name", "Joined" and "Belt Level" columns are sortable in real time, and we can apply the belt color filter in real time also via the drop down list.

We can also click on the "Edit" field to make sure all our Dojo nodes have belt levels specified.

Views and CCK - So Happy Together!

If we now edit the Dojo content type, and add the integer text field "Battles Won", together with a second similar field "Battles Lost", we can add these very easily into our view, adding them to the list of fields to be displayed, in the Fields section.  Moving the edit field to the end, when we invoke the view (after editing these fields for some Dojo nodes) we can see the information we have added in the table view.

Question: How to pass arguments to the view?

Let's go back and edit the view once again and specify belt color as the passble argument to the view. We specify "display all values" and a title "Dojo Members with %1", and save the view. Now, when we specify the URL "./dojoclass/black", we are shown only black belt dojos!

In a real world example, we could have a single view which shows different outputs according to the argument specified to the view. For example the site http://renewablepost.com shows a front_page view which by default shows voting widgets, but if you invoke a taxonomy category, as in http://renewablepost.com/front_page/Solar for example, the view is filtered via the argument passed to it. The same views code powers all the pages.

The same site has another view, recent, which is programmed to filter on the basis of publishing date, it is another view which also takes taxonomy arguments. It is much easier to administer two views rather than many views. The use of arguments is efficient and keeps the code clean.

We can add taxonomy as a way of providing arguments to our dojoclass view. We create a Free tagging vocabulary just for the Dojo node called "Dojo Style", and start it out with the terms Crane, Drunken Monkey, Lotus, Tiger, Turtle.

We edit our Dojo nodes and assign them dojo styles. We now edit our dojoclass view, eliminate the argument we had previously added (belt color), and add the taxonomy argument "Taxonomy: Term Name". We change the Default from Return Page Not Found (what to do if there is no result, i.e., a result that makes no sense to the system) to "Display All Values". We could put "Summary, sorted ascending" which will display as a clickable list, all possible values. We prefer "Display All Values".

Assuming that the filter we had created (for belt color) is still intact (in both filter and exposed filter lists in the edit views form) we can filter by both exposed filter and URL arguments: dojoclass/Crane?filter0=white - Pretty powerful stuff! This is how you can use views to generate powerful queries on your content on-the-fly. There is also a theme wizard to help you with your views. Additionally, views has a good API for writing your own type of field, or you have a module with some special setting.

Question: in the case of zero results, can we specify a text to say "Sorry, no results found." ?

Answer: Yes, that is the empty text. If we edit our dojoclass view and go to the page section and specify an "Empty text" saying "Sorry, no results found", then we can go to the Arguments section and specify "Use Empty Text" instead of "Display All Values". Try it and see what happens when zero elements are found in the results list by the query.

Question: Can we sort by fields that are hidden from the view?

Answer: Yes, specify the "Battles Won" field, for example, in the Sort Criteria Section. Or in the frontpage view, this doesn't have a table that will take over the sorting (as in the dojoclass view), so the sorting is determined by the Sticky Bit and the Created Time fields, both of which are hidden.

(See dojoclass view version 2 in the attached pastebin.txt).

Merlin's website, http://www.angrydonuts.com/ has a lot of tips on views and other things; take a look at the API for adding fields, and also it is a great system for working up functionality really quickly.

Question: Can you display search results in views?

Answer: Yes, if you edit the view, you can add a filter of type Search Index, which we expose, and mark with label Search. So when we see it upon invoking the view, we can enter "Master" as the search term, and it will search the Body field and display those containing the search term (if this is a new site, make sure the search module is enabled, and that cron has been run, either by cron itself or manually).


Panels allows you to create pages with two or three columns, or in accordance with various layout templates. You can make additional templates using an API/plugin facility. With panels you can arrange content on a page in accordance with a particular layout.

Drupal has its own idea of the main content area, and additionally, block regions (header, footer, left and right sidebars, content). The main content area is usually filled up by a module, it usually shows a node, a blog roll, a view.

Panels allows you to do multiple content piece layout arrangements within that content area of the site.

Assuming the panels module is installed on our dojo sandbox site, when we hit administer, there is a panels link in the site building group. Clicking on the Add tab will display a visual listing of the layouts available. We will start by doing a two column stacked layout.

We call it "Dojo Portal" and specify a CSS ID of "dojo_portal", with path "portal".

The layout icon identifying in this case the two column stacked layout is reflected in the blocks on the panels edit page, where you can specify the content for each of the layout areas. For each area we can specify either a post, a block, a custom text, or a view, and for this we first specify the area radio button and then hit the appropriate "Add ..." button.

Specifying the Top area, we enter "Welcome!" as custom content, and hit the Add custom button.

For the left side, we enter a node title in the "Enter the title or NID of a post" text box, and it will be recognized by the autocomplete feature. Then we hit the Add post button.

On the right side, we specify the view we have just been working on, the "Dojo example view". We click on the configure link and specify embedded, "which works better", and we override the URL with "portal" so it'll listen to our arguments. We save our panel.

Invoking the "portal" link on the panels listing page, we see what we have. The sortable fields within the table view still work.

We go back and edit our panel, adding another post to the Bottom area.

Question: How do we determine how long a panel is?

Answer: You cannot, because the appearance is determined by CSS. This is a web (HTML + CSS) limitation thing. Of course, we can work with the CSS ID we specified plus the classes placed into the HTML by the panels module itself, such as "panel-2col-stacked". So we can edit syle.css in the current theme directory and add:

#dojo_portal .node {
border: 1px solid red;

Also see http://workingcalifornians.com/ which has a panel with three columns; the header (text and photo) is displayed only on the front page. We have styled the headlines of the panel so they have background images, and we have styled the color scheme, etc.

Question: How do you limit the number of results to be displayed in a panel section?

Answer: You use views to limit the number of rows displayed, and insert that view into the panel section. Put in 3 nodes on topic Solar written today...

Install and play with this on your own server!

One thing that is fun to do is to clear the blocks off of a panel page. Administer > blocks, and specify not to show blocks on the portal url; so we begin to say a "not like Drupal" site, without sidebars, things are being styled in special ways, etc.

Question: Can a block be inserted in a panel section?

Answer: Yes.

Another interesting thing with the custom content: you can do is to put the following code, for example, into the Body field of a custom content section (like our "Welcome!" above):

global $user;
echo 'It is my pleasure to welcome you to the dojo portal, ' . theme('username', $user) . '!';

PHP can give you the ability to add custom tweaks to get just the behavior you want out of your panel.

And you can style each element. Checking the HTML source, we see a class "panel-col-last", so we can add the following code to style.css (current theme directory) to style the far column

#dojo_portal .panel-col-last {
  background-color: #ccc;

to give it a gray background, for example.

Lots of flexibility on what you can add to a panel and how you can style it. Great for special splash pages, special pages with a more user friendly view.

Or... roll your own Panels template!

You have a layouts directory under your panels module directory.

You have three files for each template, a *.css file, a *.inc file and an image *.png file. So we copy over the three twocol template files to make a "dojo" template. Then when we now go to add a panel, we get an error about redefining functions in PHP, so we need to edit our spanking new dojo.inc file, which basically declares a couple of functions. We now carefully specify the panels_dojo_panels_layouts() and theme_panels_dojo()  functions (uncharacteristic victorkane comment: plugging non GPL product: am using our spanking new, now free!, Komodo Edit, with vi emulation of course, if you want it; see http://www.activestate.com/products/komodo_edit/ - beware, this is not the Komodo IDE, which still costs plenty, this is the editor, kind of a mini-Eclipse, gives me a little holiday from Vim 7, to whom I shall always return; there is a version of Komodo Edit for all platforms, even Ubuntu AMD 64, really intelligent autocompletion... but you can see the influence of open source products like Quanta and Eclipse... anyway, it's just a change ...).

So, the dojo.inc now looks as follows (we just changed the first function, which apart from setting the name straight, adds a region, and we changed the name of the second theme function specified in the first, and added a bit of theming in the second function):

// $Id: twocol.inc,v 1.6 2006/08/22 23:54:20 merlinofchaos Exp $
* implementation of hook_panels_layouts
function panels_dojo_panels_layouts() {
$items['twocol'] = array(
'module' => 'panels',
'title' => t('Dojo Test'),
'icon' => 'layouts/dojo.png',
'theme' => 'panels_dojo',
'css' => 'layouts/dojo.css',
'content areas' => array('left_top' => t('Left top'), 'right_top' => t('Right top'), 'bottom' => t('Bottom')),

return $items;

* This function uses heredoc notation to make it easier to convert
* to a template.
function theme_panels_dojo($id, $content) {
if ($id) {
$idstr = " id='$id'";

$output = <<<EOT

<div class="panel-2col" $idstr>

<div class="panel-col-first">

<div class="panel-col-last">

<div class="panel-bottom">
<br class="panel-clearer" />
return $output;

We now invoke the new template, and create a new panel:

Name: Dojo test

CSS ID: test

Path: dojotest

We place a node in each of the top sections (left top and right top) and in the bottom we place a block (recent comments) and hit save.

This works, with left and right working via CSS.

In actual practice, one might work from CSS to the content areas, though.

Overheard on #drupal-dojo (I)

WimLeers: webchick: it does, sort of.... so summarized: views does support CCK and core content types, 
as well as manually created content types, IF you support it
webchick: WimLeers: Hm. More like, CCK has Views support built-in, so any new types/fields
you create automatically support Views integration. If you make your own custom node module
with your own fields, not using CCK, you're responsible for providing that integration.
webchick: But, all "nodes" can show up in a View, because Views has core node module integration.
WimLeers: webchick: ok that's what I meant: "CCK node types get Views support *for free*"
webchick: Oh got it. ;)

Content Construction Kit revisited

We reiterate that you don't need CCK to create content types (with title and body fields) starting with Drupal 5.0. You need CCK to add fields to content types, even those not created by CCK!

Every time you add a content type, you get a set of access permissions (create, edit, edit own) specific to each content type you create, yielding a site with more structure.

But CCK adds a whole lot more fields. These fields may be added to any node (the same field may be used in more than one node). And there is a whole family of "CCK" modules that add additional fields (image, date, calculated, etc.).

If you look at the fields tab under content types, you can see that we have already created three fields, field_belt_color, field_battles_won and field_battles_lost.

Let's create a new content type to schedule Battles, called Battle.

(Human readable) Name: Dojo Battle

(System understandable) Type: battle

Description: Where we battle!

Title and Body fields, as they are, we don't promote to front page, we leave everything else as is, and we Save content type.

We edit the new content type, and we immediately hit the Add field tab, to add a field through CCK.

Label: Contestant #1

Field type: Node Reference / Select List

Click on Create field

On the next page we make it required and have it reference Dojo nodes (under Advanced, you can actually filter the nodes that can be referenced by a CCK node reference field).

Click on Save field settings.

And repeat the process to create another field labelled Contestant #2.

We make sure we have created some Dojo's on our system, one called, for example, Crane Dojo, another Drunken Monkey Dojo, and then we create a Dojo Battle node, titled The First Battle, specifying the two dojos as Contestants #1 and #2, with a brief explanation in the Body field ("showdown!"). We submit the content and see the very basic rendering of a user created node.

Just to show you, if you hit the devel load tab, you can see how the nodes being referenced actually appear as part of the node structure:


[0] => Array
[nid] => 10


[0] => Array
[nid] => 16

So, if they had pictures or avatars associated with them, on the theme level we could add those to the rendering of the Dojo Battle load, which is actually now like seeing three nodes at once. Or we could display their won/lost records.

So to do this, we go into the current theme directory, where we have already created a node-dojo.tpl.php file, so we are going to copy it to quickly make a new template file for the battle content type, called node-battle.tpl.php.

We make the content div look as follows (see attached pastebin.txt  file for complete listing):

  <div class="content">
$contestant1 = node_load($node->field_contestant_1[0]['nid']);
$contestant2 = node_load($node->field_contestant_2[0]['nid']);
echo $node->content['body']['#value'];
echo '<h2>'.$contestant1->title.'</h2>';
echo '<h4>Won: '.$contestant1->field_battles_won[0]['value'].' Lost: '.$contestant1->field_battles_lost[0]['value'].'</h4>';
echo '<h2>'.$contestant2->title.'</h2>';
echo '<h4>Won: '.$contestant2->field_battles_won[0]['value'].' Lost: '.$contestant2->field_battles_lost[0]['value'].'</h4>';

We load each contestant node (we know the nid) and specify the values. We know which variables to use from looking at Devel load / Devel render, or starting out the content rendering with a print_r call (print_r($node->field_contestant_1), etc.).

(Check out the contemplate module for a "macro" alternative to actually coding the template as we are doing now).

Overheard on #drupal-dojo (II)

mpare: I use to spend way to much time doing this stuff until I saw josh dropping print_r all over the place, 
I am so much faster for it now and am surprised that I could do anything before without it.

CCK continued

(pastebin.txt now contains the export of the CCK content type "Dojo Battle" which you can actually import: make sure you have the latest version of CCK installed).

Question: Can we use associated nodes as a type of taxonomy? Are associated values expensive for performance?

Answer: Sort of. It's not expensive for performance because it's a single value. Only on the node_load(), but that is not the end of the world, it's a pretty well optimized function.

On the Tragic Under-use of Taxonomy

Taxonomy performs really well, it's very tight, you can use it as a kind of metadata for your content types. You can have a taxonomy that is partially about having a workflow.

Right now, in our node-battle.tpl.php theming template, we are just dumping out the taxonomy:

    <div class="meta">
<?php if ($taxonomy): ?>
<div class="terms"><?php print $terms ?></div>
<?php endif;?>

If we remove this, we will be printing nothing about taxonomy when we display the page.

So we make sure we have our free tagging Dojo Style vocabular enabled for Dojo Battle content types as well as for Dojo's.

We now add an additional single hierarchy vocabulary, Battle Type, required and applied only to Dojo Battle (weight of -1 to float above the other vocabulary). We add some terms: Blood Feud, Death Match, Sparring, Battle Without Honor.

Special Note on the nature of Taxonomy

Taxonomy as terms applied to nodes is expressed in the Drupal database as a simple many-to-many relationship in a two-column table composed of terms and nodes:

mysql> describe term_node;
| Field | Type | Null | Key | Default | Extra |
| nid | int(10) unsigned | NO | PRI | 0 | |
| tid | int(10) unsigned | NO | PRI | 0 | |
2 rows in set (0.24 sec)

Terms not applied to any node will not show up in this table.

This is the vocabulary table:

mysql> describe vocabulary;
| Field | Type | Null | Key | Default | Extra |
| vid | int(10) unsigned | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| description | longtext | YES | | NULL | |
| help | varchar(255) | NO | | NULL | |
| relations | tinyint(3) unsigned | NO | | 0 | |
| hierarchy | tinyint(3) unsigned | NO | | 0 | |
| multiple | tinyint(3) unsigned | NO | | 0 | |
| required | tinyint(3) unsigned | NO | | 0 | |
| tags | tinyint(3) unsigned | NO | | 0 | |
| module | varchar(255) | NO | | NULL | |
| weight | tinyint(4) | NO | | 0 | |

There is also a table called term_data:

sql> describe term_data;
| Field | Type | Null | Key | Default | Extra |
| tid | int(10) unsigned | NO | PRI | NULL | auto_increment |
| vid | int(10) unsigned | NO | MUL | 0 | |
| name | varchar(255) | NO | | NULL | |
| description | longtext | YES | | NULL | |
| weight | tinyint(4) | NO | | 0 | |

So you could do the following query:

mysql> select tid from term_data where vid = 7;
| tid |
| 14 |
| 15 |
| 16 |
| 17 |
4 rows in set (0.05 sec)

and then:

mysql> select * from term_data where tid in (6,7,8,9);
| tid | vid | name | description | weight |
| 6 | 5 | boca | NULL | 0 |
| 7 | 5 | river plate | NULL | 0 |
| 8 | 5 | river | NULL | 0 |
| 9 | 6 | Tiger | NULL | 0 |
4 rows in set (0.05 sec)

and finally:

mysql> select * from term_node tn INNER JOIN term_data td on tn.tid = td.tid;
| nid | tid | tid | vid | name | description | weight |
| 1 | 1 | 1 | 3 | Static | NULL | 0 |
| 14 | 3 | 3 | 4 | River Plate | NULL | 0 |
| 13 | 4 | 4 | 4 | Boca | NULL | 0 |
| 13 | 6 | 6 | 5 | boca | NULL | 0 |
| 14 | 7 | 7 | 5 | river plate | NULL | 0 |
| 14 | 8 | 8 | 5 | river | NULL | 0 |
| 10 | 10 | 10 | 6 | Crane | NULL | 0 |
| 12 | 10 | 10 | 6 | Crane | NULL | 0 |
| 16 | 11 | 11 | 6 | Drunken Monkey | NULL | 0 |
9 rows in set (0.06 sec)


mysql> select * from term_node tn INNER JOIN term_data td on tn.tid = td.tid where td.vid = 6;
| nid | tid | tid | vid | name | description | weight |
| 10 | 10 | 10 | 6 | Crane | NULL | 0 |
| 12 | 10 | 10 | 6 | Crane | NULL | 0 |
| 16 | 11 | 11 | 6 | Drunken Monkey | NULL | 0 |

which will give results based on the actual content and application of terms to nodes.

Views does a lot of queries like this behind the scenes. Looking at how the taxonomy system works will teach you how to make good relational databases and it's a good way to learn because it is really important for building extensible systems.

Guide to the perplexed

vid is vocabulary id in this table. vid is revision id in the node table. 'nuff said.

Applying this to answer the original question

So how can we take advantage of this rendered field that gives you your taxonomy? It is passed on my phptemplate, but it simply lumps them all together.

Let's do some custom taxonomy theming, continuing with node-battle.tpl.php (with a little help from print_r and the API reference, of course).

So we replace (7 is the vid of Battle Type vocabulary on my box):

    <div class="meta">
<?php if ($taxonomy): ?>
<div class="terms"><?php print $terms ?></div>
<?php endif;?>


    <div class="meta">
<?php if ($taxonomy) {
$taxonomy_links = array();
foreach($node->taxonomy as $tid => $term) {
// replace the hard-coded 7 with the vid of vocab Battle Type
if ($term->vid == 7) {
$battle_type = '<b>'.$term->name.'</b>';
else {
$taxonomy_links[] = l($term->name, 'taxonomy/'.$tid);
$taxonomy_links = implode(' | ', $taxonomy_links);
echo '<div>'.$battle_type.'</div>';
echo '<div>'.$taxonomy_links.'</div>';

So now we're handling our taxonomy separately by vocabulary.

Question: Is it possible to set whether a taxonomy is required by a nodetype? The required setting is global.

Answer: But of course... it's very easy to change with FormAPI, by removing the "none" value, or set required to be true in the Dojo module for the hook_form_alter() for battle forms.

  if ($form_id == 'battle_node_form') {
// substitute vid for Battle Type on your system
// on Josh's it was 2
$form['taxonomy'][7]['#required'] = TRUE;

You could have an admin page, with an array of checkboxes to optionally require vocabaularies for given node types.

To end the lesson, we have some skeleton code to get people started (see pastebin.txt for complete dojo.module listing):

* Implementation of hook_perm
* Set some example permissions to check for dojo.module

function dojo_menu($may_cache) {
$items = array();
$items[] = array(
'path' => 'admin/settings/dojo',
'title' => t('Dojo Settings'),
'description' => t('Staring a granular taxonomy requirement system'),
'callback' => 'drupal_get_form',
'callback arguments' => array('dojo_settings'),
'access' => user_access('administer site configuration'),
'type' => MENU_NORMAL_ITEM, // optional
return $items;

function dojo_settings() {
$types = node_get_types();
// print_r($types);
$vocabs = taxonomy_get_vocabularies();
// print_r($vocabs);
$header = array(t('Vocabulary'));
foreach($vocabs as $vid => $vocab) {
// make a row
$row = array();
$row[] = $vocab->name;
foreach($types as $type => $data) {
$row[] = '[x]';
$header[] = $type;
$rows[] = $row;
$form = array();
$form['table'] = array(
'#type' => 'markup',
'#value' => theme('table', $header, $rows),
return $form;


pastebin.txt20.22 KB

Drupal Dojo

Group notifications

This group offers an RSS feed. Or subscribe to these personalized, sitewide feeds: