Jun 01 2012
Jun 01

Conditional Fields vs AHAH Helper

Posted on: Friday, June 1st 2012 by Rexx Llabore

I worked on a project in which node forms display text fields based on the values of other fields. For example, if a checkbox field is checked, the form should display a text field below. Another example would be if a user chooses the option "other" in a select box, a text field should be displayed as well.

The first thing that came to my mind was AHAH, which I could easily use with the help of AHAH Helper module. After further research, I discovered the perfect solution to my problem: Conditional Fields.

Conditional fields define dependencies between content type fields based on the state of their states and values. An example scenario would be a selection box of "Occupations". If the user did not see their occupation in the predefined list, a text box will be displayed so that they can enter the right information.

Another good thing about using conditional fields is that it can be exported via features. You don't need to write code to do this and maintenance does not become a nightmare.

In order to use conditional fields here is what you need to do:

  • enable the module
  • navigate to "admin/content/node-type/CONTENT_TYPE_NAME/fields/FIELD_NAME"
  • under "Controlling fields", select the choose the field's value that would be used to display current field

Jan 24 2012
Jan 24

Exposing filters in a view is a great way to add interactivity to lists. Out of the box Drupal Views lets you use CCK select lists as an end user visible filter.  The downside is that the select list is a clunky user interface.  To select multiple options you have to hold down the control button while mousing around – not so bad with a short list, but rapidly unmanageable with a longer list.

To solve this user experience conundrum mikeker provided the new and improved exposed filters… aptly called “better exposed filters“  – one thing I like about drupal is that the naming conventions tend to be fairly obvious.

Here’s a brief ~2 minute walkthrough of setting it up

Before Better Exposed Filters

After Better Exposed Filters

Note: you do of course have to expose the filter in views before being able to use the “better” exposed filter…

The tools are available in a new block within your view

Initially after turning the module on I expected to see the new options available from within the filter section of the view, however the module creates a new menu region within views… afterwards style the output as you see fit with css – such that happiness may reign.  any questions or comments welcome

Jan 09 2012
Jan 09

CCK in Drupal 6 and FieldAPI in Drupal 7 give site builders a variety of structural and formatting options for carving out new content types. Multi-value fields in particular allow a node or other content element to store lists, collections of links to taxonomy terms, and more. The downside, of course, is the formatting: by default, Drupal's HTML output for multivalue text fields is a big old bag of DIVs. Taxonomy fare a bit better: they're output as proper HTML lists, a semantically proper approach. Both still require considerable CSS wrangling if you're interested in displaying multiple values simply, however. And that is where the Text Formatter module comes in.

Screenshot of administration screen

Text Formatter is a simple CCK/FieldAPI formatter for Drupal 6 and 7 that outputs multi-value text and taxonomy fields as comma delimited lists, or customizable HTML lists. It can do a few other fancy formatting tricks, as well: the last item in a comma-delimited list can be preceded by a grammatically correct 'and', and comma-delimited lists can be closed with a period. Ordered and unordered lists can also be chosen, and a custom CSS class can be specified for a particular list if you're interested in targeting special CSS rules or JavaScript behaviors. The module even allows you to format single-value long text fields as lists if they contain multiple lines of text.

Screenshot of Text Formatter in action

Text Formatters was originally part of the much older "CCK Formatters" module. Its list formatting options were by far the most popular, and were split out into a separate module for ongoing maintenance. The module is stable, trouble-free, and does just what it promises. If you need to whip a few multi-value fields into shape, check it out!

*/
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

Dec 08 2011
rmo
Dec 08

Drupal 7’s Fields API – Initial Perspective

Posted on: Thursday, December 8th 2011 by Richard Mo

I’m sure that there's a lot of blogs, tutorials, and presentations out in the public already, which educate us about the Fields API. I’m not going to write “another blog post” about that. Instead, I'm going to share my initial impression as well as what I’ve discovered helpful to people who are new to the API.

Fields creation for content-types behaves exactly the same as CCK module of Drupal 6: selection of fields that can be attached, widget types that can be used with a field, and configuration of fields. The UI, in particular, looks almost identical to CCK module. So, a lot of the basic field types from the CCK module are part of core Drupal 7, however, like I said, it’s only the basic, if I want to have a more complex setup, I must default to something more appropriate…

From experience, CCK API is not easy to work with. If you want to create a set of textfield and textarea, group them as a whole, and allows for multiple sets. CCK can give you a headache. I’m actually quite impressed of how Fields API greatly simplifies the creation process. Although the learning curve is quite steep, yet once you learned where things are its quite easy. (As a side note, the API documentation on Drupal.org is not beginner-friendly.) So to help out our fellow Drupal 7 developers, there are five things you must implement in order for a field to work:

  1. hook_field_info()
  2. hook_field_is_empty()
  3. hook_field_widget_info()
  4. hook_field_widget_form()
  5. hook_field_schema()

The hook_field_validate(), hook_field_formatter_info(), and hook_field_formatter_view() is not required for the field to work.

Sep 27 2011
Sep 27

A simple CCK formatter for on/off checkbox fields

Posted on: Tuesday, September 27th 2011 by Sheila Mullins

The behavior of Drupal cck's single on/off checkbox field is a little different than other cck fields. Normally, a field has a label above its input element (text field, select list, ...). With a single on/off checkbox the field label as entered when creating the field does not appear on the node edit form. Instead, the label associated with the 'ON' value will appear to the right of the checkbox. This is the intended behavior and when the label associated with the 'ON' value is descriptive, it is intuitive enough on the node edit form. However, some difficulties can arise when trying to accommodate both the node view (or views display) and the node edit form. Here is an example of a possible solution for one particular case.

The problem

Suppose a field called 'Club Member' to record whether the node/person is a Club Member or not. I want to store the actual value as an integer, so I use an integer type field. I have to choose a field label as well as key|label pairs for the ON and OFF values.

By default these will be displayed in this way:

on the node page
Field Label:
Value Label

and on the node edit page
[] ON-Value Label

So if I name my field 'Club Member' and use 0|NO and 1|YES as key|label pairs I get:

on the node page
Club Member:
No

and on the node edit page
[] YES

This obviously doesn't work on the node edit page because I have no idea what I'm saying YES to.

So I try key|label pairs 0|Not a Club Member and 1|Club Member and get:

on the node page
Club Member:
Not a Club Member

and on the node edit page
[] Club Member

That works for the edit page but is awkward on the node page. If I change the display to 'unformatted' (at admin/content/node-type/my_node_type/display), a 0 or 1 will be displayed on the node page instead of the value label:

on the node page
Club Member:
0

I would prefer to display YES or NO on the node page and also in a view:

Club Member:
NO

The solution

Use a custom cck formatter to output YES or NO instead of 1 or 0. If you haven't done this before, it's easier that you might think! It terms of the actual formatter, this one is dead simple. We will have to use couple of hooks to expose the formatter to the Drupal admin UI.

1. Register the formatter with hook_field_formatter_info(). Note the machine name of the integer field type is number_integer. The 'label' you choose will appear in the select list for the display of each integer field on the 'display fields' page (admin/content/node-type/my_node_type/display). The array key – 'mymodule_yes_no_boolean' – can be anything but must match what is used in hook theme.

/**
 * Implementation of hook_field_formatter_info().
 */
function mymodule_field_formatter_info() {
  $formatters = array(
    'mymodule_yes_no_boolean' => array(
      'label' => t('YES or NO'),
      'field types' => array('number_integer'),
      'description' => t('Displays YES or NO.'),      
    )
  );
  return $formatters;
}

2. Register your formatter with hook_theme(). Here the $themes array key is composed of your module name followed by 'formatter' followed by the $formatters array key you used in hook_field_formatter_info(). Declaring a file is not necessary but it is good practice to keep your .module file lighter.

/**
 * Implementation of hook_theme().
 */
function mymodule_theme($existing, $type, $theme, $path) {
  $themes = array(
    'mymodule_formatter_mymodule_yes_no_boolean' => array(
      'arguments' => array('element' => NULL),
      'file' => 'includes/mymodule.theme.inc'
    ),
  );
  return $themes;
}

3. Write the theme function. Since we declared 'includes/mymodule.theme.inc' as the file in which to find the theme function, that's where we'll put it. The function is named 'theme_' followed by the $themes array key we used in hook_theme(). All we need to do is check the $element['#item']['value'] which will be either 0 or 1 (as defined in your key|label pairs), and then we can output whatever we want instead. Actually, if your number_integer field isn't an on/off field then you could have other values, but then you wouldn't choose 'YES or NO' as your formatter!

