Tutorials

Material for live demos and workshops.

Adding CSS Style To TinyMCE Editor

Below is some example text in various styles - how can we get text to display the same way in a TinyMCE editing text box while it's being edited?

Heading 1

Heading 2

Heading 3

Heading 4

Heading 5

Typical paragraph text with bold and a link.

Another paragraph, below is an un-ordered list.

Here's an ordered list:

  1. Phasellus sodales. Donec pharetra fringilla diam.
  2. Vestibulum magna mauris

TinyMCE CSS File

New TinyMCE CSS File

Create a new CSS file for the theme you're using and add it to the directory of your theme:

themes/example-theme/
or
sites/default/themes/example-theme/ (better)

You can call the new CSS file anything you like, in this tutorial we'll call it format.css.

Include the new TinyMCE CSS File

Log in to your site and go to:

admin/settings/tinymce

Add a profile or edit an existing one. Note: you need to edit all existing TinyMCE profiles.

Scroll down to the 'CSS' fieldset, and enter these values:

Editor CSS: define css
CSS path: sites/default/themes/example/format.css
* whatever the path to the CSS file you added in Step One
* path shouldn't start with a '/'

Submit the form.

Edit New TinyMCE CSS File

Copy Existing Style.css 

TinyMCE is now using the new CSS file that you've added, but what styles should be added to the file?

We want to keep this file as small as possible, so we should only add styles for elements that wil display in the editing text box, such as headings, paragraph and bold styles. We can leave out structural elements such as structuring styles used to create multi-column pages.

Open the style.css file of your theme and copy across  styles that format text - quickest way is to copy accross eveything and remove what is not required.

Modify TinyMCE CSS

Now the real work begins! 

There are some elements that need to be overridden because TinyMCE puts the contents of the editing box in an iFrame. Any elements that rely on inheriting their style from the body element need to be changed like this:

body.mceContentBody

Example:

body h1{
  font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif;
  margin: 0;
}

Change to... 

body.mceContentBody h1{
  font-family: "Trebuchet MS", Geneva, Arial, Helvetica, SunSans-Regular, Verdana, sans-serif;
  margin: 0;
}

Any element that relies on inheriting a style from .content should work if you remove '.content ' from it.

.content h1 {
  color: #369;
  font-size: 1.9em;
}

Change to...

h1 {
  color: #369;
  font-size: 1.9em;
}

Drupal Projects - What Not To Do!

Things to avoid when developing your Drupal project, why, and how to avoid them.

Code In Database

Avoid including code in your database.

Common places code is included:

  • Pages
  • Blocks
  • Contentplate

Why?

  • Revision control becomes impossible
  • Difficult to document
  • Difficult to debug
  • Hard to move between systems
  • Difficult to upgrade

How To Avoid

  • Add your code into a new module
  • Add your code to the theme template.php file

 

Custom Code Instead of Using API

Drupal is a framework with great APIs, instead of writting your own custom code, try to use the built in APIs.

 

 

Don't Put Modules and Themes In Wrong Place

At the top level of Drupal there are two directories:

  • module
  • themes

This seems like the right place to put extra modules and themes, but it's not!

These are for core modules and themes.

Why?

When it comes time to do an update to Drupal, your custom and contrib modules and themes would be mixed up with Drupal core, making the upgrade hard work

Solution

Add all custom modules and themes to the sites/ directory:

  • sites/modules
  • sites/themes

 

Hack Contrib Modules

Try to avoid being relient on amendments to third party contributed modules.

Do make modifications to the contrib module, create a patch containing your changes, and file a feature request or bug report with your patch attached.  However,  in case your patch is not commited to the module by it's maintainer, it's best to also make those modifications through a seperate module.

Why

  • You will need to add the amendments in again when you upgrade to a new version of the module

How To Avoid

  • Create a new module with all the overides you want to make to the contrib module
  • Use hooks to overide the contrib module's logic
  • Use the theme system to overide it's output 
  • Use Locale system to overide it's interface text

 

Hack Drupal Core

You should at all costs avoid making any changes to Drupal core.

Why Not 

  • Any changes you make to Drupal core will also need to be added again when you ugrade your project with a security release
  • or update your project to the next version of Drupal
  • Your system may become unstable as you are changing the framework within which modules operate
  • You may create security vulnerabilities

 How To Avoid

  • Using the Drupal hook system it's possible to override most of Drupal's processing
  • The theme system gives you the possibility of overriding all content being output
  • Locale can be used to change the wording of Drupal's interface

 

Insecure Site

If your site can be hacked, it will be hacked!

How To Avoid

  • Use Drupal's built in security functions when you write custom code
  • Subscribe to the security email newsletter or RSS feed
  • Apply security upgrades as soon as they become available - both to Drupal core and contrib modules

 

Un-Documented Code

Write documentation for any code you write, and for how your system is set up.

