Posts Tagged 'Drupal'

Node import and domain access

If you are using Node import 1.x-rc4 or below with Domain Access, you can get this error on each row to be imported:

An illegal choice has been detected. Please contact the site administrator.

This error in this case is presented when Domain Access try to import a node without assigning it to a domain. Node import 1.x-rc4 and below  lacks Domain Access support on 1.x-rc4.

Domain Access support will be available on Node import by 1.0 RC5 version, you have to dowload the -dev version to have it now.

After that, the error should disappear. For more information, read the first lines of node_import/node_import.inc , where this error is explained.

Theme a multiple CCK field with a table

Sometimes CCK contrib modules cannot do exactly what you want. It’s time to build your custom CCK field!

Official documentation on CCK fields creation for Drupal 6 is incomplete and some passages are obscure. If there is a good howto you have to read before do any CCK customization, this is Creating Custom CCK Fields. This howto supposes you’ve read and understand it before continue. If you want to create a custom field, you can read the complete Creating a Compound field. A custom multiple compound field (with more than one field for element, e.g. an image and its description).

Read these howto well, you’ll spare time later

Well, you have followed the howto, you have your own compound field but now you have a problem. You want to display compound field data as cells in a table, and each field as row.

On following example, we have a name list made with a multiple compound field with “firstname” and “lastname” columns.

KarenS tell you that you’ve to use CONTENT_HANDLE_MODULE instead of CONTENT_HANDLE_CORE on hook_formatter_info() .

// The machine name of the formatter.
function my_funny_module_field_formatter_info() {
  return array(
    'default' => array(
      'label' => t('Default'),
      // An array of the field types this formatter
      // can be used on.
      'field types' => array('examplefield'),
      // CONTENT_HANDLE_CORE:   CCK will pass the formatter
      // a single value.
      // CONTENT_HANDLE_MODULE: CCK will pass the formatter
      // an array of all the values. None of CCK's core
      // formatters use multiple values, that is an option
      // available to other modules that want it.
      'multiple values' => CONTENT_HANDLE_MODULE,
    ),
  );
}

/** Set the formatter **/
function my_funny_module_theme() {
  return array(
    'my_funny_module_formatter_default' => array(
      'arguments' => array('element' => NULL),
      'function' => 'funny_display_table',
    ),
  );
}

/** Here you format your table data as array **/
function my_funny_module_formatter_default($element) {
  $data = array(
      $element['#item']['firstname'],
      $element['#item']['lastname'],
  );
  return $data;
}

/** This function will display a table even where data array is empty:
 ** You have to put an additional control statement to avoid this.
 ** $element will have $data from formatter_default() above
 **/
function my_funny_module_display_table($element) {
  $header = array(
    t('First name'),
    t('Last name'),
  );
  $i = 0;
  while (!$end) {
    /** Any row will contains **/
    if(array_key_exists($i, $element)) {
      $rows[] = array(
        'firstname' => $element[$i]['#item']['firstname'],
        'lastname' =>$element[$i]['#item']['lastname'],
      );
      $i++;
    }
    else {
      $end = TRUE;
    }
  }
  /** Theme a table with data from element and header **/
  return theme('table', $header, $rows);
}

Note: to format a table you have to change only “multiple values” on my_funny_module_field_formatter_info(): you can leave my_funny_module_widget_info() as is.

See also:

Add CSS style for a block into the same block

This simple code can be pasted into a PHP filtered block (or into a block declared by a module) to set some style from a block into the block container itself.

<?php
echo 'My block content';
/** Put styles inline on html head**/
drupal_set_html_head('<style type="text/css">
#my-block-id {
  /* my style*/
}
</style>
');
?>

This code is placed on html head, applied only to the pages where block appears, without touching css optimization. If you want to include an external CSS file instead, use drupal_add_css instead: in any case you can exclude this file for aggregation, setting $preprocess attribute to FALSE.

If block appears in very few pages, and it can change quickly (e.g. a banner with custom styles on home page), using drupal_set_html_head could be the best way, even according to Yahoo Performance Best Practices. In other cases, use drupal_add_css.

See also:

Cron cannot run on Drupal: the drupal_goto() case

Sometimes you want to redirect a page to another on drupal. You can do this using a simple function called drupal_goto().

On few sites I’ve enabled the PHP filter module and then created a new page with PHP code input format with drupal_goto(‘node/2′) to redirect the current page to a specified node. Bad idea.

