Upgrade Your Drupal Skills

We trained 1,000+ Drupal Developers over the last decade.

See Advanced Courses NAH, I know Enough
Nov 17 2013
Nov 17

Customizing the TinyMCE editor's font selection is straight forward thanks to a hook provided by the Drupal Wysiwyg module. The hook_wysiwyg_editor_settings_alter() function allows a module to update the settings passed to the editor. The programming is simple. Discovering and understanding the values of the settings can be a challenge.

Font selection is enabled in the Wysiwyg profile. Edit the profile (Administration > Content authoring > Wysiwyg profiles > Edit) and enable the Font Family dropdown selector by checking the Fonts checkbox in the Button and plugins section. This is as far as the user interface takes us.

Enabling the Font Family dropdown selector

In the TinyMCE initialization code the font selector drop down is configured by a JavaScript string variable named theme_advanced_fonts. The string is a list of font selections with fall backs, separated by semi-collons:

Display name 1=font1,font_fallback1,font_fallback2;Display name 2=font2,fallback_font3, fallback_font4

A real example:

Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde

If the specified fonts are natively available in the web browser no further work is required. If the font is defined using @font-face the definition needs to be in a style sheet loaded by TinyMCE or it will not be displayed during the editing session. The list of stylesheets loaded by the editor is defined in the JavaScript variable named content_css.

Both the theme_advanced_fonts and content_css variables are available in hook_wysiwyg_editor_settings_alter() in a PHP array. In my testing I found the theme_advanced_fonts variable was not pre-populated with the TinyMCE default fonts, even though content_css and other values were. To work around this I pulled the values for theme_advanced_fonts out of the JavaScript configuration file and redefined them in my code.

For the following example the client has declared, "Friends don't let friend's use comic sans, remove it from the list! And please add this font I found on Font Squirrel named Architects Daughter".

In a module for site customizations the hook_wysiwyg_editor_settings_alter() function is defined. This hook fires for all editors so logic is added to specify TinyMCE. The editor fonts are defined in an array to make the list easy to update. In the sample code a line for the Architects Daughter font is added and the line for Comic Sans is commented out. The font list is then assigned to the theme_advanced_fonts variable in the $settings array. The CSS file containing the @font-face definition for Architects Daughter is appended to the content_css variable. This only defineds the font in the editor. The font will also need to be defined for the entire site in the theme CSS.

/**
 * Implements hook_wysiwyg_editor_settings_alter().
 */
function site_customizations_wysiwyg_editor_settings_alter(&$settings, $context) {
  if (
$context['profile']->editor == 'tinymce') {
   
$font_styles = array(
     
"Andale Mono=andale mono,times",
     
"Architects Daughter=architects_daughterregular",
     
"Arial=arial,helvetica,sans-serif",
     
"Arial Black=arial black,avant garde",
     
"Book Antiqua=book antiqua,palatino",
     
/* "Comic Sans MS=comic sans ms,sans-serif", */
     
"Courier New=courier new,courier",
     
"Georgia=georgia,palatino",
     
"Helvetica=helvetica",
     
"Impact=impact,chicago",
     
"Symbol=symbol",
     
"Tahoma=tahoma,arial,helvetica,sans-serif",
     
"Terminal=terminal,monaco",
     
"Times New Roman=times new roman,times",
     
"Trebuchet MS=trebuchet ms,geneva",
     
"Verdana=verdana,geneva",
     
"Webdings=webdings",
     
"Wingdings=wingdings,zapf dingbats",
    );
   
$settings['theme_advanced_fonts'] = implode(';', $font_styles);
   
$settings['content_css'] .= ',' . drupal_get_path('module', 'site_customizations') . '/additional_fonts.css';
  }
}
?>

And the result:

Architects Daughter font

The Wysiwyg profile has a number of CSS options. This code was tested using with Editor CSS set to Use theme CSS. It may be possible to override the content_css value with different settings.

For demonstration purposed I've created an example module named WYSIWYG Fonts and made it available on Github at https://github.com/dale42/wysiwyg_fonts. It is completely self-contained. I recommend copying the code to a site customization or helper module rather than using it as-is.

Nov 14 2013
Nov 14

Wow - what a weekend we had! All the great and good of the Irish Drupal community descended upon The People's Republic of Cork last weekend for Drupal Camp.

The camp started off with Gary Hammond (@manonmir) from iterate talking about adopting Drupal as a business, with some advice that started a great discussion about "defending Drupal" - the need to promote Drupal as a platform but also to defend the attacks that are levelled against it from proprietory vendors and others. Following this, Alan Burke (@alanjosephburke) from Annertech gave an overview of their work on the Oxfam Website, work which resulted in a large increase of site activity and also revenue for Oxfam. Lastly for this morning session, Daniel Alb (@danielalbro) from Monsoon Consulting extolled the benefits of Responsive Web Design (RWD) over unresponsive and/or mobile web design. This latter presentation gave me reason to demo a small animation I created using breakpoints. See here (on desktop, then resize the browser).

After coffee, we were treated to a talk on CoffeeScript by Mark Horgan. If I ever become a Javascript guru, I'll look into that a bit more - for this weekend, I was happy to learn as much as I did and get introduced to Grunt at the same time. Heather James (@learningdrupal) from Acquia took the podium to give us a preview blow us away with a preview of Drupal 8 - responsive images, configuration in code, in-place editing, and more, more, more. We needed lunch after that.

Mapping with Drupal - the topic I most wanted to hear about - was presented by Feargal O'Kane (@gul) from BT48. Best presentation of the weekend - Open Layers, changing map settings/layouts, layers - need I say more? Well, to host a complex mapping website, you might need the help of our next presenter - Jochen Lillich (@geewiz) from FreistilBox, who spoke about building high performance hosting stacks for Drupal. Jochen was followed by Peter Woalanin from Acquia with an in-depth look at the hook and plugin system for Drupal 8. Now, time for more coffee and sandwiches, and then on to ...

... Alan Burke to give an overview of configuration in code for Drupal 8, assisted by Conor Cahill (@conorc) from Fluid Edge who took the reins as a trusty sidekick. Suffice to say, everyone was impressed.

On Saturday we spent the morning engaged in round table discussions about various topics such as Twig, CSS animations, Grunt and Phing, and more, before moving on to our AGM in the afternoon. Ah, the AGM and our voting structures - le's just say, we all know a bit more about "single transferrable votes" now (and coin tossing!).

No need to mention the social aspects, the chats, the drinks, the food, the shuttleboard, the (nearly) dancing in a late bar, "jacketgate" in a hotel toilet, and - ultimately - the fun of yet another successful Drupal Camp. Thanks to Ruairi (@Snakedog_101) for organising.

Share (please)

Filed Under:

  1. Drupal
  2. Drupal Planet
  3. Drupal Camp
  4. Drupal Camp Ireland
Nov 02 2013
Nov 02

Published: November 2, 2013

You want to upgrade your website from Drupal 6 to Drupal 7. The easy way!

You want to upgrade your website from Drupal 6 to Drupal 7. The easy way! This means, you've heard about the great drush command drush sup which acts like Pedro - if you vote for it, all of your dreams will come true.

How do you use drush sup or drush site-upgrade? Simple, open terminal (or equivalent), navigate to your home folder (cd ~) and enter drush dl sup. Viola, you've downloaded the site upgrade extension for drush. What's next? The easy part - create a site alias for your new version as told in the readme.txt, something like:


 

$aliases['newsite'] = array(
'root' => '/srv/www/dump',
'uri' => 'mynewsite.com',
);

You need to call this file aliases.drushrc.php. You can place all of your site aliases in this file in future. And then run drush sup @newsite (or whatever the alias you called it was) - and ... then ... it doesn't work. Why not? Well, because you didn't put the code above inside php brackets, like this:

 '/srv/www/dump',
'uri' => 'mynewsite.com',
);
?>

Now it works. Go run drush sup @newsite. Did it work? No? Really? Why not? Probably because you don't know where to put the file. Simple (when you know the answer) - put it in side your drush folder, perhaps at ~/Users/yourname/dev/drush/ (there's an examples folder in there as well with some nice examples).

Now, run drush sup @newsite and Yee Haw!

Share (please)

Filed Under:

  1. Drupal
  2. Drush
  3. Drupal Upgrade
  4. Drupal 6
  5. Drupal 7
  6. Drupal 8
  7. Drupal Planet
Nov 02 2013
Nov 02

Drush makes your Drupal life easy (well, easier).

Installing Drush is simple, for most people on most systems, but sometimes you get folks who like to have a control panel, such as H-Sphere. Nothing wrong with that, unless when you install Drush, you get the dreaded "can't find php" or similar message. What to do? Follow these steps after logging in to your server via ssh:

1 - Grad a copy of Drush and untar it in your shared folder

sudo wget --quiet -O - http://ftp.drupal.org/files/projects/drush-7.x-4.5.tar.gz | sudo tar -zxf - -C /usr/local/share

(Note, change the link above you the link to the latest version of drush on Drupal.org. You can do this by going to drupal.org/project/drush and on the download link, right-click and choose copy link location.)

2 - Create a symbolic link to where Drush can be found on your server

sudo ln -s /usr/local/share/drush/drush /usr/local/bin/drush

3 - Get Drush to auto download the required "stuff"

sudo drush

Drush won't work yet as it won't know where to find php, so

4 - Open your bash_profile file and edit the PATH line from something like this:

bash_profile: PATH=/hsphere/shared/bin:/hsphere/shared/sbin:/hsphere/local/var/vpopmail/bin:/usr/local/bin:/usr/local/sbin:$PATH:$HOME/bin

to something like this (note the php path in this version):

PATH=/hsphere/shared/bin:/hsphere/shared/sbin:/hsphere/local/var/vpopmail/bin:/usr/local/bin:/usr/local/sbin:/hsphere/shared/php53/bin:$PATH:$HOME/bin

5 - Reboot your server and YEE HAW!

Share (please)

Filed Under:

  1. Drupal
  2. Drush
  3. CentOS
  4. H-Sphere
  5. Drupal Planet
Oct 21 2013
Oct 21

The 2012 Data Breach Investigations Study by Verizon shows that in 855 data breaches they examined, 71 percent occurred in businesses with fewer than 100 employees.

When I read this finding I was reminded of all the sites I’d seen successfully breached over the past several years. The sites of friends. The sites of non-profits. The sites I was asked to take a look at.

This inspired me to present at DrupalCamp MI on the topic of security. In the 45 minute time slot I could only cover a fraction of the material I would like to have. For example, I didn’t talk about social engineering types of issues. I hope someone presents on that soon.

The slides for this presentation are available on Speaker Deck and Slideshare.

One thing I came to realize is that I’d like to see more sessions and trainings at future DrupalCons and DrupalCamps. It’s a good time to raise awareness and teach people practical methods and tools to use in this space.

Oct 21 2013
Oct 21

Views are great for searches if you configure them with exposed filters. Especially if the filters are displayed in a separate block, for example in the sidebar, so that they are accessible on different pages and, once submitted, point to a results page. Modules like Better Exposed Filters can help you create great user experiences for accessing all the content you want to be found on your website. Something that I have often missed though, is the ability to have multiple exposed filter blocks, e.g. one in the sidebar, with a subset of filters, and then another one directly above the search results that provides additional (secondary or less important) filters and / or options that control the display of the search results (things like the number of items per page or the sort options). All that good stuff that is already built into views can only be displayed in one block. I know I'm not the only one missing this.
When I came across this need again for a current project of mine (unfortunately not yet online) I decided to give it a try and to solve this problem in way of a module.

While researching I have found several possible solutions to this and you can certainly find more on the web. Some examples:

The different approaches boil down to this:

  • Clone a view with exposed filters in a block so that you have essentially two identical filter blocks that you can display independently, then hide the undesired filters using CSS.
  • form_alter your way through the exposed block and add markup, so that you can style them in a way that makes them appear to be displayed independently.

Both approaches require a certain amount of maintenance overhead and are in their essence hacky solutions for a use case that the Views module does not address. Now I can't really claim that my solution is less hacky. But at least it is configurable using the Views UI and it spares you of having several identical views just for the sake of different filter blocks (resulting in less maintenance overhead) and the work of manual altering your views forms and coping with possible side effects (resulting in more flexibility and removing the need for programming skills).

You can find the solution I came up with as a sandbox module hosted on drupal.org: MEFIBS. It approaches the problem in a different way than described above. It comes as a views plugin that provides a UI that let's you enable an additional block for the exposable elements of a view (filters, sort options, items per page). Then, for each exposed element you can decide on the options form for that element whether it should be displayed in the default block or in the additional one.

The logic is pretty simple: Behind the scenes, the original exposed form is first altered to hide all elements (using CSS's display: none) that should appear in the additional form. In a second step, the original form is entirely cloned to provide the additional form block and all elements that already appear in the original form are hidden, leaving only the elements visible that have previously been hidden in the original form. All form ids in the additional block are altered to make sure that ids are unique. On form submit the submitted values are altered before the view receives its arguments, meaning the form values are rewritten to the original form ids so that the view receives exactly the form elements it expects. As often, the basic approach is pretty simple, but problems arise when trying to make this approach working in different use cases (ajax vs. non-ajax for example).

Originally I wanted to be able to provide multiple additional blocks, but I soon realized that a stable version with a single additional block is already enough work. And having two different form blocks is solving my normal use cases and should cover a great deal of the use cases of people who have already found other work arounds for this topic. So for the moment I'm focusing on getting the current two-block solution stable. Once that is done, I might turn it into a full module and maybe add support for more blocks, but that is still a long road ...

No code samples in this post, but all the code is available on drupalcode.org.

Update: The module is now available as a full project on drupal.org: https://drupal.org/project/mefibs. It's currently in dev and I started working on multiple block support and a better UI.

Oct 16 2013
Oct 16

In a recent project, we had to provide a way to easily limit the amount of menu items for several menus separately. Drupal does not provide this functionality out of the box so we had to hit the keyboard and take care of this functionality on our own. The result is our newest contrib module Menu Item Limit.

Installation

Installing the module is easy and does not require any magic. Just drop it in your sites/all/modules/contrib/ folder with the rest of your contributed modules and activate it on the modules page.

Configuration

Per default, every menu has the ability to hold unlimited amount of menu items. You can set a custom limit by editing a menu on /admin/structure/menu, i.e. the main menu. Enter any number you want greater than 0 to set a limit. Entering 0 will remove the limit.

That's pretty much it. Now you have a nice and easy way to control your menus.

Oct 14 2013
Oct 14

So when I first realised that I was neglecting this blog, I somewhat found comfort in that at least it hadn't been a year, right? OK, so now it has almost been a year. Does that mean I have stopped finding solutions to my Drupal problems (as the "About me" block states)? Well, no. The biggest problem is remembering to blog about them, or finding the time. But finding the time is not a Drupal problem, and I most definitely have not found the solution to that problem. Anyway, I digress. Let's end the streak with a quick tip that I use all the time: Syncing live databases without having drush aliases.

If you use drush-aliases, you could just do drush sql-sync. But for me, even if I do, I still prefer this method, as I find it extremely easy to debug.

First I make sure I am in my Drupal root:

$ cd /path/to/drupal

Then I usually make sure I can bootstrap Drupal from drush:

$ drush st

If that output says my database is connected, then let's test the remote connection:

$ ssh [email protected] "drush  --root=/path/in/remote/server st"

If that output also says we are connected, it means we are good to go, with my favourite command ever:

$ ssh [email protected] "drush  --root=/path/in/remote/server sql-dump" | drush sql-cli

If you have a drush-alias for the site, you can also go:

drush @live-site-alias sql-dump | drush sql-cli

OK. So what does this do?

The first part says we want to ssh in to our remote live server. Simple enough. The next part in double quotes, tells our terminal to execute the command on the remote server. And the command is telling our remote server to dump the entire database to the screen. Then we pipe that output to the command "drush sql-cli" on our local machine, which basically says that we dump a mysql database into a mysql command line.

Troubleshooting:

If you get this error:

bash: drush: command not found

I guess you could get this error if you are for example on shared hosting, and use a local installation of drush in your home folder (or something). Simply replace the word drush with the full path to your drush command. For example:

$ ssh [email protected] "/path/to/drush  --root=/path/in/remote/server sql-dump" | drush sql-cli

If you get this error:

A Drupal installation directory could not be found

You probably typed in the wrong remote directory. Try again!

I'm going to end this with a semi-related animated gif. Please share your semi-related workflow commands (or animated gifs) in the comments.

Aug 29 2013
Aug 29

Over the past few years I’ve been asked career advice numerous times by Drupalers. After some recent discussions I realized there are a lot of Drupal developers who may be interested in the same thing. So, without further adieu here’s my advice for the moment.

Know Yourself

Knowing the details about your personality, strengths, and weaknesses can really help you navigate your career. Consider using toole like Strengths Finder and learning your Myers-Briggs type.

None of these is perfect or going to tell you everything. But, they can provide you useful insights into yourself.

Focus On Your Strengths

When I was growing up a common piece of advice was work on your weaknesses to become more well rounded. This was bad advice.

Focus on your strengths. Avoid the things you are weak at. Good ways to avoid them are to outsource that work or partner with others who are strong where you are weak.

You can’t remove doing all work you are weak at. If you can’t avoid it spend as little time doing it as possible.

Don’t Hitch Your Horse To Just One Wagon

Focusing solely on Drupal development is a bad idea. Before you jump to the comments to argue with me hear me out.

Few technologies stand the test of time. If you go back 5, 10, or 15 years on the web you can see how much things have changed. Good projects from the past have come and gone. Careers tend to last longer than any piece of technology.

Doing just Drupal work can pigeon hole you into just Drupal work. You might find yourself looking for work and people aren’t interested. Or, you want to work on a project and they want someone more well rounded.

What does this mean practically? If you are a Drupal back-end developer write something that’s not in Drupal. Maybe a PHP CLI application using Symfony. This is a good place to start and you’ll learn a world of other technologies without ever leaving PHP.

If you focus on site building put in some time to using Wordpress, learn about information architecture, spend time learning customer experience, and so forth.

If you are a front-end developer learn other templating systems. PHPTemplate, the system in Drupal, is a one off solution. Learn other setups like Twig, haml, or one of the other popular options. Use SASS or Less.

If you’re feeling really daring, pick up another programming language or learn DevOps.

Learn, Learn, Learn

Technology is always changing. What’s important in technology is always changing. Go back 5 years. There was no focus on front-end performance. Now it a sub-industry of development.

Always keep learning. Put aside some time each weak for continuous learning.

A 40 Hour Work Week

Don’t work too much. 40 hours average per week is enough. For some of us it might go up to 50 hours per week. Don’t go higher than that if it’s within your power. I include side development and free time contributing in this category.

When not working on code do other things. Spend time with family. Have hobbies. Relax. The life we live has so much more to it. People to love and be loved by. Beauty to take your breath away. Friends to have a beer with. Enjoy these things.

By doing this it’s easier to focus when at work. Creativity is more likely to flow. There is a desire to get it done rather than dillydally. The focus, creativity, perspective, and added joy you have will show up in reviews, interactions with others, and your career path.

Take Part In Mentoring

Have a mentor. Mentors have experience they can share. The kinds of things you can’t learn from Google. Well, not yet anyway.

They can talk to you about your career. They can help you see things in ways you don’t know about. They can point you to technologies you should look at. They can introduce you to others. When jobs come up they can be references. When they know of good jobs they can point you to them.

Have a mentor. Pick one carefully.

Stay Focused

There is so much technology. So many shinny things to distract us. Don’t let the distractions take over. Not every problem is yours to solve. Don’t go down all the rabbit holes. You might end up in wonderland and have a great time. But, if you can’t get your work done it’s a problem.

I allow myself some distractions. They are fun and some turned out to be useful. But, the time is limited and the types of things are limited. Just like my backlog, I try to keep a prioritized list of the ones I’ll let myself do and a time limit I can spend on them.

Keep the main things the main things. Don’t let distractions stop that.

This list isn’t trying to tell you where to go. We all have different paths. The idea is to go down your own path without getting stuck and have a good time doing it.

Jul 17 2013
Jul 17

When building sites for our customers we usually create some administrative views (like for content or user administration) to make it easier for editors to work with the site. For a little more user experience we modify these views (especially the exposed form providing the filters). One of these modifications is to create a "collapsible" filter dropdown.

The Mission

Think of a content administration view with filters for content type, status, author and so on. Normally the filter for the content type allows multiple selections and would look similar to the one in the image.

But we want the filter to act as a single dropdown that could be expanded to a multi-select list if the user wants to filter for several types.

The Solution

To achieve this we need a small custom module and alter the exposed form:

  1. /**

  2.  * Implements hook_form_FORM_ID_alter().

  3.  *

  4.  * Alter views exposed forms for collapsible filters.

  5.  */

  6. function MYMODULE_form_views_exposed_form_alter(&$form, &$form_state) {

  7.   if (empty($form_state['view']) || !in_array($form_state['view']->name, array('NAME_OF_VIEW', 'NAME_OF_VIEWS_DISPLAY'))) {
  8.     // We alter the exposed form of a single views display, so return if this is

  9.     // not the expected view.

  10.     return;

  11.   }

  12.   if (isset($form['type'])) {
  13.     // Add option to select all items (equals to resetting the filter).

  14.       'All' => variable_get('views_exposed_filter_any_label', 'new_any') == 'old_any' ? t('') : t('- Any -'),
  15.     );

  16.     $options += $form['type']['#options'];

  17.     // Change size of field based on number of options (max: 5 items).

  18.     if (count($options) <= 2) {
  19.       // Hide filter if there is only one option available (additional

  20.       // to "All").

  21.       $form['type']['#access'] = FALSE;

  22.     }

  23.     $form['type']['#options'] = $options;

  24.   }

  25.   // Alter multi-value dropdowns.

  26.   $form_multiple_selects = array();
  27.   foreach (element_children($form) as $element_name) {

  28.     if (isset($form[$element_name]['#type']) && $form[$element_name]['#type'] == 'select' && !empty($form[$element_name]['#multiple'])) {
  29.       $form_multiple_selects[$element_name] = array(
  30.         'size' => isset($form[$element_name]['#size']) ? $form[$element_name]['#size'] : 5,
  31.       );

  32.     }

  33.   }

  34.   if (count($form_multiple_selects)) {
  35.     $form['#attached'] += array(
  36.       'js' => array(),
  37.       'css' => array(),
  38.     );

  39.     // Attach custom javascript to the form.

  40.     $form['#attached']['js'][] = drupal_get_path('module', 'MYMODULE') . '/js/MYMODULE.admin.js';

  41.     $form['#attached']['js'][] = array(
  42.       'data' => array(
  43.         'collapsibleFilter' => array(
  44.           'multiple_selects' => $form_multiple_selects,

  45.         ),

  46.       ),

  47.       'type' => 'setting',

  48.     );

  49.   }

  50. }

  51. ?>

Unfortunately we have to do some more magic to avoid errors after selecting the new option "All". Because we manually added the option in the form-alter, Views does not know about it and would throw an error after selecting it. The simplest way to avoid it, is to remove the filter value before displaying the results:

  1. /**

  2.  * Implements hook_views_pre_view().

  3.  */

  4. function MYMODULE_views_pre_view(&$view, &$display_id, &$args) {

  5.   if (!in_array($view->name, array('NAME_OF_VIEW', 'NAME_OF_VIEWS_DISPLAY'))) {
  6.     return;

  7.   }

  8.   foreach (array('type') as $filter) {
  9.     if (!empty($_GET[$filter]) && (is_array($_GET[$filter])) && reset($_GET[$filter]) == 'All') {
  10.       // Remove the filter value because it is manually added and thus

  11.       // unknown to Views.

  12.       unset($_GET[$filter]);
  13.     }

  14.   }

  15. }

  16. ?>

