Adding a pager to browse between nodes

For anyone in need of a node-to-node pager that respects domain assignment and a content’s language with the added benefit of setting your own method of sorting, check out the Free Pager module

Free pager creates a new views display type, which can be configured to any liking (for me that is filtering only nodes that are available on the current domain and in the currently active language). The pager itself can then simply be placed anywhere via block configuration settings.

Finally a working pager that can be customized in any imaginable way 🙂

Adding body classes to Drupal depending on whether a region is rendered

Here’s a simple way to add body classes to your theme in dependence of the visibility of a region. The variables array shown here is specific to the Omega theme, normally it’s something like $variables['classes_array'][]

Just put the following code into your theme’s template.php:

function MYTHEME_preprocess_html(&$variables) {
  // Add a body class if first sidebar is not being rendered
  if(!isset($variables['page']['content']['content']['sidebar_first'])) {
    $variables['attributes_array']['class'][] = 'no-sidebar-first';
  } else {
    $variables['attributes_array']['class'][] = 'sidebar-first';
  }
}

Make CarrierWave respect Exif orientation

# config/initializers/carrierwave_initializer.rb
module CarrierWave
  module RMagick
    # Rotates the image based on the EXIF Orientation
    def fix_exif_rotation
      manipulate! do |img|
        img.auto_orient!
        img = yield(img) if block_given?
        img
      end
    end
  end
end

With this initializer we can add process :fix_exif_rotation to our uploader model.

Found in this great blog post by Mario Visic, which, by the way, also shows how to change jpeg quality.

Drupal Commerce: Shopping Cart Block that shows up only when it’s not empty

Normally a Drupal Commere Shopping Cart block is shown whether a user has put something inside it or not. Thanks to fonant here’s a view that displays a Drupal Commerce Shopping Cart block only when there’s been something put inside.

<?php
$view = new view();
$view->name = 'commerce_cart_block';
$view->description = 'Display a list of line items added to cart.';
$view->tag = 'commerce';
$view->base_table = 'commerce_order';
$view->human_name = 'Shopping cart block';
$view->core = 0;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */

/* Display: Defaults */
$handler = $view->new_display('default', 'Defaults', 'default');
$handler->display->display_options['title'] = 'Shopping cart';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'none';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['query']['options']['query_comment'] = FALSE;
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'none';
$handler->display->display_options['style_plugin'] = 'table';
$handler->display->display_options['style_options']['columns'] = array(
  'quantity' => 'quantity',
  'commerce_display_path' => 'commerce_display_path',
  'line_item_title' => 'line_item_title',
  'commerce_total' => 'commerce_total',
);
$handler->display->display_options['style_options']['default'] = '-1';
$handler->display->display_options['style_options']['info'] = array(
  'quantity' => array(
    'sortable' => 0,
    'default_sort_order' => 'asc',
    'align' => '',
    'separator' => '',
  ),
  'commerce_display_path' => array(
    'sortable' => 0,
    'default_sort_order' => 'asc',
    'align' => '',
    'separator' => '',
  ),
  'line_item_title' => array(
    'align' => '',
    'separator' => '',
  ),
  'commerce_total' => array(
    'sortable' => 0,
    'default_sort_order' => 'asc',
    'align' => 'views-align-right',
    'separator' => '',
  ),
);
/* Footer: Commerce Line Item: Line item summary */
$handler->display->display_options['footer']['line_item_summary']['id'] = 'line_item_summary';
$handler->display->display_options['footer']['line_item_summary']['table'] = 'commerce_line_item';
$handler->display->display_options['footer']['line_item_summary']['field'] = 'line_item_summary';
$handler->display->display_options['footer']['line_item_summary']['links'] = array(
  'view_cart' => 'view_cart',
  'checkout' => 'checkout',
);
$handler->display->display_options['footer']['line_item_summary']['info'] = array(
  'quantity' => 'quantity',
  'total' => 'total',
);
/* Relationship: Commerce Order: Referenced line item */
$handler->display->display_options['relationships']['commerce_line_items_line_item_id']['id'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['relationships']['commerce_line_items_line_item_id']['table'] = 'field_data_commerce_line_items';
$handler->display->display_options['relationships']['commerce_line_items_line_item_id']['field'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['relationships']['commerce_line_items_line_item_id']['required'] = TRUE;
/* Field: Commerce Line Item: Quantity */
$handler->display->display_options['fields']['quantity']['id'] = 'quantity';
$handler->display->display_options['fields']['quantity']['table'] = 'commerce_line_item';
$handler->display->display_options['fields']['quantity']['field'] = 'quantity';
$handler->display->display_options['fields']['quantity']['relationship'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['fields']['quantity']['label'] = '';
$handler->display->display_options['fields']['quantity']['precision'] = '0';
$handler->display->display_options['fields']['quantity']['suffix'] = ' × ';
/* Field: Commerce Line item: Display path */
$handler->display->display_options['fields']['commerce_display_path']['id'] = 'commerce_display_path';
$handler->display->display_options['fields']['commerce_display_path']['table'] = 'field_data_commerce_display_path';
$handler->display->display_options['fields']['commerce_display_path']['field'] = 'commerce_display_path';
$handler->display->display_options['fields']['commerce_display_path']['relationship'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['fields']['commerce_display_path']['label'] = '';
$handler->display->display_options['fields']['commerce_display_path']['exclude'] = TRUE;
/* Field: Commerce Line Item: Title */
$handler->display->display_options['fields']['line_item_title']['id'] = 'line_item_title';
$handler->display->display_options['fields']['line_item_title']['table'] = 'commerce_line_item';
$handler->display->display_options['fields']['line_item_title']['field'] = 'line_item_title';
$handler->display->display_options['fields']['line_item_title']['relationship'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['fields']['line_item_title']['label'] = '';
$handler->display->display_options['fields']['line_item_title']['alter']['make_link'] = TRUE;
$handler->display->display_options['fields']['line_item_title']['alter']['path'] = '[commerce_display_path]';
/* Field: Commerce Line item: Total */
$handler->display->display_options['fields']['commerce_total']['id'] = 'commerce_total';
$handler->display->display_options['fields']['commerce_total']['table'] = 'field_data_commerce_total';
$handler->display->display_options['fields']['commerce_total']['field'] = 'commerce_total';
$handler->display->display_options['fields']['commerce_total']['relationship'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['fields']['commerce_total']['label'] = '';
$handler->display->display_options['fields']['commerce_total']['element_class'] = 'price';
$handler->display->display_options['fields']['commerce_total']['element_label_colon'] = FALSE;
$handler->display->display_options['fields']['commerce_total']['click_sort_column'] = 'amount';
$handler->display->display_options['fields']['commerce_total']['type'] = 'commerce_price_formatted_amount';
/* Sort criterion: Commerce Line Item: Line item ID */
$handler->display->display_options['sorts']['line_item_id']['id'] = 'line_item_id';
$handler->display->display_options['sorts']['line_item_id']['table'] = 'commerce_line_item';
$handler->display->display_options['sorts']['line_item_id']['field'] = 'line_item_id';
$handler->display->display_options['sorts']['line_item_id']['relationship'] = 'commerce_line_items_line_item_id';
/* Contextual filter: Commerce Order: Order ID */
$handler->display->display_options['arguments']['order_id']['id'] = 'order_id';
$handler->display->display_options['arguments']['order_id']['table'] = 'commerce_order';
$handler->display->display_options['arguments']['order_id']['field'] = 'order_id';
$handler->display->display_options['arguments']['order_id']['default_action'] = 'default';
$handler->display->display_options['arguments']['order_id']['default_argument_type'] = 'commerce_cart_current_cart_order_id';
$handler->display->display_options['arguments']['order_id']['summary']['number_of_records'] = '0';
$handler->display->display_options['arguments']['order_id']['summary']['format'] = 'default_summary';
$handler->display->display_options['arguments']['order_id']['summary_options']['items_per_page'] = '25';
/* Filter criterion: Commerce Line Item: Line item is of a product line item type */
$handler->display->display_options['filters']['product_line_item_type']['id'] = 'product_line_item_type';
$handler->display->display_options['filters']['product_line_item_type']['table'] = 'commerce_line_item';
$handler->display->display_options['filters']['product_line_item_type']['field'] = 'product_line_item_type';
$handler->display->display_options['filters']['product_line_item_type']['relationship'] = 'commerce_line_items_line_item_id';
$handler->display->display_options['filters']['product_line_item_type']['value'] = '1';
/* Filter criterion: Commerce Order: Order status */
$handler->display->display_options['filters']['status']['id'] = 'status';
$handler->display->display_options['filters']['status']['table'] = 'commerce_order';
$handler->display->display_options['filters']['status']['field'] = 'status';
$handler->display->display_options['filters']['status']['value'] = array(
  'cart' => 'cart',
  'checkout_checkout' => 'checkout_checkout',
);

/* Display: Shopping Cart Block */
$handler = $view->new_display('block', 'Shopping Cart Block', 'block_1');
$handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
$handler->display->display_options['block_description'] = 'Shopping Cart Block (not when empty)';
?>

Source

Multilingual Multisite with Drupal and Domain Access

When it comes to multisite Drupal installations Domain Access is my module of choice. For multilingual sites i18n is the way to go. Bringing those two rather complex modules together can be quite a hassle. In order to set different system settings (such as default front page, site name and slogan) per domain and per language you need to install Domain Variable. Unfortunately there’s no real documentation to tell you that Domain Variable will not work as expected as long as you’ve got the Domain Settings submodule enabled. As soon as you disable that you’ll be shown those per domain and per language settings on the forms you’ve enabled inside Domain Variable settings.

Create a select list from Active Record Object in Rails 3

Build the select form in your view…

<% @categories = Category.find(:all, :order=>"title") %>
<%= collection_select(:service, :category_id, @categories, :id, :title) %>

And access the selected value from the controller (inside the create and update methods):

@service.category_id = params[:service][:category_id]

Of course category_id needs to be made accessible in the services model.

Prevent CKEditor from creating empty paragraphs upon pasting rich text

When using CKEditor with Drupal more often than not annoying empty paragraphs <p>&nbsp;</p> slip into your article when pasting formatted text. To prevent this stupid behavior it turns out you can set config.autoParagraph = false in CKEditor’s configuration at sites/all/modules/ckeditor/ckeditor.config.js. Of course it’s not a good idea to hack this part of the module directly, so I’m still gonna have to look for a way to set that option independently (I guess there even is an option available on the CKEdtior configuration page that allows to load a customized config file).

Anyways, I needed to get rid of those pesky paragraphs ASAP so here’s the relevant part of my ckeditor.config.js

CKEDITOR.editorConfig = function(config) {
    config.autoParagraph = false;
}

In case you’re worried: this does not keep CKEditor from inserting paragraphs upon hitting the enter key.

Update:

If you don’t want to be evil and hack ckeditor.config.js (and you don’t want!) directly in the modules directory you can provide your ckeditor settings with the path to a custom configuration file:

CUSTOM JAVASCRIPT CONFIGURATION:
config.customConfig = '/sites/all/themes/mytheme/ckeditor.config.js';

If you don’t use an admin theme you can alternativly simply set the option LOAD CKEDITOR.CONFIG.JS FROM THE THEME PATH to yes, but keep in mind that this option won’t work with an admin theme!

Remove unwanted CSS from a library out of Drupal 7

Libraries that get implemented using the Libraries module sometime bring their own stylesheets which you might not want at all. Instead of overriding each selector you can unset the entire stylesheet as follows:

function mytheme_css_alter(&$css) { 
    unset($css[libraries_get_path('flexslider') . '/flexslider.css']); 
} 

Sticky Footer with Omega Theme for Drupal

As I keeping going back and searching the d.o. issue queue for those few lines constantly I’ll put them up here for future reference:

html, body {
  height: 100%;
}

#page {
  min-height: 100%;
  position: relative;
}

#section-content {
  padding-bottom: XX;
}

#section-footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: XX;
}

Pure CSS, no template changes needed, works with Omega right out of the box.

Origin:

Using Drush to install Drupal

This requires the drupal files to be present (for example by previously executing a drush make script).

drush site-install standard --locale=de --db-
url=mysql://dbuser:dbpass@dbhost/dbname --account-name=admin --
account-pass=drupalpassword site-name=DrupalSiteName site-
mail=hello@mail.me

Drupal: Packaging Content Types with Features while keeping Field Groups intact

Disclaimer: I have no idea why this works, but at least it does.

It seems like a good idea to package complex Drupal content types into reusable packages using the great features module. One fundamental part of content types are field groups. While field groups seem to be detected nicely as dependencies when creating a feature they just seem to never appear when dropping the feature into another site.

After digging around the forums I found out that adding the following code inside the function myfeature_ctools_plugin_api() (located inside myfeature.features.inc) did the trick:

if ($module == 'field_group' && $api == 'field_group') {
    return array('version' => 1);
}

So the implementation of hook_ctools_plugin_api currently looks like this (due to using display suite and responsive images and styles):

/**
 * Implements hook_ctools_plugin_api().
 */
function myfeature_ctools_plugin_api() {
  list($module, $api) = func_get_args();
  if ($module == "ds" && $api == "ds") {
    return array("version" => "1");
  }
  list($module, $api) = func_get_args();
  if ($module == "resp_img" && $api == "default_resp_img_suffixs") {
    return array("version" => "1");
  }
  if ($module == 'field_group' && $api == 'field_group') {
    return array('version' => 1);
  }    
}

Here’s the issue where I got this from and where – hopefully – things will get resolved: http://drupal.org/node/1619154

Drush Make on Domainfactory Hosting

Shortly before going completly mad I got this stuff working.
By default Domainfactory runs php4.4.9-cgi when simply calling php on the command line. As Drush needs PHP5 I used to help myself by aliasing the drush command to call something like php5-53STABLE-CLI drush.php through my .bashrc file.

Turned out that wile this works more or less well for Drush itself it doesn’t cut it for drush make. Long story short, there’s a DRUSH_PHP variable. Once that gets fed with the path to a somewhat current version of PHP5 everything works well. Also, I don’t have to call drush.php anymore. So here’s my .bashrc:

alias drush="~/drush/drush"
export DRUSH_PHP=/usr/local/bin/php5-53STABLE-CLI

Happy Making!

Update:
Somehow this stopped working recently, the following Alias does work though:

alias drush='/usr/local/bin/php5-53LATEST-CLI ~/drush/drush.php --php=/usr/local/bin/php5-53LATEST-CLI'

And here’s a different approach that works by telling Drush directly which version of PHP to use: Link

Styling input type=”search”…

When you use a input of type search and try to style it via CSS you might go crazy upon all your fancy decoration not displaying properly. Even custom borders won’t, at least on webkit browsers.

It’s easy to fix by applying the appearance property on the selector matching your input field. This will destroy the browser’s default styling:

-moz-appearance: none;
-webkit-appearance: none;

Update: and the button to reset the form value can be removed, too:

input[type=search]::-webkit-search-cancel-button {
     display: none;
}