/**
 * CCK Formatter theme function for single on/off checkbox integer fields.
 */
function theme_mymodule_formatter_mymodule_yes_no_boolean($element) {
  $output = ($element['#item']['value']) ? 'YES' : 'NO';
  return $output;
}

Here is a link to a blog I found helpful when creating my first cck formatter.

As always, your feedback and other ideas are welcomed.

Sep 22 2011
Sep 22

Tomorrow is the last day of Summer but the Drupal training scene is as hot as ever. We’ve scheduled a number of trainings in Los Angeles this Fall that we’re excited to tell you about, and we’re happy to publicly announce our training assistance program.

First, though, we’re sending out discount codes on Twitter and Facebook. Follow @LarksLA on Twitter, like Exaltation of Larks on Facebook or sign up to our training newsletter at http://www.larks.la/training to get a 15% early bird discount* toward all our trainings!

Los Angeles Drupal trainings in October and November, 2011

Here are the trainings we’ve lined up. If you have any questions, visit us at http://www.larks.la/training or contact us at trainings [at] larks [dot] la and we’ll be happy to talk with you. You can also call us at 888-LARKS-LA (888-527-5752) with any questions.

Beginner trainings:

Intermediate training:

Advanced trainings:

All our trainings are $400 a day (1-day trainings are $400, 2-day trainings are $800, etc.). We’re excited about these trainings and hope you are, too. Here are some more details and descriptions.

Training details and descriptions

   Drupal Fundamentals
   October 31, 2011
   http://ex.tl/df7

Drupal Fundamentals is our introductory training that touches on nearly every aspect of the core Drupal framework and covers many must-have modules. By the end of the day, you’ll have created a Drupal site that looks and functions much like any you’ll see on the web today.

This training is for Drupal 7. For more information, visit http://ex.tl/sbd7

   Drupal Scalability and Performance
   October 31, 2011
   http://ex.tl/dsp1

In this advanced Drupal Scalability and Performance training, we’ll show you the best practices for running fast sites for a large volume of users. Starting with a blank Linux virtual server, we’ll work together through the setup, configuration and tuning of Drupal using Varnish, Pressflow, Apache, MySQL, Memcache and Apache Solr.

This training is for both Drupal 6 and Drupal 7. For more information, visit http://ex.tl/dsp1

   Drupal Architecture (Custom Content, Fields and Lists)
   November 1 & 2, 2011
   http://ex.tl/ccfl1

Drupal Architecture (Custom Content, Fields and Lists) is our intermediate training where we explore modules and configurations you can combine to build more customized systems using Drupal. You’ll create many examples of more advanced configurations and content displays using the popular Content Construction Kit (CCK) and Views modules.

This training is for Drupal 6. For more information, visit http://ex.tl/ccfl1

   Developing RESTful Web Services and APIs
   November 3, 4 & 5, 2011
   http://ex.tl/dwsa1

Offered for the first time in Southern California, Developing RESTful Web Services and APIs is an advanced 2-day training (with an optional third day of additional hands-on support) for those developers seeking accelerated understanding of exploiting Services 3.0 to its fullest. This is THE training you need if you’re using Drupal to create a backend for iPad, iPhone or Android applications.

This training covers both Drupal 6 and Drupal 7. For more information, visit
http://ex.tl/dwsa1

Training assistance program

In closing, we’d like to tell you about our training assistance program. For each class, we’re setting aside a limited number of seats for students, unemployed job seekers and people in need.

For more details about the program, contact us at trainings [at] larks [dot] la and we’ll be happy to talk with you. You can also call us at 888-LARKS-LA (888-527-5752) with any questions.

* Our early bird discount is not valid toward the Red Cross First Aid, CPR & AED training and 2-year certification that we’re organizing. It’s already being offered at nearly 33% off, so sign up today. You won’t regret it and you might even save someone’s life. ^

Aug 19 2011
Aug 19

When the multigroup module was introduced as part of the CCK 3.x dev branch in 2009, it took CCK to a whole new level. It allowed people to easily create their own multi-value, multi-field widgets. Currently it is still only available as an alpha release for Drupal 6 though, most likely to focus more on Fields for Drupal 7 and to allow an easier upgrade path from Drupal 6 CCK 2 to Drupal 7 Fields. More information about this decision can be found on the drupal.org issue about the Status of CCK3 and plans for D7

In Drupal 7, the core Fields module doesn't offer the option to create groups, let alone multigroups. To create simple groups, you need the Field group module. It's much more powerful than its predecessor in that it allows you to not only create fieldsets, but also vertical and horizontal tabs and accordion groups.

Field group configuration

The Field group module does not cover any multigroup functionality however, and it never will. It states so on the module's project page, and more information can be found in the issue queue.

Luckily there's the Field collection module which uses the Entity API to create multi-value, multi-field widgets. The Field collection table module adds a table formatter to the collections, and Field collection also integrates with Field group to offer amazing flexibility and power. As an added bonus, both modules integrate flawlessly with Features. At this time, there is no upgrade path from D6 Multigroup to D7 Fields with Field collection though.

Jul 30 2010
Jul 30

Setting up a Wysiwyg or rich text editor in Drupal is a straightforward task: you download the Wysiwyg module along with the library of your favorite editor and you're good to go. You will run into issues when you're using CCK multiple value fields though:

  • You will experience data loss when adding more fields: all content added to existing fields will disappearing when you click the "Add another item" button to add a new field.
  • Depending on which editor you use, there's a chance of hick-ups and data loss when using the drag-and-drop interface to rearrange fields.
  • Depending on which editor you use, you will run into different kinds of unexpected behaviors when adding your first new item after clicking the "Add another item" button. These issues can range from the editor only appearing on the first field, to only appearing on the last field, or from the editor disappearing completely.

The first two issues can be fixed by installing the Wysiwyg API CCK Integration module, which itself depends on three other modules: JS Alter, jQuery Form Update and jQuery AOP. These modules require no configuration: just enable them to fix the problems.

The third issue however is more tedious. I've tried fixing the bug with hook_form_alter() and CCK fields along with the Wysiwyg integration guide but these don't play along nicely. The added difficulty is that the problem varies depending on which editor you use. The only bulletproof "solution" I've found this far is to simply use the FCKeditor editor: it's the only editor that doesn't appear to cause any unexpected behavior. Do note that these problems are only fixed if you use the FCKeditor with the Wysiwyg module: the FCKeditor module itself still has these issues.

Jun 17 2010
Jun 17

By knowing who you are you will be directed to a page where we have displayed all our drupal services relevant to your professional identity.

Jun 04 2010
Jun 04

Twice today I've had to deal with writing a SQL query that needed data in a CCK field. The naive approach is to just look at the table and field names and plug them into your query:

<?php
$result
= db_query("SELECT COUNT(*) AS count FROM {node} n
INNER JOIN {term_node} tn ON n.vid = tn.vid
INNER JOIN {content_type_date} ctd ON n.vid = ctd.vid
WHERE tn.tid = 25 AND ctd.field_date_value > NOW() AND n.changed > %d"
, $newtime);
?>


Often this will work just fine but since CCK can dynamically alter the database schema (when you add a field to a second content type or change the number of values) the query may break.

Fortunately CCK provides functions for finding a field's table and column names so it's simple to do it correctly:

<?php
$field
= content_fields('field_date');
$db_info = content_database_info($field);
?>

A var_dump($db_info) gives:

array(2) {
  ["table"]=>
  string(17) "content_type_date"
  ["columns"]=>
  array(2) {
    ["value"]=>
    array(6) {
      ["type"]=>
      string(7) "varchar"
      ["length"]=>
      int(20)
      ["not null"]=>
      bool(false)
      ["sortable"]=>
      bool(true)
      ["views"]=>
      bool(true)
      ["column"]=>
      string(16) "field_date_value"
    }
    ["value2"]=>
    array(6) {
      ["type"]=>
      string(7) "varchar"
      ["length"]=>
      int(20)
      ["not null"]=>
      bool(false)
      ["sortable"]=>
      bool(true)
      ["views"]=>
      bool(false)
      ["column"]=>
      string(17) "field_date_value2"
    }
  }
}

After noting that the field has two columns and making our choice, we've got the pieces to plug into the query:

<?php
$field
= content_fields('field_date');
$db_info = content_database_info($field);
$result = db_query("SELECT COUNT(*) AS count FROM {node} n
INNER JOIN {term_node} tn ON n.vid = tn.vid
INNER JOIN {"
. $db_info['table'] ."} ctd ON n.vid = ctd.vid
WHERE tn.tid = 25 AND ctd."
. $db_info['columns']['value']['column'] . " > NOW() AND n.changed > %d", $newtime);
?>

The query is a bit harder to read, but you've future proofed your code so you won't be back to fix six months from now when you reuse that date field on another node type.

Jan 25 2010
mg
Jan 25

I was trying to create different tabs (using YUI) to edit a form. I wanted to create a separate tab for each of the field groups. However, in the form_alter method of the form for my module, I could not see the fields in the fieldgroup. On debugging I found my fields in the fieldgroup to be present in the form_alter of other modules. Problem: The CCK fieldgroup module has a weight of 9. If your module has a weight less than 9, then it is called before CCK fieldgroup, hence the problem. Solution: Set your module's weight more than 9. The setting is in the system table, under the column weight. This fixed the problem for me. It took a long time to solve the problem and thanks to some of the other posts mentioned below for writing about this.

Dec 19 2009
mg
Dec 19

There are several modules I have used during building sites. I am not going to get into details of the modules because you should visit the modules. But by far, the most important / useful modules for me have been:

1. Views ; You can think of views as output to your custom query (except you don't write the query). There are several ways to present the views and Calendar is one example. So this is a query builder which can execute queries at run time and present the data however you want.

2. : You can add custom fields to a node or define a new content type based on CCK, This is powerful functionality with no coding. You can order the fields according to your preference. And CCK will become very powerful once is introduced. As of now, Multigroup is in alpha, but I have been using it with no problems. It lets you create a composite or a compound field, consisting of different base CCK fields.

3. : The module renders all administrative menu items below 'administer' in a clean, attractive and purely CSS-based menu at the top of your website. This is a life saver if you are administering a site. Essentially it is a overlay of the entire admin functionality as a ribbon on the top of your web site.

4. : I have to give it to katrib for this module. This is great if you want spreadsheet functionality in your website. We did some pretty advanced things with the module like embedding it within a node as a tab and YUI integration. We also configured a java bridge to be able to upload excel sheets from desktop to the website. Google spread sheet integration is pretty straightforward. This is based on SocialText platform

5. Enables users to create and manage their own groups (like forums). This can bring great social aspects to your website. For some of the sites, we changed the module to represent a company and it was great.

6. : This adds a lot of umph to the site. It is Mainly used to showcase featured content at a prominent place on the frontpage of the site. Demos and tutorials are excellent.

7. : Essential for developers and themers It can generate SQL query summaries, create data for your test site, show the theme information on your site. Don't develop without this module.

8. Pathauto : It automatically generates path aliases for various kinds of content (nodes, categories, users) without requiring the user to manually specify the path alias. Helps get rid of default / ugly paths (node/3 ... )

9. jQuery : jQuery depends on jQuery UI and jQuery update in drupal 6. And here is a good overview of jQuery.This architectural diagram might also be a useful reference. jQuery modules provides all the nice functionality jQuery library.

10. : This module provides several useful extensions to the login system of drupal. What I found really useful was to allow email based logins, email confirmation during registration and auto logout. Happy drupaling :)

Oct 19 2009
Oct 19

We had a client requirement that a single view be the combination of:

  1. a random list of attorneys with offices in a given state
  2. a random list of attorneys that are licensed in a given state.

There should be no duplications and all of list 1 must precede list 2. We could not accomplish this with views alone. After talking to Earl, we were able to access the created SQL statement within views. Here we built each of the two queries. Then, we defined a 'pre' exit and replaced the query in our view with a new one.

The new query is in the form of:

SELECT node.nid FROM ( ( select #1 ) UNION (select #2 ) ORDER BY .... ) AS node.

This is especially important in that we absolutely have to name our selected fields the same as what views would typically name them. If you don't preserve the same naming structure it will simply not work. Now this is only true for the selected fields. Fields on which you join, filter, etc can utilize any naming convention you desire. Of course, both of the select statements were required to provide the exact same column output.

So, as an example the statement looked something like: (with LEFT JOIN detail left out for easier reading)

SELECT node.nid
FROM (
  ( // First select statement
    SELECT node.nid, RAND() as _order
    FROM node node
    WHERE node.attorney_state='%s'
  )
  UNION // concatenate to next
  SELECT
  ( // Second select statement
    SELECT node.nid, (1+RAND()) as _order
    FROM node node
    WHERE node.licenses_state = '%s'
  )
  // This does the sort on all the selects together.
  ORDER BY _order
)
// Now, for Drupal, this outer select MUST be aliased as 'node'.
AS node;

Some of the secret sauce was:

  • use of an outer select with an internal union
  • The outer select needed to be aliased to node for it to work.
  • we only needed the nid as all the data was pulled using node-> syntax.
  • There seemed to be bug in cck based on this, obviously, edge case and it took 2 lines of code to fix.
    • cck expects 'vid' to absolutely have a value.
    • wrapping an if ($vid)... around that expectation removed the warning error that we were getting.
    • bottom line... we hacked it, but hopefully it will make sense to the cck authors and it can be submitted back to the module.
  • the views hook was hook_views_pre_execute(&$view) and we altered the query directly via $view->build_info['query']. If you'd like to preserve the dynamic nature of certain aspects of the views interface, you must respect your $view->build_info['query_args']. This may require some alteration as well.
  • Note that we did a RAND() and 1+RAND(). This is because RAND() returns a random decimal between 0 and 1, thus 1+RAND() would be a random decimal between 1 and 2, allowing us to order by this random number and be sure that our second query's results ALWAYS come after the first query.
Jul 30 2009
Jul 30

CCK formatters are pieces of code that allow you to render a CCK field content how you want. In Drupal 6 this basicaly means a theme function.

As an example, we will build a formatter for the field type 'nodereference'.
This type of field, which is part of the standard CCK package, allows you to "reference" a node inside another.
The formatter that nodereference has by default, prints a standard link to the referenced node.

We are going to give the users other options, allowing them choose if they want the link to open in a new window or, if they have the popups module activated, that it opens in a jQuery modal window.

Let's call our module 'formattertest'.

Step 1: Declare our CCK formatters

To do this, the only thing needed is to implement our hook_field_formatter_info() in our module:

<?php
/**
* Implementation of hook_field_formatter_info().
*
* Here we define an array with the options we will provide in display fields page
* The array keys will be used later in hook_theme and theme_
*/
function formattertest_field_formatter_info() {
 
$formatters = array(
   
'newwindow' => array(
     
'label' => t('Open in new window link'),
     
'field types' => array('nodereference'),
     
'description' => t('Displays a link to the referenced node that opens in a new window.'),
    ),
  );
  if (
module_exists('popups')) {
   
$formatters['popup'] = array(
     
'label' => t('Open in a popup window'),
     
'field types' => array('nodereference'),
     
'description' => t('Displays a link to the referenced node that opens in a jQuery modal window.'),
    );
  }
  return
$formatters;
}
?>

In this function, you have to return an arrays of arrays, that define each formatter that the module provides.
  • label: The name that the user will choose in the display fields configuration page
  • field types: an array with the types of cck fields that the formatter supports.

It's important to remember that the array keys you use, in our case 'newwindow' and 'popup', will be used later on to construct our functions hook_theme and theme_.
Note that in the second formatter, first we check if the module popups is active in the system, and then we add our formatter array that makes use of it.

2. Implement hook_theme

In hook_theme() you also return an array of arrays, defining the theme_ functions that will take care of rendering the cck field content. 'element' will be the content of the cck field, that will be used as the parameter for our theme function.

<?php
/**
* Implementation of hook_theme().
*
* We declare our theme functions according to the array keys in  hook_field_formatter_info
*/
function formattertest_theme() {
 
$theme = array(
   
'formattertest_formatter_newwindow' => array(
     
'arguments' => array('element' => NULL),
    ),
  );
  if (
module_exists('popups')) {
   
$theme['formattertest_formatter_popup'] = array('arguments' => array('element' => NULL));
  }
  return
$theme;
}
?>

'formattertest_formatter_newwindow' and 'formattertest_formatter_popup' will be used to build our functions in the next step.

3. Build our theme functions.

Remember taht you can do dsm($element); (if you have devel installed), to see what you have to play with ;)

<?php
/**
* Theming functions for our formatters
*
* And here we do our magic. You can use dsm($element) to see what you have to play with (requires devel module).
*/
function theme_formattertest_formatter_newwindow($element) {
 
$output = '';
  if (!empty(
$element['#item']['nid']) && is_numeric($element['#item']['nid']) && ($title = _nodereference_titles($element['#item']['nid']))) {
   
$output = l($title, 'node/'. $element['#item']['nid'], array('attributes' => array('target' => 'blank_')));
  }
  return
$output;
}
/* Theme function for popup links */
function theme_formattertest_formatter_popup($element) {
 
$nid = $element['#item']['nid'];
 
$link_id = 'popup-'. $nid; // we want an unique id for each link so we can tell popups api to only do those =)
 
$output = '';
  if (!empty(
$nid) && is_numeric($nid) && ($title = _nodereference_titles($nid))) {
   
$output = l($title, 'node/'. $nid, array('attributes' => array('id' => $link_id)));
  }
 
popups_add_popups(array('#'. $link_id));
  return
$output;
}
?>

In the first function, we start from the formatter that nodreference has by default, and we just add a target="_blank" so that the browser opens it in a new window.

In the second function, first we put inside the variable $nid the nid of the referenced node, in order to build the id that we'll use on the link ($link_id). We need this so that we can tell popups to only use the js on those specific link. That way we avoid having to scan the whole document for popup links, making our site faster in the front end.

Conclusion.

Imagine for example, that your module also provides a default view. You can then use this view to pull out information depending on the content of a cck field. Any cck field that is using your formatter. No longer would you have to write complex and hard to maintain code in your template.php. You could just assign your formatter to any new field you create on any content type, reusing the same code.

Attachment Size formattertest.tar.gz 1.22 KB
Jun 02 2009
Jun 02

My rule of thumb for deciding what to post on this blog has been to document anything I've spent more than an hour trying to figure out. Today I've got a good one for anyone trying to create CCK fields as part of a module's installation process.

Back in Drupal 5 the Station module was made up of lot of custom code to track various values like a playlist's date or program's genre and DJs. During the upgrade to Drupal 6 I migrated that data into locked, CCK fields that were created when the module was installed. As people started to install the 6.x version of module I began getting strange bug reports about the Station Schedule that I couldn't seem to replicate on my machine.

Eventually after trying it on a fresh installation, I discovered the problem was that its fields weren't being created correctly by the hook_install() implementation when CCK and/or the field modules were installed at the same time as the Station modules. Meaning that the user who setup a new Drupal site, downloaded all the modules, checked the Station Schedule check box on the module list and let Drupal figure out the dependencies from the .info files would think the modules had installed correctly but they'd actually be missing several required field instances which would cause errors down the line. My first response to this problem was to add a hook_requirements() implementation that prevented the Schedule from being installed at the same time as the other modules:

<?php
/**
* Implementation of hook_requirements().
*/
function station_schedule_requirements($phase) {
 
$requirements = array();
 
$t = get_t();
  if (
$phase == 'install' && !module_exists('userreference')) {
   
$requirements['station_schedule_userreference'] = array(
     
'description' => $t('Sadly the Station Schedule cannot be installed until the User Reference module has been fully installed. User Reference should now be installed, so please try installing Station Schedule again.'),
     
'severity' => REQUIREMENT_ERROR,
    );
  }
  return
$requirements;
}
?>

This at least removed the "Surprise, you've got a broken site!" element, but it was annoying to have to reinstall the module. When I realized that it wasn't just the Schedule that was suffering from this problem—but also the Program and Playlist modules—I decided to look for a better solution.

After six hours of debugging via print statement—technically the Devel module's dsm() function (yes, I know the time would have been better spent figuring out how to get a proper PHP debugger running on OS X)—I found it boiled down to two issues:

  1. The field's columns weren't being populated because the fields' .module files weren't being included.
  2. CCK uses drupal_write_record() to record the field information but it was failing because content_schema() wasn't being called.

The first was simple enough to correct, I could manually include the module files. The second was much trickier, drupal_get_schema() calls module_implements() so that it only returns schema information for enabled modules but drupal_install_modules() installs the group of modules then enables the group. I was expecting that when hook_install() was called the required modules would be both installed and enabled. So in order to create my fields in station_schema_install() I'd need to get CCK and the fields enabled first. Feeling close to one of those head slapping moments I started studying module_enable() and realized it seemed safe to call from within a hook_install() implementation. It had the added bonus of including the module which solved the first problem.

I love it when you figure out the right way to do something and it turns out to also be the short way. It's really this simple:

<?php
/**
* Implementation of hook_schema().
*/
function station_schedule_install() {
 
drupal_install_schema('station_schedule');  // To deal with the possibility that we're being installed at the same time
  // as CCK and the field modules we depend on, we need to manually enable the
  // the modules to ensure they're available before we create our fields.
 
module_enable(array('content', 'userreference'));  $dj_field = array (
   
// FIELD DEFINITION OMITTED.
 
);  // Create the fields.
 
module_load_include('inc', 'content', 'includes/content.crud');
 
content_field_instance_create($dj_field);
}
?>

As always, I hope this saves someone else some trouble.

Feb 06 2009
Feb 06

I wasted more time that I want to admit do trying to figure this out. I was trying theme a specific CCK field named field_images on all the nodes where it appears. The devel_themer module was listing content-field-field_images.tpl.php as a candidate:

But after copying CCK's content-field.tpl.php into my theme and renaming it I couldn't seem to get the theme to pick it up. Roger López gave me the frustratingly simple answer on irc: "i think you need to have both templates in place"... duh. Copied content-field.tpl.php into my theme and everything worked great.

Jan 31 2009
Jan 31

When I first started this blog, I used Drupal's built in jQuery library to randomly show a picture in my header from a stockpile I put on my webserver. It worked great, but it was a manual process: selecting the pictures, sizing & cropping them, uploading them, etc. In the end, I rarely added any new pictures because it was a hassle.

I had long wanted to try something more automated and show a wider variety of more current pictures on my blog. The goal: automatically show selected pictures from my flickr stream with no extra steps other than posting to flickr and maybe adding a special tag.

Finally, the images you see in my header are randomly shown from a set provided automatically from my flickr stream and are automatically cropped and sized. All I have to do to add a new image to my site header rotation is add one extra tag to a picture when I upload it to flickr.

Here's the Drupal technology I used to make this possible. FYI: everything is done with stock Drupal core and contributed modules...absolutely no coding required!

  1. Create a special page type for images: I use some basic cck modules to create a special page type on my blog to display images and related information about them.
  2. Sync with flickr: I use the flickrapi and flickrsync modules to automatically download any new images I put in my flickr stream. Flickrsync syncs the images automatically on a schedule I determine via Drupal's stock cron process. Each new image downloaded from flickr creates a new image page and the title, description and tags from flickr are added to the page along with the image. I download all my flickr images, including the ones marked with a special tag to show them in my header.
  3. Size and crop the images: I use the imagecache module to automatically size and crop a copy of each image so they fit perfectly in my header. The automatic process doesn't always work perfectly...I might size and crop differently if I did it myself. But it often works well and sometimes even comes up with interesting, surprising results. Best of all: I don't have to do it ;)
  4. Display images randomly in the header: I use the indispensable views module to create a special view of the image pages created in the steps above that shows just one random image a random selection from only those images marked with the special image tag and displays the copy of the image copies of the images cropped and sized specifically for my header.

As an added benefit, I end up with a lifestream-style copy of all my flickr images on my blog, which I can also use as a dashboard to find images and change tags if I want to override the tags I've set in flickr itself (eg, to add or subtract an image from the header rotation).

Now with Dynamic Rotation

Thanks to an experiement I undertook for my review of Matt Butcher's excellent Drupal 6 JavaScript and jQuery book, I've now come full circle and used jQuery to add dynamic rotation to my header images. Here's how I did it.

First, I made a small adujstment to the view I created in step 4 above to have it return more than just one randomized image. You can adjust the view to return whatever number of randomized images you want by changing only the value for "items to display".

Then, I modified a the sticky_rotate.js example code Matt supplied with his book to affect my header images rather than the original sticky node teasers. I actually simplified the code because all my header images are the same height, so I could remove his code that calculated the tallest teaser. I mostly just changed the function and variable names to pertain to my use case. You can see my header_rotate.js script below and for a full explanation of what's going on, you should just buy Matt's book, because it's money well spent.

Finally, I just put my new header_rotate.js file in my theme folder and modified the theme.info file to include this new script

Voila! My header images now rotate dynamically with an adjustable delay and a nice fade feature. I'm sure something more sophisticated is warranted (like loading the images as needed via ajax or something more clever), but I was happy to have this small demonstration of Drupal and jQuery's simplicity and power after only a few minutes work.

// $Id$
/**
* Rotate through all header images returned by view,
* using jQuery effects to display them.
*
* Based on StickyRotate from Drupal 6 Javascript & jQuery
* Chapter 3, by Matt Butcher
* http://www.packtpub.com/drupal-6-javascript-and-jquery/book
*/

// Our namespace:
var HeaderRotate = HeaderRotate || {};

/**
* Initialize the header rotation.
*/
HeaderRotate.init = function() {
var headerImages = $(".view-header-rotating .views-row");

// If we don't have enough, stop immediately.
if (headerImages.size() <= 1) {
return;
}

headerImages.hide().css('height', '100px');
HeaderRotate.counter = 0;
HeaderRotate.headerImages = headerImages;

headerImages.eq(0).fadeIn('slow');
setInterval(HeaderRotate.periodicRefresh, 7000);
};

/**
* Callback function to show a new header image.
*/
HeaderRotate.periodicRefresh = function () {
var headerImages = HeaderRotate.headerImages;
var count = HeaderRotate.counter;
var lastHeaderImage = headerImages.size() - 1;

var newcount;
if (count == lastHeaderImage) {
newcount = HeaderRotate.counter = 0;
}
else {
newcount = HeaderRotate.counter = count + 1;
}

headerImages.eq(count).fadeOut('slow', function () {
headerImages.eq(newcount).fadeIn('slow');
});
};

$(document).ready(HeaderRotate.init);
Jan 08 2009
krs
Jan 08

Did you know the CCK Link field can accept tokens in the link title? I didn't. Now the link module provides all kinds of ways to format the combination of url + title that get entered, but when asked to have the actual node title link to the given url - I thought for sure I would have to resort to writing some code, either in template.php or in a tpl file.

But the Link module and Token module together made it easy. Set the Title to "static title" - this way the user will never be prompted when creating a node, and remember this is the link title, not the node title. Then set it to the token [title-raw] and you're done! CCK and possibly Views will all work together to get you something like <a href="http://drupalhigh.onsugar.com/link-fields-tokens-2672729/[user entered url]">[node title]</a>!

Filed in: drupal
Tagged with: cck
Jul 02 2008
Jul 02

I spent some time today trying to figure out how to create a CCK field as part of an hook_update_N function. Unlike previous versions of CCK, in 6 it's very easy to manipulate the fields from code.

The first step is to create the field using CCK's UI. Once you've got the field setup the way you'd like it use PHP's var_export() to dump the contents of the node's field as an array:

var_export(content_fields('field_translator_note', 'feature'));

That'll give you some massive array definition that you can copy and paste into your code.

<?php
$field
= array (
 
'field_name' => 'field_translator_note',
 
'type_name' => 'feature',
 
'display_settings' =>
  array (
   
4 =>
    array (
     
'format' => 'hidden',
    ),
   
2 =>
    array (
     
'format' => 'hidden',
    ),
   
3 =>
    array (
     
'format' => 'hidden',
    ),
   
'label' =>
    array (
     
'format' => 'hidden',
    ),
   
'teaser' =>
    array (
     
'format' => 'hidden',
    ),
   
'full' =>
    array (
     
'format' => 'hidden',
    ),
  ),
 
'widget_active' => '1',
 
'type' => 'text',
 
'required' => '0',
 
'multiple' => '0',
 
'db_storage' => '0',
 
'module' => 'text',
 
'active' => '1',
 
'columns' =>
  array (
   
'value' =>
    array (
     
'type' => 'text',
     
'size' => 'big',
     
'not null' => false,
     
'sortable' => true,
    ),
  ),
 
'text_processing' => '0',
 
'max_length' => '',
 
'allowed_values' => '',
 
'allowed_values_php' => '',
 
'widget' =>
  array (
   
'rows' => '',
   
'default_value' =>
    array (
     
0 =>
      array (
       
'value' => '',
      ),
    ),
   
'default_value_php' => NULL,
   
'label' => 'Translator\'s note',
   
'weight' => NULL,
   
'description' => '',
   
'type' => 'text_textarea',
   
'module' => 'text',
  ),
);
// Need to load the CCK include file where content_field_instance_create() is defined.
module_load_include('inc', 'content', 'includes/content.crud');// I wanted to add the field to several node types so loop over them...
foreach (array('athlete', 'feature', 'product', 'tech') as $type) {
 
// ...and assign the node type.
 
$field['type_name'] = $type;
 
content_field_instance_create($field);
}
?>

High-fives to all the CCK developers for making this so easy.

Jun 18 2008
Jun 18

You want to print the content type name along with the 'submitted' info. This will be particularly useful for those who have created custom content types, with or without CCK.

Solution

  1. Edit node.tpl.php to add the content type name within a span with a class name (e.g. 'content-type-name') and...

  2. style that span element in your theme's style.css file.

In the following example, the human-readable name for the content type is 'News', and the content type is displayed to the right of the 'submitted' info. Let's try and achieve that.

Your file node.tpl.php may look like this before the edit (I am only providing a snippet of the template here):

<?php if ($submitted): ?>
    <span class="submitted"><?php print $submitted; ?></span>
<?php endif; ?>

If it does, then you will change the above snippet to that:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?> &mdash; <span class="content-type-name"><?php print node_get_types('name', $node); ?></span></span>
<?php endif; ?>

You may want to exclude a certain content type. For example, you may not want 'page' to appear in page nodes. Whenever you want to exclude one content type, you will do like so:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?><?php print ($node->type != 'page') ? ' &mdash; <span class="content-type-name">' . node_get_types('name', $node) . '</span>' : ''; ?></span>
<?php endif; ?>

When you want to exclude more than one content type, say 'story' and 'page', you will do like so:

<?php if ($submitted): ?>
  <span class="submitted"><?php print $submitted; ?><?php print (!in_array($node->type, array('story', 'page'))) ? ' &mdash; <span class="content-type-name">' . node_get_types('name', $node) . '</span>' : ''; ?></span>
<?php endif; ?>

This will work in Drupal 5 as well. Again, this solution works only for PHPTemplate-powered themes.

You need to provide the machine-readable names for content types you wish to exclude, because $node->type is used in the condition, and it is the machine-readable name for the content type. Why are we comparing machine-readable names? Simply to avoid database queries. There is one database query we cannot avoid in our solution, it is the one query we execute to print the human-readable name for the content type.

Possible variations: place the content type information somewhere else in the node; display the content type information only in the teaser; etc.

The machine-readable name of a content type is usually not the same as its human-readable name. Sometimes the difference is only in capitalization:

We used the Drupal function node_get_types() in our solution.

The function can be used in so many ways that it gets rather confusing, as webchick points out here. The function can be used in these seven different ways:

node_get_types('type', $node);
node_get_types('type', $node->type);
node_get_types('names');
node_get_types('name', $node);
node_get_types();
node_get_types('types', NULL, TRUE);
node_get_types('module', $node);

When we use the function like so:

node_get_types('name', $node);

... the returned value is a string, and it is the human-readable name for the content type of the $node object.

By the way, the $node object is available in all these templates:

  • comment.tpl.php
  • node.tpl.php
  • page.tpl.php when a node is displayed on its dedicated page, ie: at node/x

In Drupal 7, there will be a new function to get the human-readable name for the content type of a node:

$name = node_get_name($node);

Until then, we'll make-do-and-mend with node_get_types().

Last edited by Caroline Schnapp about 5 years ago.

May 12 2008
May 12

Install ImageAPI before upgrading to ImageCache 2.x!!

I'm finally comfortable enough with my Image* namespace to have official releases of the 2.x series of ImageField and ImageCache + ImageAPI...

I'd like to extend thanks to everyone who has filed issues and submitted patches, especially Drewish and Quicksketch.

Now that my 2.x's are out I can start on the 6.x ports. ImageAPI already has a working port in HEAD, but needs some bug fixes ported from 5.x-1.x. There is a patch for ImageCache pending in it's queue and it should be a trivial port.

ImageField will be the first to see an official release, since it will be needed to test the ImageCache port.

I will be developing a core compatible file api for imagefield and filefield to consolidate specialized functions used by both to maintain path correctness, implement hook_file, and hide a few messages from some of the chattier core file functions.

This will delay D6 ImageField development a little. Since CCK is still at alpha I don't see it delaying and official ImageField release. I will also probably be getting back to the CCK issue queue so I can get my D6 ports out and stable. /me waves to KarenS and Yched.

--outta the weeds
.darrel.

Apr 01 2008
Apr 01

What is GeoLocation module?
For OSCMS, over a year ago, I wrote a module called geolocation as an example CCK field. It has languished in CVS for quite a while, I finally set up a project for it today, and I'm looking for a co-maintainer.

Why do you need help?
I don't have time right now. I'm in the midst of major upgrades on the image* modules I maintain, doing new development on filefield and other media related modules, trying to keep up with my day job, and preparing to do 6.x ports of the modules I maintain.

Ok, so what do you need?
I need someone who would like to run with the module and implement a few necessary features for it. I want to stay involved because I want to play with different proximity searching techniques and would like a free hand to do it.
This would be a great role for an aspiring developer who already has some basic CCK and Views skills and wants a chance to expand them, and work on a module with a lot of potential.

What is the condition of GeoLocation?
GeoLocation is currently functional as a field module and works with Drupal 5.x. It has a basic text widget as well as yahoo maps integration that hasn't been tested since OSCMS. (I did yahoo maps since we were on their turf.)

What does GeoLocation need?

  • Improve the views integrations by adding filters for longitude, latitude, and proximity.
  • Google Maps input widget
  • Gmap Module integration

If you're interested please contact me @ http://drupal.org/user/22202 and I will be happy to discuss in further detail what I'm looking for in a co-maintainer and the direction of the module.

Jan 25 2008
Jan 25
This is a cross-posting from a handbook page I wrote on drupal.org. It may or may not accurately reflect the latest revision; for the latest revision, please view the original handbook page.

"There is more than one way to do it." This philosophy isn’t limited to Perl; it generally extends to open source software itself. Likewise, there are four modules that are intended to integrate taxonomy fields into the Content Construction Kit (CCK) for Drupal; these modules have been identified to have similar or overlapping functionality. Below is a list of the 4 modules.

  • Content Taxonomy (content_taxonomy)
  • CCK Taxonomy Fields (cck_taxonomy)
  • Taxonomy Super Select (taxonomy_super_select)
  • CCK Taxonomy Super Select Ultra (cck_taxonomy_ssu)

This document serves to compare and contrast these modules in order to make it easier for site owners to choose one to suit their site development needs. The following issues are addressed for each module:

  • ease of installation and ease of use
  • list of any dependencies or companion modules associated with each module (CCK, Taxonomy, Views, API modules, externally hosted code, etc.)
  • features and functionality
  • pros and cons
  • list of access permissions
  • evaluation of the documentation/handbook (if any)
  • status of the most stable release (development, alpha, beta, official)
  • if it is still in development, an evaluation for its potential for successful completion
  • a brief examination of the its issue queues for any glaring or recurring problems

Content Taxonomy

Content Taxonomy comes with three modules in addition to the main module. It only depends on the Content module (the main CCK module), the core Taxonomy module (obviously), and in addition to the default "Select List" widget, it also supports the widgets "autocomplete," "options," and "activeselect." "Activeselect" depends on another third party module Activeselect, since it's not a default widget. The amount of widgets it supports is even greater than the core Taxonomy module, even allowing autocomplete on non-freetagging vocabularies. It can optionally support Views (another third party module) if you have Views installed, but Views itself also has taxonomy support.

Installation and usage are relatively easy. Upon enabling, Taxonomy Fields would be available on the "Add field" page for a content type, depending on what widget modules you enabled. Once they are added, they replace existing fields provided by the Taxonomy module. Unfortunately, there is a funky reciprocal relationship problem; a vocabulary is normally limited to a few content types (or only one), but Content Taxonomy lets you choose any vocabulary to be added on to as a CCK field. It should respect Taxonomy settings and only let users choose a vocabulary that is meant for the content type they're configuring. Thus a user has to be careful to make sure that this vocabulary-content type relationship is valid in both ways. This is perhaps the only logical problem for Content Taxonomy. Aside from that, users can choose the depth of the taxonomy tree displayed (it can be unlimited), specific terms to be displayed (if the vocabulary is a tree), whether not it's required, whether not multiple values are allowed, default values, where the taxonomy information is stored (as a tag, in CCK table, or both), and etc. In other words, Content Taxonomy replicates some of core Taxonomy's functionality. It should simply honor the settings set in core Taxonomy.

At a glance

  • Dependency: Taxonomy and Content. Optionally Activeselect (if installed) and Views (if installed).
  • Access permissions: Content Taxonomy does not provide any additional access permissions on its own.
  • Documentation: There are no documentation or handbook pages on Content Taxonomy, but given its straight-forward easiness, there's no need for any.
  • Release: Content Taxonomy has no official releases. There are only development snapshots, but they're relatively stable.
  • Issues: There are quite a few issues in Content Taxonomy's issue queue. In the past two weeks, there are 6 bug reported. The queue is quite active, however; there are patches pending review.
  • Development: Content Taxonomy is still apparently in development. But once small edges are ironed out, it should have an official release. There are quite a number of users of Content Taxonomy.

Screenshots

CCK Taxonomy Fields

CCK Taxonomy Fields comes with only one main module. It depends on the Content module (the main CCK module), the core Taxonomy module (obviously), and Views. Views is not optional; it is mandatory.

Installation and usage are relatively easy. Upon enabling, CCK Taxonomy Fields would be available on the "Add field" page for a content type, depending on what vocabularies you have defined. Once they are added, they coexist with existing fields provided by the Taxonomy module. Unfortunately, CCK Taxonomy Fields suffers from the same problem Content Taxonomy suffers from: you can add a taxonomy field for a vocabulary that is not meant for a particular content type. However, the author, Robert Douglass, states that this is intentionally designed that way, so that the whole taxonomy mechanism can be sidestepped. In addition to that, you also cannot choose different widgets or act on a freetagging vocabulary, which is something Content Taxonomy can do. Unlike Content Taxonomy, however, CCK Taxonomy Fields simply honors the settings set in core Taxonomy, which is a great plus. This means CCK Taxonomy Fields is a great drop-in module to simply bring core Taxonomy-defined fields into the world of CCK fields.

At a glance

  • Dependency: Taxonomy, Content, and Views.
  • Access permissions: CCK Taxonomy Fields does not provide any additional access permissions on its own.
  • Documentation: There are no documentation or handbook pages on CCK Taxonomy Fields, but given its straight-forward easiness, there's no need for any.
  • Release: CCK Taxonomy Fields has a stable, official release.
  • Issues: There is one bug in the issue queue that involves filters and CCK Taxonomy in the past three weeks. It seems to have something to do with the upgrade path.
  • Development: CCK Taxonomy Fields is pretty mature.

Screenshots

  • Fields are based on vocabularies in CCK Taxonomy, unlike Content Taxonomy, whose fields are based on widget types.
  • There are few options to tinker with in CCK Taxonomy.
  • What the field actually looks like during node editing.
  • What the field actually looks like during node viewing without any custom theming.

Taxonomy Super Select

Taxonomy Super Select comes with only one main module. It only depends on the core Taxonomy module, not even requiring CCK.

Installation and usage are relatively easy. Upon enabling, Taxonomy Super Select adds on options to settings pages for individual taxonomy vocabularies. Therefore, there is no entanglement between the relationship of a vocabulary itself and its corresponding CCK field, because CCK isn't even involved. Unlike Content Taxonomy, Taxonomy Super Select judges which widget is better for you, depending on the vocabulary. It can elegantly handle any kind of vocabulary settings a user can throw at it, and it would come up with a good, enhanced interface for it. Taxonomy Super Select achieves the ultimate integration with core Taxonomy by not integrating with CCK fields at all. For those merely wanting to change taxonomy selection widgets for nodes, Taxonomy Super Select is blissfully simple. The only limitation Taxonomy Super Select has is that it can only work up to five levels of nesting on tree vocabularies; the author needs technical help regarding this.

At a glance

  • Dependency: Only Taxonomy.
  • Access permissions: Taxonomy Super Select does not provide any additional access permissions on its own, nor does it need any.
  • Documentation: There are no documentation or handbook pages on Taxonomy Super Select, but given its straight-forward easiness, there's no need for any.
  • Release: Taxonomy Super Select has a stable, official release.
  • Issues: In the past three weeks on the issue queue, there have been two instances of an issue where Taxonomy Super Select would stop working mysteriously, after which it would only work on freetagging vocabularies.
  • Development: Taxonomy Super Select is in its first official release, so there may be a few things to iron out.

Screenshots

  • Less is more: the only additional options that are tacked into vocabulary editing pages provided by core Taxonomy. (click to enlarge)
  • All your vocabulary and user interface are belong to us. (as seen when viewing a node, click to enlarge)

CCK Taxonomy Super Select Ultra

CCK Taxonomy Super Select Ultra comes with only one main module. It depends on the Content module (the main CCK module) and the core Taxonomy module (obviously). It aims to combine CCK Taxonomy with Taxonomy Super Select.

Installation and usage are relatively easy. Upon enabling, CCK Taxonomy Super Select Ultra would be available on the "Add field" page for a content type, depending on what vocabularies you have defined. Once they are added, they coexist with existing fields provided by the Taxonomy module. CCK Taxonomy Super Select Ultra combines the CCK integration offered by CCK Taxonomy with the user interface and widget sweetness of Taxonomy Super Select. It also inherits the weakness (or feature, depending on your viewpoint) of CCK Taxonomy: you can apply CCK Taxonomy Super Select Ultra fields to vocabularies that are not meant for that content type. Likewise, it inherits the honoring quality of both CCK Taxonomy and Taxonomy Super Select, respecting the settings set in core Taxonomy.

At a glance

  • Dependency: Taxonomy and Content.
  • Access permissions: CCK Taxonomy Super Select Ultra does not provide any additional access permissions on its own.
  • Documentation: There are no documentation or handbook pages on CCK Taxonomy Super Select Ultra, but given its straight-forward easiness, there's no need for any.
  • Release: CCK Taxonomy Super Select Ultra has a stable, official release.
  • Issues: There are two minor bug reports on the issue queue for the past four weeks.
  • Development: CCK Taxonomy Super Select Ultra is in its first official release, so there may be a few things to iron out.

Screenshots

  • Like father, like son. CCK Taxonomy Super Select Ultra inherits the simplicity in configuring from Taxonomy Super Select. (click to enlarge)
  • Original core Taxonomy field (top) versus CCK Taxonomy Super Select Ultra field (bottom), during node editing.

The Verdict

There's so many choices, so what to use? There's actually more than one answer. Each answer is based on the pros and cons of each module.

  • If you need to bridge taxonomy fields to CCK and you want the freedom to choose widgets for yourself, Content Taxonomy is your best choice.
  • If all you need is to CCK-ify taxonomy fields and you like, and want the user interface that Taxonomy Super Select provides, then CCK Taxonomy Super Select Ultra is for you.
  • If you don't need the user interface fanciness and all you need is to simply bridge taxonomy fields to CCK with no other user interface objections, CCK Taxonomy is what you want.
  • If all you need is a better user interface and you don't need CCK, Taxonomy Super Select is it.
Dec 11 2007
Dec 11

the blessing and curse of cck is the ability to quickly create very complex node types within drupal. it doesn't take very long before the input form for a complex node type has become unmanageably long, requiring your user to do a lot of scrolling to get to the bottom of the form. the obvious solution is to break your form into multiple pages, but there is no easy way to do this. there do exist two proposed solutions to this, the cck wizard module and a drupal handbook entry. however, the well-intentioned cck wizard module doesn't seem to work, and the example code in the drupal handbook becomes tedious to repeat for each content type. to fill the void, i bring you cck witch

cck witch is based on the same premise as the handbook entry : the most natural way to divide a cck form into pages is to use field groups. from there, however, cck witch diverges, taking a relatively lazy, yet effective approach to the problem of multi page forms: on every page we render the entire form, but then simply hide the fields and errors that do not belong to the current step. it also offers an additional feature : when the form is complete and the node is rendered, an individual edit link is provided for each step - allowing the user to update the information only for a particular page in the form, without having to step through the entire wizard again.

if you've now read enough to be curious to see the goods, then please, be my guest and skip straight to the live demo.

the demo

in the demo, you will walk through a three step multi page cck form that invites you to specify your dream house. before proceeding to the next step in the form, the user must complete the required fields on the previous steps. on all steps other than the first step, the user may go back and edit their data for the previous step.

when the form is complete and the node is viewed, we add an edit link inside each field group. clicking this link allows the user to edit only the fields within that group, rather than requiring the user to step through the entire form again.

disclaimer

be warned, this is a pre-alpha release. also, this wizardly wonder is not meant for the drupal novitiate. before using it you must

  • patch drupal core, adding 4 lines to the form_set_error function.
  • override two form related theme functions
  • follow a simple set of conventions when configuring your cck content type

manual

step zero - download cck witch

get your copy of this pre-alpha release here

step one - patch drupal core

in forms.inc replace the form_set_error with the following method. this exposes an option to remove errors from the list. it also stops drupal from adding form errors to the drupal message list. do not perform this step without also performing step two. if you do, all form errors will mysteriously vanish.

function form_set_error($name = NULL, $message = '', $remove = FALSE) {
  static $form = array();
 
  if(!$remove) {
    // Set a form error
    if (isset($name) && !isset($form[$name])) {
      $form[$name] = $message;
    }
  }
  else {
    // Remove a form error
    if (isset($name) && isset($form[$name])) {
      unset($form[$name]); 
    }
  }
 
  return $form;
}

step two - override two form theme functions

next, you need to override the theme function for a form element to display the form error messages inline in your form, instead of in a big blob of messags at the top. this is a nice thing to do regardless of whether or not you want multi page cck forms. do this by overriding theme_form_element method and then adding the following in the location of your choice. (right at the bottom, immediately before the closing div will do fine.)   if($element['#parents']) {
    $form_element_error = form_get_error($element);
  }

  if ($form_element_error && $element['#type'] != 'radio') {
    $output .=' <div class="form-error">' . $form_element_error . "</div>\n"; 
  }

and, if you want all the buttons at the bottom of the form to line up nicely, override the theme_node_form method with the following

function theme_node_form($form) {
  $output = "\n<div class=\"node-form \">\n";
  $output .= "  <div class=\"standard\">\n";
  $output .= drupal_render($form);
  $output .= "  </div>\n";
  $output .= "</div>\n";
  return $output;
}

step three - configure your cck content type

when configuring your cck content type, create one group per page. you must name the groups "step 1", "step 2", etc. also, you must visit the display fields tab and submit the form there. you don't have to change anything, just submit the form. (this is clearly a cck bug, but we'll just work around it for now.)

