Showing posts with label drupal. Show all posts
Showing posts with label drupal. Show all posts

Friday, 25 May 2012

Drupal 7 - Render a block in a template file

I can see how Drupal's region system works well for content that flows vertically e.g. sidebars, and maybe even the main content area, but I've always struggled with positioning blocks in the header and footer. This is because for me at least, these areas have lots of blocks of content, in a more complex arrangement (perhaps separated into multiple columns etc). I thought about adding custom regions, maybe one for each column of blocks i wanted to display, but this seemed overly rigid, and I don't see the advantage of doing this over just manually rendering the blocks in my page.tpl.php for example (I think it would be slower to login to the website and mess around with the block admin GUI than to just make a tweak to my page.tpl.php).

So I had to search around for a good way to manually render blocks in a template file, and there are lots of sub-optimal solutions floating around the internet, so when I found a good solution, I thought I would document it here.


Solution

Chuck the following function at the bottom of your theme's template.php file:

/**
 * Custom function to render a block so I can manually position it in the markup
 */
function block_render($module, $block_id) {
  $block = block_load($module, $block_id);
  $block_content = _block_render_blocks(array($block));
  $build = _block_get_renderable_array($block_content);
  $block_rendered = drupal_render($build);
  return $block_rendered;
}

Next, use a preprocess function to set some variables for your tpl file e.g. in your THEME_preprocess_page() function, add something like this for each block you want to render:

$variables['commerce_cart_block'] = block_render('commerce_cart', 'cart');

Where the first argument is the module that produced the block, and the second is the block name - you can get these from going to the block admin page, and hovering the "configure" link e.g. for the Commerce Cart example above, the URL was http://example.com/admin/structure/block/manage/commerce_cart/cart/configure.

Now you have a $commerce_cart_block variable in your page.tpl.php to print wherever you want! I should think this would work for node.tpl.php as well (you would have to set the variables in your THEME_preprocess_node() function instead).

print $commerce_cart_block;


References
http://api.drupal.org/api/drupal/includes%21module.inc/function/module_invoke/7#comment-15709

Thursday, 2 February 2012

Drupal: iterating array items that are not properties (hashes)

I recently needed to iterate over the elements of an array, ignoring any "properties" (signified in Drupal using hashes e.g. $form['items']['#value']). Originally I did this by iterating over all the elements, and then filtering out those that I didn't want. Turns out there is a neater way: element_children(). This does the filtering for you, and returns the keys of normal (non-property) elements. You can use it like this:

foreach (element_children($form["items"]) as $i) {
    // something with $form["items"][$i]
}

Friday, 2 December 2011

Drupal 6: Enable/Disable the WYSIWYG Editor on Specific Forms

You will need your own custom module for this, in order to implement hook_form_FORM_ID_alter().

DISABLING
You can disable the WYSIWYG editor on any form by setting the FAPI (Forms API) setting #wysiwyg to FALSE e.g. for the Page node edit form, you can do this:
function MODULE_form_page_node_form_alter(&$form, &$form_state) {
    $form['body_field']['body']['#wysiwyg'] = FALSE;
}

ENABLING
  1. Some contrib modules (e.g. Ubercart) have completely disabled WYSIWYG editors on some of their textareas, and I haven't found a way around this.
  2. If you have checked the "Enable by default" option, the WYSIWYG editor should automatically be enabled for any form fields that have the "Input format" option below them.
  3. For other forms (with simple textareas that dont have the "Input format" option) you must manually enable the "Input format" option yourself. This can be done by again altering the $form object in your hook_form_FORM_ID_alter() e.g. for the Description field on the form to edit a content type:
function MODULE_form_node_type_form_alter(&$form, &$form_state) {
    $form['identity']['format'] = filter_form();
}


Update: if you're more into GUIs than fiddling in the code, you could just install the Better Formats module, which gives you loads of control on which Input formats are used for which forms. Then by having one plain-text input format, and another one associated with a WYSIWYG editor, you can easily control which forms use which.

Drupal 6: hook_form_node_form_alter() not working

I've long known about the versatile hook_form_alter() function, which allows you to alter any form before it gets displayed on the page, using your own custom module. Recently I discovered the wonderful hook_form_FORM_ID_alter() function, which is only fired for a specific form id, making it a better solution when you want to target only 1 form.