Finally we add our JavaScript to append the "plus sign" to the dropdown and add the "collapse" functionality:

  1. (function($) {

  2.   /**

  3.    * Change multi-value dropdown to single-value dropdown and back (visually).

  4.    */

  5.   Drupal.behaviors.collapsibleFilterRewriteMultipleSelect = {

  6.     attach: function(context, settings) {

  7.       $.each(Drupal.settings.collapsibleFilter.multiple_selects, function(name, settings) {

  8.         $('select#edit-' + name)

  9.               .once('collapsible-filter-multiple-rewrite')

  10.               .each(function() {

  11.           var selectionCount = $('option:selected', $(this)).length;

  12.           if (selectionCount <= 1) {

  13.             // Set size of select to 1 if there is not more than 1 selected.

  14.             $(this).attr('size', 1);

  15.             // Remove attribute "multiple".

  16.             $(this).removeAttr('multiple');

  17.             // Set default option.

  18.             if (selectionCount === 0 || $(this).val() === 'All') {

  19.               $(this).val('All');

  20.             }

  21.             // Add link to expand the dropdown.

  22.             $expand = $('')
  23.                     .addClass('select-expander')

  24.                     .attr('href', '#')

  25.                     .attr('title', Drupal.t('Expand selection'))

  26.                     .html('[+]')

  27.                     .click(function() {

  28.                       // Get corresponding select element.

  29.                       $select = $(this)

  30.                               .parent('.form-type-select')

  31.                               .find('.collapsible-filter-multiple-rewrite-processed');

  32.                       // Expand element.

  33.                       $select.attr('size', settings.size)

  34.                               .attr('multiple', 'multiple');

  35.                       $(this).remove();

  36.                     })

  37.                     .appendTo($(this).parent());

  38.           }

  39.         });

  40.       });

  41.     }

  42.   };

  43. })(jQuery);

Result

After you have all this together, users will be able to choose whether to select a single type from a dropdown or multiple values from a list. If a user selected multiple values the filter automatically displays as select list. 

Jul 04 2013
Jul 04

This post adds to the successful and popular "Theming in Drupal 8 with Twig?" (Part 1, Part 2) series from Steffen. It deals with converting Drupal phptemplate themes to use the new Twig template engine, like we recently did with our Busy theme. This is all for Drupal 8, so this might not be helpful to you just yet, but if you wanna get a headstart on converting your phptemplate theme to Twig, nothing's holding you back from starting right now! If you don't know how to get your Drupal 7 theme to Drupal 8, there's an older write-up on how to convert a Drupal 7 theme to Drupal 8 in our blog (keep in mind that things in Drupal 8 change rapidly so some of that text might already be outdated).

First of all, Drupal 8 has Twig as its default template engine. That means if you don't write any other setting into the THEME_NAME.info.yml, Drupal will use Twig as its template engine. So, if the current theme is based on phptemplate, you must delete the line "engine: phptemplate" from your THEME_NAME.info.yml. In the following example it's line #5.   

  1. name: Busy

  2. type: theme

  3. description: 'The ultimate Drupal business theme.'

  4. core: 8.x

  5. engine: phptemplate

After that you can start with the "real" rebuilding. Make sure you know the basic syntax and features of Twig.

Copy the template file and rename it from TEMPLATE_NAME.tpl.php to TEMPLATE_NAME.html.twig. Then you have to look at each line and replace PHP commands with Twig commands. The following example shows you such a conversion of a template file:

Similar conversions have to be applied to arrays and loops.

  1.   {% set users = ['Paul', 'Guenther', 'Max'] %}
  2.   {% for user in users %}
  3.     {{ user }}  

  1. $users = array('Paul', 'Guenther', 'Max');
  2. foreach ($users as $user) {

  3.   echo $user;

  4. }

If you like to save some time, there is a nice Drupal 7 module called Twigify (sandbox-project). It tries to convert your theme automatically from a phptemplate theme to a new Drupal 8 Twig based theme. But keep in mind: complex code still needs to be edited manually.

So, don't be afraid of converting your themes to use the Twig template engine, it isn't that hard.

Jun 30 2013
Jun 30

Another great day, and the final day, of Drupal Developer Days Dublin! The conference continued with more excellent sessions and, of course, sprinting.

There were a few sore heads after the party last night, but it didn't stop people turning up this morning for the first sessions at 9:30am. Yesterday saw two Annertechies presenting sessions, and today continued with another Annertechie session, this time from Anthony on "Maps and Openlayers". Unfortunately, I continued in my trend and didn't make it to this one either, as, during the final session of the day, I was busy wrapping things up and tearing down posters so we could be out of the building on time.

As it happens, it wasn't quite the final session of the day. I had to give one to wrap up the event, including sharing some final numbers on the event. As the slide above shows, there were:

  • Over 210 attendees
  • 24 sessions
  • 2 workshops
  • 1 job speed dating event
  • 120+ people sprinting, over 7 days
  • Over 120 commits to Drupal core, including 85 commits made on Irish soil
  • And many people (including me!) uploaded their first Drupal 8 core patch

Before I sign off this final blog post of the week, I really need to thank some people, without which I couldn't have organised this event:

  • Mike, Drupal Ireland chair and Annertech project manager, who managed my time both in work and on Drupal Dev Days, and who made sure I stayed on top of things
  • Conor, for organising all the tea/coffee, sandwiches and cake!
  • Edward, for taking the lead on the website development, design and for designing the t-shirts too
  • Eoin in DIT, for helping secure the venue in DIT - which they gave to us entirely for free!
  • Heather, for all her promotion and sponsorship wrangling
  • Gary, Louis and Paul for managing the t-shirt stand all weekend
  • Andrew, José and the numerous other volunteers who contributed their time
  • Gábor, chx, xjm and YesCT for helping promote the event and encouraging people to come

Thank you all for all your help, and thanks to everyone who came too. It's been a really great week, although an exhausting one! I'm looking forward to the next Drupal Dev Days, wherever that will be. However, for now, I'm looking forward to getting to sleep!

Thanks everyone!

Jun 29 2013
Jun 29

Day 6 of Drupal Developer Days Dublin, and Day 1 of the sessions, got underway today. Over 200 people are in attendance for this part of the conference, and there's a great atmosphere around the place.

There were many great sessions on today. Unfortunately I was running around for most of the day ensuring rooms were ok, wifi stayed up and the tea/coffee kept flowing, so didn't get to see many of the sessions. However, the couple of sessions I did manage to sneak into were fantastic, namely "Multilingual Drupal 8 - what to look forward to?" from Gábor Hojtsy and "Dependency Injection in Drupal 8" by Kat Bailey.

I didn't even get to two sessions given by my fellow Annertechies! Alan presented a session on the new templating system in Drupal 8, Twig, while Edward gave a session entitled "Think CSS" which looked at the history of CSS, gave an overview of writing maintainable CSS and its future.

Lunch today was sponsored by CommPress, and our caterers went that extra step further and added the sponsor's name and the #drupaldevdays hashtag to the packaging, which was a really nice touch. However, Conor had organised a special surprise for all the attendees - blue cupcakes in the shape of a Druplicon! They even turned your tongue blue :)

Following the sessions came the offical party (sponsored by Freistilbox) in the The Odeon bar just up the road. We had the whole of the top floor to ourselves, and they even put on a summer BBQ outside. Of course, the sprints continued with many coders returning to the sprint rooms after the start of the party fesitivies.

Last day of Dev Days is tomorrow, for which there should be some more great sessions, and some last minute patching ahead of the code freeze on Monday.

Jun 28 2013
Jun 28

Drupal Developer Days Dublin continues! Day 5 of the event saw even more people arriving and the start of the event "proper", with 2 workshops and our new "Job Speed Dating" event.

The day kicked off with the Community Tools Workshop, and then followed in the afternoon by the "Upgrading Your Modules to Drupal 8" workshop, which was actually so popular we had to move it to a larger room!

However, the main highlight of the day for me was the introduction of a new event at Drupal Dev Days - "Job Speed Dating". It's not your normal job fair type affair. Instead each of the companies looking to recruit, gave a 2 minute pitch from the stage and then prospective candidates chose which companies they want to chat with and took a time slot from each. They were then given 5-10 minutes to have a chat and exchange details. Employers can then follow up with the most interesting candidates later if they wish to talk further. Overall, it seemed to go quite smoothly with a number of sponsors taking part, including Annertech! Here's hoping some fruitful conversations were had!

T-shirts also went on sale today, with a variety of colours doing the rounds, depending on whether you're a sponsor, volunteer or attendee. Be sure to get yours tomorrow if you haven't already, they're selling quickly!

Drupal Dev Days begins in earnest tomorrow, with our first day of sessions. I can't wait!

Jun 28 2013
Jun 28

Whenever working in a Drupal project you have to deal with a bunch of modules - core, contrib, custom and features. In the lifetime of a project you enable new ones and disable and uninstall modules you don't need anymore or replaced with a better one. But how do you track that, and how do you make sure that those modules that should not be active really are not?

I did not find any existing solution in the Drupal contrib modules that deals with this issue, so I wrote a small helper module: Master.

Master Yoda icon designed by Artua

Drupal module whitelists with master

You really should know and should define what modules have to be enabled to fulfill the project's functionality. Those modules that are meant to be active should be defined to be active in a way. With master you got a tool to whitelist those modules that have to be active. We call those modules master modules.

Configuration in settings.php

This is done by simply defining $conf['master_modules'] in your settings.php:

$conf['master_modules'] = array( 'myworld_news', 'myworld_contact', );

Module dependencies

Modules that are not in this whitelist may also be required by those modules. Therefore master also takes care of module dependencies. So if your module myworld_news needs node, features, strongarm and views enabled, you do not have to write those modules in your $conf['master_modules'], you simply place those as dependencies in your myworld_news.info.

name = "MyWorld News" description = "News section for my world" core = "7.x" dependencies[] = features dependencies[] = node dependencies[] = strongarm dependencies[] = views

As you might know, Views has a dependency to the Chaos Tools Suite - it is not listed in myworld_news.info as the dependency is already declared in the Views module. Master is aware of that. Modules that are a direct or indirect dependency of a master module are required modules.

Redundant modules

Modules that are no master modules and have no dependency to one of those master modules are meant to be redundant. They shall not be active in the given site. If you want a redundant module to be meant to be active, you have to define it as master module (add it to settings.php configuration) or add it as a direct or indirect dependency of one of the master modules.

Using master

So how can you use that? After you defined your modules in your settings.php, you can use drush commands to check the status of the module whitelist and to ensure the intended module status.

Status

drush master-status [--status=…] [--pipe] [--sort] [--scope=…] [--skip-scope-validation]

With the master-status command you will get an output of the current state of your master modules and their dependencies. So you can make sure certain modules are active or disabled.

The command lists all modules in specific categories:

  • Master modules: those defined in $conf['master_modules']
  • Required modules: dependencies of the master modules
  • Missing modules: modules that are either master or required, but are not enabled
  • Redundant modules: modules that are not master and not required, but are enabled
  • Uninstall modules: modules that are disabled, not master and not required, but not completely uninstalled

By default the list is printed to the shell in a human readable table with additional information. You can limit the output to specific groups of modules by using --status: e.g. drush master-status --status="master,required" for only master modules and their dependencies. You can use the --pipe option to only return a whitespace delimited list of module names or use the --sort option to sort the modules by name.

The --scope option is for using different sets for different environments. This functionality is described in detail below. By default a scope has to be given, unless you use the --skip-scope-validation option.

Ensure modules

drush master-ensure-modules [--no-disable] [--no-uninstall] [--scope=…] [--skip-scope-validation]

This is the heart of Master. With drush master-ensure-modules you can make sure all modules are enabled, disabled and uninstalled as they are meant to be - in one single command. The command retrieves the information from your master modules configuration. It enables all master and required modules. Additionally it disables and uninstalls all redundant modules.

Especially when using Master the first time, you first should check the status of your modules using drush master-status, because maybe you forgot to put some modules in your configuration or module dependencies. Without any additional option, Master would disable and uninstall all modules that are not defined to be active.

Master will make sure of the order modules are enabled, disabled and uninstalled. Dependency will be enabled first and disabled last. When modules are disabled they are uninstalled before the next module will be disabled (and uninstalled). This way we make sure dependent modules may still be active - just for a reason it might be needed.

Master also makes sure already disabled modules are really uninstalled: in most cases, uninstalling a module means removing tables and variables from the database - this is managed by hook_uninstall() and/or hook_schema().

By using the options --no-disable and --no-uninstall you can avoid disabling and uninstalling modules when using the master-ensure-modules command.

--scope and --skip-scope-validation fulfill the same purpose as in drush master-status.

Different modules on different environments

When you work with different staging environments or even different production environments you may have the need to enable an additional set of modules on these sites. For example you might have a language specific feature for the German website that is not part of the international one, or you simply want to make sure your developers have fieldui_ and devel enabled on the local development environment.

For that use case you can define scope specific additions to the global master module configuration. You will also do that by defining a variable in your settings.php. The variable is called $conf['master_modules:SCOPE'], where SCOPE is the name of the scope (e.g. local or live-de).

$conf['master_modules:local'] = array( 'devel', 'field_ui', 'stage_file_proxy', 'views_ui', ); $conf['master_modules:live-de'] = array( 'myworld_de', );

The listed modules will be merged with the modules defined in $conf['master_modules'] and therefore build a bigger set of master modules.

Note: Instead of listing additional separate modules it might be better to add a "dev-Feature", so you can provide additional configuration too (e.g. for devel).

When using the drush commands, you simply append a --scope=local to the command and the given set will be used:

drush master-status --scope=local drush master-ensure-modules --scope=local

Scope validation

As any accidental deactivation or uninstallation of a module might solve data loss, I implemented "scope validation" for any command. This means that a scope has to be defined for the command being executed.

You define a scope by simple setting an array (at least an empty one) for the scope specific module list:

$conf['master_modules:live'] = array();

When you do enter a scope that is not defined that way or enter no scope, the command will fail with an error The given scope "" is not valid!. This might happen if you accidentally type in drush master-ensure-modules --scope=locla or drush master-status. As said, the error is also triggered, when you enter no scope, so you do not accidently forget to enter your scope name.

You can disable scope-validation by using the --skip-scope-validation option in your drush commands. That way we make sure the user decides to do so.

Do not uninstall

In some cases it might be necessary to not completely uninstall the modules that shall be disabled. That might be the case when you only want to temporarily disable a module and leave content intact for the moment - e.g. you disabled the forum or just want to disable the migrate module for now but keep the migrate mapping tables.

For that case, you can fill your master_uninstall_blacklist in the settings.php with modules that shall not be uninstalled by Master when they are about to be.

$conf['master_uninstall_blacklist'] = array( 'myworld_module', ); $conf['master_uninstall_blacklist:live] = array( 'migrate', );

As you see, there is also a scope specific setting, using master_uninstall_blacklist:SCOPE.

Note: This functionality is no replacement for doing a database backups before changing your code. Be sure to make a backup before you use Master!

How we use it.

We at undpaul simply put the drush commands in one of our update.sh files:

040_modules.sh

################################################################################ # Enable our modules and features. ################################################################################ # We use the master module for controlling the module status. # @see http://drupal.org/project/master drush $DRUSH_PARAMS --yes pm-enable master # And finally execute master. drush $DRUSH_PARAMS --yes master-ensure-modules --scope=$STAGE_INDICATOR

This is located right before reverting features, so we do not revert old features.

What it (currently) does not do.

Currently Master does not have any UI for the admin backend. Everyone is welcome to contribute to a master_ui, but in our workflow with automated drush deployment scripts, we simply did not need it by now.

Master does not manage any dependencies by downloading missing components from drupal.org or any third party site. I guess this would be some part drush make might be good at.

Additionally I had in mind to provide a command that will delete all of the module projects, that are not in use in the current site, so we do not clutter up our modules directory - especially when we use a VCS.

I'm curious about your comments and maybe some contributions. The project is available on d.o. You're invited to p

Jun 27 2013
Jun 27

And the sprinting continues! Each day more and more people are arriving and again the sprints are continuing on into the early hours of the morning.

However, tonight I didn't join the sprinters - instead I went to the first official social evening of Drupal Developer Days Dublin, which was held in the Against the Grain bar on Wexford St. It was a really good night, with lots of new faces who just arrived in Dublin today ahead of the workshops tomorrow. It was a welcome break to step away from the laptops and event organisation and just go have a few drinks and enjoy each other's company.

Jun 27 2013
Jun 27

Clickjacking is one of the malicious attacks used against people on the web. Back in 2009 Microsoft came out with a new measure in IE8 to fight against clickjacking that’s since been adopted by Firefox, Chrome, Safari, Opera, and others. This is through servers setting a http header of X-Frame-Options and browsers following the settings.

While The security conscious players like Google, Microsoft, Dropbox, Stackoverflow, Apple, and many others have all implemented this in appropriate places, many of the sites we regularly visit have not yet done so. Some sites not implementing frame protections include drupal.org, wordpress.com (though Wordpress itself has it for login and admin pages), reddit, Evernote, and many others. Since Drupal doesn’t have support by default for X-Frame-Options I assume most Drupal sites have not yet implemented this.

If you are interested in learning how to implement X-Frame-Options keep reading as we’ll dive into a few ways to do this.

The Basics

A great place to learn the basics is on the Mozilla Developer page about X-Frame-Options. For the purposes here we’ll only look into the options that have wide adoption. These are:

  • DENY: When the X-Frame-Options http header is set to this value a page can never be embedded in a frame/iframe.
  • SAMEORIGIN: In this case only the originating domain can embed pages in a frame/iframe. This is specific to a domain including the subdomain. Pages on foo.example.com cannot embed pages from bar.example.com if this value is used.

Apache Setup

Apache can be setup to use one of these values by default. If mod_headers is installed than this setting can be used:

Header always append X-Frame-Options SAMEORIGIN

Nginx Setup

For nginx in the server or location configuration you can use something like:

add_header X-Frame-Options SAMEORIGIN;

In PHP

In any PHP application the header can be set before page content is sent. This is done using the header function.

header('X-Frame-Options: SAMEORIGIN');

Drupal

I want to touch on Drupal for a moment because it’s the second most widely used CMS. I would talk about Wordpress, but they already covered their basis on this. Drupal has a custom way to deal with headers through the use of drupal_add_http_header. Using this function you could do:

drupal_add_http_header('X-Frame-Options', 'SAMEORIGIN');

This could be used for specific paths or site wide. If you are using the Overlay module you’ll want to use SAMEORIGIN rather than DENY or the overlay will not work.

Alternately, the module seckit provides some security options including this one. I have not used this module and make sure you thoroughly check it out before using it.

Jun 26 2013
Jun 26

Drupal Developer Days Dublin is really under way now, with more and more sprinters arriving every day. There's a really fantastic buzz around the place.

The big highlight for me today was actually being able to find the time to do some sprinting! At the sprints last night I was able to work on some Drupal 8 core issues, but as the lead organiser of the conference, being able to find the time during the day and not having to run around sorting out various issues, is a big thing for me. It may not happen again during the conference, but I'm glad I got the opportunity to do so today.

Some of the issues I worked on were:

Fingers crossed I'll find some more time to sprint at this conference, but I'm not holding my breath :)

Jun 25 2013
Jun 25

Things were a bit crazy here today at Drupal Developer Days Dublin. Turns out there were some problems finding and gaining access to the sprint venue last night (I knew I should have gone along!). That combined with the fact that the venue not being able to stay open later than 10pm and some attendees wanting to sprint late into the night, a new evening sprint venue was called for.

As it happened, a new venue wasn't that tricky to find. I already had a list of backups from the other venues I'd researched in the preparation for Dev Days, so after a quick call and a little bit of anxious waiting, the Fitzwilliam Hotel on St. Stephen's Green came to our rescue. I really must thank Lorna in the Fitzwilliam for being so helpful and for sorting it out so quickly for us.

I'm actually writing this blog post from the new sprint venue in the Fitzwilliam and it's great. There's plenty of space and power strips, though the wifi is a little shaky on one side of the room, but I'm sure they'll be able to sort that out for us once we report it in the morning. The staff are very accomodating today.

Here's to another great sprint day tomorrow!

Jun 24 2013
Jun 24

Get ready, get set, GO!! After months of preparation, today marks the first day of Drupal Developer Days 2013 being held here in Dublin in the Dublin Institute of Technology (DIT) and organised by yours truly. Of course, there are many volunteers involved, including Mike, Conor, Heather, and others but more about them in another blog post.

For this first part of the conference, there are no sessions, with just full day code sprints from Monday to Thursday. It's the first time that we've held week-long sprints at Dev Days, and mainly driven by the API Code Freeze that's happening the day after Dev Days finishes - 1st July.

It's been a successful day so far, with almost 30 people attending the sprints, including a big turnout from the Multilingual Initiative team. There were some teething problems to begin with, mainly in relation to wifi, but the staff at DIT were amazing and got that sorted fairly quickly.

The sprints continue tonight in TCube which is a coworking space, but also provides space for meetups. Unfortunately I'm unable to attend tonight, so here's hoping they're able to find it ok!

Get your ticket now!

Jun 14 2013
Jun 14

CKEditor 4.x has been out for a while now. Something I really enjoy about the new release is the new skin, for which the people at CKEditor ran a contest. The winner of the contest was Moono, but I also really like the silver skin. So today I want to show you how you can change the skin when using CKEditor 4.x in Drupal. There is an overview of skins on ckeditor.com, but there's not much there yet. Moonocolor is worth a look, but we are going to focus on silver, which you can find on Github.

I'm going to show you how it's done by writing just a few lines of code (which stBorchert wrote for me :)) and I'm also including a feature module which you can just throw into your site to get going (make sure to grab a database dump first, just in case).

Assumptions

Here's the code that goes in your custom module's .module file:

  1. /**

  2.   * Implements hook__wysiwyg_editor_settings_alter().

  3.   */

  4. function MODULENAME_wysiwyg_editor_settings_alter(&$settings, $context) {

  5.  global $base_url;

  6.   if ($context['profile']->editor == 'ckeditor') {

  7.    $skins_path = drupal_get_path('module', 'MODULENAME') . '/ckeditor/skins';

  8.    // For flexibility we use a variable to get the active skin name.

  9.    $active_skin = variable_get('MODULENAME_skin', 'silver');

  10.    // Set custom skin.

  11.     $settings['skin'] = sprintf('%s,' . '%s/%s/%s/', $active_skin, $base_url, $skins_path, $active_skin);
  12.   }

  13. }

I created a WYSIWYG feature that serves as my custom module and contains my text format, its wysiwyg settings and the silver skin. If you have WYSIWYG module and CKEditor where they belong (as stated under Assumptions) then you can just download this feature and activate it like you would any other module. I created my own text format so it doesn't interfere with the text formats you might already have on your site. The settings are identical to the Filtered HTML format. Check the .module file of the feature to see above code in action. I have included a README.txt in the feature. You have to switch to text format undpaul WYSIWYG to see the editor in the silver skin.

Download the feature

Get the feature and try it out!

Have fun and let me know what you think.

Jun 02 2013
Jun 02

Back when Drupal 7 was being developed, there was a big inititiative about getting new pretty themes into Drupal core. There were three really good suggestions, one of them being Bartik, which the most people got behind, so it could be finished in time to get into Drupal 7 core. One really good suggestion that couldn't be finished in time was Corolla by Jeff Burnz, which is now a contrib theme. The third suggestion was our theme Busy by eigentor, a theme targeted at corporate websites. Just like Corolla, there wasn't enough time to finish it up for Drupal 7 core. Busy has been a contributed theme ever since, because we didn't want it to get lost only because it didn't make it into Drupal core.

After DrupalCon Portland we got really excited about Drupal 8 so we decided to see how porting Busy would go. Work had to be done to exchange some template variables and rename some files, but it was pretty straightforward and only took a little more than an hour to finish. After twig was moved to core as the new default template engine, some more work had to be done (see "Upgrading a phptemplate theme from Drupal 7 to Drupal 8").

