drupal

Drupal Migrate: Don't forget to de register your classes

Often times when writing a module to migrate content leveraging the Migrate module you will find yourself disabling and re enabling your module as you add classes to it.
When you do this you might find that you get errors about classes not being found or that your migrations are still being displayed in the interface even though your module is disabled (when using a class from another module such as d2d_migrate).
To get around this, make sure you de register your migration classes in a HOOK_disable() function in your module.

 function MYMODULE_disable() { 
Migration::deregisterMigration('my_role_migration');
Migration::deregisterMigration('my_user_migration');
}

Fixing drush * needs a higher bootstrap level to run

Today while setting up a Drupal dev environment on a new PC I encountered the drush error "Command * needs a higher bootstrap level to run".
The first thing I did as many probably would is do a quick search on google. This though yields many different results and fixes. The problem though is I didn't know why it was happening so I didn't know which fix would work.
Turns out though it was quite easy to determine the actual problem. Drush has a nice option that will output dubug messages, funny enough, the option is --debug .
So, to get a better understanding of why you might be getting the "Command * needs a higher bootstrap level to run", simple enter this drush command: drush pm-list --debug and run through the output looking for what might be causing the problem.
In my case, I had a line that read "mysql is not recognised as an internal or external command", suggesting to me that my environment variable for mysql was not set up properly.

Stopping Drupal spam registrations without an annoying captcha

There are many modules out there to prevent spam registrations in Drupal, but many of them involve some sort of Captcha, which I hate, or a paid for service. Stopping fake site registrations should not make it harder for the legitimate people trying to register on your site, and having to pay to prevent spam does not sit well with me.
While Drupal does have a setting to "Require email validation", I do usually have this enabled, but it does not stop the actual registration process and your user table will increase in size pretty quickly, also, some bots seems to be able to get around this too.
Here is a list of the modules I enabled to stop spam registrations to great effect.

Include a honeypot field

The honeypot module is a great module to implement as your first line of defense. It adds a hidden field to your forms, that when a value is added, the form will not submit.
It also has a "Honeypot time limit" setting that is the "Minimum time required before form should be considered entered by a human instead of a bot.". Not though, that using this setting will disable page cache for whatever page that the form on, As on many of my sites have the registration form on all/most pages I usually set this to 0 effectively disabling it so the pages still cache.

Block certain username or email patterns

Often spam registrations have email addresses at a few common domains and you can stop this by enabling the user restrictions module , but this may not be enough and maybe you want to check the username field for a pattern instead. Enter the Regex Registration Deny module , This is a module...

Removing the entity ID from Drupal entity reference fields

On a site I am currently working on I have a content type that includes an entity reference field. When creating a node, the entity reference autocomplete includes the entity id in the field after selecting the entity desired. This can be confusing for the user.
When trying to find a solution to this problem I came across many issues and different proposed solutions including a sandbox module called Entity reference trim .

I tried the entity reference trim module but it has problems when the form was submitted and had errors.
Taking a closer look into this module I could see that it was altering the widget (adding a hidden field) and overriding some of the core Drupal js to do with the autocomplete functionality. While poking around the autocomplete javascript I noticed that the autocomplete fires a 'autocompleteValue' event.
Using this event I could change the entity reference field value and store it, then change it back when the form is submitted.

Below is the code that I am using but be aware that I have not yet tested it extensively and not tested it with multiple entity reference fields. Also, as the event bubbles, a more generic version could probably be coded.