see below for an example configuration:

step four - configure cck witch

finally, visit admin -> content -> multi-page form settings and set the number of pages for each cck content type. the cck witch module will only interact with those content types where the number of pages is greater than one.

future improvements

  • currently, cck witch presumes that your content type does not include a body field. complex cck node types rarely do. handling the body field is easy, it's just not obvious to me which page the body should appear on.
  • if there are other, non-cck options on your form (for example, the administrative meta tags or menu settings) these currently appear on all pages of the form. you can set them whenever you please. possibly, these should all be moved to an implied final page in the flow?
Nov 29 2007
Nov 29

using the term "content management system" to describe the drupal cms understates it's full potential. i prefer to consider drupal a web-application development-system, particularly suitable for content-heavy projects.

what are the fantastic four?

drupal's application development potential is provided in large-part by a set of "core" modules that dovetail to provide an application platform that other modules and applications build on. these modules have become a de-facto standard: drupal's fantastic four. our superheros are cck, views, panels and cck field types and widgets. if you are considering using drupal to build a website of any sophistication, you can't overlook these. note that cck field types and widgets isn't a real module, but rather a set of related modules.

flying with the four

getting a feel for how these modules work and interact isn't trivial, so i'll give you a brief introduction to the super-powers of each of them, and then take you step-by-step through an example, with enough detail that you can easily get it working on your system. or, if you want to see a professional implementation built on the same principles, check out the zicasso photo competition.

meet our heros

the content construction kit or as it's more commonly referred to, cck, provides point-and-click attribute extensibility to drupal's content-types. for example, if you site is about photography, you could define a type of page on your site called "photograph" and then add typed attributes to it, shutter-speed (integer), flash (boolean) etc. cck then automagically creates forms for you (or your users) to create and edit these types of pages, providing suitable validation, gui controls etc.

the cck fieldtype modules each define a new type of field that can be used in your cck content types. one example is the imagefield module, allowing your cck types to have fields of type image. this allows your "photograph" page to contain the actual photograph itself. there are many more types that you can find in the cck modules download area.

the views module allows simple point and click definition of lists of drupal nodes, including your cck nodes. you can control not only what is in the list, but how the list is displayed, including sorting, pagination etc. these lists can be conveniently displayed as blocks, full blown pages or even rss feeds. for example, you could define a list of photographs that had been highly rated by users on your photography site.

the panels module allows you to create pages divided into sections, each section containing a node, block, view or any custom content. so without any knowledge of html or css you can create complicated and powerful layouts. for example, you could create a page with two views, one showing a list of recently submitted photographs and one showing a list of highly ranked photographs. this module is currently undergoing a huge facelift and panels2 is in alpha at the time of writing

an example

to illustrate how the fantastic four can be put to good use, let's continue with our photography theme and create a simple photo-competition application. this application (shown to the right) allows the creation of a simple photo competition entry using a form. the main page shows two lists, one of recent entries and of "featured" entries. the application also has a detail page for each photograph where anonymous users can leave comments.

step one - install the modules

i'm going to assume that you've got a basic drupal install up-and-running. if you haven't, please refer to one of my previous blogs, easy-peasy-lemon-squeezy drupal installation on linux. once you've done this, you should install 6 modules. cck, views, panels2, imagefield, email field and imagecache. on linux, you can do this as follows. cd to your drupal directory (the one containing cron.php etc.), create the directory sites/all/modules if necessary, and download the modules:

# wget http://ftp.drupal.org/files/projects/panels-5.x-2.0-alpha14.tar.gz \
http://ftp.drupal.org/files/projects/views-5.x-1.6.tar.gz \
http://ftp.drupal.org/files/projects/cck-5.x-1.6-1.tar.gz \
http://ftp.drupal.org/files/projects/imagefield-5.x-1.1.tar.gz \
http://ftp.drupal.org/files/projects/imagecache-5.x-1.3.tar.gz \
http://ftp.drupal.org/files/projects/email-5.x-1.x-dev.tar.gz

then unzip them and set the permissions properly:

# for file in *.gz; do tar xvfz $file; done
# chown -R www-data.www-data *

now to to the administrative interface, http://example.com/drupal/admin/build/modules and enable the modules in question.

finally, now go to http://example.com/drupal/admin/user/access and grant access to the panels and views module features to the role you are using e.g. "access all views" to "authenticated user" and "administer views" to your "developer" or "admin" roles. also grant "post comments without approval" and "post comments" and "access comments" to the anonymous user.