I’ve noticed that, after this change, cron.php operations failed, if you have Search module enabled. On cron new contents are indexed by the Search module: when it got my PHP page, it tries to index it but suddenly is redirected to another. You can also found an error like “Maximum function nesting level of ‘100′ reached” on php error log, symptom of an indexing blocked by pages with drupal_goto inside.

Solution:

  1. Comment all drupal_goto() instruction in your site within pages.
  2. Use an alternative method to redirect from a node to another.
  3. Run cron from Status Report page: you can adjust indexed content per cron on Search configuration page (admin/settings/search on 6.x)

You can add a new block with PHP code inside or (better) create a new module for this simple block (with a simple PHP switch statement as content), displaying it only on specified pages (on the bottom of block configuration). But if you create a PHP block via UI, and you put that block on every page, your site could be loop, so creating a module is the cleanest and secure way (if something go wrong, you can delete your module from the codebase and correct it). You can also find some contrib modules for redirect on drupalmodules.com.

See also:

Fatal error: Maximum function nesting level of ‘100′ reached, aborting!

Save user profile on Drupal

Tested on:

  • Drupal 5.x

After you have created some user fields through Profile module provided by core, you can have the need to save value into the user object. Here a quick howto to do this.

On user creation:

/** create user profile ($new_user will be an user object) */
$new_user_array = array (
  'name' => "funnyusername",
  'pass' => "MyVerySecurePassword",
  'mail' => "info@example.gom",
  'status' => 1, # status: active
);
$new_user = user_save(NULL, $new_user_array, $category = 'account');
/** assign values to profile fields */
$new_user_edit =  array(
'profile_surname' => "Yumemiya",
'profile_name' => "Arika",
);
/** create and save profile fields */
profile_save_profile($new_user_edit, $new_user, "Character ID");

Where “Character ID” is the category name for profile_surname e profile_name.

To load current user instead creating new one, you have to use

global $user;

instead a previously declared user object $new_user.

See also:

Update:

  • Using this method during a cronjob (using hook_cron) I experienced an error: profile values are passed, but not written, but only if cronjob is launched automatically, and not forced by Report screen (as admin). After some days, I discovered that it’s a permission issue.
    Problem:
    Add a “cronbot” user with some privileges over user (“administer user”) to allow writing even on hidden Profile field.
    Solution:
    On a dedicated server, with a dedicated IP, you can automatically login by IP (by IP Login module for 5.x and 6.x) the cronjob using the server IP or loopback address (127.0.0.1) depending on server configuration (I use the first in production, the latter on local testing).

    1. Add an ip_login Profile field (single line text field, hidden field)
    2. Enable IP Login module
    3. Assign ip_login field to IP login by IP Login configuration screen
    4. Create a new role named “cronbots”, with “administer users” permission.
    5. Create a new user named “cronbot”, with “cronbots” role assigned
    6. Change the “IP login” field for “cronbot” to your server IP (127.0.0.1 or your static IP address as listed on ifconfig on *nix servers)

    On the next automatic cron run (not force it), you’ll see the “cronbot” user logging in. On Drupal logs, the cronjob execution pass from “Anonymous” to “cronbot”, and profile fields are rightly written.
    The other way:
    Just write profile field via db_query. (You don’t want to do a weird thing like that, right? ;) )

Customize exposed filter on Drupal View

Tested on:

  • Drupal 5.x
  • Views 1.6

When you have to filter a view by a content type, you have to use Exposed filters. Since default list is somewhat ugly (a select with some elements and CTRL to be pressed) we transform it in simple checkboxes.

Copy and paste this code into your template.php:

# I use imagecache because on my site is active
# and doesn't use hook_form_alter
/** Display checkboxes instead select for exposed views filters */
function imagecache_form_alter($form_id, &$form) {
  if($form_id == 'views_filters' && arg(0) == 'change_to_your_view_path_before_slash') {
    if(!empty($form)) {
      foreach ($form as $id => $field) {
        if ($form[$id]['#type'] == 'select' && $form[$id]['#multiple'] == 'multiple') {
          # from select to checkboxes
          $form[$id]['#type'] = 'checkboxes';
	  foreach($form[$id]['#options'] as $key=>&$content) {
            # hide from list all content types that aren't mycontenttype or mycontenttype2
            if($key!='mycontenttype' && $key!='mycontenttype2'){
		unset($form[$id]['#options'][$key]);
	    }
	  }
        }
      }
    }
  }
}