I had used this successfully a few times, and then hit a wall when I tried using it for the main node edit form (the form's id is node-form, so I called the function hook_form_node_form_alter). Half an hour later, I stumbled upon the solution: it does not want the HTML ID attribute, it wants the internal Drupal ID for the form. So in my custom module, I used this code to get the ID:

function MODULE_form_alter(&$form, &$form_state, $form_id) {
 echo "formID=".$form_id;
}

Which turned out to be page_node_form, so the final function that worked was called MODULE_form_page_node_form_alter().

REFERENCES
http://www.hashbangcode.com/blog/drupal-hookformalter-node-form-501.html

Thursday, 31 March 2011

Drush - Backup and Migrate command documentation

Drush is an amazing command line tool for administrating Drupal. Backup and Migrate is a Drupal module which simplifies the process of backing up and restoring your Drupal database(s). When I found out that Backup and Migrate now includes Drush support, I was really excited, but then I could not find any documentation for it anywhere (at the time of writing, the documentation link on the module page was broken). Eventually I thought to try the command line, and found everything I needed in the drush help output, so thought I would post it here in case it helps others.

Commands
bam-backup - Backup the site's database with Backup and Migrate.
bam-backups - Get a list of previously created backup files.
bam-destinations - Get a list of available destinations.
bam-profiles - Get a list of available settings profiles.
bam-restore - Restore the site's database with Backup and Migrate.
bam-sources - Get a list of available sources.



bam-backup
Backup the site's database using default settings.

Examples:
drush bam-backup - Backup the default databse to the manual backup directory using the default settings.
drush bam-backup db scheduled mysettings - Backup the database to the scheduled directory using a settings profile called "mysettings"
drush bam-backup files - Backup the files directory to the manual directory using the default settings. The Backup and Migrate Files module is required for files backups.

Arguments:
source - Optional. The id of the source (usually a database) to backup. Use 'drush bam-sources' to get a list of sources. Defaults to 'db'
destination - Optional. The id of destination to send the backup file to. Use 'drush bam-destinations' to get a list of destinations. Defaults to 'manual'
profile - Optional. The id of a settings profile to use. Use 'drush bam-profiles' to get a list of available profiles. Defaults to 'default'



bam-backups
Get a list of previously created backup files.

Examples:
drush bam-backups manual - List of backup files currently in the destination called "manual"

Arguments:
destination - Required. The id of destination to list backups from. Use 'drush bam-destinations' to get a list of destinations.



bam-destinations
Get a list of available destinations.



bam-profiles
Get a list of available settings profiles.



bam-restore
Restore the site's database with Backup and Migrate.

Examples:
drush bam-restore db manual "LCC-31.03.2011-14.01.59.mysql.gz" - restore the default database using the given dump file, which can be found in the destination called "manual"

Arguments:
source - Required. The id of the source (usually a database) to restore the backup to. Use 'drush bam-sources' to get a list of sources. Defaults to 'db'
destination - Required. The id of destination to send the backup file to. Use 'drush bam-destinations' to get a list of destinations. Defaults to 'manual'
backup id - Required. The id of a backup file restore. Use 'drush bam-backups' to get a list of available backup files.



bam-sources
Get a list of available sources.



Closing Notes
I'm not sure if this is out of date or something, but in my experience you provide the name and not the id for all of the arguments.

Drupal 6 - Drush warning about orphaned actions

When I was executing drush commands, I got a warning similar to this:
WD actions: 2 orphaned actions (comment_unpublish_action, comment_publish_action) exist in the actions table. Remove orphaned actions
Solution is to use a badly-documented Drupal feature which automatically removes all orphaned actions. Simply visit the following hidden URL on your drupal site: yoursite.com/admin/settings/actions/orphan, and it will remove any orphaned actions and forward you to the Manage Actions page, where you will see they are no longer listed!


REFERENCES
https://drupal.org/node/445922#comment-2977526

Thursday, 24 February 2011

Drupal 6 - Webforms: Loading GIF on Submit

If you have a slow mail server, then submitting webforms can sometimes appear to do nothing for a while. For me, this is anywhere between 2 and 8 seconds, but I have read about a lot of other people in similar situations. While the problem is with the mail server, sometimes there's nothing that can be done about this, and a simple solution to keep the user calm is to add a little loading animation. No Ajax involved: the GIF is simply shown when the user clicks submit, and then after the wait, they will be taken to the success page.

1) Grab the right GIF animation from this amazing site: http://ajaxload.info/ and put it here: /sites/all/images/loading.gif

2) The CSS: make it so that the GIF is used when a certain class is applied to an input element
input.form-submit.loading-gif {
 background: url('/sites/all/images/loading.gif') 50% 1px no-repeat;
}

3) The jQuery script: in your theme dir, create js/submit-loading.js which will apply the CSS class to the submit button when it is clicked
$(document).ready(function()
{
    // preload image
    (new Image()).src = "/sites/all/images/loading.gif";
    
    $(".webform-client-form").submit(function() {
        // select the button
        var $button = $(this).find("#edit-submit");
        // remove the text, but persist the width of the button
        var $buttonWidth = $button.outerWidth();
        $button.attr('value', '').width($buttonWidth);
        // display the loading gif and disable the button
        $button.addClass("loading-gif").attr('disabled','disabled');
    });
});

4) Add the script only to the right page with a bit of PHP in your theme's template.php. See #2 in my article: Different ways of adding JavaScript to a page

Wednesday, 2 February 2011

Drupal 6: Different ways of adding JavaScript to a page

REMEMBER
  • Always use drupal_add_js() as this lets Drupal handle it i.e. preprocess/cache it.
  • All of the examples assume your JS is in a script file, but you can also include an "inline" script by sending it as a string for the first argument, and setting the second to "inline" e.g. drupal_add_js('alert("Hello!")', 'inline');
  • If your script is in your theme directory, you can use path_to_theme() and then the relative path from there (with a preceding slash) e.g. path_to_theme().'/js/myscript.js'

1) Add a script to all pages
a) using template.php
drupal_add_js('sites/all/scripts/tweaks.js');
b) using your theme's .info file (the script needs to be somewhere within your theme directory)
scripts[] = script.js

2) Add a script to page(s) with a specific URL / URL pattern using template.php (the following will only add the script on site.com/my/page)
NOTE: if you are using URL aliases, you need to know the un-aliased version for this (see site.com/admin/build/path)
function mytheme_preprocess_page(&$vars) {
  if (arg(0) == 'my' && arg(1) == 'page' && arg(2) == null) {
    drupal_add_js(path_to_theme().'/js/myscript.js');
    $vars['scripts'] = drupal_get_js();
  }
}
OR use the following condition to add it to any pages that start with the URL site.com/my/page e.g. /my/page/1, /my/page/2, /my/page/2/edit etc.
if (arg(0) == 'my' && arg(1) == 'page') {...}
OR use the node ID
if (isset($vars['node']) && $vars['node']->nid == 23) {...}
OR apply to all nodes of a specific content type
if (isset($vars['node']) && $vars['node']->type == 'book') {...}

3) Add a script to a specific page by editing the page in Drupal, and setting the input format to "PHP code"
drupal_add_js('sites/all/scripts/tweaks.js');

4) Add a script to a block, which you can then setup to be included in a specific page or set of pages
drupal_add_js('sites/all/scripts/tweaks.js');


Also note that it is very similar to add stylesheets to pages. The function is drupal_get_css().


REFERENCES
http://drupal.org/node/304178
http://api.drupal.org/api/drupal/includes--common.inc/function/drupal_add_js/6
http://stackoverflow.com/questions/61735/include-css-or-javascript-file-for-specific-node-in-drupal-6

Wednesday, 22 December 2010

Drupal: Collapsible blog archive (similar to Wordpress / Blogger) using Views

I was really surprised that after a lot of digging, I could not find any pre-built solution to this seemingly popular problem. Surely all big CMSs should have this functionality. It turns out it's not that hard to build something similar to the Blogger archive block on your own using Views. You can see mine in action at the bottom of the sidebar on this page. It will have the following structure (with collapsible months):
▼ December 2010
   Article 1
   Article 2
   Article 3
► November 2010
► October 2010
1) Create a new View
Give it a name and type=node and create a new block display

2) Fields
Add 2 fields; Node:PostDate (no label, and with custom format "F Y"), Node:Title (no label, and check "link field to it's node")

