• Apache is installed.
  • PHP is installed.
  • MySQL is installed.

To check if you have all the requirements, do the following steps:

  1. Paste <?php phpinfo(); ?> into a text editor and save as info.php
  2. Copy info.php into the root directory of the webserver(eg. htdocs folder)
  3. Open with an web browser http://yourDomain.com/info.php. You should see PHP Version X.X.X and MySQL Support. Otherwise, some requirements are not installed.
  4. Don't forget to delete info.php after you are done.

Install Drupal

  1. Download Drupal from http://drupal.org/.
  2. Decompress downloaded file and copy all decompressed files in the root directory of your server.
  3. TODO

Enable Clean URLs To enable Clean URLs, you have to do the following steps:

  1. Make sure that mod_rewrite is enable in Apache. You can see which modules are loaded with phpinfo() function.
    1. Paste <?php phpinfo(); ?> into a text editor and save as info.php
    2. Copy info.php into the root directory of the webserver(eg. htdocs folder)
    3. Open with an web browser http://yourDomain.com/info.php. You should see mod_rewrite in Loaded Modules section.
    4. Don't forget to delete info.php after you are done.
  2. Open the configuration file of Apache( httpd.conf ) and set AllowOverride parameter to All. Make sure also that AccessFileName parameter is set to .htaccess.
    AllowOverride All
    AccessFileName .htaccess
  3. Copy Drupal's .htaccess into the root directory of the webserver(eg. htdocs folder).
  4. Restart Apache.
  5. You should be able to enable Clean URLs at Administer->Clean URLs.


Configuration Change site name To change site name, go to Administer->Site configuration->Site information. You can also change the slogan, mission, Footer message, etc.

Change Themes TODO favicon.ico

Change color

Unable Color module. Color module requires your file download method to be set to public. "color picker" (which is what you need to find in order to use the module) then go to your theme's configuration page (i.e. hit the "configure" link for the relevant theme at admin/build/themes).

Add Menu TODO

Core Modules Show the number of time each page has been viewed

  1. Enable Statistics module at Administer->Site Building->Modules.
  2. Enable Count content views at Administer->Logs->Access log settings.
  3. You have decide who is allowed to see this statistic by checking view post access counter box at Administer->User management->Access control.

Allow anonymous users to post comments

  1. Enable Comment module at Administer->Site Building->Modules.
  2. Check access comments and post comments/post comments without approval for anonymous user at Administer->User management->Access control. Note that you must check access comments so that users can access comments and then post their comments.

Rename URLs By default, when node(Page,Story, etc) is created, Drupal names the node with number. This doesn't create a user friendly path to the node. In order to be able to rename path of node by enabling Path module.

  1. Enable Path module at Administer->Site Building->Modules.
  2. When creating node(Page,Story, etc), you can rename the node by entering the desired name under URL path settings.

Useful links

MISC how to restrict access to nodes based on some criterion associated with the user. http://api.drupal.org/api/file/developer/examples/node_access_example.module/6/source

Install and configure Apache, PHP and MySQL for Drupal

Install Apache, PHP and MySQL

Before installing Drupal, you need to install Apache, PHP and MySQL. The easiest way to install them is to use an all-in-one installer that will install all 3 softwares. I used WampServer 2.0-h. Please note that Drupal 6.x doesn't support PHP 5.3.x. Therefore, choose carefully the version of WampServer that you download. As of version 2.0-i, WampServer includes PHP 5.3.0.

  1. Here are the steps to install WampServer.
  2. Download WampServer 2.0-h.
  3. Double click on the WampServer's executable(e.g. WampServer2.0h.exe).
  4. Click Yes.
  5. Click Next.
  6. Select I accept the agreement and Click Next.
  7. Click Next.
  8. Click Next.
  9. Click Install.
  10. If you have Firefox installed, the message above will show up. Click on Yes/No depending on your preference.
  11. Click Next.
  12. Click Finnish.
  13. Note: If you restart your computer, you need start the WampServer again. Simply execute C:\wamp\wampmanager.exe.

Set the root password of MySQL

  1. Open the MySQL console by clicking on the WampServer icon located in the system tray, select MySQL->MySQL console.
  2. By default, there is no root password. Therefore, when MySQL console window appeared, simply press Enter.
  3. Execute the following SQL queries to change the root password. Simply change mypassword to whatever you want.
        GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' IDENTIFIED BY 'mypassword' WITH GRANT OPTION;

Create drupal database in MySQL

In MySQL console, execute also the following query to create a database for Drupal:

Image icon wampserver_install_02.png23.15 KB

Install Drupal

Before continuing reading further, it is assumed that you already installed Apache, PHP and MySQL and you have created a database for Drupal.

  1. Download Drupal.
  2. Decompress drupal-x.x.tar.gz and copy the drupal-x.x/ directory into the root directory of Apache(e.g. C:\wamp\www)
  3. Copy the C:\wamp\www\drupal-x.x\sites\default\default.settings.php file to C:\wamp\www\drupal-x.x\sites\default\settings.php.
  4. Open http://localhost/drupal-x.x using a browser.
  5. Click on Install Drupal in English link.
  6. Type in the followings(Change the values accordingly if your settings are different):
        Database name: drupal
        Database username: root
        Database password: mypassword
    Then, click on the Save and continue button.
  7. Type in the followings:
        Username: x
        E-mail address: x@1243p.com
        Password: x
        Confirm password: x
    Then, click on the Save and continue button.
  8. It will display a big red warning that it can't sent email. Simply ignore it.
        * warning: mail() [function.mail]: Failed to connect to mailserver at "localhost" port 25, verify your "SMTP" and "smtp_port" setting in php.ini or use ini_set() in C:\wamp\www\drupal-6.16\includes\mail.inc on line 193.
        * Unable to send e-mail. Please contact the site administrator if the problem persists.
    Click on "... your new site" link.
  9. That's it. You have installed Drupal.

