Site Design Notes

This book is intended for the webmaster, and only contains information relevant to the maintenance of the site.  Before you begin, there are two things you should do:

  1. Get and read a couple of good books on Drupal, one on using the admin interface, and one on how the underlying system works (Pro Drupal Development is an excellent resource for the later).
  2. Download, install, and run the "Site Documnetation" module:  This will provide you with a complete overview of the structure of the site.  All of its themes, node-types, blocks, and contents.
  3. Review the Views documentation ( - the site makes extensive use of Views, and CCK.

Once you've got a basic idea about what Drupal is and how the site is structured, this document will fill in some of the niggly little details.


Some known issues we should try to resolve:


Custom Theme

One of Drupal's primary powers is the seperation of the data processig from the presentation. takes full advantage of these abilities by defining a custom theme.  The "lasqueti theme" is documented here, along with some sub-pages that describe some of the custom theme programming.

The Lasqueti theme is a "hard-coded" version of the Denver theme.  Denver is a very flexible theme and specifies vitually everything with configurable parameters.  That is great for proto-typing, but slow for the production site.
The Denver theme was set-up to look "correct", and them copied to the Lasqueti theme.  The Lasqueti theme was then editted to "hard code" the various parmater values set in Denver.

Several additional changes were made to Lasqueti theme, for example:

All of this is documented in the sites/all/themes/lasqueti readme.

The Lasqueti theme could have a set of sub-themes, which simply change the banner for special parts of the site (e.g., like the marketplace) or provide style to hide images or other bandwidth intensive content (a low-bandwidth style).

Important - from the Denver Theme readme file:
"to prevent rendering errors in IE you must enable the CSS Aggregator in admin/settings/performance... Enable "aggregate and compress CSS files" to make your production drupal site run faster, and to prevent IE rendering errors."

Custom Code in PHPtemplate

The following functions were added to PHPtemplate in the lasqueti theme to obtain the required functionality.  Some of these should probably be made into a custom module.

Custom Node (page) titles using images

see: HowTo: Replace node title with a related image |


// This function is used to substitue the node title with an image of the same name.
// Really good candidate to turn into a small module.
function _phptemplate_variables($hook, $vars = array()) {
  switch ($hook) {
    case 'page':
    // substitute node title with an image, if a suitable replacement can be found.
    // save original title text for use in head and breadcrumb.
    $vars['breadcrumb_title'] = $vars['title'];
    $vars['head_title'] = $vars['title'];
    // Substitute title only for nodes...
    if (arg(0) == 'node' && is_numeric(arg(1))) {
      $node = node_load(arg(1));  // (expensive, unless arg(1) is already loaded, which is should be at this point.)
      // ... of one of the listed types - add node types to process to the array.
      if (in_array($node->type, array('page', 'page_layout_b'))) {
        // convert title to suitable filename and derive both file system path and URL.
        $titleFile = clean_title($vars['title']) . '.gif';
        $titleImage = base_path() . "files/images/titles/" . $titleFile;
        $titleURL = "http://" . $_SERVER['SERVER_NAME'] . $titleImage;
        $titlePath = $_SERVER['DOCUMENT_ROOT'] . $titleImage;
        // $vars['title'] = $titleURL;   // DEBUG - see what's being produced

        // Determine if a suitable replacement image for title can be found, and make substitution.
        if ( file_exists($titlePath) ) {
          $newTitle = '<img alt=\'' . $vars['title'] .'\' src=\'' . $titleURL .'\' />';
          $vars['title'] = $newTitle;
  return $vars;

// Prepare some general title text for use as a file name.
// Remove special HTML characters, trim whitespace, convert to lower-case
// repace spaces with underscores.
function clean_title($string)
    $cleanString = htmlspecialchars_decode( strtolower(trim($string)), ENT_QUOTES );
    $cleanString = str_replace(array("& ", "'"), '', $cleanString);
    // $string = strtolower(preg_replace("/[^a-zA-Z0-9 ]/", "", $string));  // slower, but more general purpose.
    return str_replace(' ', '_', $cleanString );;


Custom Theming for Summary Views

// Create a mock-menu from a view's summary (usually for a block view)
// See Example: How to Display a Summary View as a Mock Menu
function _summary_menu($view, $level, $nodes) {
  foreach ($nodes as $node) {
    $list .= '<li class="leaf">' . views_get_summary_link($view->argument[$level]['type'], $node, $view->real_url) . "</li>\n";

  if ($list) {
    return "<div class='menu'><ul>$list</ul></div>";

// Photo albums are per-user, so agrument is user ID UID
function phptemplate_views_summary_photo_albums($view, $type, $level, $nodes, $args) {
  if ($type == 'block') {
    return _summary_menu($view, $level, $nodes);
  else {
    drupal_add_css(drupal_get_path('theme', 'lasqueti') .'/css/image_gallery.css', 'theme');
    // Need generic way to get image file info? - must use name of CCK field.
    // Replace this with the name of your image field.
    // Uncomment the print_r below to figure out what it is.
    $cck_fid_field = 'node_data_field_image_field_image_fid';
    $content .= '<ul class="galleries">';
    $title = "'s Personal Photo Album";
    $baseUrl = $view->url;
    // Build a small, single node teaser for each query in the summary and
    //   use it as a link to the actual view page with that argument.
    foreach ($nodes as $query) { 
       // Get the argument for this view from the query.
       $uid = $query->uid;     // argument is user id;
       $user = $query->name;   // query also has user's name.
       // Build a 1 item query to grab the first node in this view
       $result = views_build_view('items', $view, array($uid), false, 1);

       // Grab the image from that node to display in our summary.
       // The file id comes from the node info. we got back from views.
       // print_r($result['items'][0]);
       $fid = $result['items'][0]->$cck_fid_field;
       $modDate = $result['items'][0]->node_changed;

       // Query the database to get the file object
       $file = _imagefield_file_load($fid);
       $imgTag = theme('imagecache', 'Thumbnail', $file['filepath'], 'Latest image from gallery', $user . $title);

       $url= $baseUrl .'/'. $uid;
       $linkText = $imgTag .' '. $user . $title;
       $content .= _theme_gallery_summary($imgTag, $url, $user.$title, $query->num_nodes, $modDate, NULL);
    $content .= "</ul>\n";

    return $content;

// Produce the themed output for the image gallery summary.
// This code was ripped from the image gallery module.
function _theme_gallery_summary($imgTag, $url, $title, $count, $modDate, $description=NULL)
   $content = "";
   $content .= '<li class="clear-block">';
   if ($count>0) {
     $content .= l($imgTag, $url, array(), NULL, NULL, FALSE, TRUE);
   $content .= "<h3>". l($title, $url) ."</h3>\n";
   if ($description) {
      $content .= '<div class="description">'. check_markup($description) ."</div>\n";
   $content .= '<p class="count">'. format_plural($count, 'There is 1 photo in this gallery', 'There are @count photos in this gallery') ."</p>\n";
   if ($modDate) {
     $content .= '<p class="last">'. t('Last updated: %date', array('%date' => format_date($modDate))) ."</p>\n";
   $content .= "</li>\n";
   return $content;

// Photo galleries are per-term, so argument is taxonomy term.
function phptemplate_views_summary_photo_gallery($view, $type, $level, $nodes, $args) {
  if ($type == 'block') {
    return _summary_menu($view, $level, $nodes);
  else {
    drupal_add_css(drupal_get_path('theme', 'lasqueti') .'/css/image_gallery.css', 'theme');
    // Need generic way to get image file info? - must use name of CCK field.
    // Replace this with the name of your image field.
    // Uncomment the print_r below to figure out what it is.
    $cck_fid_field = 'node_data_field_image_field_image_fid';
    $content .= '<ul class="galleries">';
    $title = " Photo Gallery";
    $baseUrl = $view->url;
    // Build a small, single node teaser for each query in the summary and
    //   use it as a link to the actual view page with that argument.
    foreach ($nodes as $query) { 

       // Get the gallery description from the term's vocab.
       $term = $query->letter;  // argument for taxonomy terms;
       $tid = $query->tid;      // term IS the argument!
       $termObj = taxonomy_get_term($tid);
       $description = $termObj->description;
       // Build a 1 item query to grab the first node in this view
       $result = views_build_view('items', $view, array($term), false, 1);

       // Grab the image from that node to display in our summary.
       // The file id comes from the node info. we got back from views.
       // print_r($result['items'][0]);
       $fid = $result['items'][0]->$cck_fid_field;
       $modDate = $result['items'][0]->node_changed;

       $file = _imagefield_file_load($fid);
       $imgTag = theme('imagecache', 'Thumbnail', $file['filepath'], 'Latest image from gallery', $term . $title);

       $url= $baseUrl .'/'. $term;
       $linkText = $imgTag .' '. $term . $title;
       $content .= _theme_gallery_summary($imgTag, $url, $term.$title, $query->num_nodes, $modDate, $description);
     $content .= "</ul>\n";

     return $content;

Theme the node add / edit forms for custom CCK types

    This simply addes a collapsible fieldset for the CCK fields in the "Market Page" type, and hides the Categories fieldset from non-admins, to try to improve useability. See for details.

function phptemplate_page_layout_b_node_form($form) {
  // Collapse the taxonomoy fieldset by default (as this rarely changes).
  $form['taxonomy']['#collapsed'] = true;
  // enforce crude access rights on the taxonomy terms.
  global $user;
  if (!($user->uid==1 || in_array('contributor',$user->roles)) ) {
  // These are the fields that should be in the fieldset
  $fields = array('field_teaser_image', 'field_bio_image', 'field_bio');
  // Add fieldsets to the page_layout_b input form & render it.
    _add_fieldset($form, 'Images', $fields);
    return drupal_render($form);

 * Add the given fields to a new fieldset on the form.
 * @form - the form to add the fieldset to
 * @name - a string name of the fieldset
 * @fieldsInSet - field ids (names) for the fields in
 *     the form that should go into the fieldset.

function _add_fieldset(&$form, $name, $fieldsInSet) {
  // Add a fieldset to the form to hold related fields.
  $form[$name] = array (
     '#type'        => 'fieldset',
     '#title'       => $name,
     '#collapsible' => 1,
     '#collapsed'   => 0,
     '#weight'      => -2,
     '#tree'        => 1,
  // Move all the related fields into the new fieldset.
  foreach ($fieldsInSet as $field) {
    if ($form[$field]) {
      $form[$name][$field] = $form[$field];
      $form[$name][$field]['#parents'] = array ($name, $field);

Custom theme the book navigation links.

    When a node's page is viewed in a tab, we don't want to show any book navigation.

function phptemplate_book_navigation($node) {
  if ($node->inTab)
    return NULL;
    return theme_book_navigation($node);

Custom display of Views exposed filter

  Exposed filters allow the user to have control over what nodes are viewed by selecting from a widget.  The default layout is to have the widget displayed above the view, in the page - yuck - very ugly and poor usability.   This  "snippet" hides the default exposed filter for a view with the name "test" - it can be copied for any View where you want to do this.  Then a custom block is created to display the exposed filter widget off to the side, or "rolled-up"!  Much nicer.

function phptemplate_views_display_filters_test() {
    // Do absolutely nothing.

Custom Forms

Anywhere the user can enter data is a form.

Mostly, these forms are generated by Drupal and the sequence the fields are presented is controlled using the field weights (manage fields under Content Types).  However, there were a few cases where input forms were customised to improve their usability (mostly to make them less daunting to the community).

  1. Page node input forms.  These forms display when the a new Page node is created or when a Page node is editted. Customized node forms include page_layout_b (Market Page).  These are long, fairly complicated forms, so I grouped some of the fields into fieldsets to make them easier to manage.  Code includes:
    • phptemplate_node_form($form) - selects forms to be modified and applies custom template
    • also provides a crude access control mechanism to hide the Taxonomy selectors, which generally don't need to change for these types of nodes once they have been set (by the admin on page creation).
    • See for details


Custom Templates

Any page, node, or block can have a custom template that defines its look.  This is a very easy way to subtly alter the look of different parts of the site, or to customize the display of different types of data.


Heavily modified the Denver Theme version to remove almost all conditional theming and unused divs.  Made a few other customizations to:


Basically, this is straight from the Denver theme, but with:

  • "teaser" class added to the "node" div to allow custom styles for teasers (used to float image right on node page, but left on teaser view).

Defines the footer portion of the page that includes the "Our Community" and "MarketPlace" random image blocks.


Defines a custom layout for the "Market Page".  Basically, this just adds the code to pull in the "mini-gallery" at the bottom of the page.


Added this to customize the look and feel of comments. 
See Theming Drupal Comments, Exemplifying with Garland | All Drupal Themes


Some things just can't be developed as a full-fledged module...
Sometimes a hard-coded argument is required (e.g., specify vocabulary for a view), sometimes the code just needs further development to create a nice, general-purpose module.  In any case, these small "modulettes" are less than a full-fledged module, but more than simply a snippet.  They all perform some custom theming, often for a summary view of some sort. 

Directory View


This module creates a view of nodes by-category, to form a simple, 2-column directory.



  • a taxonomy to list nodes by category - I use the primary "Category" vocabulary here;
  • a View to select the nodes to be listed in the directory and create a nice layout for them;  I use the 'market_directory' view to select Marketplace nodes, and format them in a "Table" layout.


  • Any number of columns can be created;  I am using a 2-column display.
  • The "depth" of traversal can be set as the views argument option;  I use a depth of 2 here.
  • A complete listing a all terms is possible; I only list terms associated with some node data.
  • By default, each tree for top-level terms is put into a collapsed fieldset using the term as the legend.  This creates a very tidy, minimalist view of the directory, making it easier to navigate.

Source Code

All code snippets, modules, and modulettes on this site are covered by GPL - see license.txt

The source code is attached below...

Full instructions for installing and using this module are included as comments in the source file.

directory_view.zip15.28 KB

Image Gallery Summary View


This module themes a views summary for a set of image galleries.  The image galleries are defined with a view using an argument (usually Term or User).  The summary view shows a thumbnail of the latest image from each gallery, the page title, last modified date, and number of images in the gallery.  This is nearly identical to the gallery summary provided by the Image module's Image Gallery contrib module.  This can be used to theme per-user gallery summaries or per-term gallery summaries.   I can't think of (and haven't tested) other possibilities, but it should work with any argument type and any node type that defines at least one image where you want a nice looking summary.

  • I just really liked the look of the Image Gallery module's summary page, but I wanted to use CCK imagefield rather than the Image module.  There seem to be a lot of people out there looking for this, but there just were no nice looking summary views available. 
  • All of the theming code was ripped directly from image_gallery.module :
  • The module is used, for example,  to create the summary view at:



  • a view with (1) a CCK imagefield defined as one of the fields; and (2) a view argument (like Term or User)
  • an imagecache preset named "Thumbnail" (this can be configured in the script)
  • a style-sheet for styling the gallery summary view - I've included image_gallery.css, ripped directly from the Image module.


  • combine this with the Views Summary Mock Menu modulette for a complete image gallery browsing experience!

Source Code

All code snippets, modules, and modulettes on this site are covered by GPL - see license.txt

The source code is attached below...

Full instructions for installing and using this module are included as comments in the source file.

summary_view_image_gallery.zip15.01 KB

Image Titles


This module "automatically" replaces text titles with an "associated:" image, if a suitable replacement can be found.  It simply converts the title into a filename and looks for that file - if it finds it, it formats the image tag to replace the title.



  • a collection of GIF image titles located in files/images/titles (configurable in script)
  • a call from _phptemplate_variables(), or some other appropriate place, to substitute the title.

Source Code

All code snippets, modules, and modulettes on this site are covered by GPL - see license.txt

The source code is attached below...

Full instructions for installing and using this module are included as comments in the source file.

replace_title.zip13.35 KB

View Summary "Mock Menu"


This very tiny module creates a "mock menu" from a views summary - a menu item is created for each query, or potential argument value, in the views summary.  This is handy for providing a menu that links to the various pages provided when a view has an argument.

Source Code

All code snippets, modules, and modulettes on this site are covered by GPL - see license.txt

The source code is attached below...

Full instructions for installing and using this module are included as comments in the source file.

summary_view_mock_menu.zip13.03 KB

Vocabulary "Mock Menu"


This module creates a "mock menu" from a vocabulary, with links to the terms at a given base-path.  This is very handy for providing a menu that links to a view with a Taxonomy:Term ID argument.



  • a vocabulary to create the menu items from;  I use the primary "Category" vocabulary here;
  • a base-path for the menu links - the term ID will be added to this base-path; my base-path is "marketplace/browse/listings"


  • most useful when combined with a View using a Taxonomy:Term ID argument;  I use the 'market_listing' view to generate this menu as a "summary view" in a block displayed at the base-path.
  • The "depth" of traversal can be set to limit the menu depth;  I use a depth of 2 here.

Source Code

All code snippets, modules, and modulettes on this site are covered by GPL - see license.txt

The source code is attached below...

Full instructions for installing and using this module are included as comments in the source file.

category_mock_menu.zip13.95 KB

Development Tips

Here are some of the "tricky" ways I've manipulated Drupal to get the job done, without resorting to custom modules or theming.


Custom Menus (tabs)

The menus module in Drupal core allows you to manipulate the system menus through the admin GUI interface (admin >> build >> menus)

However, it does not give you control over at least two key aspects of the menu:

  1. Access control - does not allow any specification of who can access the menu item;
  2. Tabbed menu - not possible to add or edit tab-style (local task) menu items;

These can be controlled if the menu is built programmatically.  For these reasons, I created a custom lasqueti menu module (lasqueti_menu).  This module does the following:

  • Creates the menu items for this Design Notes book, and restricts access to only those who have 'access administration pages' privleges.   Otherwise the Design Notes can either not be placed on a menu, or will show up to authenticated users on the site (in their personal menu - the Navigation menu). 
    An alternative would be to use the "menu-per-role" module, but this seemed overkill for hiding one book, and the module requires a core "hack", so not nicely maintainable.
  • Creates the "tabbed" menu pages under the Primary Menu tabs.  The module does two things of interest:
    1. creates the basic structure of the tabbed menu system;
    2. handle display of single node pages that appear on tabs, where required.
    3. handle display of teaser views that appear on tabs, where required.

See the following pages for more information about working with and maintaining these tabbed menus.


Conceptual Overview

The idea behind tabbed navigation is to provide the user with a spatial orientation to the context of the page they are viewing.  The tabbed layout creates its own, implicit "breadcrumb" by always showing the user exactly where they are in the structure.  Thus, you'll notice that the breadcrumb has been removed from these pages.

The tabbed menus only present information that is available elsewhere on the site.  It is meant as a seperate navigational structure, primarily aimed at users who are new to the site, and simply want to browse around.  Thus, the tabs are organized into "categories of interest" - so users can view all content on the site related to their interests.

To this end, tabs "pull-in" two distinct kinds of data:

  • Individual pages - where relevant, single pages exist, these are displayed in their full form on the tab;
  • View of related teasers - where there are multiple pages on the topic, a view of teasers is presented.

The teaser lists are almost all generated by a single view named "by_category".  This view simply lists teasers for all nodes, using a "Taxonomy:term id" as an argument.  The tabbed menu code supplies the relevant taxonomy terms for the Views argument  when the view is built, to pull-in all the related nodes.   In this way, whenever a new node is added or a the categories of a node are modified, it will automatically show up on the right tabbed page.

When a list of teasers is presented, there is always a "heading" node placed at the top of the list (simply by tagging it with the right categories and making it "sticky").  Although this "heading node" is just a normal node with the title set to the tab title and the body containing an overview of the tab's contents, it is not used as a normal node, and should not appear in any other displays (I hope!).   Some special logic was added to template.php and node.tpl.php to handle this node, but ultimately, these may need to be stored in a special node type so their display in other lists can be contolled more easily.  An alternate implementation would be to display the heading node explicitly during _display_view in the custom tabs module.  This way those nodes could be controlled very precisely.

Farmer's Market

Pages in the Farmer's Market, with their associated produce lists, represent a fairly complex set of inter-relations.  Understanding how the various node types, cck fields, categories, and views fit together will take some time.  However, setting up and maintaining a Farmer's Market page is not hard, but you need to be quite careful to get the relationships right:

  • Add a new term to the "Lasqueti Market" category  for the new producer.
    ** This term must be the same as the path of the Market page (case insensitive). 
    These terms are used to create the Farmer's Market (market_products view), listing products for each farm - it shows a list of available products tagged with each term.
  • Add a new Market Page for the new producer.
    ** This must be a Market Page so that the "mini-gallery" (created by template.php using the product_gallery view) can be attached . 
    ** The page must be tagged with Topic category term "Farmer's Market".
    ** The URL path for this page must be the same as the Lasqueti Market category term (case insensitive).  This allows Taxonomy Redirect to redirect links from the category term directly to the page (since the term name and the page's URL path are the same).

Thus, a "Famer's Market page" is a Market Page, tagged with Topic "Farmer's Market, with the same URL path as a term in the Lasqueti Market category.  These rules are used to identify these pages in the code - so pages that don't adhere will not be considered part of the Farmer's Market, and won't work correctly.  This is a little sketchy, perhaps even a bit hacky, and limits the re-use of the Product node type ouside the Farmer's Market - but it was expedient, and seemed cleaner than creating a new node type or yet another taxonomy.  Although the Topic category is used to identify Farmer's Market pages,  the Lasquti Market term and URL path still need to be identical for Taxonomy Redirect to work.
The Author of a correctly configured Farmer's Market page will see two new tabs on their page: 

  • Edit Product List
    This shows the my_products view that allows the author to edit, delete, and change the availability of their produce.
  • Add Product
    This is simply a short-cut to the Add Product form.

These two tabs are created by the custom Lasqueti Menu module - they are "dynamic" local menu items.  It is important to note that only the page author will see these tabs, so once the admin gives authorship over to another user, they won't see these tabs anymore.  I could not figure out how to make this work so admin always sees all.

There are three distinct "Product List" views:

  • market_products view is used to display the "products-by-producer" lists on the Farmer's Market page.  The most staightforward way to create this view was by using a taxonomy (since I already had a module to list nodes-by-term).  This is why each product must be tagged with the correct "Lasqueti Market" category.  The alternative would be some custom code that first finds all Farmer's Market pages, then, for each one, selects all product nodes that have a node reference to that page.  This will be quite a bit slower, and represents a significant effort - thus I adopted the taxonomy route because it was easy to implement, expedient, and efficient.  Note that this view does not use the node reference field at all!
  • product_gallery view is used to display a list of products related to a given producer, in their Market Page's mini-gallery.  Again, I already had the code to produce a mini-gallery using a node reference.  In this case, using the node refernce was the expedient and effiecient way to generate the view, because you already have the Market Page node itself (it is the one being displayed!).   It would be possible to use the taxonomy term (which is the same as the Market Page title, remember!) to create this view, using the term as an argument passed into the view.  That would eliminate the need for the node reference field in the Product node.  Hmmm... seems like a reasonable idea now that I think of it - but that would mean the Product node would lose its "This product is related to...." link - although the Market page would still be linked from the product via the taxonomy term and Taxonomy Redirect.   Perhaps worth investigating to simplify things a bit.
  • my_products view is used to display a list of products authored by an individual, and is shown on the "Edit Product List" tab for Farmer's Market pages.   This could be confusing if one person is "owner" of multiple Market pages, because I could not find a simple way to filter the products by the node being viewed (since I don't have that context).   I suspect this will be possible with Views2.  For now, this lists all of a user's products, regardless of which page they relate to.

The upshot?
When user's create a new product,  they need to select both the correct Lasqueti Market taxonomy (so the product is listed correctly in the Farmer's Market) and the correct Market Page node reference (so the product is added to their mini-gallery).  The node reference selector uses the user_pages view to restict them to selecting references to only pages they own.  This is good because it avoids confusion and mistakes, but it is bad because it means the admin cannot create these node reference links without first "masquarading" as the author of the page.  The Lasqueti Market category terms would be more difficult to restrict in this way, so they are open, and thus prone to error.
This reliance on authorship for a number of the functions (e.g., edit tab availablitity, node editing access, node reference list, etc.) also implies that only one person can manage a given market page.  It is certainly possible to set-up group access and do things based on groups rather than author, but there would be a lot of overhead (and extra work!).  An easy workaround would be to create a group ID for the website that all members of the group could use to update the shared resource.

Installed Modules

This page should document the reasons why each contrib module has been installed.

Note that some modules are actually packages, and not all individual modules in the package are enabled.

Site Administration

These modules are used only for site administration and site development - they do not affect the "user" view of the site.  In general, these modules can be removed from the site (e.g., to improve performance if that is an issue) without affecting anyone except the site admin.

Access Control

These modules provide access control functions to restrict who sees and edits what.  Used in conjunction with the built-in access control panel, both to secure the site from anonymous users and to simplify the view of the site for novice users.  These module could be removed if the access control function they provide is no longer required.

Behind the Scenes

These modules are used for automating some of the site building tasks, and creating specific behaviours.  Users do interact with these at some level, but not directly - mostly they act in the background.   Be careful to understand how these modules are used before removing them - the behaviour of the site depends on them, but they tend to act in the background, so its not always obvious they are at work.

Content Types

These modules are installer to allow creation of custom content  types (e.g., CCK).  The site could not function without these modules as CCK is used extensively throughout.

User Interaction & Functions

These modules are installed primarily to provide extra functionality to the user. 


Custom Modules?

Almost everything I ever thought of has been done in Drupal by someone else - it's a beautiful thing.  But there have been a few things I've imagined would be really useful that I couldn't find anywhere...
I'd consider writing these myself one day, if time ever permits...

Expiry Date

This module would define a new CCK date type (using Date API) to define an expiry date for a node.  The node owner would get an e-mail reminding them that their node was about to expire, and if no action was taken, the node would simply be removed.   This would be very handy for announcements and to create a CCK classified ads module.  Code could largely be ripped from ed_classified module.

Replace Title

This module would look for an image in a specific directory with a name "similar" to the node title.  If a suitable image is found, then replace the node title with the image.  Use hook_node_?
DONE: But code in template.php - need to move it to a custom module.

Publishing Options Access

This small module uses a hook_form_alter to create per-role access rights to elements of the Publishing Options fieldset on the node-edit form.  This would allow admin to grant rights to user-roles to, for example, promote to front page.
I have already made a good start on this module and will look to contributing it.

Summary Views Plugins

A View with an argument creates a "summary view" when the argument is missing.  The default summary view is simply a list of potentail argument values, linked to their repsepective view "pages".   I could only find one example of a customized summary view (Example: How to Display a Summary View as a Mock Menu | ).  I used this to create the "pop-open" menus when browsing the photo gallery, for example. 

I heard a rumour that Views Theming is going to get MUCH easier in Drupal 6, so this may be a wait-and-see.

But there are some other summary views I'd find a great use for:

  • Image Gallery - the photo albums and galleries on are built with views, using user id, and taxonomy term, respectively, as arguments.  The "summary view" is custom coded in the phptemplate file of the lasqueti theme.  It would be nice to turn this into a real view plugin that shows the individual gallery views as a Bonus Grid.
    DONE: but currently coded in template.php - need to move to a custom module
  • List All - it would be nice if the summary view could simply list all of the nodes, with a pager, in some order.  This could be as title links only, or as teasers, or, preferably, customisable.   To make this work flexibly, Views would have to support a different view-type selector for the summary view.
    TRY: Setting the argument option to List All in the View - probably does this?
  • Nodes-by-Argument summary - would show all node titles, with links, organized by the argument values (e.g., by the taxonomy tree, for example.)  I have written a preliminary version of this module named nodes-by-category.  Perhaps directory plugin?  or nodes-by-category summary?
    Started: custom Nodes-by-Category views plugin.
    TRY: Views Group-by to group nodes by term in the summary?
  • Single-Node- for views that return just a single node (e.g., random image block), the table or list markup is superfulous and requires special styling to make it invisible.  This view simply outputs one node, with no layout formatting.
    DONE: custom views plugin written.




Publishing Options Access

Module attached below - install as-per-usual.

This small module uses a hook_form_alter to create per-role access rights to all 4 elements of the Publishing Options fieldset on the node-edit form.  This would allow admin to grant rights to user-roles to, for example, promote to front page.
I have already made a good start on this module and will look to contributing it.

I have contacted RobRoy, author of a similar module:
about integrating these two similar modules.


publish_access.zip7.73 KB

Module Customization

Forgive me Drupal, for I have sinned...

One of the key benefits of Drupal is that you DON'T (usually) need to "hack" away at existing code to customize it - you can use the hook_ and theme_ systems to add your custom code, thus making upgrading much easier.  Nonetheless, there were a couple modules that I needed to "hack" a little, just to suit my finicky tastes... These changes now need to be "re-implemented" any time these modules are updated (unless you want the old behaviour to revert)...

  • Module     File                          Modification
  • notify        notify.module        changed name on user's tab to "Notify Me"
  • ads        ed-classified.module    changed name on user's tab to "My Ads"
  • imce        imce.module            changed name on user's tab to "My Files"
  • views   modules/  applied patch:
    (hopefully this will be fixed in next version of Views)
  • views   views.module               added $view parameter to theme_views_more()
  • date           added new views filter
  • lightbox lightbox2.module    theme_imagefield_image_imagecache_lightbox2 altered to use image title as the description text instead of the alt text.  Due to imagefield using image filename as default alt text - yuck.  Better fix would be to patch imagefield to use title text as default if alt text not set.



Useful Modules?

I have tried to install only modules that I needed to meet the immediate designs I was trying to achieve.  In my travels, though, I thought these modules looked very interesting and worth checking out:

- Hovertips and Clicktips |  - uses JQuery (AJAX) to create nice pop-up or roll-down tips.  Really useful for displaying complex information or hiding stuff that's not used much (e.g., hide all comments current user has already seen.

- Panels |  - provides API and UI for creating sophisticated panelled layouts.  With related "mini-panel" modules to add AJAX tabbed panels and carousel panels Mini Panels demo | Wim Leers

- Node Queue | - allows you to display items from a "queue" that automatically drops last item when a new one is added.  Can be used manually for creating "top 5" type lists, or integrated with actions module to automate.

- module_builder - I install this locally to make custom module development easier.  It simple creates a template for a new module based on a few settings.  Only useful for developing a new custom module from scratch.

- form_inspect - a companion for the devel module, this is useful if you are working with forms (say implementing a hook_form_alter), and want an easy way to see view the form data structure.

- chipin - for collecting money for a cause  ** NOT AVAILABLE in 5.0 YET **

- nodewords - meta tag (description, keywords) for each page.

- rsvp - enables rsvp for events (probably won't work with the CCK event types I defined :-(   Perhaps re-develop for CCK date field?)

- volunteer_timeslots - for organising events where volunteers sign up for a timeslot - ditto!

- spam - spam blocking tools. 

- taxonomy_access - access control for users based on taxonomy categories.

-taxonomy_role - access control to taxonomy field on node-edit forms.  Useful to allow admin to place "hidden" taxonomy term in a node, probably for organisational purposes.

- cck_role - same thing for a CCK field.  Could be handy.

- Notifications - a potential replacement / upgrade for Notify and/or Subscriptions?

- Custom Breadcrumb - format breadcrumbs for custom views.

Maintenance tips

Here's what I've learned about maintaining a Drupal site.  There are probably other (better) ways to do this, but its how I do it...

Installation, Migration, and Backups:

Best Practices:

Back-up you DB regularly.

I use MyPHPadmin to create a dump of the database.  This can be imported back in case of a problem.  This should be done on a regular basis - remember, all of the data, configuration settings, etc. are all stored in the database.

Useful article:

Back-up your Files directory

The Files area is the only one that is changed by non-admins.  It is where all file uploads (images, attachements, etc.) are stored. The rest of the Drupal install is "replaceable" by re-installing Drupal and the contrib. modules.  The only exceptions are: (1) custom modules; and (2) custom themes;  - these should be backed up whenever you make a change to them.  (Keep an archive of old versions in case you ever need to go back to a previous version!)

Develop on a Test Site

I run Apache/PHP/MySQL/Drupal locally on my own machine.  I keep the version on my machine in sync with the production system on a regular basis by simply importing my database backup.  I use this version as a "sandbox" to try things out thoroughly before implementing them on the production site.  I NEVER export the database from my sandbox and upload to the producton site, because (1) the sandbox is never clean; and (2) users may have added data to the production site since I grabbed the database.

Here are some useful articles on this topic: