Thursday, May 2, 2013

How to write a custom ctools access plugin for a Boolean field.

I had the delightful task at work of determining the visibility of a view (in a node variant) based on the value of a boolean field.  But it got better!  Said boolean field was not on the node that was being viewed, it was on a node that referenced the node being viewed.  But wait!  It gets better!  The referenced node was an organic group, and those references work differently then a usual entity reference!  Confused yet?  Yeah, me too.  I got it in the end though, and here it is, on the vague assumption that such an odd use case will ever come up again...

First things first: you have to let ctools know that there is a plugin to use.  In your custom module (or in my case, in the relevant feature) .module, you need something like this:
/**
 * Implements hook_ctools_plugin_directory().
 *
 * It simply tells panels where to look for the .inc file that
 * defines various args, contexts and content_types.
 */
function my_feature_ctools_plugin_directory($module, $plugin) {
 if ($module == 'ctools' && !empty($plugin)) {
   return "plugins/$plugin";
 }
}



Then in your feature (or custom module), create the directory, so you have a my_feature/plugins/access folder structure.

In the access folder, create a new .inc file.  For the purposes of this exercise, the field we are using to determine visibility will be called field_widget, so I would call my file field_widget.inc.

You have to start by defining the plugin, so:
<?php

/**
 * Plugins are described by creating a $plugin array which will
 * be used by the system that includes the file.
 */
$plugin = array(
  'title' => t('Node: Widget'),
  'description' => t('Only displays this pane if the Widget field on the related Home Page for this Organic Group is set to On.'),
  'callback' => 'my_feature_field_widget_ctools_access_check',
  'default' => array('field_widget' => 1),
  'summary' => 'my_feature_field_widget_ctools_access_summary',
  'required context' => new ctools_context_required(t('Node'), 'node'),
);  



Now write the callback:
/**
 * Custom callback defined by 'callback' in the $plugin array.
 *
 * Check for access.
 */
function my_feature_field_widget_ctools_access_check($conf, $context) {

  // If for some unknown reason that $context isn't set, return false.
  if (empty($context) || empty($context->data)) {
    return FALSE;
  }

Because the field is *not* on the node that is currently being viewed, we have to identify the correct home page node.
  // Identify the home page node for the current organic group.
  $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'division_home')
      ->fieldCondition('og_division_home_division_ref', 'target_id', $context->data->nid);
    $result = $query->execute();




Now we have to load the node, to access the field_widget value.
    $home_page_nid = current($result['node'])->nid;
    $home_page_node = node_load($home_page_nid);


Here is where we check the value of field_widget.
  Being a boolean, if the value is 0, that means the boolean is not checked, and we want to hide the pane.
// If the home page widget field is not checked, hide the pane.
  if (!$home_page_node->field_widget['und'][0]['value']) {
  }
Otherwise, show the pane.
  return TRUE;
}



Here is the whole kit and caboodle:
<?php

/**
 * Plugins are described by creating a $plugin array which will
 * be used by the system that includes the file.
 */
$plugin = array(
  'title' => t('Node: Widget'),
 
 'description' => t('Only displays this pane if the Widget field on 
the related Home Page for this Organic Group is set to On.'),
  'callback' => 'my_feature_field_widget_ctools_access_check',
  'default' => array('field_widget' => 1),
  'summary' => 'my_feature_field_widget_ctools_access_summary',
  'required context' => new ctools_context_required(t('Node'), 'node'),
);   

/**
 * Custom callback defined by 'callback' in the $plugin array.
 *
 * Check for access.
 */
function my_feature_field_widget_ctools_access_check($conf, $context) {

  // If for some unknown reason that $context isn't set, return false.
  if (empty($context) || empty($context->data)) {
    return FALSE;
  }



  // Identify the home page node for the current organic group.
  $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', 'node')
      ->entityCondition('bundle', 'division_home')
      ->fieldCondition('og_division_home_division_ref', 'target_id', $context->data->nid);
    $result = $query->execute();

    $home_page_nid = current($result['node'])->nid;
    $home_page_node = node_load($home_page_nid);



// If the home page widget field is not checked, hide the pane.
  if (!$home_page_node->field_widget['und'][0]['value']) {
  }

  return TRUE;
}


And there you have it.  :)

1 comment: