From 427d0df1d7c9f98470b8ddac752ffbbcdef88e2b Mon Sep 17 00:00:00 2001
From: Ron Shimshock <ron@shimshockgroup.com>
Date: Mon, 26 Sep 2016 16:48:34 -0500
Subject: [PATCH] Generic entity support

---
 context.core.inc                              | 75 ++++++++++++++++++++++
 context.plugins.inc                           | 28 +++++++-
 plugins/context_condition_entity.inc          | 92 +++++++++++++++++++++++++++
 plugins/context_condition_entity_taxonomy.inc | 66 +++++++++++++++++++
 4 files changed, 260 insertions(+), 1 deletion(-)
 create mode 100644 plugins/context_condition_entity.inc
 create mode 100644 plugins/context_condition_entity_taxonomy.inc

diff --git a/context.core.inc b/context.core.inc
index 41ed93a..bda782d 100644
--- a/context.core.inc
+++ b/context.core.inc
@@ -118,6 +118,54 @@ function context_entity_prepare_view($prepare, $entity_type) {
 }
 
 /**
+ * Implements hook_entity_view_alter().
+ */
+function context_entity_view_alter(&$build, $type) {
+  $object = menu_get_object($type);
+  if (isset($build['#entity_type']) && isset($build['#bundle'])) {
+    switch ($build['#entity_type']) {
+      case 'user':
+        $entity_key = '#account';
+        break;
+      case 'taxonomy_term':
+        $entity_key = '#term';
+        break;
+      default:
+        if (isset($build['#' . $build['#entity_type']])) {
+          $entity_key = '#' . $build['#entity_type'];
+        }
+        else {
+          $entity_key = '#entity';
+        }
+    }
+    $entity = $build[$entity_key];
+    if ($object) {
+      list($object_id) = entity_extract_ids($type, $object);
+      list($entity_id) = entity_extract_ids($type, $entity);
+      if (isset($object_id) && $object_id == $entity_id) {
+        context_entity_condition($entity, $type, 'view');
+      }
+    }
+    else {
+      $info = entity_get_info();
+      $objects = entity_load($type, array($entity->$info[$type]['entity keys']['id']));
+      $entity_object_id = key($objects);
+      if (isset($objects[$entity_object_id]->delta)) {
+        $delta = $objects[$entity_object_id]->delta;
+      }
+      else {
+        $delta = FALSE;
+      }
+      $object_id = (is_numeric(arg(1))) ? arg(1) : arg(2);
+      list($entity_id) = entity_extract_ids($type, $entity);
+      if (($object_id == $entity_id && $entity_object_id == $entity_id) || $object_id == $delta) {
+        context_entity_condition($entity, $type, 'view');
+      }
+    }
+  }
+}
+
+/**
  * Implementation of hook_node_view().
  */
 function context_node_view($node, $view_mode) {
@@ -128,6 +176,22 @@ function context_node_view($node, $view_mode) {
 }
 
 /**
+ * Centralized entity condition call function.
+ */
+function context_entity_condition(&$entity, $type, $op) {
+  if ($plugin = context_get_plugin('condition', 'entity')) {
+    $plugin->execute($entity, $type, $op);
+  }
+  if (module_exists('taxonomy')) {
+    if ($plugin = context_get_plugin('condition', 'entity_taxonomy')) {
+      $plugin->execute($entity, $type, $op);
+    }
+  }
+  // Allow other plugins to easily be triggered on node-related events.
+  drupal_alter('context_entity_condition', $entity, $type, $op);
+}
+
+/**
  * Implementation of hook_form_alter().
  */
 function context_form_alter(&$form, $form_state, $form_id) {
@@ -138,6 +202,7 @@ function context_form_alter(&$form, $form_state, $form_id) {
   // Trigger the condition in an after_build function to avoid being skipped
   // when there are validation errors.
   $form['#after_build'][] = 'context_form_alter_node_after_build';
+  $form['#after_build'][] = 'context_form_alter_entity_after_build';
 }
 
 /**
@@ -152,6 +217,16 @@ function context_form_alter_node_after_build($form, &$form_state) {
 }
 
 /**
+ * Form #after_build callback for context_form_alter().
+ */
+function context_form_alter_entity_after_build($form, &$form_state) {
+  if (!empty($form['#entity']) && arg(0) != 'admin') {
+    context_entity_condition($form['#entity'], $form['#entity_type'], 'form');
+  }
+  return $form;
+}
+
+/**
  * Clear out block info cache when an admin area form is submitted.
  */
 function context_admin_form_submit(&$form, $form_state) {
diff --git a/context.plugins.inc b/context.plugins.inc
index 95d003d..c812d46 100644
--- a/context.plugins.inc
+++ b/context.plugins.inc
@@ -16,6 +16,11 @@ function _context_context_registry() {
       'description' => t('Set this context on the basis of other active contexts. Put each context on a separate line. The condition will pass only if <em>all</em> of the contexts are active. You can use the <code>*</code> character (asterisk) as a wildcard and the <code>~</code> character (tilde) to prevent this context from activating if the listed context is active. Other contexts which use context conditions can not be used to exclude this context from activating.'),
       'plugin' => 'context_condition_context_all',
     ),
+    'entity' => array(
+      'title' => t('Entity type'),
+      'description' => t('Set this context when viewing an entity page of a specific entity type and bundle.'),
+      'plugin' => 'context_condition_entity',
+    ),
     'node' => array(
       'title' => t('Node type'),
       'description' => t('Set this context when viewing a node page or using the add/edit form of one of these content types.'),
@@ -86,8 +91,13 @@ function _context_context_registry() {
     );
   }
   if (module_exists('taxonomy')) {
+    $registry['conditions']['entity_taxonomy'] = array(
+      'title' => t('Entity Taxonomy'),
+      'description' => t('Set this context when viewing an entity with the selected taxonomy terms.'),
+      'plugin' => 'context_condition_entity_taxonomy',
+    );
     $registry['conditions']['node_taxonomy'] = array(
-      'title' => t('Taxonomy'),
+      'title' => t('Node Taxonomy'),
       'description' => t('Set this context when viewing a node with the selected taxonomy terms.'),
       'plugin' => 'context_condition_node_taxonomy',
     );
@@ -183,6 +193,14 @@ function _context_context_plugins() {
       'parent' => 'context_condition_path',
     ),
   );
+  $plugins['context_condition_entity'] = array(
+    'handler' => array(
+      'path' => drupal_get_path('module', 'context') . '/plugins',
+      'file' => 'context_condition_entity.inc',
+      'class' => 'context_condition_entity',
+      'parent' => 'context_condition',
+    ),
+  );
   $plugins['context_condition_node'] = array(
     'handler' => array(
       'path' => drupal_get_path('module', 'context') . '/plugins',
@@ -248,6 +266,14 @@ function _context_context_plugins() {
     ),
   );
   if (module_exists('taxonomy')) {
+    $plugins['context_condition_entity_taxonomy'] = array(
+      'handler' => array(
+        'path' => drupal_get_path('module', 'context') . '/plugins',
+        'file' => 'context_condition_entity_taxonomy.inc',
+        'class' => 'context_condition_entity_taxonomy',
+        'parent' => 'context_condition_entity',
+      ),
+    );
     $plugins['context_condition_node_taxonomy'] = array(
       'handler' => array(
         'path' => drupal_get_path('module', 'context') . '/plugins',
diff --git a/plugins/context_condition_entity.inc b/plugins/context_condition_entity.inc
new file mode 100644
index 0000000..d5b2ace
--- /dev/null
+++ b/plugins/context_condition_entity.inc
@@ -0,0 +1,92 @@
+<?php
+
+/**
+ * Trigger context on entity view only.
+ */
+define('CONTEXT_ENTITY_VIEW', 0);
+
+/**
+ * Trigger context on entity view and entity form.
+ */
+define('CONTEXT_ENTITY_FORM', 1);
+
+/**
+ * Trigger context on entity form only.
+ */
+define('CONTEXT_ENTITY_FORM_ONLY', 2);
+
+/**
+ * Expose entity view of a specific entity type as a context condition.
+ */
+class context_condition_entity extends context_condition {
+  function condition_values() {
+    $values = array();
+    foreach (entity_get_info() as $type => $info) {
+
+      // Ignore configuration entities.
+      if (!empty($info['configuration'])) {
+        continue;
+      }
+      // Ignore entities without a view mode, which indicates that the entity
+      // objects cannot be viewed on their own.
+      if (empty($info['view modes'])) {
+        continue;
+      }
+
+      if (!empty($info['bundles'])) {
+        foreach ($info['bundles'] as $bundle => $bundle_info) {
+          if ($info['label'] != $bundle_info['label']) {
+            $values[$type . ';' . $bundle] = $info['label'] . ': ' . $bundle_info['label'];
+          }
+          else {
+            $values[$type] = $info['label'];
+          }
+        }
+      }
+      else {
+        $values[$type] = $info['label'];
+      }
+    }
+    return $values;
+  }
+
+  function options_form($context) {
+    $defaults = $this->fetch_from_context($context, 'options');
+    return array(
+      'entity_form' => array(
+        '#title' => t('Set on entity form'),
+        '#type' => 'select',
+        '#options' => array(
+          CONTEXT_ENTITY_VIEW => t('No'),
+          CONTEXT_ENTITY_FORM => t('Yes'),
+          CONTEXT_ENTITY_FORM_ONLY => t('Only on entity form')
+        ),
+        '#description' => t('Set this context on entity forms'),
+        '#default_value' => isset($defaults['entity_form']) ? $defaults['entity_form'] : TRUE,
+      ),
+    );
+  }
+
+  function execute($entity, $type, $op) {
+    list($id, $vid, $bundle) = entity_extract_ids($type, $entity);
+    // If the entity has bundles, add the current bundle to the comparison.
+    if (isset($bundle)) {
+      $type .= ';' . $bundle;
+    }
+
+    foreach ($this->get_contexts($type) as $context) {
+      // Check the entity form option.
+      $options = $this->fetch_from_context($context, 'options');
+      if ($op === 'form') {
+        $options = $this->fetch_from_context($context, 'options');
+        if (!empty($options['entity_form']) && in_array($options['entity_form'], array(CONTEXT_ENTITY_FORM, CONTEXT_ENTITY_FORM_ONLY))) {
+          $this->condition_met($context, $type);
+        }
+      }
+      elseif (empty($options['entity_form']) || $options['entity_form'] != CONTEXT_ENTITY_FORM_ONLY) {
+        $this->condition_met($context, $type);
+      }
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/plugins/context_condition_entity_taxonomy.inc b/plugins/context_condition_entity_taxonomy.inc
new file mode 100644
index 0000000..2df71f6
--- /dev/null
+++ b/plugins/context_condition_entity_taxonomy.inc
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * Expose entity taxonomy terms as a context condition.
+ */
+class context_condition_entity_taxonomy extends context_condition_entity {
+  function condition_values() {
+    $values = array();
+    if (module_exists('taxonomy')) {
+      foreach (taxonomy_get_vocabularies() as $vocab) {
+        if (empty($vocab->tags)) {
+          foreach (taxonomy_get_tree($vocab->vid) as $term) {
+            $values[$term->tid] = check_plain($term->name);
+          }
+        }
+      }
+    }
+    return $values;
+  }
+
+  function condition_form($context) {
+    $form = parent::condition_form($context);
+    $form['#type'] = 'select';
+    $form['#size'] = 12;
+    $form['#multiple'] = TRUE;
+    $vocabularies = taxonomy_get_vocabularies();
+    $options = array();
+    foreach ($vocabularies as $vid => $vocabulary) {
+      $tree = taxonomy_get_tree($vid);
+      if ($tree && (count($tree) > 0)) {
+        $options[$vocabulary->name] = array();
+        foreach ($tree as $term) {
+          $options[$vocabulary->name][$term->tid] = str_repeat('-', $term->depth) . $term->name;
+        }
+      }
+    }
+    $form['#options'] = $options;
+    return $form;
+  }
+
+  function execute($entity, $type, $op) {
+    // build a list of each taxonomy reference field belonging to the bundle for the current node
+    $fields = field_info_fields();
+    $instance_fields = field_info_instances($type);
+    $check_fields = array();
+    foreach ($instance_fields as $bundle => $bundle_fields) {
+      foreach ($bundle_fields as $key => $field_info) {
+        if ($fields[$key]['type'] == 'taxonomy_term_reference') {
+          $check_fields[] = $key;
+        }
+      }
+    }
+
+    if ($this->condition_used() && !empty($check_fields)) {
+      foreach ($check_fields as $field) {
+        if ($terms = field_get_items($type, $entity, $field)) {
+          foreach ($terms as $term) {
+            foreach ($this->get_contexts($term['tid']) as $context) {
+              $this->condition_met($context, $term['tid']);
+            }
+          }
+        }
+      }
+    }
+  }
+}
-- 
2.9.3

