diff --git a/includes/og_vocab.og_vocab.inc b/includes/og_vocab.og_vocab.inc index 54f06d477d3579105b60e0dc8a327c43813867a6..868a2223762252367e398940cec8fc0d994a9018 100644 --- a/includes/og_vocab.og_vocab.inc +++ b/includes/og_vocab.og_vocab.inc @@ -205,6 +205,35 @@ class OgVocab extends Entity { parent::save(); } + + /** + * Overrides Entity::delete(). + */ + public function delete() { + $use_queue = variable_get('og_use_queue', FALSE) && variable_get('og_vocab_use_queue', FALSE) && module_exists('advancedqueue'); + + if ($use_queue) { + // Add item to the queue. + $group = og_vocab_relation_get($this->vid); + $task = array( + 'vid' => $this->vid, + 'entity_type' => $this->entity_type, + 'bundle' => $this->bundle, + 'field_name' => $this->field_name, + 'group_type' => $group->group_type, + 'gid' => $group->gid, + // The last processed OG-membership ID. + 'last_id' => 0, + ); + + $queue = DrupalQueue::get('og_vocab'); + $queue->createItem($task); + } + + // After defining the item data, if we used queue, delete the entity. + parent::delete(); + + } } /** diff --git a/og_vocab.module b/og_vocab.module index 2f2184522022210e36b9e0ab65a7b879eb671bf6..56679b6467b2727a8b35be53699d6307e1988209 100644 --- a/og_vocab.module +++ b/og_vocab.module @@ -1138,6 +1138,27 @@ function og_vocab_form_alter(&$form, $form_state, $form_id) { $form['#submit'][] = 'og_vocab_redirect_to_group_vocabularies'; } +/** + * Implements hook_form_FORM_ID_alter(). + */ +function og_vocab_form_og_ui_admin_settings_alter(&$form, $form_state) { + $form['og_vocab_use_queue'] = array( + '#type' => 'checkbox', + '#title' => t('Delete OG vocab terms reference with the queue'), + '#description' => t('Use the advance queue for deleting the OG vocabulary term reference when OG vocabulary is deleted.'), + '#default_value' => variable_get('og_vocab_use_queue', FALSE), + '#disabled' => !module_exists('advancedqueue'), + '#states' => array( + 'visible' => array( + ':input[name="og_use_queue"]' => array('checked' => TRUE), + ), + ), + '#attributes' => array( + 'class' => array('entityreference-settings'), + ), + ); +} + /** * After deleting the group vocabulary, redirect to the taxonomy group admin * page. @@ -1421,3 +1442,96 @@ function og_vocab_modules_enabled($modules) { Migration::registerMigration('OgVocabMigrate', 'OgVocabMigrate' . ucfirst($bundle), array('bundle' => $bundle)); } } + +/** + * Implements hook_advanced_queue_info(). + */ +function og_vocab_advanced_queue_info() { + $items['og_vocab'] = array( + 'worker callback' => 'og_vocab_advancedqueue_worker', + // Message-subscribe will deal itself with deleting claimed items. + 'delete when completed' => FALSE, + ); + return $items; +} + +/** + * Advanced queue worker; Process a queue item. + * + * The item holds the Message ID, and user ID of the last user the email + * was sent to. + */ +function og_vocab_advancedqueue_worker($item, $end_time = FALSE) { + $queue = DrupalQueue::get('og_vocab'); + $data = $item->data; + extract($data); + + // Get range of the group content. + // We don't use entityFieldQuery() as we want to join to the entity base + // table, to make sure we process only entities from the correct bundle. + $entity_info = entity_get_info($entity_type); + + $query = db_select('og_membership', 'ogm'); + $query->fields('ogm', array('id', 'entity_type', 'etid')); + $query + ->condition('ogm.group_type', $group_type) + ->condition('ogm.gid', $gid) + ->condition('ogm.entity_type', $entity_type) + // The last processed OG-membership ID. + ->condition('ogm.id', $last_id, '>') + ->orderBy('ogm.id'); + + $base_table = $entity_info['base table']; + $bundle_key = $entity_info['entity keys']['bundle']; + $id_key = $entity_info['entity keys']['id']; + + $alias = $query->innerJoin($base_table, NULL, "ogm.etid = $base_table.$id_key"); + $result = $query + ->condition("$alias.$bundle_key", $bundle) + ->range(0, 100) + ->execute() + ->fetchAllAssoc('id'); + + if (empty($result)) { + // No more group-content for this entity, so we can delete the task item. + return $queue->deleteItem($item); + } + + foreach ($result as $row) { + $last_id = $row->id; + $wrapper = entity_metadata_wrapper($row->entity_type, $row->etid); + if (!$terms = $wrapper->{$field_name}->value()) { + // Field doesn't have any term, so we can skip. + continue; + } + + // Array with the original and new term IDs. + $original_tids = array(); + $new_tids = array(); + foreach ($terms as $term) { + $original_tids[] = $term->tid; + + if ($term->vid != $vid) { + $new_tids[] = $term->tid; + } + } + if ($original_tids != $new_tids) { + // Some terms were removed so re-save the entity. + $wrapper->{$field_name}->set($new_tids); + $wrapper->save(); + } + } + + // Update the item with the last OG-membership ID. + $item->data['last_id'] = $last_id; + db_update('advancedqueue') + ->fields(array( + 'data' => serialize($item->data), + )) + ->condition('item_id', $item->item_id) + ->execute(); + + // Release the item. + $queue->releaseItem($item); + return TRUE; +} diff --git a/og_vocab.test b/og_vocab.test index 57041a02653ac7725ff4f786b76660678314e790..bf3383aec03bea1b59ad5ee7fb1a5543cf51ba55 100644 --- a/og_vocab.test +++ b/og_vocab.test @@ -478,3 +478,116 @@ class OgVocabApiTestCase extends DrupalWebTestCase { } } +class OgVocabUnbindFromContentType extends DrupalWebTestCase { + private $user; + private $group; + private $first_group_content; + private $second_group_content; + + public static function getInfo() { + return array( + 'name' => 'OG vocabulary removing vocabulary from content type.', + 'description' => 'Check the removing of the vocabulary delete the OG vocabulary term reference from nodes.', + 'group' => 'Organic groups vocabulary', + 'dependencies' => array('advancedqueue'), + ); + } + + public function setup() { + parent::setUp('og_vocab', 'advancedqueue'); + + // Create group. + $group_type = $this->drupalCreateContentType(); + og_create_field(OG_GROUP_FIELD, 'node', $group_type->type); + + $settings = array(); + $settings['type'] = $group_type->type; + $settings[OG_GROUP_FIELD][LANGUAGE_NONE][0]['value'] = 1; + $this->group = $this->drupalCreateNode($settings); + + // Create two groups type. + $type = $this->drupalCreateContentType(); + og_create_field(OG_AUDIENCE_FIELD, 'node', $type->type); + $this->first_group_content = $this->drupalCreateNode(array( + 'type' => $type->type, + )); + + $wrapper = entity_metadata_wrapper('node', $this->first_group_content); + $wrapper->{OG_AUDIENCE_FIELD}->set(array($this->group)); + $wrapper->save(); + + $type = $this->drupalCreateContentType(); + og_create_field(OG_AUDIENCE_FIELD, 'node', $type->type); + $this->second_group_content = $this->drupalCreateNode(array( + 'type' => $type->type, + )); + + $wrapper = entity_metadata_wrapper('node', $this->second_group_content); + $wrapper->{OG_AUDIENCE_FIELD}->set(array($this->group)); + $wrapper->save(); + + variable_set('og_use_queue', TRUE); + variable_set('og_vocab_use_queue', TRUE); + } + + /** + * When a vocabulary is related to a bundle and it's term are reference to the + * group content, un-bind it from the bundle the terms should not be + * reference any more to the node. + */ + public function testUnbindContentType() { + $groups_contents = array($this->first_group_content, $this->second_group_content); + + // Create a dummy vocabulary. + $vocabulary = new stdClass(); + $vocabulary->name = "Vocabulary " . $this->group->nid; + $vocabulary->machine_name = "vocabulary_" . $this->group->nid; + taxonomy_vocabulary_save($vocabulary); + + // Relate vocabulary to group. + og_vocab_relation_save($vocabulary->vid, 'node', $this->group->nid); + + // Create the vocabulary. + foreach ($groups_contents as $groups_content) { + // Create OG vocabulary entity. + $og_vocab = og_vocab_create_og_vocab($vocabulary->vid, 'node', $groups_content->type); + $og_vocab->save(); + + // Create terms in the vocabulary. + $terms = array(); + for ($i = 1; $i < 3; ++$i) { + $term = new stdClass(); + $term->name = "Vocab $vocabulary->vid term $i"; + $term->vid = $vocabulary->vid; + taxonomy_term_save($term); + $terms[] = $term->tid; + } + + // Reference between the taxonomy terms and the nodes. + $wrapper = entity_metadata_wrapper('node', $groups_content); + $wrapper->{OG_VOCAB_FIELD}->set($terms); + $wrapper->save(); + } + + // Verify the terms are attached to the nodes. + foreach ($groups_contents as $groups_content) { + $node = node_load($groups_content->nid); + $this->assertTrue(!empty($node->{OG_VOCAB_FIELD}), format_string('Terms are attached to the node @title.', array('@title' => $groups_content->title))); + } + + // Delete the last vocabulary. + $og_vocab->delete(); + + // Execute manually the queue worker. + $queue = DrupalQueue::get('og_vocab'); + $item = $queue->claimItem(); + og_vocab_advancedqueue_worker($item); + + // Verify the terms are attached to the nodes. + $node = node_load($this->first_group_content->nid); + $this->assertTrue(!empty($node->{OG_VOCAB_FIELD}), format_string('Terms are attached to the node @title.', array('@title' => $this->first_group_content->title))); + + $node = node_load($this->second_group_content->nid); + $this->assertTrue(empty($node->{OG_VOCAB_FIELD}), format_string('There are no terms attached to the node @title.', array('@title' => $this->second_group_content->title))); + } +}