Drupal 7 how to

Index

Debugging errors

So you can place watchdog() calls around the code to see what’s going on, or install the devel module and use dpm() or kpr() instead, but if all you’re getting is a generic exception, such as an EntityMetadataWrapperException, then it can be almost impossible to figure out where in the code it was generated. E.g:

EntityMetadataWrapperException: Invalid data value "foobar" given for wrapper chain user:. in EntityDrupalWrapper->set() (line 769 of ~/dev/test/d7/public/sites/all/modules/entity/includes/entity.wrapper.inc).

In this case, you can use PHP’s debug_backtrace() to print an array of all function calls leading up to the exception. Temporary place the following just before the line that generates the exception (you should see which line this is in the exception message, as above):

print('<pre>'.print_r(debug_backtrace(),true).'</pre>');
die();

Note that the output could be very long and verbose, but it should provide enough to narrow down on the cause of the problem.

Reference: Tracing & Debugging Drupal Errors.. Backwards!

Override a theme function

I needed to change the icon output by the views_pdf module, which defined a theme_views_pdf_icon() function to theme the icon.

First determine the registry key, which you can get from the hook_theme() function call:

function views_pdf_theme() {
  return array(
    'views_pdf_plugin_style_table' => array(
      'render element' => 'form',
      'file' => 'views_pdf.admin.inc',
    ),
    'views_pdf_icon' => array(
      'render element' => 'form',
      'variables' => array('url' => NULL, 'title' => NULL),
    ),
  );
}

Alternatively poke around in the registry to find it - see View the theme registry.

You will find an associated theme function, in this case theme_views_pdf_icon().

Next, alter the registry with hook_theme_registry_alter() and change the theme function, so that in this case I could change the path of the icon image.

function mymodule_theme_registry_alter(&$theme_registry) {
	if (isset($theme_registry['views_pdf_icon']['function'])) {
		$theme_registry['views_pdf_icon']['function'] = 'mymodule_theme_views_pdf_icon';
  }
}

function mymodule_theme_views_pdf_icon($vars) {
  ...
  $imagePath = drupal_get_path('module', 'mymodule') . '/images/pdf.png';
  ...
}

I also wrote about this here: Add text with link to pdf icon

Reference: Overriding a module’s theme function

View the theme registry

If you’d like to use hook_theme_registry_alter() function to override a theme function, then it’s useful to be able to inspect the registry.

With the devel module installed (for the dpm() call), you can view the registry as follows:

global $theme;
$registry = _theme_load_registry($theme);
dpm($registry);

Reference: Is there any way to see the content of the theme registry?

Update an unlimited date field

To programmatically add a date to an unlimited date field, use entity_metadata_wrapper():

// We already have $mid, the membership ID.

$membership = membership_entity_load($mid);
$membership_wrapper = entity_metadata_wrapper('membership_entity', $membership);

// Check that the field exists
if (isset($membership_wrapper->field_date_foobar)) { // Can only set field if it exists.

  $membership_wrapper->field_date_foobar[] = REQUEST_TIME;

}

$membership_wrapper->save();

Check isset to avoid ‘EntityMetadataWrapperException: Unknown data property field_date_foobar. in EntityStructureWrapper->getPropertyInfo()’ when entity doesn’t have the field (e.g. if added to entity bundle after some have been created).

If the value (REQUEST_TIME in this example) isn’t a timestamp you’ll get ‘EntityMetadataWrapperException: Invalid data value given. Be sure it matches the required data type and format. in EntityMetadataWrapper->set()’

Reference: Save a new value with entity_metadata_wrapper to an entity field which is an array

Render a node’s field in page.tpl.php

First hide the field in node.tpl.php:

      hide($content['field_manufacturer_web_site']); // We render manufacturer web site field in page.tpl.php.

Then render the field in page.tpl.php:

        // Print manufacturer web site below the 'content' region so it appears below view blocks in content.
        if ($node) {
          $manufacturer_web_site_fields = field_get_items('node', $node, 'field_manufacturer_web_site');
          if ($manufacturer_web_site_fields) {
            print render(field_view_field('node', $node, 'field_manufacturer_web_site', $manufacturer_web_site_fields[0]));
          }
        }

Reference: http://stackoverflow.com/questions/16015514/render-link-field-url-and-title-separately-in-node-template-in-drupal-7

Get the value of a node’s field

Use field_view_field function.

$field = field_view_field('node', $node, 'field_name');
$output = render($field);

That could render the title of the field as well as it’s value, depending on how you’ve set up the field’s display.

If you’d like the value only, you can use field_get_items with field_view_value.

$field = field_get_items('node', $node, 'field_name);
$field_value = field_view_value('node', $node, 'field_name', $field[0]);
$output = render($field_value);

If the field has more than one value then you’ll have to replace the 0 with the appropriate deltas.

Reference: Rendering Drupal 7 fields (the right way)

Render a node’s field in page.tpl.php

First hide the field in node.tpl.php:

      hide($content['field_manufacturer_web_site']); // We render manufacturer web site field in page.tpl.php.

Then render the field in page.tpl.php:

        // Print manufacturer web site below the 'content' region so it appears below view blocks in content.
        if ($node) {
          $manufacturer_web_site_fields = field_get_items('node', $node, 'field_manufacturer_web_site');
          if ($manufacturer_web_site_fields) {
            print render(field_view_field('node', $node, 'field_manufacturer_web_site', $manufacturer_web_site_fields[0]));
          }
        }

Reference: http://stackoverflow.com/questions/16015514/render-link-field-url-and-title-separately-in-node-template-in-drupal-7

Get the value of a node’s field

Use field_view_field function.

$field = field_view_field('node', $node, 'field_name');
$output = render($field);

That could render the title of the field as well as it’s value, depending on how you’ve set up the field’s display.

If you’d like the value only, you can use field_get_items with field_view_value.

$field = field_get_items('node', $node, 'field_name);
$field_value = field_view_value('node', $node, 'field_name', $field[0]);
$output = render($field_value);

If the field has more than one value then you’ll have to replace the 0 with the appropriate deltas.

Reference: Rendering Drupal 7 fields (the right way)

Add content to region

In a theme hook e.g. theme_preprocess_page():

function my_theme_preprocess_page(&$variables) {
  $variables['page']['header']['foo'] = array('#markup' => '<p>bar</p>');
}

You can use a render array to e.g. load a template like [my_module]/templates/foo.tpl.php:

/**
 * Implementation of hook_theme() to register templates for this theme.
 */
function my_theme_theme($existing, $type, $theme, $path) {
  return array('foo' => array('path' => drupal_get_path('theme', 'my_theme').'/templates',
                              'template' => 'foo'));
}

/**
 * Implementation of template_preprocess_page(). Show the 'foo' template in the header region.
 */
function my_theme_preprocess_page(&$variables) {
  $variables['page']['header']['foo'] = array('#theme' => 'foo');
}

Create a basic theme

  1. Create a directory sites/all/themes/[mytheme]
  2. Add a [mytheme].info file
  3. Copy templates from modules/system/ e.g. html.tpl.php into your theme directory and customise as desired.
  4. Follow Theming Drupal 6 and 7 guide.

Use theme CSS with CKEditor

In the CSS section of the WYSIWYG profiles for CKEditor, ‘Use theme CSS’ doesn’t work. Instead I had to ‘Define CSS’ with the following value:

/sites/all/themes/[your-theme]/css/[some.css],/sites/all/themes/[your-theme]/css/[some-more.css]

Note that the tokens could not be used either (e.g. %b%t/../[your-theme]/css/[some.css]) as the double dot doesn’t work.

Customise empty term page message

By default, when you view a taxonomy term page which has no nodes you get the default message “There is currently no content classified with this term.”. You can change this by altering the markup in the page preprocess function:

function [theme]_preprocess_page(&$variables, $hook) {
  if(isset($variables['page']['content']['system_main']['no_content'])) {
    $term = $variables['page']['content']['system_main']['term_heading']['term']['#term'];
    if ($term->vid == 2) { // Target vocab ID 2 only.
      $variables['page']['content']['system_main']['no_content']['#prefix'] = '<p>';
      $variables['page']['content']['system_main']['no_content']['#markup'] = t('No nodes classified with this term dagnamit!');
      $variables['page']['content']['system_main']['no_content']['#suffix'] = '</p>';
    }
  }
}

Get body of page

To get the body of the page within node.tpl.php, use the $body variable as it will contain the body for the user’s current language - using $node->body[$lang] requires knowledge of $lang (e.g. ‘und’ for undefined, or ‘en’, ‘fr’, etc).

Get current language

global $language;
$language->language;

Reference: global $language

$node->language is a node’s default language.

Run text through a filter

check_markup('<a href="#">Test anchor</a>', 'filtered_html')

Text sanitisation

Use t() for text sanitisation.

There are three different types of placeholders for the t function:

  1. @ is sanitized - escaped to HTML using check_plain().
  2. % is sanitized - escaped as a placeholder for user-submitted content using drupal_placeholder(), which shows up as emphasized text.
  3. ! is not sanitized - text is inserted as is.

See Sanitization in Drupal: the t function

Theme view fields

You can override the Views field templates (copy from sites/all/modules/views/theme/ folder into your own theme):

  • views-view-fields.tpl.php
  • views-view-field.tpl.php

You can also target specific views and fields, e.g:

  • views-view-field–[view_name]–[field_name].tpl.php

Preprocess functions:

  • [theme]_preprocess_views_view_fields()
  • [theme]_preprocess_views_view_field()

The Views module does most of its work theming fields in its template_preprocess_views_view_fields() function, where it creates an object for each field to hold its output (markup) and other related variables.

You can override to e.g. remove markup that’s added by the Views module.

function [theme]_preprocess_views_view_fields(&$variables) {
  $view = $variables['view'];
  if (in_array($view->name, array('highest_rated_products','promoted_products', 'latest_products'))) {
    foreach($variables['fields'] as $id => $field) {
      $field->wrapper_prefix = '';
      $field->label_html = '';
      $field->wrapper_suffix = '';
    }
  }
}

Get field name from [theme]_preprocess_views_view_fields()

E.g. This adds YAML subtemplate markup around some view fields.

function [theme]_preprocess_views_view_fields(&$variables) {
  $view = $variables['view'];
  if (in_array($view->name, array('highest_rated_products','promoted_products', 'latest_products'))) {
    foreach($variables['fields'] as $id => $field) {
      $field->label_html = '';
      switch($field->handler->options['id']) {
        case 'field_multimedia':
          $field->wrapper_prefix = '<div class="subcolumns"><div class="c33l"><div class="subcl">';
          $field->wrapper_suffix = '</div></div>';
          break;
        case 'body':
          $field->wrapper_prefix = '<div class="c66r"><div class="subcr">';
          $field->wrapper_suffix = '</div></div></div>';
          break;
        default:
          $field->wrapper_prefix = '';
          $field->wrapper_suffix = '';
          break;
      }
    }
  }
}
l('link text', '/a/link', array( 'attributes' => array( 'class' => a_class )));
l('<img src="whatever.jpg" alt="whatever"/>', '/a/link', array( 'html' => TRUE, 'attributes' => array( 'class' => a_class )));

Alter form fields and validation

TODO - reminder to grab code from my f3_rmi_contact_form module and update this page!

Get node URL

url(drupal_get_path_alias('node/' . $node->nid), array('absolute' => TRUE));

Write to the log

watchdog('[module name]', 'the message');

Hide taxonomy term page

Write a custom module and use hook_menu_alter to change the access callback for ‘taxonomy/term/%taxonomy_term’ to a function that does what it needs to do and returns the appropriate permission.

TODO - reminder to grab code from my f3_protect_taxonomy_term_pages module and update this page!

Get URL from Drupal internal URI (e.g. public://..)

$file = $variables['field_image']['file'];
$url = file_create_url('large', $file->uri);

Here, $file->uri would be something like public://field/image/some-image.jpg and $url would be something like http://somesite.com/sites/default/files/styles/large/public/field/image/some-image.jpg.

Get URL for a particular image style from the image URI

If you have an image URI you can get the URL of the image associated with an image style using the image_style_url function, e.g. in [theme]_preprocess_node function, where the field is called ‘field_image’ and the style is ‘large’:

$file = $variables['field_image']['file'];
image_style_url('large', $file->uri);

Get node object in [theme]_preprocess_node

$node = $variables['node'];

Iterate over a node field’s values

E.g. a multimedia field may have many values, each of which is a media item (audio, image, video, etc).

$node = $variables['node'];
foreach ($node->field_multimedia[$node->language] as $key => $value) {
  $file = $value['file'];
  ...
}

Render a field in preprocess function

$field = field_view_value('node', $node, 'field_myfield', $node->field_myfield[$node->language][0], 'full');
$markup = render($field);
  • This can be done with any entity, e.g. user, taxonomy, etc. Just change ‘node’ to the entity type and replace $node with the appropriate object.
  • The fourth argument is the specific item of the field that you’d like to get. E.g. A Media module field field_multimedia may have many image items. In this example we’re simply getting the first item. Change the delta from 0 to 1 to get the second item, to 2 to get the third, etc.
  • Change ‘full’ to e.g. ‘teaser’ to get the rendering for a different view / display mode.

See function field_view_value.

Rename the contact form

The contact form takes its title from its default menu item, so rename that and the page name will change. This works even if the default menu item is disabled and you’ve a custom item in another menu.

Remove menu items

TODO - reminder to view code in template.php from my “rmi” site and update this page!

Remove class from fields

E.g. To remove the ‘clearfix’ class from the categories and tags fields, use the following in [theme]’s template.php:

function [theme]_preprocess_field(&$variables) {
  $field_names = array('field_categories','field_tags');
  if(in_array($variables['element']['#field_name'], $field_names)) {
    foreach($variables['classes_array'] as $key => $value) {
      if ($value == 'clearfix') {
        unset($variables['classes_array'][$key]);
        break;
      }
    }
  }
}

E.g. To remove the ‘inline’ class from the links, use the following in [theme]’s template.php:

function [theme]_preprocess_node(&$variables, $hook) {
  foreach($variables['content']['links']['#attributes']['class'] as $key => $value) {
    if ($value == 'inline') {
      unset($variables['content']['links']['#attributes']['class'][$key]);
      break;
    }
  }
}

Run tests from command line

php ./scripts/run-tests.sh --url [the url] --verbose --all

Change default input filter per content type

Can’t be done in Drupal 7 core :-/

Get stylesheets array

drupal_add_css()

Get top taxonomy term by vocabulary

$terms = taxonomy_get_tree(5);
print('Top term is in vocab 5 is '.$terms[0]->name);

TODO: Is there a more efficient way to do this?

Get all terms in a vocabulary

taxonomy_get_tree($vid);

Get vocabulary by machine name

taxonomy_vocabulary_machine_name_load('machine name');

Get vocabulary ID

Inspect the output of taxonomy_get_vocabularies().

Theme specific node types and view modes

To add support for node type and view node templates, use the following theme hook suggestions code:

function mytheme_preprocess_node(&$variables) {
  $variables['theme_hook_suggestions'][] = 'node__' . $variables['type'] . '__' . $variables['view_mode'];
}

See Addition of a node–teaser.tpl.php by default.

TODO: Does this automatically add preprocess functions? If not, use the following code (from Zen STARTERKIT):

function theme_preprocess_node(&$variables, $hook) {
  // Optionally, run node-type-specific preprocess functions, like
  // theme_preprocess_node_page_full() or theme_preprocess_node_story_teaser().
  $function = __FUNCTION__ . '_' . $variables['node']->type . '_' . $variables['view_mode'];
  if (function_exists($function)) {
    $function($variables, $hook);
  }
}

Check out project from git

Use the git.drupal.org domain rather than drupalcode.org. See the Git section above.

Preprocess a particular field in a particular view (Views 3)

E.g. for the mytheme theme:

/**
 * Change div into span for 'foo' fields in 'bar' view.
 */
function mytheme_preprocess_views_view_field(&$variables, $hook) {
  if ($variables['view']->name == 'bar') {
    // Either
    $field = $variables['field_foo'];

    // Or
    $view = $variables['view'];
    $field = $view->field['field_foo'];

    // Then
    $field->options['element_type'] = 'span';
  }
}

Change module weights

See How to update a module’s weight.

Reorder module hooks

See function hook_module_implements_alter.

Change salt and reset passwords

First change the salt in [drupal]/sites/default/settings.php:

$drupal_hash_salt = 'whatever-you-want-but-make-it-random';

If you’re on Linux you could use the makepasswd command to generate it:

makepasswd --chars 23

Once you’ve changed the salt you’ll need to update the database with the new salted passwords. First generate the salted passwords using Drupal’s password-hash script (in the Drupal root):

./scripts/password-hash.sh 'the-password'

Example output:

me@pc ~/tmp/dev/drupal $ ./scripts/password-hash.sh 'XhMPi5uHI6t4'

password: XhMPi5uHI6t4 		hash: $S$DWKCUpZbdddB21hlkdDJjwN67hp32RLsE/N9VPyevytdJG8L5rvw

Again, make sure the password is strong. For the previous example I used makepasswd to generate a random 12 character string.

Once you have the salted password (AKA hash) you can add it to the database via an SQL query. First determine the user ID (uid) of the user you’d like to update:

mysql> select uid,name from users;
+-----+-----------------+
| uid | name            |
+-----+-----------------+
|   0 |                 |
|   1 | admin           |
|   2 | stephan         |
+-----+-----------------+
6 rows in set (0.00 sec)

mysql>  update users set pass='$S$DWKCUpZbdddB21hlkdDJjwN67hp32RLsE/N9VPyevytdJG8L5rvw' where uid='2';
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1  Changed: 0  Warnings: 0

Go through that process with every user.

References: Reset admin password in drupal 7

Dynamically alter a menu

To alter a menu dynamically, or more specifically a menu item, implement hook_menu_link_alter() to flag a menu item as alterable then implement hook_translated_menu_link_alter() to alter it, like so:

/**
 * Implements hook_menu_link_alter().
 *
 * Flag the 'Join' menu item as alterable via hook_translated_menu_link_alter().
 */
function mymodule_menu_link_alter(&$item) {
  if ($item['menu_name'] == 'user-menu' && $item['link_title'] == 'Join') {
    $item['options']['alter'] = TRUE;
  }
}

/**
 * Implements hook_translated_menu_link_alter().
 *
 * Remove the 'Join' link if the user has a membership.
 */
function mymodule_translated_menu_link_alter(&$item) {
  if ($item['menu_name'] == 'user-menu' && $item['link_title'] == 'Join') {
    global $user;
    $memberships = membership_entity_load_by_user($user);
    if (!empty($memberships)) {
      $item['access'] = FALSE;
    }
  }
}

References: How can I change a menu link dynamically? (D7)

Last modified: 01/07/2016 Tags: ,

This website is a personal resource. Nothing here is guaranteed correct or complete, so use at your own risk and try not to delete the Internet. -Stephan

Site Info

Privacy policy

Go to top