note we're using the alpha panels version, panels2. it's not quite ready for prime time, but it's hard to resist. it kicks ass.

step two - create a new content type

now it's time to create a new content type. navigate to the content types page at http://example.com/drupal/admin/content/types, and create the "photo competition entry" as shown below.

now let's add two new custom fields to our photo competition type: email and photograph. these fields make use of the new cck field type modules we just installed.

create the email field as follows:

create the photograph field as follows:

now go to http://example.com/drupal/admin/user/access and allow anonymous users to "create photo_entry content" and "edit own photo_entry content"

step three - setting our themes

because i'm bored with garland, let's change the default theme to "minnelli" in http://example.com/drupal/admin/build/themes, change the administratin theme http://example.com/drupal/admin/settings/admin back to garland.

step four - create some content

now that we've defined our new content type, we can go ahead and create some new content. navigate to http://[...]/node/add/photo-entry and fill out a few entries. you can see your new create form in action, complete with validation (shown to the right).

it's best to do this as the anonymous user to see the usual user experience. it's convenient to stay logged in as admin and use another browser e.g. internet explorer (bleah) for your regular (anonymous) user.

step five - configure imagecache

the imagecache module allows you to define an arbitrarily large number of image transformations (presets) including scaling, resizing and cropping. let's define two transformations, one preview to create a 200px wide scaled down preview. the second transformation, thumbnail is slightly more complex, and creates a square image, 120px by 120px that is a scaled, centered crop of the original. rockin.

