Drupal Planet Posts

New contrib module: Info Plugins

Today I would like to introduce a new module that I released for the Drupal (developer) community to use and share: Info Plugins. This is for developers who are tired of implementing a bunch of messy _info hooks (and their corresponding callbacks), lumping together functionality that frankly shouldn't even touch. I'm talking about those ugly switch statements in which we have cases for the various extensions we provide. Don't get me wrong Drupal, I love your hook system.

With Info Plugins, much (and progressively more and more) of this becomes cleaner by exposing all those extension points to the cTools plugin system. What I mean by this is that using this module you will be able to declare things like Drupal blocks, filters, field formatters and more, as cTools plugins. This way, all the logic related to one plugin (block, filter or field formatter to continue with the above examples) resides neatly within one single plugin include file.

Let us look at an example a bit more in depth: the Drupal block. Instead of implementing up to 4 hooks (_info, _view, _configure and _save) to manage your blocks, you can now define a new core_block plugin within your cTools plugins folder like so:

$plugin = array(
  'title' => t('Example block'),
  // Your own callback for building the display of the block (called by `hook_block_view()`)
  // Leaving this empty, the function name would default to `my_module_example_block_view()`
  'view' => 'my_module_render_example_block',
  // Your own callback for building the config form for the block (called by `hook_block_configure()`)
  // Leaving this empty, the function name would default to `my_module_example_block_configure()`
  'configure' => 'my_module_configure_example_block',
  // Your own callback for saving the config for the block (called by `hook_block_saved()`)
  // Leaving this empty, the function name would default to `my_module_example_block_save()`
  'save' => 'my_module_save_example_block',
  // ... all other options you may want to pass to `hook_block_info()`
);

/**
 * Returns a renderable array that represents the block content, i.e. 
 * the same as you would return from `hook_block_view()`.
 *
 * @param $delta
 *   The block delta
 */
function my_module_render_example_block($delta) {
  return array(
    '#type' => 'markup',
    '#markup' => 'My custom example block'
  );
}

/**
 * Returns a form to be used as the block configuration form, i.e. the same
 * as you would return from `hook_block_configure()`
 *
 * @param $delta
 *   The block delta
 */
function my_module_configure_example_block($delta) {
  $form = array();
  $form['my_custom_field'] = array(
    '#type' => 'textfield',
    '#title' => t('My custom block field'),
    '#default_value' => variable_get($delta, '')
  );

  return $form;
}

/**
 * Saves the block configuration, i.e. the same as you would do inside
 * `hook_block_save()`
 *
 * @param $block
 *   An array of values representing the block with it's configuration, i.e. the
 *   `$edit` array passed to `hook_block_save()`
 */
function my_module_save_example_block($block) {
  variable_set($block['delta'], $block['my_custom_field']);
}

So as you can see, your block definition, render and configuration resides in one file. If you need more blocks, you just define more plugins and you are good to go. There is such a documentation oriented example inside the plugin_examples folder of the module for each available plugin type. Plus, don't forget the README.md file.

Once you install the module, you will have only one administration screen at admin/config/info-plugins where you'll be able to select which plugin types you need. For example, if you need to define a custom block, enable the Core Block plugin type. If you need others, enable them as well. But I recommend you keep them disabled if you don't use them so that their logic doesn't get included in memory (all the relevant hooks and what not). My goal here is to keep a very low foot print.

To see which plugin types are available to use, please consult the project page. And keep checking back because I will add more. And if you want to contribute, please don't hesitate. Take it out for a spin, give it a test and please open issues to let me know of any bugs you find. I will be active on this for a while and want to hammer out all the potential problems before creating a stable release.

Cheers!

Write your own Views Bulk Operations actions in Drupal 7

Views Bulk Operations (VBO) is a powerful module that leverages Views to allow site administrators to perform bulk operations on multiple entities at once. It does so efficiently by processing the items in batches over multiple requests to avoid timeouts.