Busy will stay minimally maintained for now, though we might port it to use twig in the future.

If you want a new look for your Drupal 8 site, check out Busy and let us know what you think!

Upgrading a phptemplate theme from Drupal 7 to Drupal 8

So now that twig is in core as the default template engine, how do you port your theme to Drupal 8? We considered converting the theme to twig, but that would take up too much time for now. We just wanted to publish the theme for Drupal 8 as soon as possible. At first of course, we ported the theme to the new structure. These are just a few changes, like renaming the THEME.info file to THEME.info.yml and changing its contents to yml, which is simple. You can check core's bartik.info.yml to see how this needs to be done. Your template.php file is renamed to THEME.theme, the contents of this file didn't have to be changed for Busy. We did have to change some variable names in some of our template files (like the comment template) by referring to core's templates, which are nicely documented within each file. For example, in our comment.tpl.php, we had to replace $picture with $user_picture.

THEME.info.yml file

If you have a phptemplate theme, simply add this line to the theme's .info.yml file:

  1. engine: phptemplate

Without this line you will not even be able to activate the theme.

Menus, breadcrumbs, messages

After adding this line, you might still have problems seeing the theme you're used to. In Busy we got fatal errors due to the way we printed the menus in our page.tpl.php. This is what it looks like in Drupal 7:

[gist:5690836]

And this is what we need to do now to print menus like the main menu in your page.tpl.php:

If you want to add classes or ids to the menu, you can do the following in your THEME.theme file (In Drupal 7 this file is called template.php):

  1. /**

  2.  * Implements hook_preprocess_HOOK() for page.tpl.php

  3.  */

  4. function busy_preprocess_page(&$variables) {

  5.   // Pass the main menu and secondary menu to the template as render arrays.

  6.   if (!empty($variables['main_menu'])) {

  7.     $variables['main_menu']['#attributes']['id'] = 'main-menu';

  8.     $variables['main_menu']['#attributes']['class'] = array('links', 'clearfix');

  9.   }

  10.   if (!empty($variables['secondary_menu'])) {

  11.     $variables['secondary_menu']['#attributes']['id'] = 'secondary-menu';

  12.   }

  13. }

Now if you print variables like $messages just using print $messages, you will not get any output. Look through the code in your page.tpl.php for any print $VARIABLE and make it print render($VARIABLE). We had to do this for $breadcrumb and $messages.

Drupal 8's theming system is still under heavy construction and I'm sure there will be more stuff we'll have to change in our template files very soon, but this way works well for now if you are just interested in porting your phptemplate theme to Drupal 8.

May 23 2013
May 23

Drupal is a lot like enterprise software. Before you think this is a bad judgement on Drupal or a slur please hear me out. It’s more a description of Drupal based on my experience with it for nearly 8 years and my last couple years dealing with enterprise software. I want to dive into some specific points that may be good, bad, and even make us unhappy with some of the things that make us happy.

Architecture

One of the surprising things about enterprise applications is that I’ve found one architect in the application often doesn’t know how the whole thing works. The software is complicated and applications can end up with numerous people who have architect in a title. Drupal has a similar experience. With all of the things going on, even the wisest of contributors and initiative leads don’t know how the whole thing works.

Drupal is complicated like enterprise software.

Monolithic

It recently struck me that Drupal is a fairly monolithic application. A trend in applications is to have small parts that operate together to create a whole. For example, one application provides the user interface. That application talks to an API server. The API server schedules things to happen via a queue. Workers grab items from the queue and act on them. In this picture each of the different types of things is a different application.

Or, you could have a small application. I think of Wordpress as a fairly small application and it comes in around 200k lines of code. For a small application it does a lot.

Drupal is nearing 950k lines of code. I won’t be surprised when it passes 1 million lines of code. This is just core and no one just builds a site with core anymore.

Drupal also does a lot. Sites can have a message queue (using Drupal), caching (using database caching through Drupal), and so much more. It’s big.

Drupal Specific Terms and UI

I’ve had the opportunity to teach people about Drupal who have had years using web applications, have worked with content and other CMS, and some who write applications. I’ve learned there are a lot of Drupalisms. A whole vocabulary has grown around Drupal and you have to have domain knowledge to use Drupal, build with it, or just understand conversations around it. In addition the UI isn’t intuitive to outsiders. Someone needs to understand the product down to the major version to perform everyday tasks.

These are both elements I’ve found in enterprise software. In many ways these custom aspects lock users into the product because it raises the barrier to go to other products due to change. If successful it can help build an ecosystem around the product (more on that later).

Major Version Update… What?!?!

Have you tried to update major versions of a Drupal site? How about updating when you jump a major version? Going from Drupal 7 to 8, much less Drupal 6 to 8, is going to be a huge effort for anyone who tries to take it one. The architecture changes that happen between major versions means custom code needs to be rewritten, contributed modules need to work and have an update path, and the data needs to be migrated because it often can’t just be updated.

This can be costly. It can end up in the hands of consultants to handle the upgrade just to keep the same base functionality. That doesn’t even mean adding support more responsive design.

Ecosystem - Consultants and Partners

Many enterprise software applications will have an ecosystem around them. There will be partners that sell the software. There will be partners that support the software. There will be consultants who help those who implement the software do so. When you have a complicated piece of software someone new to implementing it needs the ecosystem.

Does this sound at all like Drupal (with the exception of selling Drupal)? Drupal has an amazing ecosystem around it with many fantastic people. And, many companies could not implement Drupal as a solution without them.

I’m Not Complaining

I could continue on this thread but many people won’t continue to read (short attention spans). I’m not complaining but merely observing what Drupal has become.

May 08 2013
May 08

Some upcoming events that could be of interest: Drupal Training, and DrupalCamp Dublin.

Annertech are presenting a public training day on 23rd May in Dublin. The courses presented will be:

Drupal in a day: http://drupalinaday2013.eventbrite.ie/
Drupal for developers in a day: http://drupalsitebuilding2013.eventbrite.ie/

Some upcoming events that could be of interest: Drupal Training, and DrupalCamp Dublin.

Annertech are presenting a public training day on 23rd May in Dublin. The courses presented will be:

Drupal in a day: http://drupalinaday2013.eventbrite.ie/
Drupal for developers in a day: http://drupalsitebuilding2013.eventbrite.ie/

Drupal in a day
Get a solid introduction to Drupal (6 or 7) in one day. Learn about the most essential features and concepts of Drupal through hands on activities. By the end of this day you will be familiar with Drupal terminology and be able to identify how many Drupal sites are constructed. You will know how to identify and choose modules to get the functionality you need.

Full details and signup at http://drupalinaday2013.eventbrite.ie/

---

Drupal for Developers in a day
Participants will be brought up to speed on Drupal and will be ready to tackle their own project. Learn how to manage media, build calendars and complex, dynamic queries of content by selecting and configuring the most popular modules.

Full details and signup at http://drupalsitebuilding2013.eventbrite.ie/

---

For either course, you'll need to bring your laptop, with Drupal installed, but don't worry if you haven't got that far. I'll be there on the day from 8am to help you get setup in advance.

If you feel that this could of interest to any of your colleagues, please forward it on.

[If you can't pay by Credit card, please contact us and we'll work something out]
---

DrupalCamp Dublin
Meet and learn from the best Drupal minds in Ireland bringing together developers, themers, end users and those interested in learning more about the platform. DrupalCamp Dublin takes place on the 24th and 25th May at the Guinness Enterprise Centre, right next to the Guinness Storehouse.

This is a free 2 day event - more details at http://www.drupalcampireland.org/

Apr 04 2013
Ed
Apr 04
Recently I have been looking at how we measure contributions to science in a way that is more well-rounded than the h-index and similar initiatives. Most of this relates to how we measure a user's contributions to projects such as Scratchpads, ViBRANT and eMonocot.

The "alternative metrics" movement has been around for a number of years now, and one of the more established outfits is Altmetric who provide badges for research articles showing how much attention that article has received on a number of purely social (Twitter, Facebook) and 'academic social' (Mendeley, Connotea) networks.

As the badges are pretty easy to implement I have made a small Drupal module that displays an Altmetric badge on Biblio node pages, and provides a configuration page to allow the badges to be customised. The module is available here: Drupal biblio altmetric.


Apr 04 2013
Apr 04

The Views module is the most installed Drupal module and gives great power to developers for building websites. Because of its rich feature set, Views will be integrated in core as part of Drupal 8. Developers may extend the Views functionality by writing custom plugins using the complex plugin system. This helps implementing functionality for special use cases in Views.

In this blog post I would like to explain how to write a custom "Default Argument Handler" for Views and how to develop a simple context system using the Flag module by providing a sample use case as example.

Hint: The complete code of the module can be accessed for testing purposes at github.

The Use Case

Our client produces organic pet food as a family business and wants to increase sales using e-commerce. Furthermore, the daughter of our client will publish a monthly magazine with tips and tricks for each dogs and cats. The magazine should be advertised on the new website of the customer. Older magazine arcticles will be published on the website later on.

A key feature of the new website will be a context system which delivers products and articles to the user based on his favorite pet. We use the Flag module to flag the pets for each user.

Hint: To keep it simple we flag the pets by hand.

Preparations

Creating flags

First of all we create a vocabulary "flag context" with some terms which will be flagged later.

Afterwards we create a new flag at admin/structure/flags with the flag type "taxonomy term" choosing "flag context" as bundle. 

Extending the content type

By adding an "Entity Reference" field to a content type (i.e. article of the standard profile) we make sure that we can link content to the terms which will be flagged by the user. After adding the field we create at least one node for each term. For this use case we would also need to add this field to our products but this will be sufficient for our example.

Next, we have to flag a term for testing the functionality later. Just visit a term page and flag the term using the link on the page.

Creating Views

The user should see what their favorite pet is. We make this happen by creating a View.

The View filters terms by our created vocabulary "flag context" and has a relationship "Flags: Taxonomy Term flag" on "Flag Context" and  "Current User".  We use "block" as a display for easy embedding.

In addition to the title we also add a flag link allowing the user to unflag the term in the block.  

For testing purposes, we add the new block to the "sidebar first" region. 

At last, we create the basics for our final test. We create a page View showing only "article" nodes. We add "Content:has taxonomy term ID" as "Contextual Filter". This filter will be extended later with our context functionality.

Coding the handler

A views handler has to been included in a module to be recognized by the plugin system of Views. It is not a lot of work but despite that there are some lines where we could struggle. 

1. The .info file

A common pratice is to put views handlers and plugins in their own subdirectorys. Drupal needs to know where those files are located so we include the path into the info file.

  1. name = Views Flag Context

  2. description = Provides a flag context.

  3. core = 7.x

  4. package = Flags

  5. version = 7.x-0.1

  6. dependencies[] = flag

  7. files[] = handlers/views_argument_default_flag.inc

2. The .module file

We need to implement hook_views_api to tell Views that our module is of interest: 

  1. /**

  2.  * Implements hook_views_api().

  3.  */

  4. function views_flag_context_views_api() {

  5.   return array(

  6.     'api' => '3',

  7.   );

  8. }

Now, Views looks for a file with relevant code. Best practice is to create a modulename.views.inc file.

3. The .views.inc file

By implementing hook_views_plugin we return an array in which we define the kind of plugin we like to create and the place where to find it.

  1.  /**

  2.   * Implements hook_views_plugins().

  3.   */

  4. function views_flag_context_views_plugins() {

  5.     'argument default' => array(
  6.       'views_argument_default_flag' => array(
  7.         'title' => t('Default Flags'),

  8.         'handler' => 'views_argument_default_flag',

  9.         'path' => drupal_get_path('module', 'views_flag_context') . '/handlers',

  10.       ),

  11.     ),

  12.   );

  13.   return $plugin;

  14. }

  15. ?>

With these 3 steps we prepared our handler. Now it is time to add functionality to the handler.

4. The views_argument_default_flag.inc file

In this file we define the handler as class by inheriting and overriding methods from the views_plugin_argument_default.

