Using CTools Access Rulesets in module development.

Oct
12

CTools is a powerful toolbox for Drupal developers, but there are many features of this huge module that aren't well known or used. I would like to share some of my experience with using one of those lesser-known features: CTools Access Rulesets.

In this article you'll learn how to utilize the nice CTools Access Rulesets GUI in combination with some custom code to limit file downloads based on certain criteria. This promotes generic code and separates the access logic out to a place where it is customizable by a site administrator.

This gives you the ability to use flexible and powerful Panels selection rules outside of Panels.

Introduction

CTools Access Rulesets allow you to create access rules based on different contexts. It is widely used in page manager when we define rules for access to the page. When we enable the ctools_access_ruleset module, we use a UI to create our own access rules (admin/structure/ctools-rulesets/list).

One way we recently used this functionality was to limit private file downloads. I wanted to have a flexible rule that was customizible via UI, one that I would be able to extend if client's requirements changed (and I knew they would). To manage this, we wrote our own module and used hook_file_download to trigger our ctools access ruleset to determine whether we should allow the user to download the file or not.

We decided to base access control on taxonomy terms linked to the node that have the file attached. To do this we can create a ruleset that will have the context as nodes and there we check for the condition of whether or not the specified taxonomy terms are attached to these nodes.

Setting up the rule (in the GUI)

The screenshots below (click to enlarge) will allow us to see the UI interface to see the how to set it up.

First we need to open the list of access rulesets: admin/structure/ctools-rulesets/list

Each rule consists of three parts: basic infomation, contexts, and rules. Basic information is the name and description. I will skip this part.

For the Contexts we add the required context Node.

We also add the Relation to the taxonomy term we want to use. In my case, the term is from the Category vocabulary.


Now lets look at the settings of the Rules.


We have only one Rule based on a taxonomy term. The settings are as follows:


Creating the code

Now let's see how we can execute the ctools ruleset in our own module.

I would like to thank Earl Miles (merlinofchaos) and Kris Vanderwater (EclipseGc) for their support on IRC on this issue.

The key function is ctools_access($access, $context). $context is the array of contexts that we pass to the ruleset. $access is the configuration array. The machine name of my ruleset is document_download_rule.

<?php
 
// Prepare arguments for ctools_access().
  // Name of created ruleset is document_download_rule.
 
$access = array('plugins' => array(
    
0 => array(
      
'name' => 'ruleset:document_download_rule',
      
'settings' => NULL,
      
'context' => array('node_context'),
      
'not' => FALSE,
     ),
   ),
  
'logic' => 'and',
  
'type' => 'none',
  
'settings' => NULL,
  );

  ctools_include('context');
 
$node_context = ctools_context_create('entity:node', $node);
 
$contexts = array('node_context' => $node_context);

  if (ctools_access($access, $contexts)) {
   
// Allow access.
 
}
  else {
   
// Deny access.
 
}
?>

$node is the fully loaded node object that we find based on the file $uri. I will skip this part as it is not too important here. However you can take a look at a full example that is attached to this post here: Download Example

If we will decide to extend the ruleset in the future, the only things we will need to do is add new rules and contexts via the UI.

In order to understand how to prepare the $access configuration array, I would recommend doing what I did: enable module page manager, play with access rules by creating rulesets, and debug the $access variable in ctools_access function.

Conclusion

By using CTools Access Rulesets we created a custom module, which allowed us to limit file downloads based on certain criteria. We showed that this method is a way to quickly write really flexible code that can adapt to changing needs.

It should give you a great perspective on what is possible using CTools Access Rulesets and stir your imagination. Have you used CTools Access Rulesets in a clever way in the past? Please share your experiences in the comments.

Thanks for reading!

AttachmentSize
1. access_rulesets_list.png38.52 KB
2. access_ruleset_contexts.png55.3 KB
3. access_ruleset_node_context.png50.13 KB
4. access_ruleset_contexts_relationship_taxonomy_term.png57.51 KB
5. access_ruleset_rules.png53.14 KB
6. access_ruleset_rule_taxonomy_term.png61.6 KB
custom_download_access.tar.gz1.06 KB

6 Comments

Very useful article!! Thank

Very useful article!! Thank you Yuriy !!

Nice article! As Rules

Nice article!
As Rules fanatic I'd also like to mention the Rules condition components, that works in a similar way to CTools access plugins but have more flexible ways of creating and combining conditions.

Condition components are explained here: http://nodeone.se/node/993, http://nodeone.se/node/990

How to invoke them from your custom module is explained here: http://drupal.org/node/905262

Cheers!

Hi Johan, That is nice, but

Hi Johan,

That is nice, but if I remember correctly it is possible to use rules even as access plugins for panels since some time now (I read that on IRC by one of the rules maintainers).

So one could use rules to configure the advanced conditions and the ctools access plugin for more general coding.

In my eyes that would be the ideal approach that combines the best out of both worlds.

Btw. I am a huge fan of your learning series of videos. Thank you for that!

Best Regards,

Fabian Franz

Hi, When using custom access

Hi,

When using custom access rulesets manually, it might be better to use ctools_ruleset_ctools_access_check. It also avoids having to build your own $settings array for ctools_access, which feels a bit odd to me.

For a simple ruleset with a single required node context, I've been using custom menu access callback functions that look roughly like:

ctools_include('context');
$context = array(ctools_context_create('entity:node', $node));
ctools_include('plugins');
$plugin = ctools_get_plugins('ctools', 'access', 'ruleset:<ruleset_machine_name>');
return ctools_ruleset_ctools_access_check(FALSE, $context, $plugin);

The first parameter goes unused inside the function, so passing anything should work.

Thanks for the article. It was a huge help in getting on the right path for this!
-G

First of all I would like to

First of all I would like to thank Yuriy for this valuable article.
gdl, code from you comment works like a charm. Thanks!

Your idea is good but can you

Your idea is good but can you pls tell me.how to limit the number of downloads to a registered user per day.foe eg x user(registered user) can download only 10 pdf /day..i cant understand your way.i have enable custom acess ruleset as you have done but wat about limitations for downloading.i have node from which a user can download pdfs..now how can i limit the number of downloads.pls help

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • Lines and paragraphs break automatically.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.

More information about formatting options