Installing the module will already provide you with a host of various actions you can perform in bulk on various entities. You can publish 1000 nodes at once, delete them or even change their author. And these are just a few examples of what you can do.

In this article we are going to look at programatically creating our own action that we can trigger with VBO to affect multiple node entities. Sometimes you need to write your own because the use case does not quite fit in the extensive list of actions the module provides.

For our example, let's assume we have an entity reference field called field_users on both the article and basic page content types. This field can reference whatever user it wants. And the requirement is to be able to bulk update the value of this field on a bunch of nodes of both these node types at once.

Out of the box, VBO provides us with an action to change the value of a field but this doesn't help us in this case. When adding a value to this field via VBO, we are presented with as many instances of the field as different node types are in the selection. And this is not ideal if we want to scale the functionality to more than one content type. What we want is to select a number of nodes and then only once provide a value to this field. So let's see how we can define a custom VBO action for this use case.

The action

To define a new action we need to implement hook_action_info():

/**
 * Implements hook_action_info().
 */
function my_module_action_info() {
  return array(
    'my_module_my_custom_action' => array(
      'type' => 'entity',
      'label' => t('Add a user to Users field'),
      'behavior' => array('changes_property'),
      'configurable' => TRUE,
      'vbo_configurable' => FALSE,
      'triggers' => array('any'),
    ),
  );
}

With this hook implementation we are defining our own action called my_module_my_custom_action which is available to be triggered on all entity types (because we specified entity for the type) and it acts as a property changer. It is configurable using the default Action API but we don't need any kind of VBO specific configuration. For more information on all the values that you can pass here, feel free to consult the documentation page for VBO.

Next, it's time to create the configuration form for this action, namely the form that will be presented to us to select the user we want to add to the field_users reference field:

function my_module_my_custom_action_form() {
  $form = array();
  $form['user'] = array(
    '#type' => 'textfield',
    '#title' => t('User'),
    '#maxlength' => 60,
    '#autocomplete_path' => 'user/autocomplete',
    '#weight' => -1,
  );

  return $form;
}

The function name takes from the machine name of the action suffixed by _form and is responsible for creating and returning a form array. All we need is one field which uses the core user/autocomplete path to load users via Ajax. Simple enough.

So now after we make a bulk selection and choose our action, we'll be prompted with this form to choose the user we want to add to the reference field. It follows to couple it with a submit handler that will save the value into the context of the operation:

function my_module_my_custom_action_submit($form, &$form_state) {
  $uid = db_query('SELECT uid from {users} WHERE name = :name', array(':name' => $form_state['values']['user']))->fetchField();
  return array(
    'uid' => $uid,
  );
}

The naming of this function is similar to the previous one except for the suffix being _submit this time around. In it, we load from the database the uid of the user that was referenced in the form field by name and return that inside an array. The latter will then be merged into the $context variable available in the next step.

So it's now time to write the final function which represents this step by adding the selected user to the existing ones in that field across all the selected nodes, regardless of their type:

function my_module_my_custom_action(&$entity, $context) {
  if (!isset($entity->field_users)) {
    return;
  }

  if (!isset($context['uid'])) {
    return;
  }

  if (!empty($entity->field_users)) {
    foreach ($entity->field_users[LANGUAGE_NONE] as $ref) {
      if ($ref['target_id'] === $context['uid']) {
        return;
      }
    }
  }

  $user =  array(
    'target_id' => $context['uid'],
  );

  if (!empty($entity->field_users)) {
    $entity->field_users[LANGUAGE_NONE][] = $user;
    return;
  }

  $entity->field_users[LANGUAGE_NONE] = array($user);
}

The name of this function is exactly the same as the machine name of the action, the reason for which we prefixed the latter with the module name. As arguments, this function gets the entity object that is being changed (by reference) and the context of the operation.

We start by returning early if the current entity doesn't have our field_users field or if by any chance the uid key is not available inside $context. Then we loop through all the values of the field and return if the selected uid already exists (we don't want to add it twice). And last, we add the selected uid to the list of existing users in the field by taking into account the possibilities that the field can be empty or it can already contain values. After passing through this action, VBO will automatically save the node with the changes for us.

And that is pretty much it. Clearing the cache will make the new action available in the VBO configuration of your view. Adding it will then allow you to select as many nodes as you want, specify a user via the autocomplete field and have that user be added to the field_users field of all those nodes. And the cool thing is that you can select any node you want: if the field doesn't exist on that content type, it will just be skipped gracefully because we are checking for this inside the action logic.

Hope this helps.

Go custom or use a contributed module?

That is the main question. If you came here looking for a definitive answer, I'm afraid you won't find one. What you maybe will find is a discussion I propose on this topic and my two cents on the matter.

Why am I talking about this?

I've been working on a big Drupal website recently which has many contributed and custom modules. One of my recent tasks has been enabling validation of an existent text field used for inputting phone numbers. The client needed this number in a specific format. No problem, a quick regex inside hook_node_validate() should do the trick nicely. But then it got me thinking? Isn't there a module I can use for this instead? Well yes there is: Field validation.

I installed this module which comes with a plethora of validation rules and possibilities. The default phone validation for my country was not matching the client expectation so I had to use a custom expression for that. No problem, achieved basically the same result. But was this the better option under the circumstances? You might say yes because people have been working on this module a long time to perfect it, keep it secure and provide all sorts of goodies under the hood. Not to mention that it's used on over 10.000 websites.

However, the Field Validation module is big. It has a bunch of functionality that allows you to perform all sorts of validation on fields. This is not a bad thing, don't get me wrong. But does my little validation need warrant the installation and loading into memory of so much code? My custom solution was very targeted and took no more than 10 or so lines of code.

One argument would be that yes, because I may need other kinds of validation rules in the future so you can use this module also for those. But I think that being already very far in the lifetime of this website the odds are quite low of that. And even if this is the case, will an extra 2-3 validation needs warrant the use of this module?

On the other hand, you can argue that your custom code is not vetted, is difficult to maintain, can be insecure if you make mistakes and basically represents some non-configurable magic on your site. These can all be true but can also all be false depending on the developer, how they document code and the functionality itself.

I ended up with the custom solution in this case because on this site I really want to introduce new modules only if they bring something major to the table (performance being my concern here). So of course, the choice heavily depends on the actual module you are considering, the website it would go on and the custom code you'd write as an alternative.

Moreover, please do not focus on the actual Field Validation module in this discussion. I am not here to discuss its merits but the whether or not installing any such module that serves a tiny purpose is the right way to go. This is mostly a Drupal 7 problem as in D8 we use object oriented practices by which we can have as much code as we want because we only load the necessary parts when needed.

So what do you think? Do you have a general rule when it comes to this decision or you also take it case by case basis? If the latter, what are your criteria for informing your choice? If the former, why is this? I'd love to hear what you have to say.

Drupal 8: custom data on configuration entities using the ThirdPartySettingsInterface

In this article we are going to look at how to use the ThirdPartySettingsInterface to add some extra data to existing configuration entities. For example, if you ever need to store some config together with a node type or a taxonomy vocabulary, there is a great way to do so using this interface. Today we are going to see an example of this and add an extra field to the menu definition and store the value in this way.

There are a number of steps involved in this process. First, we need to alter the form with which the entity configuration data is added and saved. In the case of the menu entity there are two forms (one for adding and one for editing) so we need to alter them both. We can do something like this:

/**
 * Implements hook_form_alter().
 */
function my_module_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  if ($form_id === 'menu_add_form' || $form_id === 'menu_edit_form') {
    my_module_alter_menu_forms($form, $form_state, $form_id);
  }
}

Inside this general hook_form_alter() implementation we delegate the logic to a custom function if the form is one of the two we need. Alternatively you can also implement hook_form_FORM_ID_alter() for both those forms and delegate from each. That would limit a bit on the function calls. But let's see our custom function:

/**
 * Handles the form alter for the menu_add_form and menu_edit_form forms.
 */
function my_module_alter_menu_forms(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $menu = $form_state->getFormObject()->getEntity();
  $form['my_text_field'] = array(
    '#type' => 'textfield',
    '#title' => t('My text field'),
    '#description' => t('This is some extra data'),
    '#default_value' => $menu->getThirdPartySetting('my_module', 'my_text_field'),
    '#weight' => 1
  );

  if (isset($form['links'])) {
    $form['links']['#weight'] = 2;
  }

  $form['#entity_builders'][] = 'my_module_form_menu_add_form_builder';
}

In here we do a couple of things. First, we retrieve the configuration entity object which the form is currently editing. Then, we define a new textfield and add it to the form. Next, we check if the form has menu links on it (meaning that it's probably the edit form) in which case we make its weight higher than one of our new field (just so that the form looks nicer). And last, we add a new #entity_builder to the form which will be triggered when the form is submitted.

The getThirdPartySetting() method on the entity object is provided by the ThirdPartySettingsInterface which all configuration entities have by default if they extend from the ConfigEntityBase class. With this method we simply retrieve a value that is stored as third party for a given module (my_module in this case). It will return NULL if none is set so we don't even need to provide a default in this case.

Let us now turn to our #entity_builder which gets called when the form is submitted and is responsible for mapping data to the entity:

/**
 * Entity builder for the menu configuration entity.
 */
function my_module_form_menu_add_form_builder($entity_type, \Drupal\system\Entity\Menu $menu, &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if ($form_state->getValue('my_text_field')) {
    $menu->setThirdPartySetting('my_module', 'my_text_field', $form_state->getValue('my_text_field'));
    return;
  }

  $type->unsetThirdPartySetting('my_module', 'my_text_field');
}

Inside we check if our textfield was filled in and set it to the third party setting we can access from the config entity object that is passed as an argument. If the form value is empty we reset the third party setting to remove lingering data in case there is something there.

And that's pretty much it for the business logic. We can clear the cache and try this out by creating/editing a menu and storing new data with it. However, our job is not quite finished. We need to add our configuration schema so that it becomes translatable. Inside the /config/schema/my_module.schema.yml file of our module we need to add this:

system.menu.*.third_party.my_module:
  type: mapping
  label: 'My module textfield'
  mapping:
    my_text_field:
      type: text
      label: 'My textfield'

With this schema definition we are basically appending to the schema of the system.menu config entity by specifying some metadata about the third party settings our module provides. For more information on config schemas be sure to check out the docs on Drupal.org.

Now if we reinstall our module and turn on configuration translation, we can translate the values users add to my_text_field. You go to admin/config/regional/config-translation/menu, select a menu and when translating in a different language you see a new Third Party Settings fieldset containing all the translatable values defined in the schema.

Hope this helps.

Creating pseudo-fields in Drupal 8

In this article we are going to look at how we can create and use pseudo-fields in Drupal 8.

What are pseudo-fields?

Pseudo-fields are simple display fields that you can control from the display settings of a particular entity type. An example of such a field is the core Links field on the node or comment entities.

Why are they useful?

Pseudo-fields are great if you have some content or data that you need to render together with that of a particular entity. And since you don't want to be hacky and hardcode all of this inside a template or preprocessor, you can use pseudo-fields to expose some control over this to the UI. This means that you can use the core drag-and-drop functionality to control their visibility and position relative to regular entity fields.

Pseudo-fields are a great way to display data that is tightly coupled to the entities but is not part of them or their fields.

So how do they work?

There are 2 main steps we need to take in order to create a pseudo-field. First, we need to implement hook_entity_extra_field_info() which is similar to Drupal 7.

/**
 * Implements hook_entity_extra_field_info().
 */
function my_module_entity_extra_field_info() {
  $extra = array();

  foreach (NodeType::loadMultiple() as $bundle) {
    $extra['node'][$bundle->Id()]['display']['my_own_pseudo_field'] = array(
      'label' => t('My own field'),
      'description' => t('This is my own pseudo-field'),
      'weight' => 100,
      'visible' => TRUE,
    );
  }

  return $extra;
}

We mustn't forget to use the NodeType class at the top of the file:

use Drupal\node\Entity\NodeType;

With this implementation we are creating one pseudo-field called My own field that will show up on the display settings of all the node bundles. After clearing the cache, you can already see it if you go to the display settings of any node bundle you have.

drupal 8 pseudo fields

The second step is making this field actually render something when the node is being viewed. For this we need to implement hook_entity_view() or any of its variants:

/**
 * Implements hook_ENTITY_TYPE_view().
 */
function my_module_node_view(array &$build, EntityInterface $entity, EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($display->getComponent('my_own_pseudo_field')) {
      $build['my_own_pseudo_field'] = [
        '#type' => 'markup',
        '#markup' => 'This is my custom content', 
    ];
  }
}

And again, let's use the necessary classes at the top:

use \Drupal\Core\Entity\EntityInterface;
use \Drupal\Core\Entity\Display\EntityViewDisplayInterface;

Above we went with the hook_ENTITY_TYPE_view() variant that applies to the node entity. Inside, we are being passed an EntityViewDisplayInterface display object which we can use to check whether or not our own component (the one we defined earlier) exists in this display. If it does, we add our custom data to the $build render array that is passed by reference. This check allows the user interface to determine the visibility of this component on each view mode, as well as the weight (position relative to the other components which can be either entity fields or other such pseudo-fields).

And that's about it. You can save, edit the display settings of your nodes, create view modes and specify exactly for which one and where this custom content should show up.

Hope this helps.

Debugging Drupal redirects

I was working on a big website with many contrib and custom modules. And I had to debug a very annoying redirect that started happening sometime in the recent past, not sure when. Some pages simply just redirected to other URLs.

I figured out that the problem was one of a 301 redirect. My browser told me that at least. But good luck figuring out where in the code I can find the culprit. Xdebug breakpoints everywhere but to no avail. A search for drupal_goto in the custom modules directory didn't help either, and God be with anyone trying to search through a contrib folder of that size.

Then it hit me. Isn't there a hook invoked inside drupal_goto? At this point I was assuming (and hoping really) that the redirect was happening somehow with a drupal_goto. And it turns out there is one: hook_drupal_goto_alter.

Armed with a new dose of hope, I implemented the hook and cleared the cache. Inside, I added the obligatory $test = ''; statement and put a breakpoint on it. Let's see what happens. After loading one of the offending pages, the breakpoint halted the execution and the Xdebug call stack in my PHPStorm immediately pointed out the problem: Global Redirect. There was some URL rewriting happening on the site so GR got a bit confused and was redirecting back to the original path. The details of the issue are however not important.

My point is that using this hook, I could see exactly who and why was calling drupal_goto. I didn't use it for anything else, apart from learning why the redirect is happening which in turn allowed me to write some code that prevented that.

Awesome. I learned about a new hook. And maybe now you as well.

Adding new HTML tags in the <head> in Drupal 8

In a previous article I've shown you how you can add new html elements to the <head> of your Drupal 7 site. Recently, however, I was working on a Drupal 8 project and encountered the need to do this in D8. And it took me a while to figure it out so I thought I'd share the process with you.

As you know, in Drupal 7 we use drupal_add_html_head() from anywhere in the code to add a rendered element into the <head>. This is done by passing a render array and most of the time you'll use the type #tag. In Drupal 8, however, we no longer have this procedural function so it can be a bit tricky to find out how this is done.

Although existing in Drupal 7 as well, the #attached key in render arrays really becomes important in D8. We can no longer add any scripts or stylesheets to any page without such proper attachment to render arrays. In my last article I've shown you how to add core scripts to pages in case they were missing (which can happen for anonymous users). In essence, it is all about libraries now that get attached to render arrays. So that is most of what you'll hear about.

But libraries are not the only thing you can attach to render arrays. You can also add elements to the head of the page in a similar way you'd attach libraries. So if we wanted to add a description meta tag to all of the pages on our site, we could implement hook_page_attachments() like so:

/**
 * Implements hook_page_attachments().
 */
function module_name_page_attachments(array &$page) {
  $description = [
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'description',
      'content' => 'This is my website.',
    ],
  ];
  $page['#attached']['html_head'][] = [$description, 'description'];
}

In the example above we are just adding a dummy description meta tag to all the pages. You probably won't want to apply that to all the pages though and rather have the content of the description tag read the title of the current node. In this case you can implement hook_entity_view() like so:

/**
 * Implements hook_entity_view().
 */
function demo_entity_view(array &$build, \Drupal\Core\Entity\EntityInterface $entity, \Drupal\Core\Entity\Display\EntityViewDisplayInterface $display, $view_mode, $langcode) {
  if ($entity->getEntityTypeId() !== 'node') {
    return;
  }

  $description = [
    '#tag' => 'meta',
    '#attributes' => [
      'name' => 'description',
      'content' => \Drupal\Component\Utility\SafeMarkup::checkPlain($entity->title->value),
    ],
  ];
  $build['#attached']['html_head'][] = [$description, 'description'];
}

Now you targeting the node entities and using their titles as the content for the description meta tag. And that is pretty much it.

Hope this helps.

Drupal 8: core javascript files for anonymous users

Drupal 8 comes with many performance improvements, one of which being that javascript is no longer indiscriminately loaded on every page. This means that for anonymous users, there are many pages where there is no jQuery or even javascript loaded.

Although this is a good thing, sometimes you do need jQuery (for example to use Ajax, in which case you'd also need other scripts). So how do you load these files? One way is to use hook_page_attachments() to #attach your own library to the page. However, this is only recommended if the assets you need to attach do not apply to something specific, but to the entire page. The recommended way is to attach the library to a render array because then it will only get loaded if necessary.

And if we look at the documentation page for assets, we'll see how we can add our own library. We need to create a my_module.libraries.yml or my_theme.libraries.yml file. And inside, we can add the following:

my_scripts:
  version: VERSION
  js:
    js/scripts.js: {}
  dependencies:
    - core/jquery
    - core/drupal.ajax
    - core/drupal
    - core/drupalSettings
    - core/jquery.once

Where my_scripts will be the name of the library we will reference when attaching.

As you can see, we are not including any javascript or css of our own, we are just making use of the dependency scripts provided by core. So then what is left to do is to attach this library to a render array (or to the entire page if that is your use case):

$render_array['#attached']['library'][] = 'my_module/my_scripts';

Hope this helps.

Custom access control for Drupal 7 entities

Have you ever used hook_node_access() to control access to various node operations on your site? Have you found this hook unbelievably awesome? Thought to yourself, boy, I'm unstoppable now? Well, I sure did.

The problem is that Drupal 7 entities do not start and end with nodes and you may want to control access to other entities in the same way. However, there is no hook_taxonomy_term_access() or hook_fieldable_panels_pane_access() that you can use to apply this tactic. So what can you do in these cases?

The first thing is to understand that each entity implementation is different. Some have a centralised access callback for all the operations while others simply define permissions or access callbacks to be used directly inside the hook_menu() definition. And I'm sure there are also other ways of handling this but these I think are the most common.

In this article we are going to look at two entity examples: taxonomy terms (from core) and fieldable panels panes (from contrib). We will trace the access pipeline from request to response and see what we can do to intervene in this process and add our own logic. So let's begin.

Taxonomy terms

The first entity type we are going to look at is the regular taxonomy term introduced by core. It's actually pretty easy to understand how this entity type is built and what we can do in order to hook into the access pipeline.

If we take a look at taxonomy_menu(), we can quickly figure out which paths it creates to add and edit terms and what access callback and arguments are defined:

Adding terms to a vocabulary

$items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add'] = array(
   'title' => 'Add term',
   'page callback' => 'drupal_get_form',
   'page arguments' => array('taxonomy_form_term', array(), 3),
   'access arguments' => array('administer taxonomy'),
   'type' => MENU_LOCAL_ACTION,
   'file' => 'taxonomy.admin.inc',
);

Editing a particular term

$items['taxonomy/term/%taxonomy_term/edit'] = array(
   'title' => 'Edit',
   'page callback' => 'drupal_get_form',
   // Pass a NULL argument to ensure that additional path components are not
   // passed to taxonomy_form_term() as the vocabulary machine name argument.
   'page arguments' => array('taxonomy_form_term', 2, NULL),
   'access callback' => 'taxonomy_term_edit_access',
   'access arguments' => array(2),
   'type' => MENU_LOCAL_TASK,
   'weight' => 10,
   'file' => 'taxonomy.admin.inc',
);

By nature, viewing taxonomy term pages has to do with node access since they show a list of nodes so we will skip this aspect here and stick with just the add/edit operations.

So let's change the access callbacks and potential passed arguments inside a hook_menu_alter() implementation.

/**
 * Implements hook_menu_alter().
 */
function demo_menu_alter(&$items) {
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add']['access callback'] = 'demo_taxonomy_term_access';
  $items['admin/structure/taxonomy/%taxonomy_vocabulary_machine_name/add']['access arguments'] = array(3, 'add');

  $items['taxonomy/term/%taxonomy_term/edit']['access callback'] = 'demo_taxonomy_term_access';
  $items['taxonomy/term/%taxonomy_term/edit']['access arguments'][] = 'edit';
}

So what happens here? We are simply replacing the access callback definition for these two menu items with our own custom function demo_taxonomy_term_access(). Additionally, we standardise a bit the arguments this function gets: $entity (either term or vocabulary object) and $op (add or edit). This covers both cases.

Now let's write our callback function:

/**
 * Access callback for taxonomy term add/edit operations
 *
 * @param null $entity
 * @param null $op
 * @return bool
 */
function demo_taxonomy_term_access($entity = null, $op = null) {
  if ($op === 'add') {
    return user_access('administer taxonomy');
  }

  if ($op === 'edit') {
    return taxonomy_term_edit_access($entity);
  }
}

Inside the function we run a check on $op and perform the access logic we want. In doing so, we make use of the $entity object that can be either the term being edited or the vocabulary to which a new term is added. In this example we are replicating exactly the intended access checks of the Taxonomy module. To add a term, a user needs to have the administer taxonomy permission while to edit one it needs either the same permission or a vocabulary specific permission (as seen inside taxonomy_term_edit_access()). Now it's up to you to include inside this logic whatever else you need. And you end up with something not so dissimilar to hook_node_access() but for taxonomy terms.

Fieldable Panels Panes (FPP)

FPP is a contributed module that creates an entity type that is primarily used inside a Panels context. Regardless of any of this though, it too exposes CRUD operations on the entities of this type. And consequently, there are access implications. So let's see how we can hook into this pipeline by starting where we did with the Taxonomy module: at fieldable_panels_panes_menu().

By looking inside this hook implementation we can find many defined paths which relate to these CRUD operations. And we also see that many of them have fieldable_panels_panes_access() as the access callback with some specific arguments passed to it.

But what does this function actually do? Nothing but taking the parameters and deferring to the controller class responsible for this entity type and its access() method. And by checking fieldable_panels_panes_entity_info(), the principle function responsible for defining the entity type, we find that this is the PanelsPaneController class. In there, we find all the logic for determining access rights for various operations.

Now that we know all this, what can we do to hook into this pipeline? We could do like before and override the hook_menu() implementation. But since there are so many menu items and the FPP class controller is already doing such a nice job, that may be counter productive. So let's instead override the entity definition and replace the class controller with one of ours that extends PanelsPaneController. In there, we then do what we want.

First, we implement hook_entity_info_alter():

/**
 * Implements hook_entity_info_alter().
 */
function demo_entity_info_alter(&$entity_info) {
  $entity_info['fieldable_panels_pane']['controller class'] = 'DemoPanelsPaneController';
}

Right after this, we create a file inside our module called DemoPanelsPaneController.inc which contains the following to start with:

<?php

/**
 * Overrides fieldable panels panes controller functionality
 */
class DemoPanelsPaneController extends PanelsPaneController {

}

Finally, we edit the module's .info file and make sure it loads this file:

files[] = DemoPanelsPaneController.inc

Then we clear the cache. If all went well, nothing really has changed on the site in terms of functionality. However, the DemoPanelsPaneController class is being used for controlling the fieldable_panels_pane entity type. And since this one extends PanelsPaneController, all previous functionality remains. It follows to now override the access() method and include our own logic to it:

public function access($op, $entity = NULL, $account = NULL) {
   // $account not always full (defaults to current user)
   return parent::access($op, $entity, $account);
}

In the example above, nothing is really changed because the logic is deferred back to the parent class. But you could add some logic in addition or instead of that depending on various contextual factors.

However, I strongly recommend/warn you to stick to the minimum amount of deviation from default is needed, and for all rest, defer back to the original logic. This is to prevent the opening of any security holes. For example, if your access() method looks like this:

public function access($op, $entity = NULL, $account = NULL) {
   if ($op === 'update') {
     return false;
   }   
}

You are indeed denying access to the edit form but now anybody can create and delete entities because there is no more check on those operations. So make sure you understand this and do not leave any loopholes. The fix in this case would be:

public function access($op, $entity = NULL, $account = NULL) {
   if ($op === 'update') {
     return false;
   }   
   return parent::access($op, $entity, $account);
}

If the operation is edit, the access is denied for everybody (probably not a good idea but suitable for this demo purpose). However, if the operation is delete, create or view, we defer to the logic of the parent class to handle those cases. In which case the default FPP permissions will be used.

Conclusion

In this article we've seen two ways we can hook into the access checking pipeline of entities in Drupal 7. We've learned that there is more than just one way of going about it depending on how the entity type in question has been defined. The purpose was to illustrate how you can approach the matter and where you need to look in order to find a solution. Hope this helps.

Dynamically sort complex data with PHP's sort functionality

PHP Logo

In a previous article I showed you how to use the usort() and uasort() PHP functions to sort some more complex data using a comparator callback function. How cool was that?

Although powerful, this technique is quite restricting though because all the logic for the sorting happens inside the comparator function. In other words, you cannot pass parameters to this callback except for the actual default values that are being compared. So what's the problem with this?

Say you have an array of objects that have a name property that can be retrieved by a getName() getter method. And let's say they also have an address property retrieved by getAddress(). And you have a listing of this data and you need to allow for sorting by any of these properties both ASC and DESC. And forget for one second about the possibility of ordering them as they come out of your data store.

Implementing this with just the default usort() function means you will need 4 different comparer functions (one for each combination of property and sort direction). And what if you want to add more columns to the listing? Oh no..

As you can imagine, the solution to this problem is having a dynamic sorting function (or method in a class) to which you can pass the items to be sorted, the property by which to sort and the direction of sort. And then not have to worry about creating all these ridiculous comparer functions. So how might this look like?

function sortByObjectProps(&$items, $method, $order) {
  if ( ! is_array($items)) {
    return false;
  }

  return usort($items, function($a, $b) use ($method, $order){
    $cmp = strcmp($a->$method(), $b->$method());
    return $order === 'asc' ? $cmp : -$cmp;
  });
}

So what happens here? First of all, $items is passed by reference so we don't need to return it. The return value will just be a boolean indicating the success or failure of the sort. Additionally, we pass the method name that retrieves the property value ($method) and the direction of the sort ($order).

Then we run usort() on the items but - and here is the kicker - with an anonymous function that can use the passed in method and order values. Then it's just a matter of comparing the return values of the getter methods and negate the integer if the order is DESC. Pretty cool no?

So now you can have as many columns as you need and get them sorted in both directions. And obviously even more complex stuff.

Hope this helps.