The tree:

  1.  /**

  2.    * Define the options form to enable selection of flags.

  3.    */

  4.   function options_form(&$form, &$form_state) {

  5.   }

  6.   /**

  7.    * Return all possible options for the view and provide default values.

  8.    */

  9.   function option_definition() {

  10.   }

  11.   /**

  12.    * Provide the default form form for submitting options.

  13.    */

  14.   function options_submit(&$form, &$form_state, &$options = array()) {
  15.   }

  16.   /**

  17.    * This function controls what to return to the contextual filter.

  18.    */

  19.   function get_argument() {

  20.   }

  21.   /**

  22.    * Initialize this plugin with the view and the argument is is linked to.

  23.    */

  24.   function init(&$view, &$argument, $options) {

  25.     parent::init($view, $argument, $options);

  26.   }

  27. ?>

First of all, we define the option form of the handler. We want to configure which flags or vocabularys we want to use later. This makes it possible to add other flags or vocabularies very easily. Furthermore, it should be possible to add multiple terms and also to define a fallback if there is no content available. 

To achieve this we get all flags from the system and create a checkboxes form item. It is important to implement the "default value" of the options correctly. The options are available in the views object:

  1. '#default_value' => $this->options['flags'],

  2. ?>

The complete function:

[gist:5264081]

As with all forms we also can add a validate and a submit function. We only add a submit function for now.

  1.   /**

  2.    * Provide the default form form for submitting options.

  3.    */

  4.   function options_submit(&$form, &$form_state, &$options = array()) {
  5.     // We filter the options on only selected ones.

  6.     $options['vocabularies'] = array_filter($options['vocabularies']);
  7.   }

Now we have to override the get_argument() function. This function will return values to the filter in the view.

We compare the flagged content with the values from the options. We filter all term ids by their vocabularies using a helper function. It is important to return the value as a string just as we would call the argument by hand.

  1.   /**

  2.    * This function controls what to return to the contextual filter.

  3.    */

  4.   function get_argument() {

  5.     // Get available flag types from the system.

  6.     $flags = flag_get_flags('taxonomy_term');

  7.     // Get all User flags.

  8.     $user_flags = flag_get_user_flags('taxonomy_term');

  9.    

  10.     // This array will collect all Term IDs which will be filtered.

  11.    

  12.     // Get the vocab foreach flag.

  13.     foreach ($flags as $flagname => $flag) {

  14.       // We only proceed with this flag, if it has been selected and the current

  15.       // user has flagged terms with that flag.

  16.       if (!empty($this->options['flags'][$flagname]) && !empty($user_flags[$flagname])) {
  17.         // Get all tids from the user flags.

  18.         $user_tids = array_keys($user_flags[$flagname]);
  19.         $vocabs = array();
  20.         // Check  which vocabularies are valid for this handler.

  21.         foreach ($flag->types as $vocab) {

  22.           if (!empty($this->options['vocabularies'][$vocab])) {
  23.             $vocabs[] = $vocab;

  24.           }

  25.         }

  26.         // We add the valid terms of the flag set to our default argument list.

  27.         $valid_tids = _views_flag_context_filter_tids_on_vocabularies($user_tids, $vocabs);

  28.       }

  29.     }

  30.     // If no tids are valid, we can fallback to a given value.

  31.     if (empty($tids)) {
  32.       // Fall back to the exception value (by default this is 'all')

  33.       return $this->options['fallback'];

  34.     }

  35.     // If there are term ids available we return them by concating the terms

  36.     // with the multiple operator (AND or OR).

  37.     else {

  38.       return implode($this->options['multiple_operator'], $tids);
  39.     }

  40.   }

The helper function for the .module file:

  1. /**

  2.  * Helper to filter tids on given vocabularies.

  3.  *

  4.  * @param array $tids

  5.  *   array of term ids

  6.  * @param array $vocabularies

  7.  *   array of vocabulary machine names

  8.  *

  9.  * @return array

  10.  *   array of terms that live in one of the given vocabularies.

  11.  */

  12. function _views_flag_context_filter_tids_on_vocabularies($tids, $vocabularies) {

  13.   $query = db_select('taxonomy_term_data', 't')

  14.     ->fields('t', array('tid'))
  15.     ->condition('t.tid', $tids);

  16.   $query->innerJoin('taxonomy_vocabulary', 'v', 't.vid = v.vid');

  17.   $query->condition('v.machine_name', $vocabularies);

  18.   return $query->execute()->fetchCol();

  19. }

This is all for the handler.

Next, we add the handler to the contextual filter we recently created.

After applying the changes there should be the article which matches our favorite pet in the preview.

Summary

The Flag modules makes it easy to handle user preferences. Page elements can react to different flags and return relevant content to the user by using the handler we created. With a few tricks we can extend the functionality, i.e. adding automatic flagging with rules reacting on content views.

Github: https://github.com/Cyberschorsch/views_flag_context

Mar 26 2013
Mar 26

Sometimes you would like to add a map to a node or block without the need for detailled configuration options. You simply want to display a map and be done with it.
Fortunately this is an easy task using Leaflet.

Say you have a value for the location and one for the country and would like to print this "address" in a map.
So you need to first install Leaflet and Geocoder and then use this function to generate the map:

  1. /**

  2.  * Generate a simple map with a location pointer.

  3.  *

  4.  * @param string $location

  5.  *   Location to use (for example the address).

  6.  * @param string $country

  7.  *   Name of the country to use.

  8.  *

  9.  * @return string

  10.  *   The rendered map.

  11.  */

  12. function mysimplemap_map_create($location, $country) {

  13.   $map = '';

  14.   // Join the address parts to something geocoder / google maps understands.

  15.   $address = sprintf('%s, %s', $location, $country);
  16.   // Try to create a geographic point out of the given location values.

  17.   if ($geo_point = geocoder('google', $address)) {

  18.     // Create a JSON equivalent to the point.

  19.     $geo_json = $geo_point->out('json');

  20.     // Get map implementation provided by http://drupal.org/project/leaflet_googlemaps.

  21.     $map = leaflet_map_get_info('google-maps-roadmap');

  22.     // Set initial zoom level.

  23.     $map['settings']['zoom'] = 16;

  24.    

  25.     // Decode the JSON string.

  26.     // Create settings for the map.

  27.     $map_features = array(
  28.         'type' => 'point',

  29.         'lon' => $geo_data->coordinates[0],

  30.         'lat' => $geo_data->coordinates[1],

  31.       ),

  32.     );

  33.     // Render the map with a fixed height of 250 pixels.

  34.     $map = leaflet_render_map($map, $features, '250px');

  35.   }

  36.   return $map;

  37. }

  38. ?>

Easy, isn't it?

Mar 20 2013
Mar 20

We're delighted to announce that Annertech will be sponsoring the upcoming Drupal Developer Days 2013 being held in Dublin later this year. We've signed up as a silver sponsor and are really looking forward to the event.

Drupal Developer Days is an annual European event that brings together the people who develop, design and support the Drupal platform. The event will feature dozens of sessions and panels from some of Drupal's best contributors, providing developers and site builders with a chance to both learn and share their knowledge with other Drupal professionals.

This year's event is particularly special. Drupal Developer Days 2013 coincides with the final days of development prior to Drupal 8 code freeze. In light of this, this year there will be a week-long Drupal 8 core code sprint in the week leading up to the Drupal Developer Days event.

It should be a brilliant event, and there are only a limited number of sponsorship slots still available, so please help support the event.

Support Drupal Developer Days too!

Feb 26 2013
Feb 26

Following up on the first blog post on Theming in Drupal 8 with Twig, this second part will cover Twig's syntax.

In order to explain these changes more clearly I want to compare the known PHPTemplate syntax with the new Twig template syntax. All examples mentioned in this blog post are based on the latest development state of the Drupal 8 Twig Sandbox.

Usually themers only have to care about the output of predefined variables or simple control structures (if conditions or for loops that output lists of content). Complex logic or further processing of variables should not be placed in the template files - I think everybody can remember a situation in which they inserted a view programmatically in a template or placed custom field on theme layer. In most cases the ability to use custom PHP in template files lead to more complexity and a lack of clarity.

Using variables in templates:

Example for the usage of variables and simple if condition in PHPTemplate:

[gist:5011792:taxonomy-term.tpl.php]

Example for the usage of variables and simple if condition in Twig:

[gist:5011792:taxonomy-term.html.twig]

In the example shown above you can clearly see the differences. Instead of using the PHP'S print function or Drupal's render() function, Twig offers the {{ }} syntax for outputting variables. Twig also improved the output of array and object elements on the theme layer. In the future we will be using the following consistent "point-syntax". Here it does not matter if we want to access array or object elements.

Example of a Views exposed filter in PHPTemplate:
[gist:5011792:syntax.php]

Example of a Views exposed filter in Twig:
[gist:5011792:syntax.twig]

Control Structures

In addition to the already mentioned if conditions there is the possibility to use for loops with the {% %} syntax.

for loop example in PHPTemplate:
[gist:5011792:book-all-books-block.tpl.php]

for loop example in Twig:
[gist:5011792:book-all-books-block.html.twig]

As in earlier versions of Drupal you should avoid putting too much logic in template files. For this purpose you should use the known preprocess function provided by Drupal. Besides the new {% %} syntax in the templates the example also shows the usage of the new nav elements of HTML5 standard, which also has been introduced in other Twig templates.

Comments in templates

For documentation in templates, Twig provides the {# #} syntax.

Example for comments in PHPTemplate (phpdoc style):
[gist:5011792:comments.php]

Example for comments in Twig:
[gist:5011792:comments.html.twig]

Reusability of templates via includes

Even though it was possible to use the PHP include or include_once function for re-using existing parts of a template, it was rarely used in the previous versions of Drupal. In Twig these includes will be used much more often. A good example for the use of includes is the image module - based on the imagestyle another template file will inlcuded by the include function of Twig.
[gist:5011792:image-formatter.html.twig]

With the help of the paramater "with" you can specify variables that are available in the scope of the included template.

Usage of filters in templates

Twig filters offer the possibility to influence the output of variables on the theme level. The following examples show a common use case for filters in Twig - they replace the check_plain function and the t-function with its Twig counterpart.

Translation in templates

Example for translations in PHPTemplate:

Example for translations in Twig:
[
gist:5011792:node-admin-overview.html.twig]

As already known from the  t-function you can use placeholders in Twig to make the translatable strings more dynamic. In the example above the variable $votes and type are replaced in the strings.

Escaping special chars in templates

In previous Drupal versions you were able to convert special chars to HTML with the help of the check_plain function.

Example in PHPTemplate:
[gist:5011792:checkplain.php]

Example in Twig:
[gist:5011792:checkplain.twig]

The escape filter can also be used via the alias e and supports parameters for different escape strategies.

  • html: escape a string for HTML body context
  • js: escapes a string for JavaScript context
  • css: escapes a string for CSS context
  • url: escapes a string for URI or parameter context
  • html_attr: escapes a string for HTML attribute context

Further information on Twig filters can be found in the Twig's documentation.

Feb 19 2013
Feb 19

At times I am confused by behavior in the powerful Rules module. Sometimes Rules data selectors for entities have their fields listed and sometimes they do not. I did not find documentation about this behavior.

So I dug through code… I thought that perhaps the following would provide a variable of an entity_type:

  'provides' => array(
    'my_variable' => array(
      'type' => 'my_entity_type',
      'label' => t('My entity type'),
    ),
  ),

However if the entity type provides bundle support, then the bundle must be explicitly defined beforehand. It does not look like it is possible for Rules to dynamically load field properties based on the returned data. This makes sense because without a known bundle, then Rules would need to load all possible fields as possible data selectors. This would be confusing to users when they would try one, and it just wouldn’t work.

I found an example of the peculiar way in which this works in rules/modules/entity.eval.inc:rules_action_entity_create_info_alter(). A rules action info_alter hook is defined in the above documentation page about providing variables. This info_alter hook extends the configuration of the action as defined in rules/modules/entity.rules.inc:rules_entity_action_info().

The alter_info implementation adds an additional parameter for the bundle dynamically based on the entity type, and then tells what bundle to use based on that configuration. The important idea to take away is that you should define a bundle parameter in your action info:

  'bundle' => array(
    'type' => 'my_bundle_entity_type', // see Entity API
    'label' => t('Bundle'),
    'description' => t('Set the bundle'),
  ),

…and then implement an info_alter function for that action that sets the bundle parameter to the provided variable:

function my_module_action_info_alter(&$element_info, RulesAbstractPlugin $element) {
  $element_info['provides']['bundle'] = $element->settings['bundle'];
}

The action just needs to return the entity metadata wrapper of the loaded entity.

Matthew Radcliffe is a Drupal developer at Kosada, Inc. and contributor to the Drupal project including Drupal core, contributed projects, development environments and the Contribution Mentoring program among other things.

Feb 18 2013
Feb 18

Besides fundamental changes in the backend of Drupal 8 (i.e usage of several Symfony 2 components) a new theme engine called Twig is also introduced to Drupal.

In the course of further development of Drupal it is necessary to look beyond our community boundaries and to be open to new ideas and technologies. This is the only way to ensure a sustained and stable growth of the community. With the introduction of the Symfony 2 components Drupal took a major step forward to the PHP community.

In addition to PHPTemplate themers can use Twig as a new theme engine in Drupal 8. This helps lowering the entry barrier for new developers since learning PHP for theming Drupal is no longer necessary.

In our multi-part blog post on Twig we want to explain the fundamental changes of the new theme engine and show new possibilities in theming introduced by its implementation in Drupal Core.

Why Twig?

Twig has its origin in the world of Django and was developed by Armin Ronacher as an alternative theme engine for writing templates directly in PHP. Since 2009 Twig has been an integral part of the Symfony framework and has proven to be a reliable and fast theme engine. Since then the further development of Twig is promoted by Fabien Potencier, the leader of the Symfony project. Further information on his thoughts about templating engines in PHP can be found in his blog post Templating Engines in PHP.

The following list provides an overview on PHP frameworks that also support Twig:

Drupal & Twig

Apart from an easier tag-based syntax, Twig offers a lot more security on theming level. The fact can be described very briefly by a quote of John Albin at DrupalCon Denver.

We hand themers a loaded gun and tell them to hammer in a nail with it. Oh and be careful!

For instance, it would be possible to access the Drupal database via PHPTemplate and delete individual tables or access the file system of your server.

Advantages using Twig

  • more security on theme level due to limited possibilities given by tag-based syntax
  • no more PHP/HTML mix in template files
  • simplification of theming system due to the reduction of preprocess functions
  • consistent and easy-to-learn tag-based syntax, that is limited to the most common use cases in template 'development'
  • Reusability of template files (via Twig includes)
  • IDE integration (Netbeans, vim, PHPStorm, Eclipse, Textmate, Sublime)
  • good documentation - Twig documentation
  • will bring Symfony and Drupal community closer, because more components can be shared

Unfortunately there is not so much documentation on drupal.org yet but the latest coding standards can be found here: Twig Coding Standards.

Disadvantages

  • learning a new template syntax
  • performance and memory issues caused by an extra theme engine
  • differing update cycles of Drupal Core and Twig regarding to the code freeze of Drupal 8

In my opinion only the first issue should be considered as a real disadvantage - in addition to the fundamentely changed backend in Drupal 8, Drupalistas also have to get used to a new template syntax. But if you have a look at other Content Management Systems or Frameworks you'll also find custom syntax for templating, unless they are using PHP as their main language for templating.
The performance issue will be adressed in one of our following blog posts on Twig. The already mentioned update cycles are a well-known problem in the Drupal community (jQuery is a good example in Drupal 7) - but I don't think that Twig will change dramatically in future releases.

Twig is part of Drupal Core

In the latest Drupal 8 release Twig is available as an additional theme engine and is ready to use in custom themes (Integrate Twig into core: Implementation issue). Currently the built-in themes are still using PHPTemplate as their theme engine - but you can try the Drupal 8 Twig Sandbox instead. The goal should be to reduce all Drupal Core templates and their related theme functions to just one Twig template and to simplify the theming system. The Stark theme located in the sandbox is a good example for the new approach - besides the module templates all parts of administrational pages are provided as Twig templates and can be easily overriden.

We won't get rid of the big "render array" (of doom) in Drupal 8, but the usage of Twig as our new templating engine will be much easier.

In the second part of our blog post series we want to talk about the syntax of Twig and show some simple examples to explain how we can benefit from these changes.

The Twig logo is © 2010-2012 Sensio Labs

Feb 14 2013
Feb 14

Image Style Basics

Drupal offers the possibility to easily process images via image styles (formerly known as imagecache presets). In our current case of a photo heavy website we had to integrate a watermark image on all images supplied by the site. For this purpose we used the image style action Overlay (watermark).

The configuration of the style is really simple - but could be enriched with scaling and all other available actions.

A major advantage of using Drupal's built in image style is the fact that all images are only processed once and cached in Drupal's file system for later usage. In case of our image gallery the first call of the page would take a long time, because all thumbnails would be rendered for the first time. But on second call all images would be available immediately.

Domain Access Basics

With the help of the Domain Access module you are able to share configuration, content and users between different domains. Compared to the known Drupal multisite approach, which uses a single database per site, Domain Access shares all data in a single database.

In one of our latest projects we used the module for a German city website called ROW-People (row-people.de / hb-people.de). Besides sharing content and configuration between these two sites we had to ensure that all images get a unique watermark rendered via Drupal's image styles. For further information on using the Domain Access module we will write an extra blog post explaining the setup process and usage.

Our Domain Access configuration for testing:

  • Domain 1 (machine_name: domain1) Default Domain
  • Domain 2 (machine_name: domain2)

If you want to test the setup on your local machine, you will have to set up two virtual hosts pointing to the Drupal installation.

Problem and our solution

Due to the fact that image styles are only rendered once, all images on both domains would get the same watermark image. But as mentioned before, we want a unique watermark per domain.

In order to achieve this goal we had to create our own image style per domain (the original style name suffixed by the domains machine_name) that contained the desired watermark image.

  • watermark (default-watermark)
  • watermark_domain2 (suffix matches the machine_name of the domain)

For testing purposes we created a simple content type with the following fields

  • Title (Standard)
  • Body
  • Image (Style watermark)

All further processing had to be done via a preprocess function supplied by the theming layer of drupal.

While configuring our domains we needed to set up a unique machine_name per domain that could be used in our template_preprocess_node().

Provide appropriate watermark image via preprocess functions

With the help of the template_preprocess_node() function you are able to rewrite the output of fields on node level. Whenever the page is called the preprocess function will rewrite the name of the image style name based on the machine_name of the actual domain.

In case of the city-portal site (row-people.de/ hb-people.de) we also had to add some preprocess functions to overwrite the output of the image fields used in several views ( e.g. (Image gallery))

Jan 16 2013
Ed
Jan 16
A new Drupal module: Biblio autocomplete.

Previsoulsy as part of eMonocot we started to use the IPNI webservice to autocomplete some fields in the Biblio content type. As one of the eMonocot objectives is to "Ensure that the tools developed are compliant with zoological nomenclature" I have extended this functionality to use the ZooBank API which is currently in a testing phase. In addition values for the autocomplete suggestions can be made from values previous entered in other Biblio nodes.

Instead of having either previsously entered values, IPNI or ZooBank attempt to autocomplete the field this module has been developed to allow any combination of these plugins to attempt the autocompletion. This will have uses in cases like the recent Lyme Regis Geo-BioBlitz where a single classification spand both animal and plant kingdoms (in this case the Dictioanry of UK Species).

The module is designed so that additional plugin modules can easily contribute results for other webservices.

This work was done as part of eMonocot as a contribution to the Scratchpads project.

Jan 08 2013
Jan 08

New versions of websites often have new content organizations with new URL patterns. When old content is assigned a new URL it's almost always desirable to redirect traffic from the content's old URL to its new URL. The redirect preserves external links to the site content (i.e., prevents "link rot") and helps maintain search engine page ranking.

If the Drupal Migrate module is used to create the content for the new site there's an easy code addition for creating the redirect at the same time as the content is created. This method uses the code described in the post: Programatically Create 301 Redirects.

The Migrate framework calls the complete method when an entity, for example a node, is successfully created. Information on the complete method can be found in this documentation: Commonly implemented Migration methods.

The complete method is passed the entity created and the row data used to create it. This provides a place to create a redirect using the Redirect module.

In the following example, the old path is part of the row data. Depending on your situation this information could alternatively come from a pre-generated lookup or separate function call.

class ExampleMigration extends Migration {
 
 
//
  // Migratation code
  //

  /**
   * Definition for Migration complete() method
   */
  </span>function complete($entity, $row) {
   
// Create a redirect from the old path to the new one
   
if (isset($row->old_path)) {
     
// Create an object with our redirect parameters
     
$redirect = new stdClass();
     
$redirect->source = $row->old_path;           // From URL
     
$redirect->source_options = array();
     
$redirect->redirect = 'node/'. $entity->nid// To URL
     
$redirect->redirect_options = array();
     
$redirect->status_code = 0;                   // Redirect Status
     
$redirect->type = 'redirect';
     
$redirect->language = LANGUAGE_NONE;
   
     
// Create the redirect
     
redirect_save($redirect);
    }
  }
 
}
?>

Dec 29 2012
Dec 29

Submitted by Dale on December 28, 2012 - 11:10pm

The Drupal 7 Redirect module gives a site the ability to redirect from one URL to another using a proper HTTP redirect status (e.g., 301 - Permanently moved). In addition to the user interface there is a programmatic method for entering redirects.

The aptly named redirect_save function takes an object containing the details of the redirect and creates a redirect for the URL.

Here is an example of creating a redirect. A status code of 0 uses the default redirect setting which is 301, unless changed in settings.

  // Create an object with our redirect parameters
 
$redirect = new stdClass();
 
$redirect->source = 'the-old-url';     // From URL
 
$redirect->source_options = array();
 
$redirect->redirect = 'node/1';        // To URL
 
$redirect->redirect_options = array();
 
$redirect->status_code = 0;            // Redirect Status, 0 is default
 
$redirect->type = 'redirect';
 
$redirect->language = LANGUAGE_NONE;

  </span>// Create the redirect
 
redirect_save($redirect);
?>

Nov 14 2012
Nov 14

When it comes to commenting code there are two sides that often come up in discussion. On one side I’ll regularly hear that code itself is readable so there is no need to comment it. And, typically code does speak for itself. Some elements of code I like commented so I don’t have to think about them each time I scan them, like crazy regular expressions.

On the ofter side of the argument is that all code needs to be commented. In many discussions, including many I’ve contributed to, what and why code needs to be commented isn’t clear. And, the truth is the lines of code themselves don’t usually need to be commented. But, there is a lot that does need to be commented. Here I’ll touch on 3 types of code comments that have little to do with the lines of code themselves.

Why Does The Code Do What It Does?

Have you ever started to work on a project that was already well developed but had few or no comments? I worked on a codebase like that in the past year. I kept asking the question, why did they do it that way? In order to figure that out I had to bug (and in some cases annoy) people to figure it out. I didn’t want to change something important because of my newness and ignorance of the codebase I was working on.

Code comments that explain why something was done in a certain way are really helpful. That context you have in your head while writing code isn’t something the next person will have who touches the code. And, you might not remember it when you work on the code a year from now. Adding that context as code comments can be really helpful.

For example, take a case where code works against an API and while writing it you learn a nuance about the API. Will you always remember that nuance? Will the next person know it who hasn’t done the work you did? Adding a code comment explaining what’s happening can be very helpful in the future.

Another example is to document your assumptions and business rules. Sure, there might be a ticket number in the commit message where the code was added. But, how often do people look at code that way? If details about some assumption or reason are in a code comment it makes it much more clear.

References

Code comments should reference relevant documents, other code, specs, etc. If someone comes along later to work on the code it would be great if they could see the reference used and go read it themselves. It’s much easier and faster than trying to search the web for the right version of the right one. Or, if you go backand what it returns plus any nuance to using it. While the name of a method may tell me what it does that doesn’t mean I know what the inputs are and all possible returns.

If I think of code as a black box, which I often do, I want to know about the inputs and outputs. This is where header comments on functions and methods come in. They explain the inputs and outputs for those who don’t know or remember the internals and don’t need to read the code to use it.

This is popular enough that IDEs and some text editors are able to use these comments to help as we code on the fly.

In Practice

I’ve worked on a lot of code in the past couple years including both overly commented and code without comments. In practice having code comments explaining these three things has made my life a lot easier. And, the absence of them has made it made more difficult to work on a codebase meaning it’s taken more time as well.

Well commented code is just easier to work in.

Oct 27 2012
Oct 27

UPDATE: Now running Drupal 8. Info is outdated again!

Background: Read this older blogpost.

So I just made a new theme for my blog, and as it turns out, I got one extra HTTP request. I started using Font Awesome with the theme and the file is too big to be embedded as a base64 encoded font. Darnit. So up to 2 internal HTTP requests.

But anyway, I made a new direction in how to cache my css, and the result is way better for mobile.

CSS is looped through (with the core function drupal_load_stylesheet() and then is processed the same way core processes css with aggregation. This way no image paths gets messed up (and the same goes for the path for Font Awesome). Since this is heavy to do on each pageload, I cache the result of this processing, and in the page files I just print out a javascript that you can see if you view the source. The same goes for javascript, a different loop, but the same result. This is for example my CSS function:

 0) {
    // Skip this one, at least for my theme.
    continue;
  }
  // The following are nicked from Drupal core.
  $contents = drupal_load_stylesheet($stylesheet['data'], TRUE);

  // Build the base URL of this CSS file: start with the full URL.
  $css_base_url = file_create_url($stylesheet['data']);
  // Move to the parent.
  $css_base_url = substr($css_base_url, 0, strrpos($css_base_url, '/'));
  // Simplify to a relative URL if the stylesheet URL starts with the
  // base URL of the website.
  if (substr($css_base_url, 0, strlen($GLOBALS['base_root'])) == $GLOBALS['base_root']) {
    $css_base_url = substr($css_base_url, strlen($GLOBALS['base_root']));
  }

  _drupal_build_css_path(NULL, $css_base_url . '/');
  // Anchor all paths in the CSS with its base URL, ignoring external and absolute paths.
  $style .= preg_replace_callback('/url\(\s*[\'"]?(?![a-z]+:|\/+)([^\'")]+)[\'"]?\s*\)/i', '_drupal_build_css_path', $contents);
}

