From c2fe3d6aa15872514eb1525187fa50b309b807d6 Mon Sep 17 00:00:00 2001
From: Jonathan Jordan <jojonaloha@1579186.no-reply.drupal.org>
Date: Mon, 22 May 2017 09:36:15 -0700
Subject: [PATCH] Issue #2508431: An entity queue for each term

---
 .../SmartQueueEntitiesQueueHandler.class.php       | 228 +++++++++++++++++++++
 .../entityqueue/handler/smartqueue_entities.inc    |  12 ++
 .../smartqueue_entities/smartqueue_entities.info   |   6 +
 .../smartqueue_entities.install                    |  13 ++
 .../smartqueue_entities/smartqueue_entities.module | 106 ++++++++++
 5 files changed, 365 insertions(+)
 create mode 100644 modules/smartqueue_entities/plugins/entityqueue/handler/SmartQueueEntitiesQueueHandler.class.php
 create mode 100644 modules/smartqueue_entities/plugins/entityqueue/handler/smartqueue_entities.inc
 create mode 100644 modules/smartqueue_entities/smartqueue_entities.info
 create mode 100644 modules/smartqueue_entities/smartqueue_entities.install
 create mode 100644 modules/smartqueue_entities/smartqueue_entities.module

diff --git a/modules/smartqueue_entities/plugins/entityqueue/handler/SmartQueueEntitiesQueueHandler.class.php b/modules/smartqueue_entities/plugins/entityqueue/handler/SmartQueueEntitiesQueueHandler.class.php
new file mode 100644
index 0000000..ba7d948
--- /dev/null
+++ b/modules/smartqueue_entities/plugins/entityqueue/handler/SmartQueueEntitiesQueueHandler.class.php
@@ -0,0 +1,228 @@
+<?php
+
+class SmartQueueEntitiesQueueHandler extends EntityQueueHandlerBase {
+
+
+  /**
+   * Overrides EntityQueueHandlerBase::settingsForm().
+   */
+  public function settingsForm(&$form_state) {
+    $form = array(
+      'warning' => array(
+        '#prefix' => '<div class="messages warning">',
+        '#suffix' => '</div>',
+        '#markup' => t('These settings cannot be changed once the queue is created.'),
+      ),
+    );
+
+    $handler_settings = array();
+    // Load handler settings from form state
+    // @todo make this so the implementing methods do not need to know the underlying form structure?
+    if (isset($form_state['input']['handler_settings']['smartqueue_entities'])) {
+      // $form_state['input'] is used because that is what is available during and #ajax callback
+      $handler_settings = $form_state['input']['handler_settings']['smartqueue_entities'];
+    }
+    else if (isset($this->queue->settings['handler_settings'])) {
+      $handler_settings = $this->queue->settings['handler_settings'];
+    }
+
+    $source_type = isset($handler_settings['entity_type']) ? $handler_settings['entity_type'] : NULL;
+
+    // Get valid target entity types.
+    $entity_info = entity_get_info();
+    unset($entity_info['entityqueue_subqueue']);
+
+    $entity_types = array();
+    foreach ($entity_info as $entity_type => $info) {
+      $entity_types[$entity_type] = $info['label'];
+    }
+    $form['entity_type'] = array(
+      '#type' => 'select',
+      '#title' => t('Subqueues entity type'),
+      '#description' => t('The entity type to use to create subqueues from.'),
+      '#options' => $entity_types,
+      '#default_value' => $source_type,
+      '#required' => TRUE,
+      '#disabled' => $this->queue->is_new ? FALSE : TRUE,
+      '#ajax' => array(
+        'callback' => 'smartqueue_entities_entity_type_callback',
+        'wrapper' => 'smartqueue-entities-source-bundles',
+      ),
+    );
+
+    $source_bundles = !empty($handler_settings['source_bundles']) ? $handler_settings['source_bundles'] : array();
+    $bundles = array();
+    foreach ($entity_info[$source_type]['bundles'] as $bundle_name => $bundle_info) {
+      $bundles[$bundle_name] = $bundle_info['label'];
+    }
+    $form['source_bundles'] = array(
+      '#type' => 'checkboxes',
+      '#title' => $source_type == 'node' ? t('Content types') : t('Bundles'),
+      '#prefix' => '<div id="smartqueue-entities-source-bundles">',
+      '#suffix' => '</div>',
+      '#options' => $bundles,
+      '#default_value' => $source_bundles,
+      '#multiple' => TRUE,
+      '#required' => TRUE,
+      '#disabled' => $this->queue->is_new ? FALSE : TRUE,
+      '#description' => t('The bundles of the entity type that will be used to create subqueues.'),
+      '#element_validate' => array('smartqueue_entities_element_validate_source_bundles'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function canDeleteSubqueue(EntitySubqueue $subqueue) {
+    return FALSE;
+  }
+
+  /**
+   * Loads the EntitySubqueue related to the given entity.
+   *
+   * @param $entity
+   *
+   * @return EntitySubqueue || FALSE
+   */
+  function loadSubqueueForEntity($entity) {
+    $entity_type = $this->queue->settings['handler_settings']['entity_type'];
+    list($entity_id, ) = entity_extract_ids($entity_type, $entity);
+    $subqueue_name = $this->generateSubqueueName($entity_id);
+
+    return entityqueue_subqueue_load($subqueue_name);
+  }
+
+  /**
+   * Creates an EntitySubqueue related to the given entity without saving it to the database.
+   *
+   * @param $entity
+   *
+   * @return EntitySubqueue
+   */
+  public function createSubqueueForEntity($entity) {
+    global $user;
+
+    $entity_type = $this->queue->settings['handler_settings']['entity_type'];
+    list($entity_id, ) = entity_extract_ids($entity_type, $entity);
+
+    $subqueue = entityqueue_subqueue_create(array(
+      'queue' => $this->queue->name,
+      'name' => $this->generateSubqueueName($entity_id),
+      'module' => 'smartqueue_entities',
+      'uid' => $user->uid,
+    ));
+    $subqueue->label = $this->getSubqueueLabel($subqueue);
+
+    return $subqueue;
+  }
+
+  public function getSubqueueLabel(EntitySubqueue $subqueue) {
+    $entity_id = str_replace($this->queue->name . '_', '', $subqueue->name);
+    $entity_type = $this->queue->settings['handler_settings']['entity_type'];
+    if ($entity_type && $entity_id) {
+      $entity = entity_load_single($entity_type, $entity_id);
+      return entity_label($entity_type, $entity);
+    }
+    return t('Missing subqueue source entity');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadFromCode() {
+    $this->ensureSubqueues();
+    $this->updateSmartqueueList();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function insert() {
+    $this->ensureSubqueues();
+    $this->updateSmartqueueList();
+  }
+
+  public function postDelete() {
+    parent::postDelete();
+
+    $handler_settings = $this->queue->settings['handler_settings'];
+    $entity_type = $handler_settings['entity_type'];
+    $queues = variable_get('smartqueue_entities_by_source', array());
+    unset($queues[$entity_type][$this->queue->name]);
+    variable_set('smartqueue_entities_by_source', $queues);
+  }
+
+  /**
+   * Makes sure that a subqueue exists for each source entity.
+   */
+  protected function ensureSubqueues() {
+    global $user;
+
+    $handler_settings = $this->queue->settings['handler_settings'];
+
+    $source_type = $handler_settings['entity_type'];
+    $source_bundles = $handler_settings['source_bundles'];
+
+    if (empty($source_type) || empty($source_bundles)) {
+      watchdog('error', 'Smartqueue @name is missing required entity type and source bundle configuration', array(
+        '@name' => $this->queue->label(),
+      ));
+      return FALSE;
+    }
+
+    $transaction = db_transaction();
+
+    // @todo add a count and range, use a batch process if there are a lot of entities
+    $efq = new EntityFieldQuery();
+    $result = $efq->entityCondition('entity_type', $source_type)
+      ->entityCondition('bundle', $source_bundles)
+      ->execute();
+    if (isset($result[$source_type])) {
+      $entity_ids = array_keys($result[$source_type]);
+      $subqueue_names = array_map(array($this, 'generateSubqueueName'), $entity_ids);
+    }
+
+    if (!empty($subqueue_names)) {
+      // Check subqueues exist for each entity id
+      $existing_subqueues = db_select('entityqueue_subqueue', 'sq')
+        ->fields('sq', array('name'))
+        ->condition('sq.queue', $this->queue->name)
+        ->condition('sq.name', $subqueue_names)
+        ->execute()
+        ->fetchCol();
+      $missing_subqueues = array_diff($subqueue_names, $existing_subqueues);
+      foreach ($missing_subqueues as $missing_subqueue) {
+        $subqueue = entityqueue_subqueue_create(array(
+          'queue' => $this->queue->name,
+          'name' => $missing_subqueue,
+          'module' => 'smartqueue_entities',
+          'uid' => $user->uid,
+        ));
+        $subqueue->label = $this->getSubqueueLabel($subqueue);
+
+        entity_get_controller('entityqueue_subqueue')->save($subqueue, $transaction);
+      }
+    }
+  }
+
+  protected function generateSubqueueName($entity_id) {
+    return $this->queue->name . '_' . $entity_id;
+  }
+
+  protected function updateSmartqueueList() {
+    // Set a variable to quickly fetch smartqueues by the source entity type.
+    $handler_settings = $this->queue->settings['handler_settings'];
+    $entity_type = $handler_settings['entity_type'];
+    $source_bundles = $handler_settings['source_bundles'];
+    $queues = variable_get('smartqueue_entities_by_source', array());
+    if (!isset($queues[$entity_type])) {
+      $queues[$entity_type][$this->queue->name] = $source_bundles;
+    }
+    variable_set('smartqueue_entities_by_source', $queues);
+  }
+
+
+
+}
\ No newline at end of file
diff --git a/modules/smartqueue_entities/plugins/entityqueue/handler/smartqueue_entities.inc b/modules/smartqueue_entities/plugins/entityqueue/handler/smartqueue_entities.inc
new file mode 100644
index 0000000..d7172aa
--- /dev/null
+++ b/modules/smartqueue_entities/plugins/entityqueue/handler/smartqueue_entities.inc
@@ -0,0 +1,12 @@
+<?php
+
+/**
+ * @file
+ * Entityqueue handler for multiple subqueues.
+ */
+
+$plugin = array(
+  'title' => t('Smartqueue entities subqueues'),
+  'class' => 'SmartQueueEntitiesQueueHandler',
+  'queue type' => 'multiple',
+);
diff --git a/modules/smartqueue_entities/smartqueue_entities.info b/modules/smartqueue_entities/smartqueue_entities.info
new file mode 100644
index 0000000..72d9960
--- /dev/null
+++ b/modules/smartqueue_entities/smartqueue_entities.info
@@ -0,0 +1,6 @@
+name = Smartqueue Entities
+description = Automatically creates a subqueue per entity. For example to have a separate subqueue for each taxonomy term. 
+package = Entityqueue
+core = 7.x
+
+dependencies[] = entityqueue
\ No newline at end of file
diff --git a/modules/smartqueue_entities/smartqueue_entities.install b/modules/smartqueue_entities/smartqueue_entities.install
new file mode 100644
index 0000000..dbb4700
--- /dev/null
+++ b/modules/smartqueue_entities/smartqueue_entities.install
@@ -0,0 +1,13 @@
+<?php
+/**
+ * @file
+ * Smartqueue Entities installation and update functions.
+ */
+
+/**
+ * Implements hook_uninstall().
+ */
+function smartqueue_entities_uninstall() {
+  // @todo ensure our subqueues are removed before being disabled
+  variable_del('smartqueue_entities_by_source');
+}
\ No newline at end of file
diff --git a/modules/smartqueue_entities/smartqueue_entities.module b/modules/smartqueue_entities/smartqueue_entities.module
new file mode 100644
index 0000000..b57b419
--- /dev/null
+++ b/modules/smartqueue_entities/smartqueue_entities.module
@@ -0,0 +1,106 @@
+<?php
+/**
+ * @file
+ * Automatically creates a subqueue per entity. For example to have a separate subqueue for each taxonomy term.
+ */
+
+/**
+ * Implements hook_ctools_plugin_directory().
+ */
+function smartqueue_entities_ctools_plugin_directory($module, $plugin) {
+  return 'plugins/' . $module . '/' . $plugin;
+}
+
+/**
+ * Implements hook_entity_insert().
+ */
+function smartqueue_entities_entity_insert($entity, $entity_type) {
+  $handlers = smartqueue_entities_get_handlers_for_entity($entity_type, $entity);
+  foreach ($handlers as $handler) {
+    if ($handler instanceof SmartQueueEntitiesQueueHandler) {
+      $subqueue = $handler->createSubqueueForEntity($entity);
+      entity_save('entityqueue_subqueue', $subqueue);
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_update().
+ */
+function smartqueue_entities_entity_update($entity, $entity_type) {
+  $handlers = smartqueue_entities_get_handlers_for_entity($entity_type, $entity);
+  foreach ($handlers as $handler) {
+    if ($handler instanceof SmartQueueEntitiesQueueHandler) {
+      $subqueue = $handler->loadSubqueueForEntity($entity);
+      if (!$subqueue) {
+        $subqueue = $handler->createSubqueueForEntity($entity);
+      }
+      $subqueue->label = $handler->getSubqueueLabel($subqueue);
+      entity_save('entityqueue_subqueue', $subqueue);
+    }
+  }
+}
+
+/**
+ * Implements hook_entity_delete().
+ */
+function smartqueue_entities_entity_delete($entity, $entity_type) {
+  $handlers = smartqueue_entities_get_handlers_for_entity($entity_type, $entity);
+  foreach ($handlers as $handler) {
+    if ($handler instanceof SmartQueueEntitiesQueueHandler) {
+      $subqueue = $handler->loadSubqueueForEntity($entity);
+      if ($subqueue) {
+        entity_delete('entityqueue_subqueue', $subqueue->subqueue_id);
+      }
+    }
+  }
+}
+
+/**
+ * Selects just the target bundles dropdown to be returned for re-rendering.
+ *
+ * @return array
+ *  A renderable array representing the target bundles dropdown.
+ */
+function smartqueue_entities_entity_type_callback($form, $form_state) {
+  return $form['handler_settings']['smartqueue_entities']['source_bundles'];
+}
+
+/**
+ * Form #element_validate callback: Cleanup source bundles in the form state.
+ */
+function smartqueue_entities_element_validate_source_bundles($element, &$form_state) {
+  $value = array_filter($element['#value']);
+  form_set_value($element, $value, $form_state);
+}
+
+/**
+ * Get a list of smartqueue handlers interested in the given entity.
+ */
+function smartqueue_entities_get_handlers_for_entity($entity_type, $entity) {
+  $handlers = array();
+
+  list($entity_id, $revision_id, $bundle) = entity_extract_ids($entity_type, $entity);
+  if (!$bundle) {
+    $bundle = $entity_type;
+  }
+  $queues = variable_get('smartqueue_entities_by_source', array());
+  $queues_to_load = array();
+  if (isset($queues[$entity_type])) {
+    foreach ($queues[$entity_type] as $queue_name => $source_bundles) {
+      if (in_array($bundle, $source_bundles)) {
+        $queues_to_load[]= $queue_name;
+      }
+    }
+  }
+
+  if (empty($queues_to_load)) {
+    return $handlers;
+  }
+
+  $entityqueues = entityqueue_queue_load_multiple($queues_to_load);
+  foreach ($entityqueues as $entityqueue) {
+    $handlers[] = entityqueue_get_handler($entityqueue);
+  }
+  return $handlers;
+}
\ No newline at end of file
-- 
2.11.0 (Apple Git-81)