Write your first post

By default, Drupal comes with 2 content types: Page and Story.

A page, similar in form to a story, is a simple method for creating and displaying information that rarely changes, such as an "About us" section of a website. By default, a page entry does not allow visitor comments and is not featured on the site's initial home page.
A story, similar in form to a page, is ideal for creating and displaying content that informs or engages website visitors. Press releases, site announcements, and informal blog-like entries may all be created with a story entry. By default, a story entry is automatically featured on the site's initial home page, and provides the ability to post comments.

Let's say that we want to create the "About Us" page. Here are the steps:

  1. Click on the Create content link.
  2. Click on the Page link.
  3. Fill in the Title and Body fields.
  4. Expand Publishing options by click on it and check also Promoted to front page.
  5. Click on the Save button.
  6. That's it. You have write your 1st post.
  7. Go to the home page and you will see your post.

Post images

Now that you know how to post. It would be nice that you will be able to post images in your posts. What you have to do is to allow <img> tag in your posts and activate Upload module so that you can update the images.

Configure Input formats to allow <img> in your post

  1. Go to Administer » Site configuration » Input formats.
  2. Click on the Configure link of Filtered HTML.
  3. Click on Configure tab.
  4. In Allowed HTML tags field, add <img>.
  5. Click on the Save Configuration button.

Activate Upload module to upload files

  1. Go to Administer » Site building » Modules.
  2. Check the Upload module.
  3. Click on the Save Configuration button.

Try to post images

  1. When you are creating a post, you will see that there is new section called File attachments located near the bottom. Click on it to expand.
  2. Browse to the image in your computer.
  3. Click on the Attach button to upload the image.
  4. Copy the link underneath your image's filename(e.g. http://localhost/drupal-6.16/sites/default/files/monkey_face.png). That is the link that you should paste in your post's Body.
  5. In the Body textarea, add the <img> tag as follows:
    <img src="http://localhost/drupal-6.16/sites/default/files/monkey_face.png" />
  6. Click on the Save button.
  7. Here are the results:
Image icon post_image_01.png20.63 KB

Install modules and themes

Install modules and themes

General steps to install modules, unless otherwise stated in the README.txt

  1. Decompress modules that you downloaded from http://drupal.org/project/modules.
  2. Put it in .../sites/all/modules.
  3. Go to Administer->Modules, check the modules.
  4. Click on the Save Configuration button.
  5. Click on the update.php link, located at the beginning of the page and follow the instructions.

General steps to install themes, unless otherwise stated in the README.txt

  1. Decompress modules that you downloaded from http://drupal.org/project/themes.
  2. Put it in .../sites/all/themes.
  3. Go to Administer->Themes, enable the themes.
  4. Click on the Save Configuration button.

Checklist before production


Search Form

  1. Go to admin/structure/block/manage/search/form/configure, set which Content types are visible and which Roles are allow to view the search block.
  2. Go to admin/people/permissions#module-search, set who has permission to use the search form.


  1. Go to admin/structure/views, open your views.
  2. Set the proper permission under Page settings->Access.
  3. For all the exposed filters, it is recommended to rename the Filter identifier under the More section of the field.
  • Go to Administer->Site Configuration->Error reporting, change Error reporting to Write errors to the log only.
  • Customize 403/404 message at Administer->Site Configuration->Error reporting.
  • For exposed filters, uncheck the Optional. Otherwise, < Any > option is available for users to select, which will not limit the filtering.
  • Did you set correctly the access permission of each view?


Applying patches

Go the root directory of the project and then run the following:

patch -p1 < path/file.patch

Reference: https://drupal.org/node/60108

Bulk update Pathauto node aliases through cron job

  1. Put the following content into a PHP file(e.g. cron-update-pathauto.php) and put the file in your drupal's root directory.
    include_once './includes/bootstrap.inc';
    include_once './sites/all/modules/pathauto/pathauto.inc';
    include_once './sites/all/modules/pathauto/pathauto_node.inc';
    // variable_set('pathauto_max_bulk_update', 5000);
  2. Create a cron job to call this file at your desired interval. For example, the following cron job will call the file at the beginning of every hour.
    0 * * * * curl --silent --compressed http://yoursite.com/cron-update-pathauto.php > /dev/null 2>&1  

Debugging Javascript

Is Javascript file loaded?

  1. Open admin/config/development/performance.
  2. Uncheck Aggregate JavaScript files and save.
  3. Open Chrome browser.
  4. Open the Developer Tools(F12) and click on the Network menu.
  5. Open the webpage where your JavaScript is supposed to execute.
  6. You should see your JavaScript filename.

Get alias or system path

l() function converts automatically system path in alias where available.

Run drupal code outside of module: Create a node

Here is an example of Drupal code that you can run outside of module. It will create an Article node. Put the code below in your Drupal root directory, and execute it either through the browser or from the command line.

 * The 4 lines below are needed so that your drupal code can be executed.
define('DRUPAL_ROOT', getcwd());
$_SERVER['REMOTE_ADDR'] = "localhost"; // Necessary if running from command line
require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
$node = new stdClass();     // Create a new node object
$node->type = "article";    // Or page, or whatever content type you like
node_object_prepare($node); // Set some default values
$node->title    = "Node title " . date('Y-m-d H:i:s');
$node->language = LANGUAGE_NONE; // Or e.g. 'en' if locale is enabled
$node->uid = 1; // UID of the author of the node; or use $node->name
$bodytext = "This is text for the body of the node.";
$node->body[$node->language][0]['value'] = $bodytext;
if($node = node_submit($node)) // Prepare node for saving.
  echo "Node with nid " . $node->nid . " saved!\n";
  echo "<pre>";
  echo "</pre>";

