diff --git a/og.info b/og.info index 28f131c..04f4b71 100644 --- a/og.info +++ b/og.info @@ -16,7 +16,7 @@ files[] = includes/og.membership.inc files[] = includes/og.membership_type.inc files[] = includes/views/og.views.inc -; Test files. +; Test files files[] = og.test files[] = tests/upgrade/og.upgrade.test diff --git a/og.module b/og.module index cc69685..efa5e55 100644 --- a/og.module +++ b/og.module @@ -1583,6 +1583,140 @@ function og_get_membership($group_type, $gid, $entity_type, $etid) { return FALSE; } +/** + * Implements hook_entity_query_alter(). + * + * Add "og_membership" tag if there's a group audience field in the query. + * + * @see og_query_og_membership_alter(). + */ +function og_entity_query_alter(EntityFieldQuery $query) { + foreach ($query->fieldConditions as $values) { + if (og_is_group_audience_field($values['field']['field_name'])) { + $query->addTag('og_membership'); + return; + } + } +} + +/** + * Implements hook_query_QUERY_TAG_alter(). + * + * Join the {og_membership} table and alter the query. + */ +function og_query_og_membership_alter(QueryAlterableInterface $query) { + $tables = &$query->getTables(); + $fields = &$query->getFields(); + $conditions = &$query->conditions(); + + // Find the group-audience fields. + $field_names = array(); + foreach ($query->alterMetaData['entity_field_query']->fieldConditions as $values) { + $field_name = $values['field']['field_name']; + if (og_is_group_audience_field($field_name)) { + $field_names[] = $field_name; + } + } + + $aliases = array(); + $base_table = FALSE; + $base_table_alias = ''; + + foreach ($tables as $alias => $values) { + if (!$base_table_alias && empty($values['join type'])) { + $base_table_alias = $alias; + } + + if (strpos($alias, 'field_data') !== 0) { + continue; + } + $field_name = substr($values['table'], 11); + if (!in_array($field_name, $field_names)) { + continue; + } + + if (empty($values['join type'])) { + // This is the base table, so remove it in favor of OG membership. + $base_table = TRUE; + } + + unset($tables[$alias]); + $aliases[$alias] = $field_name; + } + + foreach ($aliases as $alias => $field_name) { + foreach ($tables as $key => $values) { + $condition = str_replace("$alias.entity_type", 'ogm.entity_type', $values['condition']); + $condition = str_replace("$alias.entity_id", 'ogm.etid', $condition); + $tables[$key]['condition'] = $condition; + } + } + + if ($base_table) { + // Point the revision ID and bundle to the base entity. + $entity_type = $query->alterMetaData['entity_field_query']->entityConditions['entity_type']['value']; + $entity_type = is_array($entity_type) ? $entity_type[0] : $entity_type; + $entity_info = entity_get_info($entity_type); + + // If the table of the base entity does not exist (e.g. there is no + // property condition), we need to add it, as we don't have the + // revision ID and bundle in {og_membership} table. + if (strpos($base_table_alias, 'field_data') === 0) { + $id = $entity_info['entity keys']['id']; + $query->innerJoin($entity_type, $entity_type, "$entity_type.$id = ogm.etid"); + } + + $fields['revision_id']['table'] = $entity_type; + $fields['revision_id']['field'] = $entity_info['entity keys']['revision']; + + $fields['bundle']['table'] = $entity_type; + $fields['bundle']['field'] = $entity_info['entity keys']['bundle']; + + $fields['entity_type']['table'] = 'ogm'; + $fields['entity_id']['table'] = 'ogm'; + $fields['entity_id']['field'] = 'etid'; + + $ogm = array( + 'join type' => NULL, + 'table' => 'og_membership', + 'alias' => 'ogm', + 'condition' => '', + 'arguments' => array(), + ); + + $tables = array_merge(array('ogm' => $ogm), $tables); + } + else { + $query->join('og_membership', 'ogm', "ogm.etid = $base_table_alias.entity_id"); + } + + foreach ($conditions as $delta => $values) { + if (!is_array($values)) { + continue; + } + if (strpos($values['field'], 'field_data_') !== 0) { + continue; + } + + $args = explode('.', $values['field']); + + if (empty($aliases[$args[0]])) { + continue; + } + $field_name = $aliases[$args[0]]; + + if ($args[1] == 'deleted') { + unset($conditions[$delta]); + } + elseif (strpos($args[1], 'target_id')) { + $conditions[$delta]['field'] = 'ogm.gid'; + } + else { + $conditions[$delta]['field'] = 'ogm.' . $args[1]; + } + } +} + /** * Inserts or updates an OG membership entity into the database. @@ -1792,7 +1926,7 @@ function og_check_field_cardinality($entity_type, $entity, $field_name) { @param $group_type * The entity type of the group. * @param $gid - * The group ID. + * The group entity or ID. * @param $values * Array with the information to pass along, until it is processed in the * field handlers. @@ -1834,8 +1968,16 @@ function og_group($group_type, $gid, $values = array()) { $bundle = $wrapper->getBundle(); $id = $wrapper->getIdentifier(); - $group = entity_load_single($group_type, $gid); - list(,, $group_bundle) = entity_extract_ids($group_type, $group); + if (is_object($gid)) { + $group = $gid; + + } + else { + $group = entity_load_single($group_type, $gid); + } + + // the group ID might be the entity, so re-popualte it. + list($gid,, $group_bundle) = entity_extract_ids($group_type, $group); // Get membership if exists. $og_membership = og_get_membership($group_type, $gid, $entity_type, $id); diff --git a/og.test b/og.test index 099e3fa..401eee8 100644 --- a/og.test +++ b/og.test @@ -1046,3 +1046,191 @@ class OgRoleRevoke extends DrupalWebTestCase { } } +/** + * Test queying group-audience fields using entityFieldQuery. + */ +class OgEntityFieldQueryTestCase extends DrupalWebTestCase { + + public static function getInfo() { + return array( + 'name' => 'OG audience fields query', + 'description' => 'Test queying group-audience fields using entityFieldQuery.', + 'group' => 'Organic groups', + ); + } + + function setUp() { + parent::setUp('og', 'entity_feature'); + + $user1 = $this->drupalCreateUser(); + $type = $this->drupalCreateContentType(); + $group_type = $type->type; + + $type = $this->drupalCreateContentType(); + $group_content_type = $type->type; + + og_create_field(OG_GROUP_FIELD, 'node', $group_type); + og_create_field(OG_GROUP_FIELD, 'entity_test', 'main'); + + // Add audience field to reference node. + $og_field = og_fields_info(OG_AUDIENCE_FIELD); + og_create_field('og_node', 'node', $group_content_type, $og_field); + + // Add audience field to reference entity-test, and user. + $og_field['field']['settings']['target_type'] = 'entity_test'; + og_create_field('og_entity_test', 'node', $group_content_type, $og_field); + og_create_field('og_entity_test', 'user', 'user', $og_field); + + // Create a non-group audience, entity-refence field. + $field = array( + 'entity_types' => array('node'), + 'settings' => array( + 'handler' => 'base', + 'target_type' => 'node', + 'handler_settings' => array( + 'target_bundles' => array(), + ), + ), + 'field_name' => 'node_reference', + 'type' => 'entityreference', + 'cardinality' => 1, + ); + $field = field_create_field($field); + $instance = array( + 'field_name' => 'node_reference', + 'bundle' => $group_content_type, + 'entity_type' => 'node', + ); + field_create_instance($instance); + + // Create two groups. + $group1 = entity_create('entity_test', array('name' => 'main', 'uid' => $user1->uid)); + $wrapper = entity_metadata_wrapper('entity_test', $group1); + $wrapper->{OG_GROUP_FIELD}->set(1); + $wrapper->save(); + + $settings = array( + 'type' => $group_type, + 'uid' => $user1->uid, + ); + $settings[OG_GROUP_FIELD][LANGUAGE_NONE][0]['value'] = 1; + $group2 = $this->drupalCreateNode($settings); + + $settings = array( + 'type' => $group_content_type, + 'uid' => $user1->uid, + ); + $node = $this->drupalCreateNode($settings); + + $wrapper = entity_metadata_wrapper('node', $node); + $wrapper->node_reference->set($group2); + $wrapper->save(); + + $values = array( + 'entity_type' => 'node', + 'entity' => $node, + ); + + og_group('entity_test', $group1, $values); + og_group('node', $group2, $values); + + $this->group1 = $group1; + $this->group2 = $group2; + $this->node = $node; + $this->user1 = $user1; + } + + /** + * Test the following query scenarios: + * + * - Single group audience. + * - Multiple group audience. + * - Single group audience first, with another non-audience field. + * - Non-audience field first, with single group audience. + * - Multiple entity types in entityCondition(). + * - No entity property. + * - Count query. + */ + function testEntityFieldQuery() { + $group1 = $this->group1; + $group2 = $this->group2; + $node = $this->node; + $user1 = $this->user1; + + // Single group audience. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('type', $node->type) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->execute(); + + $this->assertEqual(array_keys($result['node']), array($node->nid), 'Single group audience query is correct.'); + + // Multiple group audience. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('type', $node->type) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->fieldCondition('og_entity_test', 'target_id', $group1->pid) + ->execute(); + + $this->assertEqual(array_keys($result['node']), array($node->nid), 'Multiple group audience query is correct.'); + + // Single group audience first, with another non-audience field. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('type', $node->type) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->fieldCondition('node_reference', 'target_id', $group2->nid) + ->execute(); + + $this->assertEqual(array_keys($result['node']), array($node->nid), 'Single group audience first, with another non-audience field query is correct.'); + + // Non-audience field first, with single group audience. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('type', $node->type) + ->fieldCondition('node_reference', 'target_id', $group2->nid) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->execute(); + + $this->assertEqual(array_keys($result['node']), array($node->nid), 'Non-audience field first, with single group audience query is correct.'); + + // Multiple entity types in entityCondition(). + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', array('node', 'user'), 'IN') + ->fieldCondition('node_reference', 'target_id', $group2->nid) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->execute(); + + $this->assertEqual(array_keys($result['node']), array($node->nid), 'Multiple entity types in entityCondition() query is correct.'); + + // No entity property. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', array('node', 'user'), 'IN') + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->execute(); + + $this->assertTrue(array_keys($result['node']) == array($node->nid) && array_keys($result['user']) == array($user1->uid), 'Multiple entity types in entityCondition() query is correct.'); + + // Count query. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('type', $node->type) + ->fieldCondition('og_node', 'target_id', $group2->nid) + ->count() + ->execute(); + + $this->assertEqual($result, 1, 'Count query is correct.'); + } +} + + +