3) Filters
Add a filter on Node:Type and select the type(s) of nodes you want to include (e.g. Blog). Also add one on Node:Published (and select yes)

4) Sort Criteria
Add one on Node:PostDate and select descending

5) Basic Settings
Click style and select "HTML List", then click settings and group by post date. Now click CSS class and add your own custom class name which we will use later (I used jack-archive-block)

At this point you should save, and click preview. If you have 3 months of blog posts, then you should see 3 Month-Year titles, each with their own list of article links.

6) Block Settings
Admin name: what we will use to select it on the blocks page (I used "Jack Archive")

7) Tweaks
Now go back into the Node:PostDate field and click "hide from display" as we dont want it for every node, only for every group. Also check "re-write output" and enter the following:

 [created]

This will prefix each of the section titles (dates) with a little collapse icon, which we will manipulate later with jQuery. That should be it for coding up your View. So save, and go to the blocks admin page and whack it in your sidebar.

8) Style
Next, we style it. Here's the CSS I used:

div.jack-archive-block h3 {
 cursor: pointer;
 margin: 0;
 font-size: 1em;
 font-weight: normal;
 padding: 6px 0;
}
div.jack-archive-block ul {
 padding: 0 0 0 10px;
 list-style: none;
}
div.jack-archive-block ul li {
 background: none;
 line-height: 130%;
 padding: 3px 0 5px;
}

The first block deals with the dates; it says if you mouse over, it should look like you're hovering over a link. The second and third blocks remove any default list styling you may have, so you dont get any random bullet point graphics.

9) jQuery
Finally, the jQuery that makes the magic happen:

$(document).ready(function()
{ 
 // init: collapse all groups except for the first one
 $(".jack-archive-block ul").each(function(i)
 {
  if (i==0) $(this).siblings("h3").children(".collapse-icon").text("▼");
  else $(this).hide();
 });
 
 // click event: toggle visibility of group clicked (and update icon)
 $(".jack-archive-block h3").click(function()
 {
  var icon = $(this).children(".collapse-icon");
  $(this).siblings("ul").slideToggle(function()
  {
   (icon.text()=="▼") ? icon.text("►") : icon.text("▼");
  });
 });
});

That should be it. You should be able to click the dates, and have the relevant articles appear or disappear as desired, and have the nice little icon change accordingly. The next step would be another tier to the hierarchy, so you can collapse by year, and then by month... but I haven't got that far yet. Suggestions welcome.

REFERENCES
http://drupal.org/node/825052

Friday, 29 October 2010

Drupal 6: Altering the help message for a particular URL

I noticed that on the "All Content" page, the help message is empty, but my theme still displays a big empty bubble, which looks like a bug. After doing some research I realised the help message on this page came from the node_help() function, and was returning just a space on purpose, as a not-null value meant that the little "more help" link would still be displayed.

SOLUTION
Implement the theme_help() hook in your theme's template.php. Note: replace THEME with your theme name.

function THEME_help() {
 $help = menu_get_active_help();
 if (arg(0)."/".arg(1)."/".arg(2)=="admin/content/node" && !arg(3))
 {
  $help = "<p>My help message.<p>".$help;
 }
 
 if ($help) return "<div class='help'>".$help."</div>";
 else return null;
}

This will work for the URL admin/content/node, but can easily be altered to work for any URL.

REFERENCES
http://stackoverflow.com/questions/1296106/drupal-6-hook-for-altering-help-field-in-a-form

Thursday, 28 October 2010

Opening Drupal PHP files in Eclipse

By default, some Drupal files will not open in the proper editor (they will just be treated as plain text). Assuming you have PDT installed for PHP development, you can specify which filetypes contain PHP code and hence should be opened in the PHP editor.

Look in Windows > Preferences > General > Content Types > Text > PHP Content Type, and add each of the following associations:
*.engine
*.theme
*.install
*.inc
*.module
*.profile
*.test

PROBLEM
If when you then try opening one of these files, you get an error along the lines of:
Unable to create editor ID org.eclipse.php.editor: org.eclipse.core.internal.filebuffers.SynchronizableDocument cannot be cast to org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument

SOLUTION
Restart eclipse.


References: http://drupal.org/node/75242