NOTE: you will have to change the jQuery selector.

 (function ($) { 
Drupal.behaviors.damnId = {
attach: function (context, settings){
// Get the entity reference input
$eref = $('#edit-field-postcode-suburb-und-0-target-id', context);
if($eref.val()){
// If field has value on page load, change it.
var val = $eref.val();
var match = val.match(/\((.*?)\)$/);
$eref.data('real-value', val);
$eref.val(val.replace(match[0], ''));
}
// Listen for the
...

Drupal taxonomies pre configured and ready for import

Many times in the past I have wanted a taxonomy structure for things like car makes and models or Australian States and Suburbs and find myself recreating them every time. So today I decided its probably better to have them stored somewhere. Luckily the Taxonomy Manager module easily allows you to export and import taxonomy data easily. I know there are other options for importing and exporting taxonomies, but I found this one is probably the easiest.
Here is a few for you to use, and for my own use late :)

Simple list of car Makes and Models
 Abarth 
-500
-595
-695
-Other
Alfa Romeo
-147
-156
-159
-164
-166
-33
-75
-90
-Alfasud
-Alfetta
-Berlina
-Brera
-Giulia
-Giulietta
-GT
-GTV
-MITO
-Montreal
-Spider
-Sprint
-Other
Armstrong Siddeley
-Star Sapphire
-Other
Aston Martin
-AM
-DB4
-DB5
-DB6
-DB7
-DB9
-DBS
-Lagonda
-Rapide
-V12
-V8
-Vanquish
-Vantage
-Virage
-Volante
-Other
Audi
-100
-200
-5+5
-5E
-80
-90
-A1
-A3
-A4
-A5
-A6
-A7
-A8
-Allroad Quattro
-Cabriolet
-Fox
-Q3
-Q5
-Q7
-R8
-RS Q3
-RS4
-RS5
-RS6
-RS7
-S1
-S2
-S3
-S4
-S5
-S6
-S7
-S8
-SQ5
-TT
-V8 quattro
-Other
Austin
-1800
-A 40
-A 60
-A 99
-Freeway
-Kimberley
-Lancer
...

Drupal views exposed form in a bootstrap modal

I am currently working on a site with the Bootstrap theme which has a fullscreen views map ( Goomap ) with exposed filters. As the map is full screen I want to have the exposed filters inside a bootstrap modal. Luckily it is quite easy to theme the views exposed filters form.

To do this you have to override the views exposed filters form theme file. Copy the views theme file (sites/all/modules/views/theme/views-exposed-form.tpl.php) and place the copy in your own theme folder. To get this to work you may have to click on "Rescan template files" under "theme" in the view. Your site should now be using the new template file and you can edit it as you please. You can get more specific with the naming of the template file to target a single exposed form, (views-exposed-form--VIEWNAME.tpl.php for example).

Below is the code that I am using in my views-exposed-form.tpl.php

 <a data-toggle="tooltip" title="<?php print t('Filter Results'); ?>"> 
<button class="btn btn-default btn-sm" data-toggle="modal" data-target="#filterModal">
<i class="glyphicon glyphicon-cog"></i>
Filter
</button>
</a>

<div class="views-exposed-form">
<div class="modal fade" id="filterModal" tabindex="-1" role="dialog" aria-labelledby="filterLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title" id="filterLabel">Filter</h4>
</div>
<div class="modal-body">

<div class="views-exposed-widgets clearfix">
<?php foreach ($widgets as $id => $widget): ?>
<div id="<?php print $widget->id; ?>-wrapper" class="views-exposed-widget views-widget-<?php print $id; ?>">
<?php if (!empty($widget->label)): ?>
<label for="<?php print $widget->id; ?>">
<?php print $widget->label; ?>
</label>
<?php endif; ?>
<?php if (!empty($widget->operator)): ?>
<div class="views-operator">
<?php print $widget->operator; ?>
...

Not another Drupal mapping module: Goomap

Yes, I have done it, created yet another Drupal mapping module. I call him Goomap .

The first thing you are probably going to ask is "why?". Well, here's why:

  • Complexity
    Most of the time when trying to use one of the other Drupal mapping modules I would find the process far too complex. Especially the last time I used the Openlayers module. I wont go into detail about this but if you have used it you will know what I mean. Every time I have needed maps on a drupal site over the past couple of years I have found it easier to just use the Google maps api itself and not even bother with a drupal mapping module.
  • Map provider support
    Many people may consider this a step backward as the Goomap module only supports Google maps where as other mapping modules support other map providers. But I have only ever used Google maps as it has always served my purpose and the documentation is fantastic. So why support other map providers if I (and probably many others) never use them?
  • Map color customization
    Now I'm not exactly sure on how to customize the colors on the other modules, but with the Goomap module it is quite easy because the goomap module gives you access to the mapOptions object that is send to the Map constructor. The mapOptions object has a "styles" property which accepts an array which represents the styles (colors etc.) that the map will have. This "styles" array can simply be pasted into the mapOpions textarea in the map settings. You can easily generate and copy this JSON array from sites such as https://snazzymaps.com/
  • ...

Drupal 7 Bootstrap image field upload widget

When using the Bootstrap theme in Drupal 7, the image upload widget leaves a lot to be desired. I looks pretty crappy
Below is how I overcame this.
It should probably work with file fields too but instead of overriding THEME_image_widget, you would override THEME_file_widget, though I haven't tried it yet.

First, add this to your css

 .btn-file { 
position: relative;
overflow: hidden;
}

.btn-file input[type=file] {
position: absolute;
top: 0;
right: 0;
min-width: 100%;
min-height: 100%;
font-size: 100px;
text-align: right;
filter: alpha(opacity = 0);
opacity: 0;
outline: none;
background: white;
cursor: inherit;
display: block;
}

Then override the theme_image_widget in your template.php
A note on admin theme
If your upload widget is on a node edit page (or any admin page) the active theme will be your admin theme. This means the code will need to go into your admin themes template.php and the css in your admin themes css.

 function MYTHEME_image_widget($variables) { 
$element = $variables['element'];
$output = '';
$output .= '<div class="image-widget form-managed-file clearfix">';

if (isset($element['preview'])) {
$output .= '<div class="image-preview">';
$output .= drupal_render($element['preview']);
$output .= '</div>';
}

$output .= '<div class="image-widget-data">';
if ($element['fid']['#value'] != 0) {
$element['filename']['#markup'] = '<div class="form-group">' . $element['filename']['#markup'] . ' <span class="file-size badge">' . format_size($element['#file']->filesize) . '</span></div>';
}
else {
$element['upload']['#prefix'] = '<div class="input-group"><span class="input-group-btn"><span class="btn
...

Disable taxonomy term delete button if term has nodes.

The below snippet will disable the delete button on the taxonomy term edit page if the term has any nodes assigned to it.
It implements hook_form_FORM_ID_alter()

 function hook_form_taxonomy_form_term_alter(&$form, &$form_state) { 
$query = db_select('taxonomy_index', 't');
$query->condition('tid', $form_state['term']->tid, '=');
$query->join('node', 'n', 't.nid = n.nid');

$count = $query->countQuery()->execute()->fetchField();
if ($count) {
$form['actions']['delete']['#disabled'] = TRUE;
$form['actions']['delete']['#suffix'] = "<span>$count nodes, cant delete</span>";
}
}

Drupal Domain Access user 1 menu

Today when developing a Drupal site with the Domain Access module I had an interesting problem.
My site is set up with a custom theme and 2 domains.
My custom theme is using Zurb Foundation and so I had to override the main menu theme functions to add the Zurb classes.
This was easily done by implementing theme_links__system_main_menu, theme_menu_tree__main_menu__submenu and theme_menu_link__main_menu as outlined below (my theme name is nexus).

 function nexus_links__system_main_menu($variables) { 
$links = menu_tree_output(menu_tree_all_data(variable_get('menu_main_links_source', 'main-menu')));
$output = drupal_render($links);
return $output;
}

function nexus_menu_tree__main_menu__submenu($variables) {
return '<ul class="menu dropdown">' . $variables['tree'] . '</ul>';
}

function nexus_menu_link__main_menu($variables) {
$element = $variables['element'];
$sub_menu = '';
if ($element['#below']) {
$element['#below']['#theme_wrappers'][] = array('menu_tree__main_menu__submenu');
$sub_menu = drupal_render($element['#below']);
$element['#attributes']['class'][] = 'has-dropdown';
}
if (isset($element['#href']) && ($element['#href'] == $_GET['q'] || ($element['#href'] == '<front>' && drupal_is_front_page())) && (empty($element['#language']) || $element['#language']->language == $language_url->language)) {
$element['#attributes']['class'][] = 'active';
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . "</li>\n";
}

The problem was that all node links assigned to the main menu would show up if you were logged in as user 1, even if they were not assigned to the current domain. Though, if you were not logged in the menu would appear correct and only show links assigned to the current domain.
This is obviously the correct behavior as the domain access module uses Drupal's node access system and the access system is bypassed for user 1.
At this point I could have switched...