Why?

  • This will save you a lot of time when you come back to your code later on, or when someone else takes over maintaining it

How?

  • Add comments to your code clearly explaining what each function does
  • Include a Read Me file with each module you write
  • Write up how your site is structured

We'll do another tutorial in future on how to thouroughly document your project and the tools available:

 

Forms API

Tutorial for the Forms API.

Process Diagram

The workflow for Forms API:

Forms API Diagram

Define

function legal_display_fields() { $form = array(); $conditions = legal_get_conditions(); $form['id'] = array( '#type' => 'value', '#value' => $conditions['id'], ); $form['legal'] = array( '#type' => 'fieldset', '#title' => t('Terms and Conditions of Use'), '#weight' => 29, ); $form['legal']['conditions'] = array( '#type' => 'textarea', '#title' => t('Terms & Conditions'), '#default_value' => $conditions['conditions'], '#value' => $conditions['conditions'], '#rows' => 10, '#weight' => 0, '#attributes' => array('readonly' => ''), ); $form['legal']['legal_accept'] = array( '#type' => 'checkbox', '#title' => t('Accept Terms & Conditions of Use'), '#default_value' => 0, '#weight' => 50, '#required' => TRUE, ); $form['#after_build'] = array('legal_preview'); $form['preview'] = array( '#type' => 'button', '#value' => t('Preview'), ); $form['save'] = array( '#type' => 'submit', '#value' => t('Save'), ); $output = drupal_get_form('legal_administration', $form); return $output; }

Validate

function legal_administration_validate($form_id, $form_values) { if ( empty($form_values['conditions']) ) form_set_error('conditions', t('Terms & Conditions must be entered.')); return; }

Submit

function legal_administration_submit($form_id, $form_values) { db_query("INSERT INTO {legal_conditions} (tc_id, conditions, date, extras, changes) VALUES (NULL, '%s', %d, '%s', '%s')", $form_values['conditions'], time(), serialize($form_values['extras']), $form_values['changes']); drupal_set_message (t('Terms & Conditions have been saved.')); return; }

How To Plan A Drupal Project

Model Method 

Workflow

It's important for you and the client to have a clear idea of what is happening at each stage

Cargo Cult Development 

 

 

Preparation

Initial planning stage where the site is modeled on paper and in documents.

Consultation

The Meeting 

  • First step is to get information from the client about what the requirement of the site will be.
  • A face to face meeting with the client is vital at this stage
  • Schedule plenty of time for this consultation, at least a day
  • The client will not have a clear idea of the site's requirements, even if they think they do. It's your job to clarify the requirements.
  • At the start of the consultation it's important to explain to clients the process that will be followed - basically the outline of this tutorial

Avoid

  • Getting sidetracked
  • Be flexible in taking information, even if it should come at a different stage in the process, but always make sure to get back to discussing the current stage
  • Solutions instead of problems
    The client will try to suggest solutions before a problem has been defined, e.g. adding a forum without any clear reason to do so
  • Client playing designer
    Many clients will try to suggest designs, without having any knowledge of user interface design
  • Client adding useless features because other sites have them 

 

 

Tools

At this stage you will need:

  • Notepad
  • Pen
  • Markers (several colours)
  • Blank cards

Not using a computer will usually make the process seem less technical and easier to follow.

Client Goals

Discuss the goals that the client is trying to achive with the project.

  • Write each goal on a card
  • Use the same colour for all goals. 
  • Don't be too vague
    e.g. "improve branding" - not useful
    e.g. "associate brand with head of organisation" - useful

 

Users

Who? 

  • Define who the users of the site will be.
  • Think about what kinds of users the client will need to attract to the site to fullfill their goals.
  • Write a card defining each type of user
  • If necessary create sub groups, one on each card, giving a more specific definition of a type of user.

Personas

To make it easier to discuss the motivations and behaviour of these users, it's useful to create personas for them - give each user type an example person, their name and a short description.

if you can think of a someone you know that fits into a user type that can be very useful.

User Goals

What?

  • For each user type list what their goals will be when using the site.
  • Write a card for each goal

In practise you will be discussing this at the same time as you define users. 

Features

How?

  • Make a list of what features the site will provide for users to achieve their goals
  • Note the connection between a user-> a goal -> and a feature
  • Write each feature on to a card
  • Features can also include content

Modelling Information

Now that we've discussed with the client the who? what? and how? of their site we can start to model the information.

 

Tools

Model information on computer using a diagramming application

Applications

 

Map Information

Transfer information from cards into your diagramming application and create a map of relationships.

  1. User & Goals
  2. Goals & Features

Examples

Developing Features

Decide how features can be implemented in Drupal

List features

  • That can be implemented with existing modules
  • That might be implemented with existing modules
  • That can't be implemented with existing modules

Test modules you might use on a development site. This will be the start of the project website. 