And then to the javascript. It is dynamically printed on cache flush, so just view the source to see what it looks like. So what the script does is do a check if you have the css or js for my page cached in localStorage. It is stored inside an object with a key, so I can clear the cache and invalidate your localstorage cache. The key is generated for each flush cache action, with the hook_flush_caches() hook. If you don't have my assets, the CSS is loaded by AJAX, and I do a synchronous GET for the js (don't get me started on why it is synchronous). So how do I avoid showing the page without styling while waiting for the css? Simple. In the head of the page there is a style tag that says #page should be display none. When my css loads, it has rules saying i should show the #page. Done.

So then you think "Man, all this foreach stuff clutters up the template file". Nope. This is the actual lines from html.tpl.php:

print client_cache_styles($css);
print client_cache_scripts($scripts);

"And what's with the non-jquery javascript syntax?" you may ask. Naturally this is because the same code also loads my javascripts, and so jquery is not included at that point in the code. And also, writing native javascript is fun.

So this makes for 4 internal HTTP requests on first pageload, and 2 after that.

Expanding this, I also used the same trick on the $page variable, so the entire page was loaded with ajax (or localstorage, if available). So I actually got a full-featured, full styled CMS in a couple of KB and 2 HTTP requests. This has some side effects, obviously, so I skipped that one on the live page.

The module client_cache will probably be made available one of these days, if I get around to it. But it's probably not a good idea for most sites!

Let's finish off with a banana musician!

Oct 27 2012
Oct 27

Where I work, we have some bigger clients where we have some advanced integration systems with their accounting systems, stock control, exports to old windows systems, and the list goes on. So these things are not something we want to (or in many cases can) run on the dev version of the site.

To keep things still in version control, and not having to turn things off when dumping in a fresh database copy, we use the $conf variables in settings.php

The file settings.php is not checked in in git, and this is also where we automatically turn off js and css aggregation, by for example setting

$conf['preprocess_css'] = 0;

And some other stuff. But we also add our own variable.

$conf['environment'] = 'development';

This way we can do a check in the top of all integration functions:

So keeping this in our production code, ensures that integration functions are not run when developing on a dev site. Also, a lot of cron functions are moved to a separate drush command, and run by the server cron instead of implementing hook_cron(). This will then never be run on the dev site.

I am sure everyone has their own way of doing similar stuff, so please feel free to share your similar tricks, or improvements, in the comments.

I guess this calls for a transport related animated gif:

Oct 17 2012
Oct 17

Understanding who the target audiences for a product are can go a long way in understanding when and where you should use a product. It sets up questions like, what features do these target audiences need? And, how well does the implementation in a product meet that need? Or, are these the target audiences I need to satisfy?

On the flip side, when developing a product it is helpful to understand target audiences. You can look to see what features a target audience needs and evaluate how well multiple options meet that need for that audience.

I’ve recently started to ask these questions about Drupal. As a popular CMS that is used to power a significant number of sites this is a topic I expected someone would have dove into already. And, while I’ve found a number of opinions on the topic I did not find much of an overview. So, after a little digging here is what I found.

Before we dive into the target audiences I would like to note that this is an attempt to document where Drupal is at today and moving towards. This is not an attempt to cover the past and I’m trying not to give my opinion but instead document what’s happening.

If you’re looking for opinions on the topic of who Drupal should target you can read what Leisa Reichelt and Larry Garfield have written.

Content Readers

This is the largest target for Drupal and the one I think I’ve read the least about being targeted. When we’re creating Drupal and using Drupal to build sites we have to keep this group in mind. There’s no reason to publish content if you aren’t trying to get people to read it, right?

There an easy place to see this in Drupal as a target. Look at how there is a Drupal 8 initiative for mobile. This recognizes the migration of content consumers to different devices. While there are definitely parts of this that play well for other audiences it very nicely targets this audience.

Content Authors and Editors

Drupal is a CMS and someone has to write and manage content for readers. I think this audience has been highlighted through efforts like WYSIWYG in core among numerous other things. Note, if you want to help with WYSIWYG in core there is an issue for it.

Admins

I’m calling Admins the people administer the site. They handle module and core updates. They are the ones who may create the user accounts, manage mail settings, etc.

Drupal has long targeted this group. In Drupal 7 the built in update manager is one example. How Drupal has a pattern of settings being stored and changed through the UI making it easy for admins is another.

Site Builders

When I started using Drupal this target didn’t appear on the radar. This target has now become one of the most significant and power that Drupal targets. Through the development of modules like CCK (now fields in core), Views, and several others Drupal has put the power to build features, data models, content displays, and more in the hands of people with ideas without requiring them to learn to program. This is a very powerful idea and one Dries has said openly Drupal is targeting.

Developers

Developers is an easy group to describe, right? In all seriousness developers is the most controversial group where the Drupal community has the strongest opinions in my experience. Since developers is really a mix of sub-groups I wanted to try and document those.

Front End Developers and Themers

Drupal is targeting front end developers in an interesting way. There are really two groups of these being targeted right now. The more well known of these is typically called themers. Themers are a group of people who can map a design to the Drupal theme (templating) system. They know CSS, how to create templates in the Drupal way, how to override default Drupal markup, etc.

Then there are front end developers from the web at large. In my past with Drupal I’ve not really noticed these front end developers being targeted. With some of the contrib work in Drupal 7 and some of the efforts going into Drupal 8 I see this group starting to rise up. Some of the decisions being made in the development of Drupal 8 is with this group in mind.

I will personally be interested to see how these slightly different groups who do the same types of things work out as target audiences through some of the changes Drupal is making right now.

Professional Programmers

Drupal 8 has definitely moved into the realm of professional programmers. It can be seen in how Drupal has added more complex programming concepts. This should not be a surprise since lots of core work comes from consulting companies, companies that have services built on Drupal, and so on. Lots of professional programmers are working on Drupal. Because of this the target audience of programmers is moving to people who can work with these more complex concepts.

Now, I’m not going to go into what a professional programmer has in their expectations other than to note Drupal is being driven by programmers who do it professionally.

Also note, I do consider there to be a difference between software engineering and programming which is not lost here. But, the difference between software engineering and programming is best left for another post.

DevOps

The popularity of Drupal has lead it to being used on more complex deployments and the rise of DevOps has put it on a lot of radars. Drupal has started to show it is targeting this group through things like the new core release schedule and conference tracks for DevOps.

A Group Drupal Is No Longer Targeting

When I first started using Drupal it was something a weekend warrior programmer could jump in and work with. I know a number of core developers who jumped in this way and eventually became professional programmers because of this. But, over time the ways Drupal is being used have changed, the people contributing to core have changed, the use cases Drupal is being applied to solve have changed, and the people who have been around driving things have themselves changed.

Drupal no longer targets the weekend warrior programmer. In a subtle nuance, much of what those programmers used to do (and what I did when I started using Drupal) can be accomplished through site building.

This doesn’t mean weekend warriors are no longer a target. It doesn’t mean that weekend warrior programmers are still not around. It just appears that they are no longer a target audience in practice.

Oct 16 2012
Oct 16

When I first started using jQuery I was pulled into the pattern of using anonymous functions and closures. After learning how to properly use these language features (as opposed to overusing them which can lead to untestable code) I wanted to use them in PHP. Fast forward several years and they are available in all supported PHP versions.

QueryPath, put simply, is jQuery written in PHP for server side manipulations. Now that I can easily use anonymous functions and closures I can write and read functionality that looks more like the jQuery I’ve been writing for years.

Anonymous Functions and QueryPath

Before PHP 5.3 was around a form of anonymous (a.k.a. lambda) functions were around through the use of create_function(). Unfortunately, this method had a number of drawbacks making it something I avoided using.

In PHP 5.3 anonymous functions were introduced in a familiar manner. I find the easiest way to look at these is by example.

$foo = function($bar) {
  print $bar . "\n";
}

$foo('Hello World');

Now, let’s apply this style function to QueryPath.

$doc = qp($file);

$doc->find('.foo')
  ->each(function($index, $item) {
  
    // $item is a DOMElement
    $bar = qp($item);
  
    // Do something to each item in here.
  });

The each method can accept a number of things. This can include a callback function, an anonymous function, or anything that’s callable.

In this case using an anonymous function turns out to be fast. In my case and tests it ran faster than a normal callback function.

Closures and QueryPath

According to Wikipedia:

A closure is a function or reference to a function together with a referencing environment

The existing environment is a really important part here. Let’s look at an example.

function get_iterator($foo) {

  $bar = $foo * 2;
  
  return function ($index, $item) use ($bar) {
    // Do something here.
    
    // $bar is available from the parent environment.
  };
}

$a = get_iterator(2); // $bar is available when $a() is called as 4
$b = get_iterator(4); // $bar is available when $b() is called as 8

$doc = qp($file);
$doc->find('.foo')
  ->each($a);  // $bar inside $a() is available as 4.

The environment from get_iterator() is available to the function it returns even outside the original context. It’s important to note that this is the environment at the time get_iterator() was called to create the function.

There are many ways closures can be used. For example, they can help setup an environment for anonymous functions to be used and passed around.

QueryPath and Drupal

The context for my QueryPath code has been within Drupal and there is a module for that.

Pages

About Drupal Sun

Drupal Sun is an Evolving Web project. It allows you to:

  • Do full-text search on all the articles in Drupal Planet (thanks to Apache Solr)
  • Facet based on tags, author, or feed
  • Flip through articles quickly (with j/k or arrow keys) to find what you're interested in
  • View the entire article text inline, or in the context of the site where it was created

See the blog post at Evolving Web

Evolving Web