Reference: http://fooninja.net/2011/04/13/guide-to-programmatic-node-creation-in-drupal-7/

Use Computed Field module to fetch Field Collection values

If you have a field collection(e.g. field_transaction) that have quantity field (e.g field_stock_quantity) and you want to sum all the quantity up. Here is code to do it in Computed Field module:

$field_items = field_get_items($entity_type, $entity, 'field_transaction');
$total = 0;
foreach ($field_items as $item)
  // Note: I don't know why 'und' is within the field collection.
  //  Is there a better way to get the value of field_stock_quantity?
  $total += $item['field_stock_quantity']['und'][0]['value'];
$entity_field[0]['value'] = $total;

Use Computed Field module to fetch field values

If you have a field(e.g. field_quantity) that is configured to have unlimited values and you want to sum them up. Here is code to do it in Computed Field:

// Get all values of machine name field 'field_quantity'.
$field_items = field_get_items($entity_type, $entity, 'field_quantity');
$total = 0;
foreach ($field_items as $item)
  $total += $item['value'];
$entity_field[0]['value'] = $total;

Source: http://drupal.stackexchange.com/questions/39257/sum-of-integer-vaules-of-a-multi-value-field-using-computed-field-module

Draft, approval, publication, revision

The basic use case of a content management system where contents need to be validated before they are being published is being implemented in the Revisioning module. Obviously, the module offers much more fonctionnalities than that but for this tutorial, I will stick to the following use case described on their project's website: Authors write content that prior to being made publicly visible must be reviewed (and possibly edited) by moderators. Once the moderators have published the content, authors should be prevented from modifying it while “live”, but they should be able to submit new revisions to their moderators.

Here are the step-by-step instructions of how to install and setup the Revisioning module.

Install Revisioning, Module Grants and Smart menus Smart tabs modules

  1. Download Revisioning, Module Grants and Smart menus Smart tabs.
  2. Decompress them into .../sites/all/modules directory.
  3. Go to Administer->Modules, enable Module Grants, Module Grants Monitor and Revisioning
  4. Click on the Save configuration button and then the Continue button.

Setup the content type

We will use Story as our content type.

  1. Go to Administer->Content types, click on the Edit link of the Story.
  2. Under the Workflow settings, uncheck Published. Check Promoted to front page, Create new revision and New revision in draft, pending moderation (requires "Create new revision").
  3. Click on the Save content type button.

Create Author and Moderator roles and assign permissions to them

  1. Go to Administer->Roles, add the following 2 roles: Author and Moderator.
  2. Go to Administer->Permissions, enable the following permissions for Author and Moderator.
    Permission Author Moderator
    access All tab XX
    access I Can Edit tab  X
    access I Can View tab  X
    access I Created tab XX
    access I Last Modified tab  X
    access Published tab  X
    access Unpublished tab  X
    access contentXX
    create story contentX 
    edit any story content X
    edit own story contentX 
    revert revisions X
    view revisions X
    access Pending tab XX
    edit revisions XX
    publish revisions  X
    unpublish current revision  X
    view revision status messages X
    view revisions of own story contentX 

Test the workflow

  1. Go to Administer->Users, create an Author user and a Moderator user.
  2. Login as an Author and post a Story. The story posted will not be published. It is now up to the Moderator to review and publish the story.
  3. Login as a Moderator. Go to Accessible content->I can edit->Unpublished. Click on the Story's title.
  4. Click on Revisions->Publish->Publish to publish the story.
  5. Go to the homepage, the story posted is now published.
  6. Login as an Author and edit the posted story. Go the homepage, the changes are not shown. The moderator has to approve it again for the changes to show.

Flag: Which users are using flag module's functionalities?

Here is a sql query that show users that use flag module's functionalities.

--Note: Depending on your drupal's installation, you may have to add prefix to table names.
SELECT name, DATE(from_unixtime(fc.TIMESTAMP)) FROM flag_content fc LEFT JOIN  users u ON u.uid=fc.uid;

It is good to know this statistic so that you can decide whether to remove this module or not.

Hide CCK field from input form

The simplest way to do this is to enable Content Permissions module and then don't allow the intended user group to view or edit that particular field.

Install WYSIWYG editor using CKEditor


  • Your must have Administer->Site configuration->Clean URLs enabled. Otherwise, you will not be able to preview uploaded images.


  1. Download and decompress the following modules into your ..../sites/all/modules directory:
  2. Create ..../sites/all/libraries/directory.
  3. Download CKEditor and decompress it into ..../sites/all/libraries/directory. So the actual library can be found at ..../sites/all/libraries/ckeditor/ckeditor.js
  4. Download and decompress jQuery UI.Rename development-bundle/ directory to jquery.ui and move it to ..../sites/all/modules/jquery_ui/ directory.
  5. Go to Administer->Site building->Modules and enable WYSIWYG Image upload and ImageAPI GD2.


  1. Go to Administer->Site configuration->Wysiwyg.
  2. For the Filtered HTML, select CKEditor editor. Click on the Save button and then on the Edit button.
  3. Click on the Buttons and plugins section. Check on any buttons that you would like to appear on the rich textarea. Note: Don't forget to check Image uploading. With this button enable, users will be able to upload their image to your server. Then on the Save button.
  4. >Go to Administer->User management->Permissions and check use wysiwyg image upload for at least authenticated user.
  5. Go to Administer->Site configuration->Input formats.
  6. For Filtered HTML, click on the configure link and check Drupal Wiki inline-images filter.
  7. Click on Configure again and add <img> in Allowed HTML tags.
  8. Click on the Save configuration button.

Memory limit

If you are getting "Allowed memory size of 134217728 bytes exhausted (tried to allocate 35 bytes)", then increase memory_limit variable in php.ini.

Migrate from Drupal 6 to Drupal 7

Link Node