Site Map

Convert your features diagram into a site map showing sections and pages.

Then map out how user types relate to each section and page, giving you a clear view of how each user type will navigate to the parts of the site that are relevant to them.

Wireframes

Once we know how all the pages relate to each other we create wireframes for each one.

  • List what goes on a page then arrange the elements.
  • Think about creating a consitent interface accross the site. 

Development

The documentation produced in the planning stage is only a starting point for development, it should be used for phase 1 of the development process, but then replaced by further consultation with the client.

Phase 1: Feature Complete Site

  • Develop the main functionality of the site.
  • Create the design of the site 

Review 1

Give the client time to test out the site and give you feedback.

Phase 2: Polish Site

  • Configuration refinement
  • Appearance refinement
  • Functionality refinement

Review 2

Final review of project by client before it goes live.

Phase 3: Training, Documentation and Refinement

Development

  • Configuration refinement
  • Appearance refinement
  • Functionality refinement

Documentation

  • Create guide to using website

Training

  • Train client in use of the website

Launch Site!

Review Feedback & Behaviour

A few weeks after the project goes live analyse feedback from users and usage patterns and plan ways to improve the effectiveness of the website.

Phase 4: Post Launch Refinement

  • Configuration refinement
  • Appearance refinement
  • Functionality refinement

Node Grant

Why Use Node Grant?

Node Grant Concepts

Concepts

Realm

The user is to be assesed by a property such as:

  • Age
  • PostCode/Zip
  • Membership History
  • ...

The realm is a string that's defined to identifying that property, such as:

  • 'age'
  • 'postcode'
  • 'membership_duration'

Grant ID

A condition of a realm.

For example, if realm was 'age' then grant IDs might be:

  • is over 18
  • is between 20 - 30
  • is under 75

More than one grant ID can be defined per realm.

Each grant ID can control view, edit and delete options for a node.

Node Grant Hooks

hook_node_access_records()

Used to define a realm.

hook_node_grants()

Used to check if the user passes conditions specified for a grant ID.

Example Usage

A content type Life Lesson is set up.

Members over 18 years old can:

  • View Life Lesson nodes

Members over 30 can:

  • View Life Lesson nodes
  • Edit Life Lesson nodes

Members over 50 can:

  • View Life Lesson nodes
  • Edit Life Lesson nodes
  • Delete Life Lesson nodes

hook_node_access_records() is used to define who can do what.

hook_node_grants() is used to check if the user is over each age range.

 

 

hook_node_access_records()

In this example the 'philosophy' realm is set up and three grant IDs are set up:

function moderate_node_access_records($node) {
$grants = array();
if ($node->type == 'life_lesson') {

$grants[] = array(
'realm' => 'philosophy',
'gid' => 1,
'grant_view' => TRUE,
'grant_update' => FALSE,
'grant_delete' => FALSE,
'priority' => 100,
);

$grants[] = array(
'realm' => 'philosophy',
'gid' => 2,
'grant_view' => TRUE,
'grant_update' => TRUE,
'grant_delete' => FALSE,
'priority' => 100,
);

$grants[] = array(
'realm' => 'philosophy',
'gid' => 3,
'grant_view' => TRUE,
'grant_update' => TRUE,
'grant_delete' => TRUE,
'priority' => 100,
);
}

return $grants;
}

hook_node_grants()

function moderate_node_grants($account, $op) {

$grants = array();

if ($account->age >= 18 ) {
$grants['philosophy'][] = 1;
}

if ($account->age >= 30 ) {
$grants['philosophy'][] = 2;
}

if ($account->age >= 50 ) {
$grants['philosophy'][] = 3;
}

return $grants;
}

SuperFish menus

This tutorial(presentation really) is best viewed as a slideshow. Please visit SlideShare to view it: http://www.slideshare.net/poka_dan/superfish-... The presentation promotes Superfish Menus as another option for dropdown menus in Drupal. Please know the author of the Superfish (D5 and D6) modules is Bill Bostick known as roopletheme http://www.roopletheme.com

Themes

Drupal deals with content, logic and presentation in separate layers.

The theme layer controls the look of how a Drupal site's content will be displayed.

What Is A Theme?

Themes control the look of a website

Example theme 1 Example theme 2

Theme Controls

Themes are controlled through the themes setting page

Theme controls

Themes Location

Themes live in the themes directory

Theme directory

What's In A Theme

A theme is a directory containing theme files

The name of the theme directory is the name of the theme.

Theme files

Creating A Theme

The easiest way to create a theme is to make a copy of an existing theme, such as Bluemariene, then rename the new directory. Drupal will use the name of the new copy as the name of your new theme.

The new theme is now ready to be edited.

Anatomy of a Theme

Structure (Markup)

  • page.tpl.php

Presentation

  • style.css

Elements (Pre-Defined)

  • block.tpl.php
  • box.tpl.php
  • comment.tpl.php
  • node.tpl.php

Elements (Custom)

  • template.php
  • example.tpl.php

For Administration

  • screenshot.png

Structure

page.tpl.php defines the structure of the page that is displayed

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="<?php print $language ?>" xml:lang="<?php print $language ?>"> <head> <title><?php print $head_title ?></title> <?php print $head ?> <?php print $styles ?> <script type="text/javascript"><?php /* Needed to avoid Flash of Unstyle Content in IE */ ?> </script> </head> <body> <table border="0" cellpadding="0" cellspacing="0" id="header"> <tr> <td id="logo"> <?php if ($logo) { ?><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><img src="<?php print $logo ?>" alt="<?php print t('Home') ?>" /></a><?php } ?> <?php if ($site_name) { ?><h1 class='site-name'><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><?php print $site_name ?></a></h1><?php } ?> <?php if ($site_slogan) { ?><div class='site-slogan'><?php print $site_slogan ?></div><?php } ?> </td> <td id="menu"> <?php if (isset($secondary_links)) { ?><div id="secondary"><?php print theme('links', $secondary_links) ?></div><?php } ?> <?php if (isset($primary_links)) { ?><div id="primary"><?php print theme('links', $primary_links) ?></div><?php } ?> <?php print $search_box ?> </td> </tr> <tr> <td colspan="2"><div><?php print $header ?></div></td> </tr> </table> <table border="0" cellpadding="0" cellspacing="0" id="content"> <tr> <?php if ($sidebar_left) { ?><td id="sidebar-left"> <?php print $sidebar_left ?> </td><?php } ?> <td valign="top"> <?php if ($mission) { ?><div id="mission"><?php print $mission ?></div><?php } ?> <div id="main"> <?php print $breadcrumb ?> <h1 class="title"><?php print $title ?></h1> <div class="tabs"><?php print $tabs ?></div> <?php print $help ?> <?php print $messages ?> <?php print $content; ?> </div> </td> <?php if ($sidebar_right) { ?><td id="sidebar-right"> <?php print $sidebar_right ?> </td><?php } ?> </tr> </table> <div id="footer"> <?php print $footer_message ?> </div> <?php print $closure ?> </body> </html>

Elements (Custom)

In template.php, custom theme overrides can be added:

function phptemplate_xxxxxx($user, $image) { return _phptemplate_callback('xxxxxx', array('user' => $user, 'image' => $image)); }

A file called xxxxxx.tpl.php can then be created to customise this element.

<div id="something"> <?php print $user ?> <?php print $image; ?> </div>

Converting a static HTML page to a Drupal 5 theme

These are my notes from the Drupal NW UK User Group meeting in December 2007. First and foremost, this talk was heavily influenced by the theming chapter in Pro Drupal Development: see http://www.apress.com/book/view/1590597559 There's a free download of the theming chapter in the links on the left. Also, Christmas happened between the talk and the writing of these notes, so please excuse any memory errors... The aim was to convert a simple HTML page into a Drupal theme. Moved files to correct directory, sites/default/themes/yourtheme for a single site. I then renamed the files: index.html became page.tpl.php and main.css became style.css I set the admin theme to Garland (Administer -> Site configuration -> Administration theme) so we didn't have to worry about losing access to the admin screens, or, in fact, styling them. Went to admin area to set theme (Administer -> Site building -> Themes). Woo-hoo! It worked! But there was no Drupal content, it was just the static HTML... Began removing content in page.tpl.php and replacing it with variables. For our main content: <?php print $content; ?> For the sidebar: <?php print $sidebar_left; ?> For the primary links menu: <?php if (isset($primary_links)) : ?> <?php print theme('links', $primary_links, array('class' => 'links primary-links')); ?> <?php endif; ?> For the CSS: <?php print $styles; ?> in the head of the document. This led to a mini-rant by myself about omitting all the unnecessary styles that Drupal adds by doing this: /style.css" type="text/css" media="screen" charset="utf-8" /> Chris sensibly suggested using drupal_add_css() (http://api.drupal.org/api/function/drupal_add...), which seems a more Drupal way of doing things: <?php /** * adding and removing style sheets * this code goes in your template.php file */ function _phptemplate_variables($hook, $vars) { switch ($hook) { // we want this code to run when this hook is called by a page case 'page': /** * drupal builds an array of style sheet info during a callback * this array is later turned into style sheet imports with the * drupal_get_css() function -> http://api.drupal.org/api/function/drupal_get... * and finally it all get's printed in page.tpl.php with '<?php print $styles; ?>'. */ /** * We can add style sheets here using the function * drupal_add_css() -> http://api.drupal.org/api/function/drupal_add... * and remove styles by unset(ting) the appropriate array elements. */ /** * example #1 - add a css file called layout.css that's located * in our template folder - path_to_theme() does exactly what * you'd expect -> http://api.drupal.org/api/function/path_to_th... */ drupal_add_css(path_to_theme() .'/layout.css', 'theme', 'all', TRUE); /** * example #2 - remove a single style sheet - in this case system.css * calling drupal_add_css without any parameters will * return the style sheet array */ $css = drupal_add_css(); // print the array - make a note of what you want to remove //print_r($css); // use the php function unset -> http://uk.php.net/manual/en/function.unset.php unset($css['all']['module']['modules/system/system.css']); /** * example #3 - remove all style sheets added by modules * un-comment below */ //unset($css['all']['module']); /** * finally over-ride our $vars['styles'] variable (which will become $styles in * page.tpl.php) by passing our amended style sheet array through drupal_get_css() * and we're good! */ $vars['styles'] = drupal_get_css($css); break; } // don't forget to return the $vars array :) return $vars; } More variables here: http://drupal.org/node/11812 Things change a bit in Drupal 6: I'll give a talk on this in a month or two.

