Drupal 7 tip: Create a custom views access plugin

Posted by: 
Dominique De Cooman

If you want to create a custom views access plugin the reason for this will be to allow you to have an entry point to add custom logic to protect the access to your view.

Let say you need a finer access control to a view than roles and permissions. For example you are storing properties on a user object who determine if certain user can see certain views independent of their roles.

What we need to do, is to configure our views correctly with a views access plugin.

The hacky way:

You could load the user in the template file of your view and check for the property but this is not what we want to do. While this would work you would have to redo it in every template and you will not be able to configure it. It is also difficult to control. Its hacky and hacky solutions tend to cause a lot of maintenance. Making these quick solutions costing more in the long run. This is worth a different blog post entirely.

Here is the drupal way:

Views has this beautiful architecture in place that allows you to extend its base classes. If you start writing a custom views plugin the best thing to do is to examine an other plugin of its kind. For example check out the permissions access plugin. (tip: a good way to write plugins is to look at examples from views or other module implementing the same system)

The file:

/views/plugins/views_plugin_access_perm.inc

<?php
 
/**
 * @file
 * Definition of views_plugin_access_perm.
 */
 
/**
 * Access plugin that provides permission-based access control.
 *
 * @ingroup views_access_plugins
 */
class views_plugin_access_perm extends views_plugin_access {
  function access($account) {
    return views_check_perm($this->options['perm'], $account);
  }
 
  function get_access_callback() {
    return array('views_check_perm', array($this->options['perm']));
  }
 
  function summary_title() {
    $permissions = module_invoke_all('permission');
    if (isset($permissions[$this->options['perm']])) {
      return $permissions[$this->options['perm']]['title'];
    }
 
    return t($this->options['perm']);
  }
 
 
  function option_definition() {
    $options = parent::option_definition();
    $options['perm'] = array('default' => 'access content');
 
    return $options;
  }
 
  function options_form(&$form, &$form_state) {
    parent::options_form($form, $form_state);
    $perms = array();
    $module_info = system_get_info('module');
 
    // Get list of permissions
    foreach (module_implements('permission') as $module) {
      $permissions = module_invoke($module, 'permission');
      foreach ($permissions as $name => $perm) {
        $perms[$module_info[$module]['name']][$name] = strip_tags($perm['title']);
      }
    }
 
    ksort($perms);
 
    $form['perm'] = array(
      '#type' => 'select',
      '#options' => $perms,
      '#title' => t('Permission'),
      '#default_value' => $this->options['perm'],
      '#description' => t('Only users with the selected permission flag will be able to access this display. Note that users with "access all views" can see any view, regardless of other permissions.'),
    );
  }
}

This is a clear example that we can use to start with. You can see there are some essential functions to create this plugin. Lets check our example to see which ones.

Create a file called custommodule_access_plugin.inc in the root of your custom module.

<?php
 
  /**
   * Access plugin that provides property based access control.
   */
  class custommodules_access_plugin extends views_plugin_access {
 
    function summary_title() {
      return t('Custom acces plugin');
    } // summary_title()
 
  /**
   * Determine if the current user has access or not.
   */
    function access($account) {    
      return custommodules_access($account);
    }
 
    function get_access_callback() {
      return array('custommodules_access', array());
    }
 
  }

For the custom views plugin you need three functions. The summary_title to return the title in the admin interface. This is what you will see when you go to the views interface and you select your plugin. It will appear next to role based and permission based access in the views interface.
Then we have our access method where you the custom callback. Finaly you must declare to views your custom access callback. You can pass arguments if need using the array see in the example above.

We have our file with the class now we need to tell views that we have created a new access plugin.

Two things need to be done. First in the info file you must tell there is a file containing the class.

name = custommodule
description = Custom code
core = 7.x
package = custom
dependencies[] = views
files[] = custommodule_access_plugin.inc

The you need to implement a hook_views_plugins to tell views that there is a new custom access plugin available

  /**
   * Implements hook_views_plugins()
   */
  function custommodule_views_plugins() {
    $plugins = array(
      'access' => array(
        'test' => array(
          'title' => t('Custom Access check'),
          'help' => t('this is a custom access plugin'),
          'handler' => 'custommodule_access_plugin',
          'path' => drupal_get_path('module', 'custommodule'),
        ),
      ),
    );
    return $plugins;
  }

Our access callbacks doing the custom checks.

 function custommodules_access($account = NULL) {
    global $user;
    $access = false; 
    $account = user_load($user->uid);
    $optionfield = field_get_items('user', $account, 'field_option');
 
    //In the future more values are possible so this is extendible
    //For now only +eur exists
    $allowed_values = array('eur');
    $options = explode('+', $optionfield[0]['value']);
    foreach ($allowed_values as $allowed_value) {
      if (in_array($allowed_value, $options)) {
        $access = true;
      }
    }  
    return $access;
  }

Here we are checking on certain values that are stored in a field of a user of certain role. When a user has these properties we want the callback to return true. This will grant access.

Now that you have this plugin you can start using it in all your views by going to the interface access section and select the plugin.

Comments

Drupal 7 tip: Create a custom views access plugin

I would suggest using hook_node_grants and hook_node_access_records to write your own access system.
This way nodes can never be accessed directly, and the menu router knows not to return them...

I do however recommend writing custom views plugins for 'default arguments' used by contextual filters (instead of using the 'PHP code' option)

Drupal 7 tip: Create a custom views access plugin

Great post, thanks for sharing.

Drupal 7 tip: Create a custom views access plugin

Thanks for the post - i'm puzzled as to why the custom views plugin needs the two functions access() and get_access_callback(). Essentially they are doing the same thing, right? In what circumstances is either called?

Drupal 7 tip: Create a custom views access plugin

I'm not sure of the difference between access() and get_access_callback() either. From the documentation it appears to be a performace-related thing?

* This information will be embedded in the menu in order to reduce
* performance hits during menu item access testing, which happens
* a lot.)

But I don't know if access() is even used if you're using get_access_callback()? The only difference I see is that $account can be passed within access() - which can easily be called in the callback function as global $user anyway.

Just a tip. You can pass arguments directly from the URL into the arguments as mentioned in the comments from:
https://api.drupal.org/api/views/plugins!views_plugin_access.inc/functio...

Add new comment