Link node is needed so that content can be linked internally using [node:XXXX].


date_popup sub-module is needed for Order:date.


By default, Drupal allows anonymous users to view any published contents. In order to block users from viewing published contents of certain node types, nodeaccess module is used.

Date (ISO format) type: Missing operators(greater or less than, is between) in Views

After migrating CCK from Drupal 6 to Drupal 7, my content type of Date type is converted to Date (ISO format) field type. When I tried to expose this field in Views, I noticed that the usual operators available in Drupal 6 are missing: Is less than, Is greater than, Is between, etc. The solution is to enable Date Views module that is part of the Date module.

Error messages in Status Report due to SA-CORE-2013-003 that won't go away


Public files directory: Not fully protected

See http://drupal.org/SA1. CORE1. 20131. 003 for information about the recommended .htaccess file 
which should be added to the sites/default/files directory to help protect against arbitrary code execution.

Temporary files directory: Not fully protected

See http://drupal.org/SA1. CORE1. 20131. 003 for information about the recommended .htaccess file 
which should be added to the /tmp directory to help protect against arbitrary code execution.


  1. Delete the .htaccess in your files directory(e.g. sites/default/files).
  2. Go to admin/config/media/file-system, prefix a dot to your Temporary directory (e.g. ./tmp).
  3. Click on Save.

GeSHi Filter doesn't highlight code

  1. Make sure you fix Error messages in Status Report due to SA-CORE-2013-003 that won't go away.
  2. Go to admin/config/content/formats, open the text format that you are using.
  3. Make sure that GeSHi filter is activated.
  4. Make sure that GeSHi filter is at the bottom of the Filter processing order.
  5. Go to admin/config/content/formats/geshifilter.
  6. Expand GeSHi library version detected, click on Flush the GeSHi language definition cache.

Get rid of taxonomyextra

  1. After migrating to Drupal 7, go to admin/reports/fields.
  2. You should not see taxonomyextra field name. If not, run the following query to see which content are affected:

    SELECT entity_id AS node id, bundle AS TYPE, tid AS tid, name AS name 
    FROM field_data_taxonomyextra JOIN taxonomy_term_data 
    ON tid = field_data_taxonomyextra.taxonomyextra_tid;
  3. Clean the data and do the migration again.

How to change date input format of exposed filter?

  1. Go to admin/config/regional/date-time.
  2. Change the format of the Short date type.

Link all vocabularies to a content type so that there is no taxonomy extra terms

If vocabularies are not linked to a content type, taxonomy extra field will be created after migration.

Migrate CCK2's Multigroup to Field Collection

  1. Install Migrate module.
  2. Go to admin/structure/content_migrate and migrate all your content types.
  3. Download field_collection-7.x-1.x-dev(2013-Oct-10).
  4. Apply field_collection-convert_cck_multigroups-1722450-2.patch to field_collection-7.x-1.x-dev (https://drupal.org/node/1722450).
  5. If your tables use prefix, then modify the following table names in field_collection.install file: content_group, content_group_fields, field_revision_, field_data_.
  6. Install Field Collection. It is better to do this using Drush because it will show you all the errors.
  7. Drop content_group and content_group_fields tables.

Notice: Undefined index: display_url in theme_link_formatter_link_domain()

To reproduce the issue

Open a view containing a link field with Formatter=Domain, as link. At admin/reports/, open the Recent log messages, it will show the following notice:

Notice: Undefined index: display_url in theme_link_formatter_link_domain() (line 969 of ...\sites\all\modules\link\link.module).

To solve

Don't use that formatter.

Notice: Undefined index: distribution_name in drupal_install_profile_distribution_name()

To reproduce the issue

  1. Open admin/modules.
  2. At admin/reports/, open the Recent log messages, it will show the following notice:
Notice: Undefined index: distribution_name in drupal_install_profile_distribution_name() (line 207 of ...).

To solve

Adapt the table name if you use prefix and then execute the following SQL statement:

UPDATE `system` 
SET `status` = '1' 
WHERE `filename` = 'profiles/standard/standard.profile';

Reference: http://stackoverflow.com/questions/21334035/drupal-7-notice-undefined-index-distribution-name-in-drupal-install-profile

Update URL aliases: Token pattern has changed

  1. After migration, you have to update the URL aliases because token pattern has changed.
  2. Go to admin/config/search/path/patterns and make the changes.

Note: For Book, use [node:book:parents:join-path]/[node:title] pattern so that you get all the parent name of the page.

Mistakenly put drupal/ in sites/all/modules

When updating drupal and other modules at the same time, I sometimes mistakenly put drupal-6.x folder in sites/all/modules folder. Later on, I discovered my mistake and naturally, my first reflex would be to delete the drupal-6.x folder. Consequently, my website crashed and I couldn't log in. However, if I put back the drupal-6.x folder, then it is working fine.

Here is how I did to resolve my issue:

  1. Ran the following queries to remove all references to the drupal-6.x folder. Depending on the version of your drupal, change drupal-6.16 in the queries accordingly.
    SELECT file FROM menu_router WHERE file LIKE '%drupal-6.16%';
    SELECT filename FROM system WHERE filename LIKE '%drupal-6.16%';
    UPDATE menu_router SET file=REPLACE(file, 'sites/all/modules/drupal-6.16/', '') WHERE file LIKE '%drupal-6.16%';
    UPDATE system SET filename=REPLACE(filename, 'sites/all/modules/drupal-6.16/', '') WHERE filename LIKE '%drupal-6.16%';
  2. Delete drupal-6.x folder from sites/all/modules folder.
  3. Log back in to the website and go to Administer->Modules. Note: You will see a lot of errors messages. It is normal. It complains about the missing paths that point to the drupal-6.x folder.
  4. Click on Save Configuration button so that drupal rebuilds all the paths itself.


    CCK(Content Construction Kit): Add custom fields

    Allow you to add additional fields.
    • CCK + Date + jquery_ui: Date field + datepicker
    • CCK + Content Taxonomy: Autocomplete field
    • CCK + Conditional Fields: If option selected then display another input field. (e.g. If "Include Teaser" is checked, then show "Teaser" field.).
    • CCK + editablefields+Ajax Load: Turn CCK field into editable field on node display view.
    • CCK + CCK Private Fields: Allows users to set the CCK field as Public, Hidden and Private.
    • CCK + CCK Vcard: vCard fields
    • CCK + Dependent Fields: If value X is selected from field 1, then show in field 2 available values.(e.g. Country -> Provinces)
    Automatic Nodetitles
    Hide node title in the form by automatically setting value using defined patterns.
    Arrange Fields
    This module lets you drag-and-drop the fields of any CCK content type, Webform, or almost any other form in Drupal into the positions you would like for editing.
    • Views + jCarousel: Carousel image slide show
    Allow input fields to be prepopulate through the URL (e.g. http://www.yoursite.com/node/add/blog?edit[title]=this is the title).
    Spreadsheet in a node, amazing!!

    SEO(Search Engine Optimization): Make your page rank higher in search engine

    Automatically generate URL aliases for your content.(e.g. Change http://example.com/node/123 to http://example.com/node/about-us)
    Path redirect
    In case the URL aliases change, it will redirect the older alias to the new one.(e.g. Redirect http://example.com/node/about-us to http://example.com/node/about)
    Global Redirect
    Ensure only 1 URL represents a content page.

    Access Permission

    Very granular access permission to node. You can set access permission to content type by role. You can also go further. Set access permission to a specific node per role or per users.
    Menu per Role
    Allow you to set access permission to menu items per role. When using this module with Nodeaccess, it is the perfect combination to define pretty much how your contents are being accessed.


    Search using a pool of synonyms.(e.g. 'blogs', 'blogger', 'blogging', 'blog' are considered equivalent when searching)
    Search Lucene API
    Bring Solr-like search functionality to Drupal: Highlight the matches, faceted searching, etc. There are extensions that add more functionalities(e.g. Google-like "did you mean")
    Custom Search
    XXXXXXXXXXXXXXXXXXXXXXXX http://drupal.org/project/search_config

    Caching: Make your site faster

    Pre-generate your content as static html page so that it loads faster. Suitable for websites where their contents don't change frequently.


    Block spambots to post by issuing a challenge question.

    http://drupal.org/project/fuzzysearch http://drupal.org/project/csplitter

    http://drupal.org/project/form_builder http://drupal.org/project/composite

    Similar to Dependent Fields http://drupal.org/project/option_trim


    http://drupal.org/project/views_calc http://drupal.org/project/views_customfield

    http://drupal.org/project/spam http://drupal.org/project/user_delete http://drupal.org/project/views_bulk_operations



    http://drupal.org/project/views_infinite_scroll http://drupal.org/project/relation



    Here are the steps to require a captcha for anonymous node submission:

    1. Download and decompress Captcha in ...sites/all/modules.
    2. Go to Administer->Modules and then enable CAPTCHA module.
    3. Go to User management->Captcha
    4. Check Add CAPTCHA administration links to forms.
    5. Go to Create content and click on the content type(e.g Story, Page, etc.) where you want to activate the captcha.
    6. Expand CAPTCHA: no challenge enabled section.
    7. Click on the Place a CAPTCHA here for untrusted users. link.
    8. Go to Administer->User management->Permission and then enable anonymous user to access content and write to your selected content type(e.g. create story content, create page content, etc).

    Conditional Fields

    Conditional Fields module does the following: If a value of field A is selected, then display field B. A typical use case for this module is when you want users to type in something specific that is not part of your options. For example: What is your favorite type of music?

    • Rock
    • Pop
    • Country
    • Classic
    • Other

    If the user select Other option, then an Other: input field will appear for the user to type in his/her favorite type of music.

    Click on Test Conditional Fields to view the complete export of the content type illustrating the case above.

    Plain text icon Test.Conditional.Fields.txt6.21 KB

    Core Image module enable

    Enable Image module so that I can add image field in Product content type.

    Create custom image styles

    1. At admin/config/media/image-styles, add a custom image style (e.g. Cover: Resize to 250 x 353=>A4).
    2. Add another custom image style (e.g. Cover thumbnail: Resize to 35 x 49=>A4).

    Setting the image field

    1. In the image field, set the File directory so that you can contain picture for that field.
    2. Don't set the Maximum nor the Minimum image resolution. Otherwise, image will be resized. You want to preserve the original size. Simply set the Maximum upload size.
    3. Set the Preview image style.

    Difference between date, datetime and datestamp fields

    The difference resides in the way each field is stored in the database.
    The date field is an ISO 8601 standard date. It is useful if you want to store incomplete dates, like year and month only. It is stored in the database as varchar field.
    It is use for complete dates. It is stored in regular datetime field of the database.
    datestamp is the unix timestamp. It is discouraged to use this in favor of datetime.

    Reference: http://groups.drupal.org/node/731


    Remove feature module without breaking the website

    https://drupal.org/comment/6163176#comment-6163176 Note: Doesn't work with complex configurations.

    GeSHi Filter

    In Drupal 7, go to admin/config/content/formats/filtered_html and make sure that under the Filter processing order, GeSHi filter is below Limit allowed HTML tags.


    Setup Markdown in Drupal 7

    At admin/config/content/formats, make sure that your text format's Filter processing order has to be in the following order:

    1. Limit allowed HTML tags
    2. GeSHi filter
    3. Link Node
    4. Markdown
    5. Convert URLs into links
    6. Convert line breaks into HTML (i.e. < br > and < p >)
    7. Correct faulty and chopped off HTML

    Limit allowed HTML tags

    • Make sure that this filter allows all the HTML tags Markdown uses.
    • If this filter is active, then you have to put space between < and >. Otherwise, it will consider whatever that is in between as tag and will be removed because it will not be part of the list of tags allowed.

    The following filters need to be placed after Markdown filter:

    • Convert URLs into links: Otherwise, (Google)[https://www.google.com] will not work properly.
    • Convert line breaks into HTML (i.e. < br > and < p >): Otherwise, Markdown filter will not work at all.


    Markdown drupal filter cheat sheet

    # header level 1
    ## header level 2
    Unordered list: Must starts with a newline:
    * Orange
    * Meat
     * Pork
     * Veal
    * Fish
    Ordered list: Must starts with a newline:
    1. step 1
    1. next step
    2.1 sub-step (Workaround)
    List with block code:
    1. Newline + 4 spaces before your block code
        <pre lang="perl">#!/usr/local/bin/perl
        print 'Hello world.';       # Print a message    
    1. Next item.
    Markdown Extra - Definition lists:
    : Apple is a fruit.
    Escape characters:
    \*this text is surrounded by literal asterisks\*
    `<simple code>`


    Markdown editor for BUEditor

    Install Markdown filter

    1. Download Markdown filter.
    2. Enable the module.
    3. Setup Markdown.

    Install Ajax markup

    1. Download Ajax markup.
    2. Enable the module.

    Install BUEditor

    1. Download BUEditor.
    2. Enable the module.
    3. Go to admin/config/content/bueditor.
    4. Under the Role-editor assignments section, assign the editor that you like for each role.

    Install Markdown editor for BUEditor

    1. Download Markdown editor for BUEditor.
    2. Enable the module.

    Node Hierarchy: tree-like hierarchy of content


    Node Hierarchy allows nodes to be children of other nodes creating a tree-like hierarchy of content. This module seems to be designed to replace the Book module.



    1. Download Node Hierarchy and decompress it into ..../sites/all/modules.
    2. Go to Administer->Site building->Modules and then enable Node Hierarchy.

    Configuration: Replicate Book-like behavior using Story content type

    1. Go to Administer->Content management->Content types and click on the edit link of the Story content type.
    2. Under Node Hierarchy section, check Can be parent and Can be child. It means that the Story content type will be the parent and the child of itself. Thus, allow infinite nesting of itself.
    3. Click on the Save content type button.
    4. On the navigation menu, click on Create content->Story to create a story.
    5. Edit the story that you just created. You should see the Children tab. Click on it and from there you will be able to create other children stories.



    1. Install Services module.
    2. Go to Administer->Site building->Modules and then enable Services, XMLRPC Server and Node Service modules.
    3. Go to Administer->User management->Permissions and then allow anonymous user to load node data.
    4. Go to Create content and create any node.

    Retrieve all informations of a node using "XML-RPC for PHP" library

    1. Download and decompress XML-RPC for PHP(e.g. xmlrpc-3.0.0.beta.zip) library.
    2. The following code uses XML-RPC for PHP library to retrieve all informations of a node from the XML-RPC webservice.
       * Description: Retrieve all informations of a node from XML-RPC webservice.
       * Change the values of the following variables according to your case.
       * Assumptions:
       *    I want to get all the informations of node ID 23.
       *    The XML-RPC webservice is located at http://localhost/dtest/?q=services/xmlrpc  .
       *    The XML-RPC for PHP library is located at xmlrpc-3.0.0.beta/lib/xmlrpc.inc  .
      $xmlrpclibrarypath = 'xmlrpc-3.0.0.beta/lib/xmlrpc.inc';
      $hostname          = 'localhost';
      $url               = 'dtest/?q=services/xmlrpc';
      $nid               = 23;
      // Retrieve all informations of a node and print out everything.
      $node = rpcexample_retrieve_node($nid, $xmlrpclibrarypath, $hostname, $url);
      // Function taken from http://drupal.org/node/816934 and adapted for my case.
      function rpcexample_retrieve_node($nid, $xmlrpclibrarypath, $hostname, $url) 
        // Load the XML-RPC for PHP library from wherever you've put it
        // File path relative to the Drupal root
        // Turn the node ID into a standard XMLRPC data type, for transmission
        $nid = new xmlrpcval($nid, "int");
        // Create an XMLRPC message
        $m = new xmlrpcmsg('node.get',
          // Note that the "optional" second parameter is required, but it can be empty.
          // If you omit it, services returns wrong-parameter-number errors
          array($nid, new xmlrpcval(array(), "array"))
        // Create a connection to the remote server
        // Note the root URL is services/xmlrpc. If you don't turn the
        // separate XMLRPC server module on, this will result in HTTP 404
        $c = new xmlrpc_client($url, $hostname, 80);
        // Send the message to the remote server and get the response
        $r = $c->send($m);
        // The return value is a collection of xmlrpcval objects
        // which you can loop over to turn back into PHP data types
        return $r->value();
       * Convert return value of 'XMLRPC for PHP' into a nice and readable array.
      function rpcexample_rpc_convert($x) {
        $keys = array_keys($x->me);
        $val = &$x->me[$keys[0]];
        switch($x->mytype) {
          // Scalar
          case 1:
            return $val;
          // Array
          case 2:
            $ret = array();
            foreach($val as $v) {
              // Don't preserve keys
              $ret[] = rpcexample_rpc_convert($v);
            return $ret;
          // Struct
          case 3:
            $ret = array();
            foreach($val as $k => $v) {
              // Preserve keys
              $ret[$k] = rpcexample_rpc_convert($v);
            return $ret;

    User Delete

    Block user and delete all their comments

    1. Use User Delete module to block a user and unpublish all submitted content, including nodes and comments.
    2. Go to Approval queue in Administer->Content management->Comments and then delete unpublished comments.

    Note: User Delete overwrites the Delete button.


    List of useful tips

    Enable filter based on multiple field
    Under the Filters section, click on the field that you would like to expose. Then, click on the Expose button.
    Display all terms of a node inline and separated by comma
    Under the Fields section, select Taxonomy: All terms and Limit terms by vocabulary.
    Expose a list of taxonomy terms for the users to choose
    Under the Filters section, select Taxonomy: Term(term ID).
    Sort criteria don't work
    I encountered an issue with the Sort criteria section in Views. Any criterion I added under that section doesn't work. I found out that it will work as soon as I set the default sort of the table style to none.(Basic settings->Style: Table)

    Add Javascript in specific view

    Let's assume that I want to add javascript in my specific view called orders.

    Create template file of your view

    1. At admin/structure/views, open orders view.
    2. Open Advanced->Theme: Information to see the template name that you can use.(e.g views-view--orders.php).
    3. Click on Display output link and paste the code in your template file (e.g views-view--orders.php).
    4. Put your template file at sites/all/themes/YOUR_THEME/templates.

    Add Javascript in your view's template file

    Adapt and add the following code in your view's template file(e.g views-view--orders.php):

     * ....
     * @ingroup views_templates
    drupal_add_js(path_to_theme() . "/orders.js"); // This is the code to add your javascript file.
    <div class="<?php print $classes; ?>">

    Write your Javascript in orders.js

    Create orders.js file and put it in sites/all/themes/YOUR_THEME/ folder and add your code:

    (function ($) {
            // Your javascript code here...

    Add a link in the view page to directly create a node

    Views provides you the ability to display informations according to your criteria. But, wouldn't it be great if you can add a link in the view page to directly create a node? Yes, you can. I use the Header and Footer under the Basic settings section to add my custom links there.

    Let's suppose that I have a view called Story_View. The Page path of this view is http://yoursite.com/Story_View_Path. This view displays all the Story nodes that I have. I want to add a link to directly create a Story node in this view page. What I did was simple. I add the following HTML code in Header and Footer:

    <a href="/node/add/story?destination=/Story_View_Path">Add New Story</a>

    Note: In Header and Footer, make sure that the Input Format that you choose allows <a> tag. The ?destination=/Story_View_Path has been added so that when the Story node is created, it will return back to the view.

    Set default date using Relative date

    In Views, you set a default date by using the relative date option. For example, if you want to set the first day of this year as the default date, enter the following relative date: 1/1 next year -365 day. It basically means "Take the first day of the first month next year, then subtract 365 days".

    Reference: http://www.php.net/manual/en/datetime.formats.relative.php#106441

    Turn checkbox value into symbol

    Set your view so that it will output a check mark(✓) if it is true. Otherwise, a cross mark(✗).

    1. Open the checkbox field of your view and set Formatter=Key.
    2. Expand Rewrite results.
    3. Check Rewrite the output of this field.
    4. Add <span class="ow-check">&check;</span> in the Text area.
    5. Expand No results behavior.
    6. Add <span class="ow-cross">&cross;</span> in the No results text area.
    7. Check Count the number 0 as empty.

    Note: In the < span > element, it is not allowed to have style attribute because of security reason. To style your element, you have to do it in the CSS file of your theme. That is why I added my own class name.

    Views: Change the text of the Apply button of the exposed filters

    In the template.php file of your theme, add the following code:

    function YOUR_THEME_preprocess_views_exposed_form(&$vars, $hook)
        Only alter a specific exposed filter form
        Note: You need to change the form id to match yours.
      if ($vars['form']['#id'] == 'views-exposed-form-YOUR_VIEW-views-page-1') {
        // Change the text on the submit button
        $vars['form']['submit']['#value'] = t('Search');
        // Rebuild the rendered version (submit button, rest remains unchanged)
        $vars['button'] = drupal_render($vars['form']['submit']);

    jQuery tablesorter plugin integration in Views

    Install Tablesorter module

    1. Download Tablesorter.
    2. Enable the module.

    Add tablesorter class in < table > element of views

    1. In the template.php file of your theme, add the following:
    function YOUR_THEME_preprocess_views_view_table(&$vars)
      // Add tablesorter class in view <table>.
      $vars['classes_array'][] = 'tablesorter';

    Add up/down icons in the header cell of your table

    1. Adapt the following CSS code in your theme's CSS file:
    /* Tablesorter: Icons for column table sorting in Views. */
    table.tablesorter th.views-align-right
         * Leave extra space on the right side
         *  so that right-aligned header text doesn't overlap with up/down icons.
        padding-right: 2em;
    /* Position icons to the right side and centered vertically.*/
    table.tablesorter thead tr
      background-repeat: no-repeat;
      background-position: center right;
      cursor: pointer;
    table.tablesorter thead tr .header 
      background-image: url(tablesorter/bg.gif);
    table.tablesorter thead tr .headerSortDown
      background-image: url(tablesorter/desc.gif);
    table.tablesorter thead tr .headerSortUp
      background-image: url(tablesorter/asc.gif);

    Move from Dreamhost to Bluehost

    I decided to move from Dreamhost to Bluehost because Dreamhost is unbearably slow for Drupal 7. Dreamhost was always slow, even for Drupal 6. It is Drupal 7 that made me jump ship: enough is enough. I should have done this a long time ago.

    Reduce memory used by the cron job and the time it takes to run


    • When you look at your Recent log entries, you are seeing the message Cron run exceeded the time limit and was aborted.
    • When you run the cron job manually(e.g. http://yoursite.com/cron.php), you are seeing the message Allowed memory size of ######## bytes exhausted (tried to allocate ####### bytes).


    For the sake of your tiny and low resource shared hosting webserver, change the following module's settings to reduce the memory used by the cron job and the time it takes to run:

    At admin/settings/search, lower the Number of items to index per cron run.
    XML sitemap
    At admin/settings/xmlsitemap, lower the Cron limit.
    At admin/settings/performance/boost, lower the Number of URLs to grab at a time when loading up the crawler table.

    It is always better for the cron job to do less work but often than doing all the work at once and getting aborted in mid way.

    If you know other modules' settings that can affect the cron job, please comment so that I can expand this list.

    Setting Cron job

    Setting cron job to avoid the infamous message "Cron run exceeded the time limit and was aborted", see http://acquia.com/blog/s-files-cron-run-exceeded-time-limit-and-was-aborted

    wget -O - -q -t 1  http://localhost/cron.php

    Theme a specific CCK field

    See excellent tutorial: http://www.advantagelabs.com/drupal-colonoscopy-or-how-theme-cck-field-drupal-6

    Important: In your theme directory, it must content-field.tpl.php file.

    Useful Non-Core Modules

    Useful Non-Core Modules


    Themes development

    TODO: Override theme function. In your template.php: theme_username -> phptemplate_username

    Drupal 7 Template(Theme Hook) Suggestions Setting up variables for use in a template (preprocess and process functions) Theme Developer module

    Create a sub-theme

    In this tutorial, we will create a sub-theme of the Garland theme.

    1. Create a directory for your sub-theme(e.g. .../sites/all/themes/mySubTheme)
    2. Create an .info file for your sub-theme(e.g. .../sites/all/themes/mySubTheme/mySubTheme.info)
      name = Mysubtheme
      description = A tutorial showing how to create a sub theme.
      core = 6.x
      engine = phptemplate  
    3. Add the following line in your sub-theme's .info file to declare that your theme is the sub-theme of another.
      base theme = garland
    4. You have to at least add a stylesheet to your sub-theme. Otherwise, your sub-theme will not inherit any of the parent theme's stylesheets. Therefore, create an empty css file(e.g. .../sites/all/themes/mySubTheme/AtLeastOne.css) and then add the following in your .info file.
      stylesheets[all][] = AtLeastOne.css  

    In general, your sub-theme will inherit pretty much everything of the parent's theme(e.g. stylesheets, javascripts, Template.php, *.tpl.php, etc) and you will be able to override them too. For more details, see Sub-theme structure and inheritance.

    Click on the mySubTheme.zip to dowload the complete theme of this tutorial.

    Package icon Mysubtheme.zip507 bytes

    Add a link through the template.tpl.php

    function phptemplate_preprocess_node(&$vars)
       * Add Read More... link on teaser only.
        // Get all links currently available on the node.
        $links = $vars['node']->links;
        // Add read_more link to $links
        $links['read_more'] = array(
                                  'title' => t('Read More...'),
                                  'href' => 'node/'.$vars['node']->nid,
                                  'attributes' => array('title' => t('Show the full page')),
        // Update the link's html output.
        $vars['links'] = theme('links', $links, array('class' => 'links inline'));  

    Module Development

    Create a menu page

    Example showing the creation of a simple menu page in a module. To access the menu page created, open http://yourDomain.com/examples/simple_menu_page .

     * Implementation of hook_menu
     *  Note: Every times that you made changes, make sure that you go to the module page 
     *          and click on the Save configuration page. 
     *          Otherwise, changes will not be effective.
    function example_page_menu()
      $items['examples/simple_menu_page'] = array(
        'page callback' => 'example_page_simple_menu_page_function',
        'access callback'  => TRUE, // TRUE so everyone can access this menu page.
        'type' => MENU_NORMAL_ITEM,
      return $items;
     * Implementation of page callback function
    function example_page_simple_menu_page_function()
      echo 'Example of simple page';
      echo 'do something';
    Package icon example_page.zip847 bytes

    Create an empty module

    The following shows you step-by-step how to create an empty module.

    1. Create a directory for your module(e.g. my_empty_module/). The module directory name should be in lowercase and should not contain spaces. To avoid headaches, please restrict yourself to letters, numbers and underscores.
    2. Create a module_name.info file in your module directory(e.g. my_empty_module/my_empty_module.info). module_name should be exactly the same name as your directory name. This file contains the general information about your module and should contain the following informations:
      ; $Id$
      name = My empty module
      description = The description of your module. 
      Your description can span multiple lines.
      core = 6.x
      package = "my custom module"    
      name, description and core are required. name is the name of your module. Only the first character is capitalized. description is the description of your module. Your description can span multiple lines. core is the Drupal version for which your module is compatible. In this case, it is compatible with Drupal 6. package is optional. It is an arbitrary group name to which your module belongs. Your module will appear under this group at Administer->Site building->Modules page. If this information is not supplied, then Drupal will put your module under the Other group. For more options, see this page.
    3. Create a empty module_name.module file in your module directory(e.g. my_empty_module/my_empty_module.module). module_name should be exactly the same name as your directory name. This file will contain all the code of your module. For the moment, leave it blank as our module doesn't do anything.
    4. Congratulations, you have created your own custom module. Now copy your module directory to sites/all/modules/ directory located inside the directory where you have installed Drupal. By default, sites/all/modules/ directory is not created. Create it if it doesn't exist.
    5. Now login to your Drupal website and go to Administer->Site building->Modules to enable your module(e.g. My empty module).

    Debugging tips

      // Display the structure of object or arrays in Drupal's message area.
      drupal_set_message('<pre>'. print_r($array_or_object_var, TRUE) .'</pre>');
      drupal_set_message('<pre>'. print_r($node, TRUE) .'</pre>');
      // get_defined_vars() returns all variables available.
      // Use this with caution, it could output a huge chunk of codes that your browser can't handle.
      //  My browser crashed when I used it in _preprocess(&$vars, $hook).
      drupal_set_message('<pre>'. print_r(get_defined_vars(), TRUE) .'</pre>');