Using nodequeue module with actions and workflow module

This tutorial will show you how to use the nodequeue module on your site. The initial part of the tutorial will show manual use of the module and the later part will show how nodequeue can be integrated with actions and workflow modules.

We will build a block showing the 5 latest story posts. Initially we will add them manually, then we'll do it automaticallly, we'll add new Stories to our queue and then remove them at a predefined time in the future. (and on the way we'll show you how to create views of a nodequeue).

For some background on why nodequeue is important, read Merlin's post on his site.

You will need


Ok, let's get started.

Installation

You can get the Drupal modules from the links below:

Download and install on your site. Need help installing contributed modules? look at the handbook page

Enable the modules under admin/build/modules

  • Actions
  • Node Queue
  • Workflow
  • Views


Go to admin/content/nodequeue where we can add and list nodequeues.

nonodequeues

Click Add. Then enter the following values in the fields

Title: Latest Stories

Queue Size: 5

Link 'add to queue' text: Add Story

Link 'remove from queue' text: Remove Story

Roles: Click roles who are allowed to add/remove stories from the list

Types: Click Story

Click the Submit button

nodequeue entry

 

We should now see our listed with a title 'Latest Stories', Max Nodes 5, and In queue 0.

Empty nodequeue

 

Now let's add some Stories to our nodequeue...

 

Using nodequeue

Add to nodequeue

Create a node of Content type Story. For example call it Story 1, add some body text and hit submit.

As you can see below we have an Add Story link showing. Click on it and it will cause the story to be added to our nodequeue.

Add Story

Once clicked, notice how the link changes to Remove Story.

Remove Story

 

Now if we click on the Node queue link we can see that the node is on the queue.

nodequeue with one node

We can view the queue, which will allow us to maintain position and what nodes to show in the queue. The nodequeue could be used for listing your favourite nodes, the nodequeues are system wide as Earl has mentioned so currently no user level nodequeues are possible.

Go ahead and add two more Story nodes, perhaps imaginatively called Story 2 and Story 3.

 

displaying a nodequeue on your site

You may think that you just need to go to admin/build/block in order to add the nodequeue as a block to your site. That's not the case, first we need to enable the nodequeue in Views, so go to admin/build/views and you will see nodequeue_1 listed (ok, mine says nodequeue_2 well I did a dry run before writing this tutorial) with Title 'Latest Stories', notice how the Status is Disabled.

Admin Views

Click on the Enable link under the Actions column in order to enable it.

Now go back to admin/build/block and you will see the nodequeue listed under the disabled.

Admin Blocks

Select the region where you want it to be displayed and click Save Blocks button. (Note we can configure the block which will allow us to override the Title, and do all the usual things possible with Blocks permissions etc). You should now see the nodequeue appear as a block in your chosen region.

Showing the nodequeue as a block

So now we have a Block on the site which lists the content of our nodequeue, we can manually add and remove Story nodes from the queue. But what happens when we eventually have more than 5 Story nodes on our queue? The oldest story will disappear from the queue and we'll have a more link displayed at the foot of the block.

more

Also, if you inspect the nodequeue the In queue column will show 5 and the words QUEUE FULL.

nodequeue - queue full png

Before we use Actions and Workflow modules, a few observations about nodequeue:

  • Once an item falls off the queue, ie. we exceed the maximum number of queue entries, then if you edit that node the link will once again say Add Story.
  • Adding a node to the nodequeue causes the added node to be placed at the bottom of the queue. It then rises to the top as more nodes are added. By default the sort order of the nodequeue is by queue position, which you'll see when we make a view of a nodequeue. A Latest Stories block really should have the newest added nodes added at the top of the list and sink down the list rather than rise. But have no fear, Views module comes to the rescue as we will see we can add a view of a nodequeue and do all the usual view things.
  • You can use the Theme Wizard in the Views module to theme the nodequeue, YEAH! See below
Theming nodequeue

Let'smake a view of our nodequeue, so we can make the Latest Stories block list stories by the time the Story node was created.

Using nodequeue with Views

So we have a Latest Stories Block but it does not list our Stories according to the creation time of the Stories. So what do we do? Well we go back to the Views page and make a view of our nodequeue.

Go to admin/build/views then click the Add link under the Action column for nodequeue_1

Now we are in the Views edit form, scroll down to the Sort Criteria, you will see the default sort order for a nodequeue.

Old sort order

 

Change it to this, and then we will get the desired block behaviour.

New criteria

 

Now Save, and we'll see that the nodequeue has been overridden, click on the link under the URL column to see the block. The block will also appear under admin/block menu.

overridden

 

So let's push on and use Actions and Workflows modules to add and remove items from the queue automatically. I think now you are starting to see the power of the nodequeue module that Earl Miles was writing about.

Using nodequeue with actions and workflow module

Earl Miles also says he hasn't tried nodequeue with the actions and workflow module. I've used those modules so thought I'd put nodequeue through its paces.

I am assuming that you have installed actions and workflow module as documented earlier in this tutorial. So what we'll now do is

 

Go to admin/build/actions and look at the entries in the select list, you'll see we now have Add to Node Queue and Remove from Node Queue actions. Select Add to Node Queue and click the Add New Action button. Then you will see the following and we can select the desired nodequeue, which is Latest Stories.

Add action

 

Click Save, and then repeat the process for adding the Remove from Node Queue action. Then we should end up with the two new actions, hightlighted in red below.

new actions

 

So now we have the nodequeue actions at our disposal, we can turn to the workflow module. We need to create a workflow, define states for the workflow, then finally add that workflow to a node or node types.

Go to admin/build/workflow and click Add Workflow link.

workflow add

Define the workflow name as Stories then click the Add Workflow button.

workflow added

Now you will see we have the workflow and we can perform some operation on that workflow. A workflow is a series of states, probably the most simple workflow would have 2 states, on and off. For our tutorial we will use two states called Added and Expired. The idea being that we want to add stories to our Latest stories list and then change their state to expired at a chosen date and time in the future.

Let's add the states to our workflow.

workflow add state

Now define the transitions. I'm not going to get bogged down with the detail here, I think transition logic is self explanatory.

workflow states

 

Define Workflow actions

workflow define actions

 

Once defined it should look like this.

workflow added actions

Make sure the workflow is defined for the Story nodetype.

workflow assign

 

Now let's add a Story. Once you've added a story it should appear on the queue. Also if you Edit the story you'll notice we have Workflow and Node Queue links.

workflow test

Let's define an expiry time. To do this click on the Workflow link for the Story node, set Change Stories state to Removed, and Schedule for state change at (specify some datetime in the future). (It would have been nice if we could specify an elapsed time from the creation date of the node). Click submit, then once the scheduled state change time is reached the node should disappear from the node queue, (Note the scheduled transition only happens upon the next execution of cron.php).

workflow expiry

 

That about wraps up this tutorial, please post comments if you spot any errors, or ways it can be improved. Enjoy.

 

Debugging Drupal with Activestate Komodo 3.5

Ever since I started using Drupal in October 2005 I wanted to be able to walk the code and debug quickly and efficiently. There is only so much you can do with drupal_set_message and dprint_r statements, so I looked around and decided to try Activestate Komodo. I hit a few problems along the way in configuring my Mac, I think a previous evalulation copy of Zend IDE and my relative newness to OS X were the problem. But with some background reading, and some help from JeffG at Activestate I got everything working nicely.

This tutorial will show you how to configure and use Activestate Komodo to debug Drupal.

Overview

Let's look at the whole picture of what we are trying to do and what software we will be using to achieve our aim of debugging Drupal. For simplicity I am going to assume we are going to use a local web server and debug locally on the same machine.

  • Web server. I'm using Apache 1.3.33
  • PHP 4.3.11 with Xdebug v2.0.0
  • Activestate Komodo

To do this on OS X, the easiest way is to download the Apache 1.3 / PHP 4.3.11 installer package from entropy.ch as the standard version that comes with Tiger does not work.

 

Installation

There are a number of steps to ensuring we get all the applications communicating with each other.

  1. Download and install the Apache 1.3 / PHP 4.3.11 installer package from entropy.ch
  2. Download and install Activestate Komodo, (you can get a 30-day version so you can try before you buy). I am using version 3.5 personal edition.
  3. Run Komodo.
  4. Select Komodo → preferences from the menu
  5. Expand the Languages Category on the left of the dialog box, and select PHP. You should now see something like the following:
Komodo PHP Preferences
PHP Preferences in Komodo
Now we need to ensure we are using the correct php, and that we select this as the Default PHP Interpreter. So at the terminal check what php is being picked up in the path using the which command, then find out what php version we're using and whether it has Xdebug.
Check php version
Now Path to alternative PHP configuration file is where we'll store a php.ini and Xdebug library so specify somewhere under your home directory. The php.ini file will contain debug variables defining how we want to debug. I created a Komodo directory and a debug subdirectory to it to store mine.
Let's click on the Debugger Config Wizard button and setup the debugger.

Activestate Komodo Debugger Configuration Wizard

Let's run through the wizard.

 

Debug wizard screen 1
Initial screen of the debugger wizard.
Click continue.

Debugger Wizard screen 2
Select the php to use in Installations found on your system field, this should be the one which has Xdebug.
Again for Setup this installation you should have the same value as the other selected field in this dialog.
Click the Continue button. Now in the next screen the INI file to be copied should be from a path below where the selected php executable is, while the Put debug version of ini at should be set to a directory somewhere below your home directory.
Debugger Wizard screen 3
Click the Continue button. The next screen asks us where our extensions directory is. The path here should be the same path as that specified in the previous screen under the Put debug version of ini at field.
Debugger Wizard screen - php extension directory
Click the Continue button. Here we can review the settings we have entered, and then click the Continue button.
Debugger Wizard screen 6
Finally we are informed that we have finished the installation.
Debugger Wizard screen 7
Now we just need to see what it has created and whether it works.

Checking the configuration

Let's see if it works. There is a utility on the Start Page of Komodo for doing this, however we will also look at the php.ini file that the wizard created and add a few more parameters to get it all working.

To start with, we can use the Check Configuration feature on the Komodo Start Page:

Komodo Check Configuration link

Click on the Check Configuration link and if all is well you should see something like the following:

Komodo Check Configuration link - finished
Now we should look at the php.ini file created by the wizard. So load it up in your favourite editor.
php ini file
Here we can see what the wizard added. I found in my version of Komodo that the remote_host and ide_key values were not defined so I added them. My configuration did not seem to work without them. You may find similar problems and may need to change your remote_host to 127.0.0.1
One final test would be to do a phpinfo test in your web browser and ensure you can see Xdebug defined in the resulting page. If you cannot then php is not configured correctly. From the command line a php -m will show the compiled modules, and if your path is picking up the correct php then you should see Xdebug listed under a [Zend Modules] section of the output.
Ok, now the moment of truth, let's try to debug something...

Invoking the debugger

There are two ways to invoke the debugger.

  • Add &XDEBUG_SESSION_START=userid to the URL.
  • Add xdebug_break() php function to your code.

The first method is also the easiest. Simply, navigate to the page you wish to debug, append the &XDEBUG_SESSION_START=userid where userid is your user name, sp in my case I substitute userid with bpretsel, hit the browser refresh button and then the Komodo should start debugging. Switch to Komodo, also check that the listener has detected the call to debug.

debug at url

Now, if it does not work then the problem may be one of the following:

  • Hostname is not correctly configured, i.e. you may have 127.0.0.1 instead of localhost. Change this in your php.ini file. If you are going to dabble with this file it may help to comment out lines rather than delete so that you can keep track of previous values you specified.
  • Listener on Komodo may not be running. In Komodo, select Debug → Listener Status to view the status and what port the listener is using.
  • Port number of config and listener are different. Port in php.ini and Komodo Debug Listener should be 9000.
  • Your apache httpd.conf may provide some clues. You can locate yours using httpd -V look for the SERVER_CONFIG_FILE line to locate the file.

Tour of the debugger features.

Once the excitement of getting the debugger to work dies down, you'll want to know what features are available to make your life debugging Drupal easier. The following pages give you a flavour of those features with examples of usiing them with Drupal.

Common commands in a debugger

The following commands are common in most debuggers, and anyone who has used a debugger in another language, e.g. C, Perl will feel at home.

 

debug commands panel

These are reading from left to right: Step In , Step Over, Step Out, Go/Continue, break now, Stop, Detach

Step In

This will follow the code, so that if you step into a line containing a require statement or a function call, pressing this button will cause the debugger to go to the include file for a require statement, or the file containing the function definition for function calls.

Step Over

This will not follow the code, it will step over to the next line to execute. This is the most common action, and certainly you'd generally want to step over require statements.

Step Out

Allows you to step out of a function or include file. Useful if you accidentally stepped into something or you've seen enough and want to return to where the include file or function was called from.

Go/Continue

Usually you would use this one if you have some breakpoints set up. Under such circumstances you want to let the debugger continue execution as you know the debugger will halt when the next break point is reached.

Stop

Stop debugging. You may have seen enough to know the case of your problem or you are just fed up!

Detach

Stop the debugging process, but instead of stopping continue execution of the program.

Break Time!

Going through every line of code is not the most efficient way to debug, sooner than than later you'll want to start debugging at a particular line in a file or function, eventually with practice you'll be wanting to start debugging when certain criteria has been met.

Load the file you want to debug, if it is not already loaded in Komodo.

Komodo debugger load file - watch
Once loaded You will notice we can see a nice navigation window on the left, and below this Komodo displays the currently selected function's comments. This is useful for navigating the Drupal code. Here is a custom module I wrote, in the navigator window.
Komodo debugger code browser
The easiest way to add a breakpoint is to click on the grey area to the left of the text editor window. This will create a red breakpoint. Click it again and it becomes a red circle filled white to denote the break point is still there but the debugger will not break at that line. Click it once more to delete it, and once more again to reinstate the breakpoint.
Komodo debugger add a break point
Control-mouseclick or click and hold over a breakpoint in the breakpoint tab page will bring up a menu which alls us to set more properties for the breakpoint. Select Properties
Komodo debugger modify a break point
Having selected Properties the following dialog window will appear. Now we can make our break point a conditional breakpoint so that we only break whenever our condition is true.
Komodo debugger modifying the break point
Here we have set a condition to only break if the $op is ever equal to the value 'load'. Of course we could have just set a break point within the case 'load' to achieve the same thing.
Click OK and now you will see the conditional setting display in the Breakpoints tabpage.
Komodo debugger add a conditional break point - after
Once you get the hang of the breakpoints you will be able to control exactly when you break.

Variables and their values

Drupal has some pretty big nested data structures, so this feature is a nice way to see those values. Click on the Locals tabpage.

 

Komodo debugger local variables
Arrays can be expanded/collapsed by clicking on the triangles. This makes it alot easier to navigate the local variables.
Also these variables can be changed by clicking on a variable, then clicking on the pencil icon, or by pulling up the context menu, by either ctrl-clicking or click and hold on a variable.
Komodo change local variable

Watch it!

Watching variables is a common task programmers wish to employ when debugging. Usually the scenairo is that you want to know why a certain variable gets a certain value.

To add a variable to the watch list, first select the Watch tab on the bottom left of Komodo. Now click the square dotted icon with the yellow highlight on the top left of it. This will bring up the following dialog. In here type the variable name you wish to add to the watch list.

debugger watch 1
Now we can debug as normal, I've added a breakpoint just after the $return variable gets set.
debugger watch 2
After clicking the Go/Continue debugger command button, the execution breaks at the next break point. Whenever any of our watched variables get changed, the watch window updates. So here we can now see the current value of $return.
debugger watch 3
But that is not all. With Watch we can change the value of a watched variable thus changing how the rest of the program will execute. To do this click on the variable in the Watch window, then click on the pencil icon. (It is the one to the left of the add watch variable icon).
debugger watch 4
Watch is useful though its features are covered by the Locals and Globals tabs, however sometimes you just want to focus on a small subset of variables so watching just the one you are interested in is alot easier than having to watch the whole set of variables in your program.

Call Stack

a Call Stack is a special stack which stores information about the functions/subroutines in a computer program which are currently being executed. It is a stack because when one function calls another, rather than simply jumping to another part of the program.

We can see a call stack in Komodo. Clicking on entries in the Call Stack will cause the text editor section to jump to that particular function definition.

 

Komodo Call Stack

Here we can see that the main section in file index.php at line 15 called the function menu_execute_active_handler in file menu.inc at line 396. The Call Stack helps you to follow the flow of execution of the program.

Browse Drupal functions

Komodo is a good way to browse the php code, and functions in particular.

Komodo browsing functions

Then when you click on function, you can view the comments for that function. Notice the search form, typing in part of the function name will filter the window to only show functions which contain the search text. Here I typed 'cache'...

Komodo browsing functions - clicked
Also double clicking on a function in the view on the left of Komodo will make the text editor part on the right jump to that function definition.

 

Removing the breaks, stop debugging and detaching.

To remove breakpoints , view the current breakpoints in the Breakpoints tabpage. Click on the breakpoint you wish to remove and then click on the red circle icon with a black cross. To delete all breakpoints click on the icon with two red circles and a black cross. To toggle whether a breakpoint is active or not, click on the icon with the red and white circle.

 

removing breakpoints
To Stop Debugging, Click on the dark blue square on the right hand side in the Debug tabpage.
Stop and Detach Buttons

To detach debugging, i.e. continue execution but not in debug mode, click on the dark blue square with an arrow pointing out to the right.

 

Resources

Get products and technologies

  • Drupal is software that allows an individual or a community of users to easily publish, manage and organize a great variety of content on a website.
  • Activestate Komodo An integrated development environment (IDE) for dynamic languages.
  • XDebug An extension which helps in debugging your scripts by providing valuable debug information.
  • DBGP  A protocol that provides a means of communication between a debugger engine and a debugger IDE.

Other links on this subject