create the thumbnail preset as follows:

create the preview preset as follows:

you should now be able to test your presets with the content you created e.g. if you uploaded an image called myImage.jpg, you can view your transformed images at:

step six - create our views

the views module allows you to create lists of nodes. we're going to create two views:
  1. recent_photo_entries, a list of the five most recently submitted entries. the list shows a thumbail of the image and the email address of the creator.
  2. featured_images, a list of the two most recently commented on images. this list shows a preview of the image, the image title and the email address of the creator.

create the recent view as follows:

create the featured view as follows:

step seven - create the panel page

the last step is to create the panel page to host our content and views. go to http://example.com/drupal/admin/panels/panel-page and create a new "two column stacked" layout, as shown below:

put custom content in the top panel, your recent view in the left panel and the featured view in the right panel. for the views, be careful to select a "view type" of block.

the following image shows the custom content you should create in the top panel:

the final image shows the configuration screen for the recent view (left panel). the right panel is very similar:

finally go to the "site information" administrative section: http://example.com/drupal/admin/settings/site-information and set your new panel as the home page i.e. put "photo-competition" in the default front page box.

you are done and your site should look something like:

further work

there is a lot that you could simply do to enhance this example, for example:
  • installing the jrating or fivestar module and allowing users to vote on photographs using a nice javascript control.
  • creating a view that implements an rss feed for photo competition entries.
  • using css to style your views and nodes.

check out a professional drupal photo competition based on these same principles at zicasso

tech blog

if you found this article useful, and you are interested in other articles on linux, drupal, scaling, performance and LAMP applications, consider subscribing to my technical blog.
Oct 29 2007
Oct 29

previously, we discussed implementing all of the node hooks for CCK content types except hook_access. unfortunately, there is no access op for hook_nodeapi. adding this to drupal core is the topic of much discussion on drupal.org. so far a resolution to the issue has failed to be included in drupal 5 and drupal 6, and is now on deck for consideration in drupal 7.

this is a complicated issue, and the experts are debating with good cause. in the meantime though, if you need to move on, here's what you can do.

  • install this patch to node.module
  • you now have an access op exposed in hook_nodeapi

one reason that the debate is dragging on on this topic, is that the drupal developers are concerned that access control is already too complicated, and this addition will simply make drupal access control incomprehensible. this is a valid point, and to use this patch properly you do need to understand the access control order of evaluation. when determining whether or node a user may view a node, here are the questions drupal asks

  1. does the user have 'administer nodes' permission. if yes, always return true
  2. does the user have 'access content' permission. if no, always return false
  3. invoke the new hook_nodeapi methods.
    1. if no hook_nodeapi returned an explicit opinion on the matter, keep going.
    2. otherwise, if any hook_nodeapi returned true, return true
  4. invoke the hook_access method, if any. (note, there may be only one of these!)
    1. if no hook_nodeapi returned an explicit opinion, keep going
    2. if an opinion was returned, return that
  5. now check was the node_access table has to say. if no opinion, keep going
  6. is the user the author of the node? if yes, return true
  7. give up, return false

phew, that's a complicated flow of execution. are there any easy guidelines we can draw from this? yes . . .

one downside of granting access control to hook_nodeapi is that there may now be multiple modules with an opinion on the matter. this forces the drupal core developer to make a choice as to what to do when there are multiple, conflicting answers. in this patch, they have chosen to allow positive responses to dominate over negative responses. i'm personally not convinced they will stick with this decision, so, in the meantime, if you're using this patch, try and stick to a convention in which you implement only one hook_nodeapi access control method per content type. in doing this, you're simply allowing your CCK content types to function like any other content type, rather than opening a huge kettle of access control worms.

Oct 26 2007
Oct 26

a common path followed by advanced drupal developers using cck is the following

  1. create a content type using cck
  2. create a supporting custom module to handle advanced customizations. typically, the module is given the same name as the content type

in this custom module, developers then attempt to implement standard drupal hooks like hook_access and hook_submit. much confusion then arises as to why the drupal hook is not firing for the cck content type.
the reason is the following. hook_access, hook_insert, hook_submit, hook_update and hook_view only fire for the module that owns the content type. for cck content types, the module that owns the content type is content (e.g. cck) not your supporting custom module. therefore, drupal leaves your supporting custom module totally out of the loop!

so what's a developer supposed to do? for hook_insert, hook_submit, hook_update and hook_view use hook_nodeapi instead. let's say your content type is called food, and your module is also called food. then, the hook_nodeapi method might look something like

function food_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
  switch ($op) {
    case 'view':
      if($node->type == 'food') {
        food_view($node);
      }
      break;
    case 'validate':
      if($node->type == 'food') {
         food_validate($node);
      }
      break;
  }
}

in which food_view and food_validate are just normal methods that you also implement in your module.

unfortunately, hook_access is an entirely different story, now addressed in a later chapter.

May 04 2006
May 04

Watch this screencast to learn how to use Drupal to create Google Maps mashups of virtually any arbitrary data or content with no coding in minutes. For the example shown in this screencast I took a csv file of crime data provided by the San Francisco government and turned it into a usable google maps mashup in about 10 minutes.


14 minutes / 60megs

To play along at home you will need to install the following:

Please feel free to leave any comments, questions, or feedback.

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