Provide a clean back end interface for your client

Posted October 7th, 2010 in blog

Out of the box, drupal doesn’t provide an easy to use interface to administer the website. Luckily with a few contrib modules you can greatly enhance that!
If you client is not an informatic guru, he will love it : a nice design for the interface that displays only the used menu items. Unused functionalities like input format or statuses can be safely removed.

Less options means easier management and happier client!

step 1 : install a dedicated administration theme

admin module
you can install the 6.x.1.0 .The theme will be set immediately after installation but you will have less control on it (it’s not really a new theme) and the admin menu items will be changed, so be warned if you plan to use the admin_menu module!
the 6.x.2.0 requires you to install the rubik theme and the tao . the rubik theme is a child of tao. You have to manually enable the 2 themes in admin/build/themes/select and choose rubik as admin theme in admin/settings/admin/theme.

You can also add the admin_theme module if you want your admin theme to be displayed on other pages than admin/* and check the box “Content editing” in admin/settings/admin/theme.
If you need extra controls over the theme, you can create a new one that will be a child of rubik (add the line base theme = “rubik” in the .info file). You will customize it by adding a template.php and other tpl files (and leave the tao files unchanged  – always better…).

do not forget to set the permission “access admin theme” to your client’s role.


step 2 : better formats

this module helps you to control the input formats by settings defaults (and removing the format chooser in node, blocks and comments management)
install the module go to admin/settings/filters/defaults and choose the default input formats. Then on admin/user/permissions uncheck the boxes to remove format selection, tips and links (they are checked by default…)

step 3 : Form filter

If you have granted your user the “administer nodes” permission, the node/add and node/edit forms will show revision information, authoring information and publishing options (and other options provided by additional modules). These options are not always useful so if you want to remove it you can do it by code (hook_form_alter – btw I didn’t find an easier way than hide by css with $form['my_element'][#prefix] = ‘<div style=”display:none”>’ and $form['my_element'][#suffix] = ‘</div>’).
But you can do it also with the form filter module. Enable both formfilter module and formfilter ui. As administrator, 2 links will appear on the bottom of the node/add and edit pages.
You can click on “Filter all node forms” and check the 3 boxes “Hide Revision information“, “Hide Authoring information” and “Hide Publishing options“.

you can also choose a role with “view forms without filtering” permission.

step 4 : Menu per role

This is a quick and dirty way to hide menu elements for specific role. Be aware that this only hide the menu entry! the functionality will remain accessible by the url.  So it can be a security flaw… anyway, install the extension and choose a menu entry to customize.
Useful if you want to hide some admin menu links but step 6 is a better alternative…

step 5 : create custom administration pages with views

if you want to separate some items from admin/content/node to highlight it, use a view with
– style : table (with sortable fields in the style options)
– fields node: edit and node: delete link + exposed filter(s)
– access to your specific user role.
– add page display and a menu entry

step 6 : create a specific menu for admin purposes

with a link to content/list, content/add … and all others site specific needs.
show it only for your role.
useful if you don’t want to grant the “administer nodes” permission.

Multisite setup

Posted July 2nd, 2010 in blog

The goal is to have a single drupal core to handle multiple sites / instances

1. installation

install drupal as usual

2. directory structure

inside site/ create a directory for each of your site named yoursitename.tld

inside yoursitename.tld create

  • directories for the specific modules and themes (you can then split it in contrib/ and custom/) + files
  • copy settings.php and adapt db site’s specific values
  • put the modules used by every sites (cck, pathauto, devel, admin_menu, …) in site/all/modules

3. virtual host

  • DocumentRoot is the same for every sites : that’s where your installation sits (/var/www/drupal6 for example)
  • ServerName is the name of your site (!), drupal will use it to map the name of your site.tld with the site/ directory structure

4. pros

  • update core / shared modules only once
    => you can create a script to run cron for all sites
  • quick creation of new sites

5. cons

  • on core issues, all sites are down…
  • high load of the server
  • you have to manually run update.php on every site (it can take times when a site may be + – down)

more info on drupal’s website

6. sharing databases / tables

it’s possible to share data between sites.

example: if you want sitea and siteb using the same users, but with different content, you will have

  • 3 databases for the 2 sites, (1 for content sitea / siteb, 1 for shared tables)
  • shared tables (users, sessions,…) must be have the prefix of the shared database in settings.php / Database area ( shared_db. with . for sql join)
  • a default prefix (indicated only once) for other tables
  • $db_prefix = array( ‘default’ = ”, ‘users’ = ‘shared_db.’, ‘role’ = ‘shared_db.’, ‘sessions’ = ‘shared_db.’, );

  • to permit a same user login on sitea AND siteb, install single sign on module

note : it’s also possible to have only 1 databases with same prefixes for common table and different prefixes for the other ones (this time prefix without . ’cause we use the same db)

more info on drupal website

note : you can also take a look, at this video with interesting server settings

=> an other interresting module (not tested yet) : Domain

edit : I’ve test it on several projects and Domain module is awesome !! nice documentation, incredible functionalities , powerful hooks… a really must have module for multisite management !!

Image handling

Posted July 2nd, 2010 in blog

filesystem

public : /sites/default/files

every files are downloadable, they can be hidden via permissions but accessible through file system

private : /system/files

drupal control file download and show 403 if no permission


cck

imagefield D7core

Depends on filefield D7core :  ajax uploads

-> restriction on image : type / size / resolution
-> features : alt / title tags with tokens, default image
-> display : image, link, path, IMAGECACHE preset

further config / features

-> “full” gd library (not default on debian like)  or this link
-> uploadprogress install it with pear  [pecl install uploadprogress]
-> max file size (htaccess or php.ini) :  [post_max_size = 100M] and [upload_max_filesize = 2M] directive

imagecache D7core : presets on cck + views

Depends on imageapi D7core and gd library [aptitude install php5-gd]
call with [print theme('imagecache', 'imagecache_preset_name', 'original_image_filepath', "alt_tag", "title_tag", "custom_attributes_as_class_or_id"); ]

-> play with size, rotation, + effects – desaturate (bw conversion) – sharpening
-> create image on demand. if file doesn’t exist, image cache create it

imagecache_actions D7 ?

-> “layer style“  : Watermark (place an image over a source picture), Background, text overlay, canvas manipulation
-> colors : colorize image, invert, darken / lighten
-> feature : rounded corners, alpha blending, file format switcher

imagecache_profiles
imagecrop / imagefield_crop :  js resizing
imagefield_extended : Add additional data to images other than the default Description, Title and Alt.
image_fupload : upload multiple images with one simple click

bonus :

image integration with wysiwyg

wysiwyg D7ready + tinymce (see wysiwyg config) + imce D7ready + imce_wysiwyg bridge D7?
activate in “button and plugin” > imce (wysiwyg > tinymce config)

alternative to imce filefield_insert “use a cck filefield for uploading images and send the image to the RTE”

lightbox integration and plenty of gallery modules…

Concat multiple rows in one

Posted July 2nd, 2010 in blog

I will take an example to explain this tip.

imagine we want to export our drupal users with their roles. if a user has 3 roles, the result of the join query will be 3 rows for him.

Using GROUP_CONCAT will give us all roles in one single row.

SELECT u.uid, u.name, u.mail,
 GROUP_CONCAT(DISTINCT r.name SEPARATOR '/') as roles
from users u
 inner join users_roles ur on ur.uid = u.uid
 inner join role r using(rid)
 GROUP BY u.uid

notice here the GROUP BY u.uid (otherwise all users will be concatenated in 1 row).

see the syntax of this function

Order your nodes by most recent post OR comment date

Posted July 2nd, 2010 in blog

Hello,

ever wanted to sort your nodes by post or comment date like forums do ( last “action” first) ?

you can do that easily with mysql and greatest select

SELECT n.nid, title,
GREATEST(n.created, last_comment_timestamp) as dateup
 
FROM node n
INNER JOIN `node_comment_statistics` using(nid)
 
order by dateup desc

Change the weight of your items by drag and drop

Posted July 2nd, 2010 in blog

Hello,

Ever wanted how to re-order your items (nodes, custom,…) easily with drag and drop just like drupal do with menus ?

It can be done with the function

the process can be divided in 3 steps

- build a form (to change weight of your items)
- theme it as a table (to add the js stuff)
- update weight items in db (..to save it)

Let’s take the example of Benecol. They want to display / send 21 tips, 1 a day. A solution can be to store the day of the tip in the edit / add form and update it if necessary. but if we want to change day 20 to day 2 we have to update every tips from 2 to 20… not very handy and can be source if errors.

this update can be done easily with drag and drop, without errors !
in this example we will create a table containing the 21steps and displayed with step number, title, body and an edit link

1. build a form

function benecol_eplan_display_content_form() {
 $r = db_query("select title, nid, field_estdnumb_value as step, body 
 from node inner join node_revisions using(nid)
 inner join content_type_estep using(nid) 
 order by field_estdnumb_value");
 while ($row = db_fetch_object($r)){
 //create a partial table row containing the data from the table
 $data = array(
 "(".$row->step.")",
 $row->title,
 truncate_utf8(strip_tags($row->body),100,TRUE,TRUE),
 l(t('edit'), "node/".$row->nid."/edit", array('query'=>drupal_get_destination()))
 );
 //add our static "row" data into a form value
 $form['rows'][$row->nid]['data']=array('#type' => 'value','#value' => $data);
 //now create the weight form element. (if js is enabled, it will be hidden by core tabledrag.js) 
 $form['rows'][$row->nid]['weight-'.$row->nid]=array(
 '#type'=>'textfield',
 '#size'=>5,
 '#default_value'=>$row->step,
 '#attributes' => array('class'=>'weight'),
 );
 }    
 //add submit
 $form['submit']=array(
 '#type'=>'submit',
 '#value'=>t('Save changes'),
 );
 return $form;
}

2. theme the form as a table

function theme_benecol_eplan_display_content_form($form){
//loop through each "row" in the table array
 foreach($form['rows'] as $id => $row){
 //we are only interested in numeric keys
 if (intval($id)){  
 $this_row = $row['data']['#value'];
 //Add the weight field to the row
 $this_row[] = drupal_render($form['rows'][$id]['weight-'.$id]);
 //Add the row to the array of rows and css class for js
 $table_rows[] = array('data'=>$this_row, 'class'=>'draggable');
 }
 }
 //Make sure the header count matches the column count
 $header=array(
 t("Day"),t("Title"), t("Body"),t("Edit"),t("Order")
 );
 $output = theme('table',$header,$table_rows,array('id'=>'esptep-table'));
 $output .= drupal_render($form);
//will add tabledrag.js
 drupal_add_tabledrag('esptep-table', 'order', 'sibling', 'weight');      
 return $output;
}

3. update weight in db on submit

function benecol_eplan_display_content_form_submit($form, &$form_state) {
 foreach($form_state['values'] as $key=>$data){
 //we are only interested in weight elements
 if (substr($key,0,6)=='weight' && is_numeric($data)) {
 //we have the DB id of the row in the element name
 $id = str_replace('weight-','',$key);
//and the order in $data
 db_query("UPDATE {content_type_estep} SET field_estdnumb_value=%d WHERE nid=%d",$data,$id);
 }
 }
}

Load additional profile user data

Posted July 2nd, 2010 in blog

if you want to load additional data to user object with hook_user (from content profile module for example) for using it in one of your custom function, the load order of the hooks can be problematic.

if you do

function site_helper_user($op, &$edit, &$account, $category = NULL) {
if($op=='load') {
$account->loaded_content_profile = content_profile_load('profile', $account->uid);
}
}
 
function site_helper_myfunc() {
// not set
$GLOBALS['user']->loaded_content_profile;
...
}

$GLOBALS['user']->loaded_content_profile will not be set because site_helper_myfunc will be executed before site_helper_user .

the trick is to call hook_user into hook_init to be sure it’s executed first ! (organic group module do that…) and save the result in a session variable to avoid a call on every page

function site_helper_init() {
 if ($GLOBALS['user']->uid) {
 site_helper_user('load', array(), $GLOBALS['user']);
 }
}
function site_helper_user($op, $edit, &$account, $category = NULL) {
 if($op=='load') {
 $account->loaded_content_profile = content_profile_load('profile', $account->uid);
 }
}
function site_helper_myfunc() {
// set
$GLOBALS['user']->loaded_content_profile;
...
}

notice we can’t no more pass $edit by reference…

Force language prefix with l (drupal 5)

Posted July 2nd, 2010 in blog

When you create a link with l() or url(), drupal automatically add the current language prefix

If you want to produce link in another language than current used language, you can use the i18n_path function to produce href

l("my link",i18n_path("node/".$node->nid, "fr"))

Using internal link in a wysiwyg

Posted July 2nd, 2010 in blog

If you want to properly use internal link in your wysiwyg editor and that this link works on dev, stg and prod (assuming you have same db …)

you can use path filter module

within your editor, write your link “internal:node/99″ and the module will translate the path to http://example.com/news/latest

Destroy session when closing browser

Posted July 2nd, 2010 in blog

Usually when you close your browser and reopen your drupal’s site a little time after, your can see that you are always logged in with same credentials as before.

Easy and handy ! But what if you don’t want drupal remembers who you were ?

you need to modify these 2 lines in your settings.php :

ini_set('session.cookie_lifetime',  2000000);
ini_set('session.use_only_cookies', 1);

become

ini_set('session.cookie_lifetime',  0);
ini_set('session.use_only_cookies', 0);

and that’s all !

do not forget to clear the already existent cookie before testing !