To hide operators dropdown, you have to check “lock operators” on views page.

See also:

  • http://drupal.org/node/158607

Set views title dynamically for blocks and pages

Tested on: Drupal 5.x – Views 1.6

Problem: you want to display a taxonomy term name as view title, for block and themes.

Solution: On views edit screen, just add to your Argument > Argument handling code:

$args[0] = 3;

Where 3 is the Term ID you want to display as name. When term name will be updated, view title will be updated too.

You haven’t to add other arguments via GUI, just this code.

Drupal page caching

Requisites:

  • Drupal 5.x (patched 6.x version available)
  • Apache webserver (to customize .htaccess)
  • Clean URLs

Tested on

  • Drupal 5.x

Drupal page caching can be achieved using some contrib modules. I choose Boost, available for drupal 5.x and 6.x (only a 5.x patched version is now available).

According to installation instructions, query pages are not cached. Since default pagination rely on query (e.g. frontpage?page=2 to display the third page of home page view “frontpage”) you have to found a way to “clean” that URLs if you want to cache them.

A module named Clean Pagination (Drupal 5.x only) do the magic (result for example above: frontpage/2) and now even specified pages can be cached.

To test the module, open two different browser or session as anonymous user. Then, visit your paged page (e.g. frontpage/2) with the first browser (A), then go to the same page as anonymous with the other (B). (B) version of the page now should display an HTML comment inserted by Boost like:

<!– Page cached by Boost at 2008-09-30 09:59:14, expires at 2008-09-30 10:59:14 –>

Note: remember that (A) should not show any HTML comment on first visit. The “main” page cache (e.g. frontpage) if it was cached too before clean pagination will display the original pager instead the clean one until page cache expire (customizable on admin/settings/performance).

Tertiary links / 2: tree workaround

On Tertiary links on Drupal I described a simple approach to display the “tertiary” menu (childrens of secondary menus) on Drupal 5.x. This is an useful but sometimes limiting approach, since the secondary menu disappears when tertiary menu is displayed.

This is an alternative workaround made using a customized menu_tree function:

template.php:

function custom_menu_tree_secondary($pid = 1) {
  $menu = menu_get_menu();
  $output = '';

  if (isset($menu['visible'][$pid]) && $menu['visible'][$pid]['children']) {
    foreach ($menu['visible'][$pid]['children'] as $mid) {
      $type = isset($menu['visible'][$mid]['type'])
? $menu['visible'][$mid]['type'] : NULL;
      $children = isset($menu['visible'][$mid]['children'])
? $menu['visible'][$mid]['children'] : NULL;
      # display only the children menu of the current menu
      if(menu_in_active_trail($mid)) {
      	$output .= theme('menu_tree', $mid);
      }
    }
  }
  return $output;
}

page.tpl.php (in place of standard $secondary block):

<div id="secondary">
<?php if ($secondary_links): ?>
<?php echo tools_menu_tree_secondary(variable_get('menu_primary_menu', 0));?>
<?php endif; ?>
</div>

Tested on:

  • Drupal 5.x
  • Zen subtheme

Image submit buttons on Drupal 5.10

I follow this useful howto by Tom Constant on how to add image submit buttons on Drupal forms. Since I got a JavaScript error on drupal.js (line 31: button is undefined), I rewrite Tom’s implementation, and now it works fine on Drupal 5.10:

function phptemplate_button($element) {
  // Make sure not to overwrite classes.
  if (isset($element['#attributes']['class'])) {
    $element['#attributes']['class'] = 'form-'. $element['#button_type'] .' '.
 $element['#attributes']['class'];
  }
  else {
    $element['#attributes']['class'] = 'form-'. $element['#button_type'];
  }

  // here the novelty begins: check if #button_type is normal submit
  // button or image button
  switch($element['#button_type']) {
	case 'image': $button_type = 'image';
	break;
	default: $button_type = 'submit';
	break;
  }
  return '<input id="'. $element['#id'].'" name="'. $element['#name'] .'"
 type="' . $button_type . '" value="'. check_plain($element['#value']) .'" />

Next Page »


IE6: Rust in Peace

Blog Stats

  • 102,275 hits
My Bookshelf

Texts double licensed under Creative Commons Attribution - Share Alike license and GNU Free Documentation License. Examples may contain software licensed under GNU General Public License. Images and comments texts aren't necessarily licensed under these terms.