diff --git a/sites/all/modules/og/README.txt b/sites/all/modules/og/README.txt index fb54ccb7273b135cae1027efc342fc784127a287..94f3e6d0ff65cce295a22677313ffb7d5fce04d7 100644 --- a/sites/all/modules/og/README.txt +++ b/sites/all/modules/og/README.txt @@ -112,6 +112,15 @@ DEVELOPERS & SITE BUILDERS http://drupal.org/project/entityreference_prepopulate and configuring the correct settings in the field UI. Read more about it in Entity reference prepopulate's README file. + Further more, when Entity reference prepopulate module is enabled the node + "create" permissions will be enabled even for non-members. In order to allow + a non member to create a node to a group they don't belong to, you should + craft the URL in the same way. OG will recognize this situation and add the + group as a valid option under the "My groups" widget. +- When deleting groups, it is possible to delete orphan group-content, or move + it under another group. In order to do it in a scalable way, enable the + "Use queue" option, and process it using for example: + drush queue-run og_membership_orphans FAQ ---- diff --git a/sites/all/modules/og/includes/actions/user_roles.action.inc b/sites/all/modules/og/includes/actions/user_roles.action.inc index 20c33a12b08bd4b7aa29a4fc4d04e7f1e69b70f2..d436d1566951129c9ece12b70ae11ed37c00fcb3 100644 --- a/sites/all/modules/og/includes/actions/user_roles.action.inc +++ b/sites/all/modules/og/includes/actions/user_roles.action.inc @@ -9,11 +9,20 @@ function og_user_roles_action_info() { } function og_user_roles_action_form($context) { - // Get the group type and group ID from the Views arguments. - list($group_type, $gid) = $context['view']->args; + $gid = ''; + $group_type = ''; - if(empty($group_type) || empty($gid)) { - return; + if (module_exists('og_context') && $og_context = og_context()) { + // Get the group type and group ID from OG context. + $gid = $og_context['gid']; + $group_type = $og_context['group_type']; + } + elseif (empty($gid) || empty($group_type)) { + // Get the group type and group ID from the Views arguments if they exist. + list($group_type, $gid) = $context['view']->args; + if (empty($group_type) || empty($gid)) { + return; + } } if (!entity_get_info($group_type)) { diff --git a/sites/all/modules/og/includes/migrate/7000/og_add_fields.inc b/sites/all/modules/og/includes/migrate/7000/og_add_fields.inc index d9ba0421eca4866835769e1b4667561267ffb524..53b84c9a25f8c7473c9f94a920d4348ecf7f84f5 100644 --- a/sites/all/modules/og/includes/migrate/7000/og_add_fields.inc +++ b/sites/all/modules/og/includes/migrate/7000/og_add_fields.inc @@ -5,8 +5,6 @@ * Add OG related fields to group and group-content node-types. */ -if (db_table_exists('d6_og')) { - class OgMigrateAddFields extends MigrationBase { public function __construct() { @@ -92,5 +90,3 @@ class OgMigrateAddFields extends MigrationBase { return MigrationBase::RESULT_COMPLETED; } } - -} diff --git a/sites/all/modules/og/includes/migrate/7000/og_content.inc b/sites/all/modules/og/includes/migrate/7000/og_content.inc index cb463e3a6fd32fdc4a1d4c6b757518582c936490..ef837bff7f32654a8b0d59810df07737399ca827 100644 --- a/sites/all/modules/og/includes/migrate/7000/og_content.inc +++ b/sites/all/modules/og/includes/migrate/7000/og_content.inc @@ -6,8 +6,6 @@ * value. */ -if (db_table_exists('d6_og')) { - class OgMigrateContent extends Migration { public function __construct() { @@ -58,5 +56,3 @@ class OgMigrateContent extends Migration { $this->addFieldMapping('etid', 'nid'); } } - -} diff --git a/sites/all/modules/og/includes/migrate/7000/og_group.inc b/sites/all/modules/og/includes/migrate/7000/og_group.inc index 9d17ecf8586c468e655f0dfae44ebabe4d833adf..0af03c1cd66ad91d526075055006cfc9b671306d 100644 --- a/sites/all/modules/og/includes/migrate/7000/og_group.inc +++ b/sites/all/modules/og/includes/migrate/7000/og_group.inc @@ -5,8 +5,6 @@ * Upgrade nodes that are groups, by settings the correct field value. */ -if (db_table_exists('d6_og')) { - class OgMigrateGroup extends DynamicMigration { /** @@ -54,5 +52,3 @@ class OgMigrateGroup extends DynamicMigration { return drupal_strtolower('OgMigrateGroup' . ucfirst($this->arguments['bundle'])); } } - -} diff --git a/sites/all/modules/og/includes/migrate/7000/og_user.inc b/sites/all/modules/og/includes/migrate/7000/og_user.inc index 0755f469845cecca5cf1dca65e2ab1f057ac762c..19e02d6dd041d11f466a81912858fa29f2eb437f 100644 --- a/sites/all/modules/og/includes/migrate/7000/og_user.inc +++ b/sites/all/modules/og/includes/migrate/7000/og_user.inc @@ -5,8 +5,6 @@ * Create user relation to group. */ -if (db_table_exists('d6_og')) { - class OgMigrateUser extends Migration { public function __construct() { @@ -64,5 +62,3 @@ class OgMigrateUser extends Migration { parent::prepareRow($row); } } - -} diff --git a/sites/all/modules/og/includes/migrate/7200/og_og_membership.migrate.inc b/sites/all/modules/og/includes/migrate/7200/og_og_membership.migrate.inc index d60e31dfb836850ce066160da22da6267997b91e..6137ba56239673f01648c7fb9ea5d8dd9f1c49d4 100644 --- a/sites/all/modules/og/includes/migrate/7200/og_og_membership.migrate.inc +++ b/sites/all/modules/og/includes/migrate/7200/og_og_membership.migrate.inc @@ -8,8 +8,6 @@ * to branch 7.x-2.x */ -if (db_field_exists('og_membership', 'group_type') && db_table_exists('og') && !db_table_exists('d6_og')) { - class OgMigrateMembership extends OgEntityMigration { /** @@ -50,7 +48,10 @@ class OgMigrateMembership extends OgEntityMigration { } } - public function prepare($entity, $row) { + /** + * Reject the source row if the group or group content are missing. + */ + public function prepareRow($row) { $entity_type = $row->entity_type; $etid = $row->etid; $group_type = $row->group_type; @@ -59,27 +60,46 @@ class OgMigrateMembership extends OgEntityMigration { if (!$group_content = entity_load_single($entity_type, $etid)) { // The OG membership was somehow not deleted when the entity // was deleted. - return; + return FALSE; } if (!$group = entity_load_single($group_type, $gid)) { - return; + return FALSE; } + return parent::prepareRow($row); + } + + public function prepare($entity, $row) { + $entity_type = $row->entity_type; + $etid = $row->etid; + $group_type = $row->group_type; + $gid = $row->gid; + + $group_content = entity_load_single($entity_type, $etid); + $group = entity_load_single($group_type, $gid); list(,, $group_bundle) = entity_extract_ids($group_type, $group); if (!$field_name = og_get_best_group_audience_field($entity_type, $group_content, $group_type, $group_bundle)) { - // Create a new field. Pick an unused name. - $field_name = substr("og_$group_type", 0, 32); + // Create a new field. Pick an unused name, if the settings don't match. + // To maintain some backwards compatibility, if the group type is a node, + // we try to set its name to OG_AUDIENCE FIELD. + $field_name = $group_type == 'node' ? OG_AUDIENCE_FIELD : substr("og_$group_type", 0, 32); $i = 1; - while (field_info_field($field_name)) { - $field_name = substr("og_$group_type", 0, 32 - strlen($i)) . $i; - ++$i; - } $og_field = og_fields_info(OG_AUDIENCE_FIELD); $og_field['field']['settings']['target_type'] = $group_type; - list(,, $bundle) = entity_extract_ids($entity_type, $group_content); + + while ($field = field_info_field($field_name)) { + if ($field['settings']['target_type'] == $group_type && empty($field['settings']['handler_settings']['target_bundles']) || in_array($bundle, $field['settings']['handler_settings']['target_bundles'])) { + // An existing field. + $field_name = $field['field_name']; + break; + } + $field_name = substr("og_$group_type", 0, 32 - strlen($i)) . $i; + ++$i; + } + og_create_field($field_name, $entity_type, $bundle, $og_field); } $entity->field_name = $field_name; @@ -88,8 +108,7 @@ class OgMigrateMembership extends OgEntityMigration { /** * Override Migration::postImport(). * - * Remove OG-memberships that should hae been deleted. - * + * Remove OG-memberships that should have been deleted. */ protected function postImport() { if (!$this->isComplete()) { @@ -100,5 +119,3 @@ class OgMigrateMembership extends OgEntityMigration { ->execute(); } } - -} diff --git a/sites/all/modules/og/includes/migrate/7200/og_roles.migrate.inc b/sites/all/modules/og/includes/migrate/7200/og_roles.migrate.inc index cdc40b11a7a7c2fa392c1ac15aa49c97e38cc9c6..fae9b32b9c8590601e95b6b9cf361ad11b2733bd 100644 --- a/sites/all/modules/og/includes/migrate/7200/og_roles.migrate.inc +++ b/sites/all/modules/og/includes/migrate/7200/og_roles.migrate.inc @@ -9,16 +9,12 @@ */ -if (db_field_exists('og_membership', 'group_type') && db_table_exists('og') && !db_table_exists('d6_og')) { - class OgMigrateRoles extends OgEntityMigration { public $tableName = 'og_role'; public $keyName = 'rid'; - protected $dependencies = array('OgMigrateMembership'); - /** * Indicate we are updating existing data. */ @@ -106,5 +102,3 @@ class OgMigrateRoles extends OgEntityMigration { $entity->group_bundle = $bundle; } } - -} \ No newline at end of file diff --git a/sites/all/modules/og/includes/migrate/7200/og_user_roles.migrate.inc b/sites/all/modules/og/includes/migrate/7200/og_user_roles.migrate.inc index 067bc9a113de0f272704bfc1eb1ba2d2e48b7d85..44e40f6bf995e82353b7153819324087396f2ea2 100644 --- a/sites/all/modules/og/includes/migrate/7200/og_user_roles.migrate.inc +++ b/sites/all/modules/og/includes/migrate/7200/og_user_roles.migrate.inc @@ -8,8 +8,6 @@ * to branch 7.x-2.x */ -if (db_field_exists('og_membership', 'group_type') && db_table_exists('og') && !db_table_exists('d6_og')) { - class OgMigrateUserRoles extends OgEntityMigration { public $tableName = 'og_users_roles'; @@ -108,5 +106,3 @@ class OgMigrateUserRoles extends OgEntityMigration { } } } - -} diff --git a/sites/all/modules/og/includes/migrate/plugins/destinations/og_membership.inc b/sites/all/modules/og/includes/migrate/plugins/destinations/og_membership.inc index ffd33350886241036ae2859aba5345bf70993753..3af3b704cd821a8414946418255542b50ef8b5f8 100644 --- a/sites/all/modules/og/includes/migrate/plugins/destinations/og_membership.inc +++ b/sites/all/modules/og/includes/migrate/plugins/destinations/og_membership.inc @@ -64,10 +64,10 @@ class MigrateDestinationOGMembership extends MigrateDestination { throw new MigrateException('Missing group ID.'); } if (empty($entity->entity_type)) { - throw new MigrateException('Missing enity type.'); + throw new MigrateException('Missing entity type.'); } if (empty($entity->etid)) { - throw new MigrateException('Missing enity ID.'); + throw new MigrateException('Missing entity ID.'); } // Set state, make sure legacy states from OG6 are converted to @@ -78,13 +78,12 @@ class MigrateDestinationOGMembership extends MigrateDestination { else { $state = OG_STATE_ACTIVE; } - - $values = array( - 'entity_type' => $entity->entity_type, - 'entity' => $entity->etid, - 'state' => $state, - 'created' => isset($entity->created) ? $entity->created : REQUEST_TIME, - ); + $this->prepare($entity, $row); + // Allow passing OG-membership fields via og_group(). + $values = (array) $entity; + $values['entity'] = $entity->etid; + $values['state'] = $state; + $values['created'] = isset($entity->created) ? $entity->created : REQUEST_TIME; $og_membership = og_group($entity->group_type, $entity->gid, $values); @@ -109,9 +108,60 @@ class MigrateDestinationOGMembership extends MigrateDestination { } } + $this->complete($entity, $row); + return array($og_membership->id); } + /** + * Give handlers a shot at modifying the object before saving it. + * + * @param $entity + * OGMembership object to build. Prefilled with any fields mapped in + * the Migration. + * @param $source_row + * Raw source data object - passed through to prepare handlers. + */ + public function prepare($entity, stdClass $row) { + // We do nothing here but allow child classes to act. + $migration = Migration::currentMigration(); + $entity->migrate = array( + 'machineName' => $migration->getMachineName(), + ); + + // Call any general handlers. + migrate_handler_invoke_all('og_membership', 'prepare', $entity, $row); + // Then call any prepare handler for this specific Migration. + if (method_exists($migration, 'prepare')) { + $migration->prepare($entity, $row); + } + } + + /** + * Give handlers a shot at modifying the object (or taking additional action) + * after saving it. + * + * @param $entity + * OGMembership object to build. This is the complete object after + * saving. + * @param $source_row + * Raw source data object - passed through to complete handlers. + */ + public function complete($entity, stdClass $row) { + // We do nothing here but allow child classes to act. + $migration = Migration::currentMigration(); + $entity->migrate = array( + 'machineName' => $migration->getMachineName(), + ); + + // Call any general handlers. + migrate_handler_invoke_all('og_membership', 'complete', $entity, $row); + // Then call any complete handler for this specific Migration. + if (method_exists($migration, 'complete')) { + $migration->complete($entity, $row); + } + } + public function fields() { return array( 'group_type' => 'Group entity type', diff --git a/sites/all/modules/og/includes/og.field.inc b/sites/all/modules/og/includes/og.field.inc index 189afd5b365f23366ece54bd6780b6c4904adcb2..7f419ded31e3a96c1a6c829f6f0c63e4e5aadb92 100644 --- a/sites/all/modules/og/includes/og.field.inc +++ b/sites/all/modules/og/includes/og.field.inc @@ -31,7 +31,7 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode return; } - if ($field['settings']['handler'] != 'og') { + if ($field['settings']['handler'] != 'og' && strpos($field['settings']['handler'], 'og_') !== 0) { $params = array('%label' => $instance['label']); form_error($form, t('Field %label is a group-audience but its Entity selection mode is not defined as "Organic groups" in the field settings page.', $params)); return; @@ -42,7 +42,7 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode list($id,, $bundle) = entity_extract_ids($entity_type, $entity); $field_name = $field['field_name']; - $identifier = $field_name . ':' . $entity_type . ':' . $id; + $identifier = $field_name . ':' . $entity_type . ':' . $bundle . ':' . $id; if (isset($cache[$identifier])) { return array(); } @@ -63,18 +63,19 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode $field_modes[] = 'admin'; } - // Get the "Other group" group IDs. - $target_type = $field['settings']['target_type']; - $other_groups_ids = array(); - if ($id) { - $entity_gids = og_get_entity_groups($entity_type, $entity); - $entity_gids = !empty($entity_gids[$target_type]) ? $entity_gids[$target_type] : array(); + // Build an array of entity IDs. Field's $items are loaded + // in OgBehaviorHandler::load(). + $entity_gids = array(); + foreach ($items as $item) { + $entity_gids[] = $item['target_id']; + } - $user_gids = og_get_entity_groups(); - $user_gids = !empty($user_gids[$target_type]) ? $user_gids[$target_type] : array(); + $target_type = $field['settings']['target_type']; + $user_gids = og_get_entity_groups(); + $user_gids = !empty($user_gids[$target_type]) ? $user_gids[$target_type] : array(); - $other_groups_ids = array_diff($entity_gids, $user_gids); - } + // Get the "Other group" group IDs. + $other_groups_ids = array_diff($entity_gids, $user_gids); foreach ($field_modes as $field_mode) { $mocked_instance = og_get_mocked_instance($instance, $field_mode); @@ -135,7 +136,6 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode $new_element = ctools_field_invoke_field($mocked_instance, 'form', $entity_type, $dummy_entity, $form, $dummy_form_state, array('default' => TRUE)); $element[$field_mode] = $new_element[$field_name][LANGUAGE_NONE]; - if (in_array($mocked_instance['widget']['type'], array('entityreference_autocomplete', 'entityreference_autocomplete_tags'))) { // Change the "Add more" button name so it adds only the needed // element. @@ -143,30 +143,21 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode $element[$field_mode]['add_more']['#name'] .= '__' . $field_mode; } - foreach (array_keys($element[$field_mode]) as $delta) { - if (!is_numeric($delta)) { - continue; - } - - $sub_element = &$element[$field_mode][$delta]['target_id']; - - // Rebuild the autocomplete path. - $path = explode('/', $sub_element['#autocomplete_path']); - $sub_element['#autocomplete_path'] = 'og/autocomplete'; - - // Add autocomplete type - $sub_element['#autocomplete_path'] .= "/$path[2]/$path[3]/$path[4]/$path[5]"; + if ($mocked_instance['widget']['type'] == 'entityreference_autocomplete') { + foreach (array_keys($element[$field_mode]) as $delta) { + if (!is_numeric($delta)) { + continue; + } - // Add field mode. - $sub_element['#autocomplete_path'] .= "/$field_mode"; + $sub_element = &$element[$field_mode][$delta]['target_id']; + _og_field_widget_replace_autocomplete_path($sub_element, $field_mode); - // Add the entity ID. - $sub_element['#autocomplete_path'] .= "/$path[6]"; - if (!empty($path[7])) { - // Add the text. - $sub_element['#autocomplete_path'] .= "/$path[7]"; } } + else { + // Tags widget, there's no delta, we can pass the element itself. + _og_field_widget_replace_autocomplete_path($element[$field_mode], $field_mode); + } } } @@ -175,6 +166,34 @@ function og_field_widget_form(&$form, &$form_state, $field, $instance, $langcode return $element; } +/** + * Replace Entity-reference's autocomplete path with our own. + * + * @param $element + * The form element, passed by reference. + * @param $field_mode + * The field mode. + * + */ +function _og_field_widget_replace_autocomplete_path(&$element, $field_mode) { + // Rebuild the autocomplete path. + $path = explode('/', $element['#autocomplete_path']); + $element['#autocomplete_path'] = 'og/autocomplete'; + + // Add autocomplete type + $element['#autocomplete_path'] .= "/$path[2]/$path[3]/$path[4]/$path[5]"; + + // Add field mode. + $element['#autocomplete_path'] .= "/$field_mode"; + + // Add the entity ID. + $element['#autocomplete_path'] .= "/$path[6]"; + if (!empty($path[7])) { + // Add the text. + $element['#autocomplete_path'] .= "/$path[7]"; + } +} + /** * Property info alter; Change mocked field to be non-required. */ diff --git a/sites/all/modules/og/includes/og.membership.inc b/sites/all/modules/og/includes/og.membership.inc index 2d4d10557d96e6c21dd25cf94aa202798088fee8..23464e5f6f37978f16c766c6d9ee700c899ee192 100644 --- a/sites/all/modules/og/includes/og.membership.inc +++ b/sites/all/modules/og/includes/og.membership.inc @@ -18,6 +18,11 @@ class OgMembership extends Entity { public function save() { $entity_type = $this->entity_type; $etid = $this->etid; + + if ($entity_type == 'user' && !$etid) { + throw new OgException('OG membership can not be created for anonymous user.'); + } + $wrapper = entity_metadata_wrapper($entity_type, $etid); $bundle = $wrapper->getBundle(); @@ -27,7 +32,6 @@ class OgMembership extends Entity { list(,,$group_bundle) = entity_extract_ids($group_type, $group); $field_name = $this->field_name; - $state = !empty($this->state) ? $this->state : OG_STATE_ACTIVE; // Placeholder for exceptions, in case we need to throw one. $params = array( diff --git a/sites/all/modules/og/includes/og_features_permission.features.inc b/sites/all/modules/og/includes/og_features_permission.features.inc old mode 100644 new mode 100755 index a299642ce5e4dbd0af7ed4af00e8c108b46a18ed..4fc758fe71d20ad1432de1077ec4b31b14922845 --- a/sites/all/modules/og/includes/og_features_permission.features.inc +++ b/sites/all/modules/og/includes/og_features_permission.features.inc @@ -90,6 +90,9 @@ function og_features_permission_features_export_render($module, $data) { $permission['roles'] = array(); } + // Ensure consistent ordering of roles on output to avoid extraneious diffs. + asort($permission['roles']); + $perm_identifier = features_var_export($key); $perm_export = features_var_export($permission, ' '); $code[] = " // Exported og permission: {$perm_identifier}"; diff --git a/sites/all/modules/og/includes/views/handlers/og_handler_field_og_membership_link_edit.inc b/sites/all/modules/og/includes/views/handlers/og_handler_field_og_membership_link_edit.inc new file mode 100644 index 0000000000000000000000000000000000000000..6b93a667536bef3746a7c508bfa699fba81f68ea --- /dev/null +++ b/sites/all/modules/og/includes/views/handlers/og_handler_field_og_membership_link_edit.inc @@ -0,0 +1,75 @@ +<?php + +/** + * @file + * Definition of og_handler_field_og_membership_link_edit. + */ + +/** + * Field handler to present a link to edit membership. + * + * @ingroup views_field_handlers + */ +class og_handler_field_og_membership_link_edit extends views_handler_field_entity { + + function construct() { + parent::construct(); + } + + function option_definition() { + $options = parent::option_definition(); + $options['text'] = array('default' => '', 'translatable' => TRUE); + $options['destination'] = array('default' => FALSE, 'bool' => TRUE); + return $options; + } + + function options_form(&$form, &$form_state) { + $form['text'] = array( + '#type' => 'textfield', + '#title' => t('Text to display'), + '#default_value' => $this->options['text'], + ); + $form['destination'] = array( + '#type' => 'checkbox', + '#title' => t('Use destination'), + '#description' => t('Add destination to the link'), + '#default_value' => $this->options['destination'], + '#fieldset' => 'more', + ); + parent::options_form($form, $form_state); + } + + function query() { + $this->ensure_my_table(); + $this->add_additional_fields(); + } + + function render($values) { + $value = $this->get_value($values, 'id'); + return $this->render_link($this->sanitize_value($value), $values); + } + + function render_link($data, $values) { + $this->options['alter']['make_link'] = TRUE; + $this->options['alter']['html'] = TRUE; + + // Ensure user has access to edit this membership. + $og_membership = $this->get_value($values); + $group_type = $og_membership->group_type; + $gid = $og_membership->gid; + if (!og_ui_user_access_group('manage members', $group_type, $gid)) { + return; + } + + $text = !empty($this->options['text']) ? $this->options['text'] : t('edit'); + unset($this->options['alter']['fragment']); + + if (!empty($this->options['destination'])) { + $this->options['alter']['query'] = drupal_get_destination(); + } + + $this->options['alter']['path'] = "group/" . $group_type . "/" . $gid . "/admin/people/edit-membership/" . $og_membership->id; + + return $text; + } +} diff --git a/sites/all/modules/og/includes/views/handlers/og_handler_filter_user_roles.inc b/sites/all/modules/og/includes/views/handlers/og_handler_filter_user_roles.inc new file mode 100644 index 0000000000000000000000000000000000000000..c0573eebf0c6a88fd62cfb6a358e2c2640ba890f --- /dev/null +++ b/sites/all/modules/og/includes/views/handlers/og_handler_filter_user_roles.inc @@ -0,0 +1,34 @@ +<?php + +/** + * Field handler to provide a list of OG roles of a user. + */ +class og_handler_filter_user_roles extends views_handler_filter_many_to_one { + + function get_value_options() { + $this->value_options = array(); + // Get all role names. + $results = db_select('og_role', 'r') + ->fields('r') + ->groupBy('r.name') + ->execute(); + foreach ($results as $result) { + $this->value_options[$result->name] = $result->name; + } + } + + function query() { + // Get the og_membership table from relations. + foreach ($this->query->relationships as $alias => $info) { + if ($info['table'] == 'og_membership') { + // If there is a og_membership table present. Add some conditions. + $rel = $this->relationship; + $this->query->add_where_expression(NULL, $alias . '.gid = ' . $rel . '.gid'); + $this->query->add_where_expression(NULL, $alias . '.group_type = ' . $rel . '.group_type'); + break; + } + } + + parent::query(); + } +} diff --git a/sites/all/modules/og/includes/views/handlers/og_handler_relationship.inc b/sites/all/modules/og/includes/views/handlers/og_handler_relationship.inc index 03e23a13304028b6b92be81528dc5ce2d775923e..a6cbde38dc29751eef4099388c79b1ea1187e5b4 100644 --- a/sites/all/modules/og/includes/views/handlers/og_handler_relationship.inc +++ b/sites/all/modules/og/includes/views/handlers/og_handler_relationship.inc @@ -17,12 +17,46 @@ class og_handler_relationship extends views_handler_relationship { * Called to implement a relationship in a query. */ function query() { - $entity = $this->definition['entity']; - $this->definition['extra'][] = array( - 'field' => 'entity_type', - 'value' => $this->definition['entity'], - ); + // If the entity type is specific for the og_membership + // filter the join to select membership of those entity types. + if (isset($this->definition['entity'])) { + $extra = array( + 'field' => 'entity_type', + 'value' => $this->definition['entity'], + ); + + // Only add the table if og_membership is the left table since when the + // table is specified to views_join, it only translates the table alias + // of tables in the left position, however if no table is specified, + // then views_join correctly inserts the alias of the table in the right + // position of the join. + if ($this->definition['base'] != 'og_membership') { + $extra['table'] = 'og_membership'; + } + + $this->definition['extra'][] = $extra; + } + + // If the group type is specific for the og_membership + // filter the join to select membership of those group types. + if (isset($this->definition['group_type'])) { + $extra = array( + 'field' => 'group_type', + 'value' => $this->definition['group_type'], + ); + + // Only add the table if og_membership is the left table since when the + // table is specified to views_join, it only translates the table alias + // of tables in the left position, however if no table is specified, + // then views_join correctly inserts the alias of the table in the right + // position of the join. + if ($this->definition['base'] != 'og_membership') { + $extra['table'] = 'og_membership'; + } + + $this->definition['extra'][] = $extra; + } parent::query(); } -} \ No newline at end of file +} diff --git a/sites/all/modules/og/includes/views/handlers/og_plugin_argument_default_user_groups.inc b/sites/all/modules/og/includes/views/handlers/og_plugin_argument_default_user_groups.inc index 6c73ca7180aef2dd027a8e9ee56cc29b27de3da7..85c63608e1d21c641da43642e1d14be98e9f593e 100644 --- a/sites/all/modules/og/includes/views/handlers/og_plugin_argument_default_user_groups.inc +++ b/sites/all/modules/og/includes/views/handlers/og_plugin_argument_default_user_groups.inc @@ -33,7 +33,7 @@ class og_plugin_argument_default_user_groups extends views_plugin_argument_defau '#description' => t('Select the group type.'), '#options' => og_get_all_group_entity(), '#default_value' => $this->options['group_type'], - '#required' => TRUE, + '#required' => og_get_all_group_entity(), ); $form['glue'] = array( '#type' => 'select', diff --git a/sites/all/modules/og/includes/views/handlers/og_plugin_argument_validate_group.inc b/sites/all/modules/og/includes/views/handlers/og_plugin_argument_validate_group.inc index 6eb027d8e8f13a80b57d561ed525c7b8b155219a..a987e8bdd7ae9d5422992b33cbaa81400c9e6912 100644 --- a/sites/all/modules/og/includes/views/handlers/og_plugin_argument_validate_group.inc +++ b/sites/all/modules/og/includes/views/handlers/og_plugin_argument_validate_group.inc @@ -30,7 +30,7 @@ class og_plugin_argument_validate_group extends views_plugin_argument_validate { '#description' => t('Select the group type.'), '#options' => og_get_all_group_entity(), '#default_value' => $this->options['group_type'], - '#required' => TRUE, + '#required' => og_get_all_group_entity(), ); } diff --git a/sites/all/modules/og/includes/views/og.views.inc b/sites/all/modules/og/includes/views/og.views.inc index 8ae1b963be3c56ef0c22497df57b41cb5679b2ed..0def1f54333db4ab267b74dd3ca1b8db3ec42bac 100644 --- a/sites/all/modules/og/includes/views/og.views.inc +++ b/sites/all/modules/og/includes/views/og.views.inc @@ -43,8 +43,6 @@ class OgMembershipViewsController extends EntityDefaultViewsController { $data['og_membership']['gid']['title'] = t('Group ID'); $data['og_membership']['gid']['help'] = t('Og membership "gid" property.'); - - $data['og_membership']['og_roles'] = array( 'title' => t('OG user roles in group'), 'help' => t('Show all the roles a user belongs to in a group. Requires a relationship to users to be present.'), @@ -56,6 +54,47 @@ class OgMembershipViewsController extends EntityDefaultViewsController { ), ); + $data['og_membership']['og_users_roles'] = array( + 'title' => t('OG Roles from membership'), + 'help' => t('The OG Roles associated with the OG membership'), + // Provide a possible relationship to roles through uid. + 'relationship' => array( + 'label' => t('OG Roles from OG membership'), + 'base' => 'og_users_roles', + 'base field' => 'uid', + 'relationship field' => 'etid', + ), + ); + + $data['og_role'] = array( + 'table' => array( + 'group' => t('OG user roles'), + 'join' => array( + // Attach automatically to og_users_roles relations. + 'og_users_roles' => array( + 'left_field' => 'rid', + 'field' => 'rid', + ), + ) + ) + ); + $data['og_role']['name'] = array( + 'title' => t('Role Name'), + 'help' => t('The OG role name.'), + 'filter' => array( + 'handler' => 'og_handler_filter_user_roles', + ), + ); + + // Link to edit membership + $data['og_membership']['edit_membership'] = array( + 'field' => array( + 'title' => t('Edit link'), + 'help' => t('Provide a simple link to edit the membership.'), + 'handler' => 'og_handler_field_og_membership_link_edit', + ), + ); + return $data; } } @@ -65,9 +104,10 @@ class OgMembershipViewsController extends EntityDefaultViewsController { */ function og_views_data_alter(&$data) { $group_content_entities = og_get_all_group_content_entity(); + $group_entities = og_get_all_group_entity(); foreach (entity_get_info() as $entity_type => $info) { - if (empty($group_content_entities[$entity_type])) { + if (empty($group_content_entities[$entity_type]) && empty($group_entities[$entity_type])) { continue; } @@ -93,8 +133,8 @@ function og_views_data_alter(&$data) { 'title' => t('OG membership from @entity group', array('@entity' => ucfirst($info['label']))), 'help' => t('The OG membership associated with the @entity group', array('@entity' => ucfirst($info['label']))), 'relationship' => array( - 'entity' => $entity_type, - 'handler' => 'views_handler_relationship', + 'group_type' => $entity_type, + 'handler' => 'og_handler_relationship', 'label' => t('OG membership from @entity group', array('@entity' => $entity_type)), 'base' => 'og_membership', 'base field' => 'gid', @@ -108,7 +148,8 @@ function og_views_data_alter(&$data) { 'title' => t('@entity from OG membership', array('@entity' => ucfirst($info['label']))), 'help' => t('The @entity entity that is associated with the OG membership.', array('@entity' => $info['label'])), 'relationship' => array( - 'handler' => 'views_handler_relationship', + 'entity' => $entity_type, + 'handler' => 'og_handler_relationship', 'label' => t('@entity from OG membership', array('@entity' => $entity_type)), 'base' => $info['base table'], 'base field' => $info['entity keys']['id'], @@ -122,7 +163,8 @@ function og_views_data_alter(&$data) { 'title' => t('Group @entity from OG membership', array('@entity' => ucfirst($info['label']))), 'help' => t('The @entity group that is associated with the OG membership.', array('@entity' => $info['label'])), 'relationship' => array( - 'handler' => 'views_handler_relationship', + 'group_type' => $entity_type, + 'handler' => 'og_handler_relationship', 'label' => t('Group @entity from OG membership', array('@entity' => $entity_type)), 'base' => $info['base table'], 'base field' => $info['entity keys']['id'], diff --git a/sites/all/modules/og/includes/views/og.views_default.inc b/sites/all/modules/og/includes/views/og.views_default.inc index 248a6b8d553c6edfdfdd56ca5c5c3069fade8bdd..57784c85d44cdf1dd9b5abc19cf5f9bd1fd73ac2 100644 --- a/sites/all/modules/og/includes/views/og.views_default.inc +++ b/sites/all/modules/og/includes/views/og.views_default.inc @@ -334,7 +334,7 @@ function og_views_default_views() { $handler->display->display_options['arguments']['etid']['id'] = 'etid'; $handler->display->display_options['arguments']['etid']['table'] = 'og_membership'; $handler->display->display_options['arguments']['etid']['field'] = 'etid'; - $handler->display->display_options['arguments']['etid']['ui_name'] = 'We pass to the "Entity ID" the User ID.'; + $handler->display->display_options['arguments']['etid']['ui_name'] = 'We pass the user ID as the Entity ID.'; $handler->display->display_options['arguments']['etid']['default_action'] = 'default'; $handler->display->display_options['arguments']['etid']['title_enable'] = 1; $handler->display->display_options['arguments']['etid']['title'] = '%1 groups'; diff --git a/sites/all/modules/og/og.api.php b/sites/all/modules/og/og.api.php index 9a7ba62aed6aa7b38c85f07e732c8cf3c5440f5f..d6d73fd8977bace9c9a41f9104c78beb914a2f12 100644 --- a/sites/all/modules/og/og.api.php +++ b/sites/all/modules/og/og.api.php @@ -19,7 +19,12 @@ function hook_og_permission() { 'subscribe' => array( 'title' => t('Subscribe user to group'), 'description' => t("Allow user to be a member of a group (approval required)."), + // Determine to which role to limit the permission. For example the + // "subscribe" can't be assigned only to a non-member, as a member doesn't + // need it. 'roles' => array(OG_ANONYMOUS_ROLE), + // Determine to which roles the permissions will be enabled by default. + 'default role' => array(OG_ANONYMOUS_ROLE), ), ); } diff --git a/sites/all/modules/og/og.info b/sites/all/modules/og/og.info index 9895e4e94b7aa5d853772360b7a0108b989d4558..011b9a8c17dc747ca968221cb00258b4a89249f2 100644 --- a/sites/all/modules/og/og.info +++ b/sites/all/modules/og/og.info @@ -29,9 +29,11 @@ files[] = includes/views/handlers/og_handler_field_group_audience_state.inc files[] = includes/views/handlers/og_handler_field_prerender_list.inc files[] = includes/views/handlers/og_handler_field_user_roles.inc files[] = includes/views/handlers/og_handler_field_group_permissions.inc +files[] = includes/views/handlers/og_handler_field_og_membership_link_edit.inc ; Views filters files[] = includes/views/handlers/og_handler_filter_group_audience_state.inc +files[] = includes/views/handlers/og_handler_filter_user_roles.inc ; Views relationships files[] = includes/views/handlers/og_handler_relationship.inc @@ -48,16 +50,14 @@ files[] = includes/migrate/7000/og_user.inc ; Migrate from 7.x-1.x to 7.x-2.x files[] = includes/migrate/og.migrate.inc files[] = includes/migrate/7200/og_og_membership.migrate.inc -files[] = includes/migrate/7200/og_ogur_permissions.migrate.inc -files[] = includes/migrate/7200/og_ogur.migrate.inc files[] = includes/migrate/7200/og_roles.migrate.inc files[] = includes/migrate/7200/og_user_roles.migrate.inc -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og.install b/sites/all/modules/og/og.install index 6ca234a2cf1393db8b07848a775a97fa5bacb39e..d956d924e6081b239ac6f9e57c924624ffc011d1 100644 --- a/sites/all/modules/og/og.install +++ b/sites/all/modules/og/og.install @@ -52,6 +52,7 @@ function og_uninstall() { 'og_update_batch_size', 'og_upgrade_7001', 'og_node_access_strict', + 'og_features_ignore_og_fields', ); foreach ($vars as $var) { variable_del($var); @@ -72,7 +73,7 @@ function og_uninstall() { } else { $field = field_info_field($instance['field_name']); - if ($field['type'] == 'entityreference' && $field['settings']['handler'] == 'og') { + if ($field['type'] == 'entityreference' && ($field['settings']['handler'] == 'og' || strpos($field['settings']['handler'], 'og_') === 0)) { // Last instance will take care also of deleting the field itself. field_delete_instance($instance); } @@ -354,6 +355,18 @@ function og_schema() { ), ); + // Cache bins for Entity-cache module. + $cache_schema = drupal_get_schema_unprocessed('system', 'cache'); + $types = array( + 'og_membership_type', + 'og_membership', + ); + + foreach ($types as $type) { + $schema["cache_entity_$type"] = $cache_schema; + $schema["cache_entity_$type"]['description'] = "Cache table used to store $type entity records."; + } + return $schema; } @@ -1139,3 +1152,23 @@ function og_update_7203() { return $output; } + +/** + * Create cache bins for Entity-cache module. + */ +function og_update_7204() { + $cache_schema = drupal_get_schema_unprocessed('system', 'cache'); + $types = array( + 'og_membership_type', + 'og_membership', + ); + + foreach ($types as $type) { + $schema["cache_entity_$type"] = $cache_schema; + $schema["cache_entity_$type"]['description'] = "Cache table used to store $type entity records."; + } + + foreach ($schema as $name => $table) { + db_create_table($name, $table); + } +} diff --git a/sites/all/modules/og/og.module b/sites/all/modules/og/og.module index 049b1a26d36f742ecc70e7ae1da3059207d65370..e6ebeae430d90726a57cfa78547154fbecf35fc2 100644 --- a/sites/all/modules/og/og.module +++ b/sites/all/modules/og/og.module @@ -6,7 +6,7 @@ */ // Add field widget related code. -require drupal_get_path('module', 'og') . '/includes/og.field.inc'; +require DRUPAL_ROOT . '/' . drupal_get_path('module', 'og') . '/includes/og.field.inc'; /** * Define active group content states. @@ -137,6 +137,7 @@ function og_entity_info() { 'metadata controller class' => 'EntityDefaultMetadataController', 'views controller class' => 'EntityDefaultViewsController', 'access callback' => 'og_membership_type_access', + 'entity cache' => module_exists('entitycache'), ); if (class_exists('OgMembershipTypeUIController')) { @@ -172,6 +173,7 @@ function og_entity_info() { 'metadata controller class' => 'OgMembershipMetadataController', 'views controller class' => 'OgMembershipViewsController', 'access callback' => 'og_membership_access', + 'entity cache' => module_exists('entitycache'), ); // Add bundle info but bypass entity_load() as we cannot use it here. @@ -506,7 +508,7 @@ function og_node_access($node, $op, $account) { $type = is_string($node) ? $node : (is_array($node) ? $node['type'] : $node->type); if ($op == 'create' && og_is_group_content_type('node', $type) && variable_get('og_node_access_strict', TRUE)) { - if (user_access('administer group')) { + if (user_access('administer group', $account)) { return NODE_ACCESS_ALLOW; } // We can't check if user has create permissions using og_user_access(), as @@ -672,7 +674,13 @@ function og_field_create_instance($instance) { $og_field = og_fields_info(OG_AUDIENCE_FIELD); $og_field['field']['settings']['target_type'] = $entity_type; $og_field['instance']['label'] = t('Group membership'); - og_create_field($field_name, 'user', 'user', $og_field); + + // If the user entity type has multiple bundles, make sure to attach a field + // instance to all of them. + $entity_info = entity_get_info('user'); + foreach (array_keys($entity_info['bundles']) as $user_bundle) { + og_create_field($field_name, 'user', $user_bundle, $og_field); + } } /** @@ -896,7 +904,7 @@ function og_entity_delete($entity, $entity_type) { list($id, , $bundle) = entity_extract_ids($entity_type, $entity); if (og_is_group($entity_type, $entity)) { og_delete_user_roles_by_group($entity_type, $entity); - og_membership_delete_by_group($entity_type, $id); + og_membership_delete_by_group($entity_type, $entity); } if (og_is_group_content_type($entity_type, $bundle)) { // As the field attachers are called after hook_entity_presave() we @@ -1029,7 +1037,7 @@ function og_og_fields_info() { ); $items[OG_AUDIENCE_FIELD] = array( - 'no ui' => TRUE, + 'multiple' => TRUE, 'type' => array('group content'), 'description' => t('Determine to which groups this group content is assigned to.'), 'field' => array( @@ -1185,12 +1193,16 @@ function og_membership_invalidate_cache() { * * If a group membership already exists, an exception will be thrown. * + * @param $group_type + * The entity type of the group. * @param $gid - * The group ID + * The group ID. * @param $entity_type * The entity type of the group content. * @param $etid * The entity ID of the group content. + * @param $field_name + * The group audience field name. * @param $values * Optional; Array of fields values to be attached to the OG membership, that * will be processed using entity-metadata wrapper. @@ -1316,7 +1328,7 @@ function og_entity_query_alter(EntityFieldQuery $query) { function og_query_og_membership_alter(QueryAlterableInterface $query) { $tables = &$query->getTables(); $fields = &$query->getFields(); - $conditions = &$query->conditions(); + $conditions = &$query->conditions(); // Find the group-audience fields. $field_names = array(); @@ -1361,28 +1373,49 @@ function og_query_og_membership_alter(QueryAlterableInterface $query) { } } - 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); + $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); + $id = $entity_info['entity keys']['id']; + if ($base_table) { // 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. $base_table = $entity_info['base table']; if (strpos($base_table_alias, 'field_data') === 0) { - $id = $entity_info['entity keys']['id']; - $query->innerJoin($base_table, $base_table, "$base_table.$id = ogm.etid"); + // Check if the entity base table already exists. + $base_table_alias = FALSE; + foreach ($tables as $table) { + if ($table['table'] == $base_table) { + $base_table_alias = $table['alias']; + break; + } + } + if (!$base_table_alias) { + $base_table_alias = $query->innerJoin($base_table, NULL, "$base_table.$id = ogm.etid"); + } } + // Point the revision ID and bundle to the base entity. $fields['revision_id']['table'] = $base_table; // If there is no revision table, use the bundle. - $fields['revision_id']['field'] = !empty($entity_info['entity keys']['revision']) ? $entity_info['entity keys']['revision'] : $entity_info['entity keys']['bundle']; + if (!empty($entity_info['entity keys']['revision'])) { + // Entity doesn't support revisions. + $fields['revision_id']['field'] = $entity_info['entity keys']['revision']; - $fields['bundle']['table'] = $base_table; - $fields['bundle']['field'] = $entity_info['entity keys']['bundle']; + } + elseif (!empty($entity_info['entity keys']['bundle'])) { + $fields['revision_id']['field'] = $entity_info['entity keys']['bundle']; + + } + else { + // Entity doesn't have bundles (e.g. user). + $fields['revision_id']['field'] = $id; + } + $fields['bundle']['table'] = $base_table; + $fields['bundle']['field'] = !empty($entity_info['entity keys']['bundle']) ? $entity_info['entity keys']['bundle'] : $id; $fields['entity_type']['table'] = 'ogm'; $fields['entity_id']['table'] = 'ogm'; $fields['entity_id']['field'] = 'etid'; @@ -1401,29 +1434,69 @@ function og_query_og_membership_alter(QueryAlterableInterface $query) { $query->join('og_membership', 'ogm', "ogm.etid = $base_table_alias.entity_id"); } + _og_query_og_membership_alter_conditions($conditions, $aliases, $base_table_alias, $entity_info); +} + +/** + * Recursively replace the fields to their aliases in the query's conditions. + * + * See og_query_og_membership_alter(). + */ +function _og_query_og_membership_alter_conditions(&$conditions, $aliases, $base_table_alias, $entity_info) { foreach ($conditions as $delta => $values) { if (!is_array($values)) { continue; } + + // Handle conditions in a sub-query. + if (is_object($values['value'])) { + _og_query_og_membership_alter_conditions($values['value']->conditions(), $aliases, $base_table_alias, $entity_info); + } + + // Handle sub-conditions. + if (is_object($values['field'])) { + _og_query_og_membership_alter_conditions($values['field']->conditions(), $aliases, $base_table_alias, $entity_info); + continue; + } + if (strpos($values['field'], 'field_data_') !== 0) { continue; } - $args = explode('.', $values['field']); + // Explode spaces on the fiels, for handling only the first part in values + // such as "foo.nid = bar.nid". + $field_parts = explode(' ', $values['field'], 2); + list($table, $column) = explode('.', $field_parts[0]); - if (empty($aliases[$args[0]])) { + if (empty($aliases[$table])) { continue; } - $field_name = $aliases[$args[0]]; + $table = 'ogm'; - if ($args[1] == 'deleted') { + // Replace entity_id or any other primary id (e.g. nid for the node + // entity). + $id_columns = array('entity_id', $entity_info['entity keys']['id']); + if (in_array($column, $id_columns)) { + $column = 'etid'; + } + + if ($column == 'deleted') { unset($conditions[$delta]); + continue; } - elseif (strpos($args[1], 'target_id')) { - $conditions[$delta]['field'] = 'ogm.gid'; + elseif (strpos($column, 'target_id')) { + $column = 'gid'; } - else { - $conditions[$delta]['field'] = 'ogm.' . $args[1]; + elseif ($column == 'bundle') { + // Add the bundle of the base entity type. + $table = $base_table_alias; + $column = $entity_info['entity keys']['bundle']; + } + + $conditions[$delta]['field'] = "$table.$column"; + // Add the second part if it exists. + if (!empty($field_parts[1])) { + $conditions[$delta]['field'] .= ' ' . $field_parts[1]; } } } @@ -1468,21 +1541,176 @@ function og_membership_delete_multiple($ids = array()) { } /** - * Delete all OG memberships by group. + * Implements hook_cron_queue_info(). */ -function og_membership_delete_by_group($group_type, $gid) { +function og_cron_queue_info() { + $items['og_membership_orphans'] = array( + 'title' => t('OG orphans'), + 'worker callback' => 'og_membership_orphans_worker', + 'time' => 60, + ); + return $items; +} + +/** + * Queue worker; Process a queue item. + * + * Delete memberships, and if needed all related group-content. + */ +function og_membership_orphans_worker($data) { + $group_type = $data['group_type']; + $gid = $data['gid']; + $query = new EntityFieldQuery(); $result = $query ->entityCondition('entity_type', 'og_membership') ->propertyCondition('group_type', $group_type, '=') ->propertyCondition('gid', $gid, '=') + ->propertyOrderBy('id') + ->range(0, 10) ->execute(); - if (!empty($result['og_membership'])) { - og_membership_delete_multiple(array_keys($result['og_membership'])); + if (empty($result['og_membership'])) { + return; + } + + $ids = array_keys($result['og_membership']); + if ($data['orphans']['move']) { + _og_orphans_move($ids, $data['orphans']['move']['group_type'], $data['orphans']['move']['gid']); + $queue = DrupalQueue::get('og_membership_orphans'); + return $queue->createItem($data); + } + elseif ($data['orphans']['delete']) { + _og_orphans_delete($ids); + // Create a new item. + $queue = DrupalQueue::get('og_membership_orphans'); + return $queue->createItem($data); + } +} + +/** + * Helper function to delete orphan group-content. + * + * @param $ids + * Array of OG membership IDs. + * + * @see og_membership_delete_by_group_worker() + */ +function _og_orphans_delete($ids) { + // Get all the group-content that is now orphan. + $orphans = array(); + $og_memberships = og_membership_load_multiple($ids); + + foreach ($og_memberships as $og_membership) { + $entity_type = $og_membership->entity_type; + $id = $og_membership->etid; + // Don't delete users. + if ($entity_type == 'user') { + continue; + } + $entity_groups = og_get_entity_groups($entity_type, $id); + // Orphan node can be relate to only one type of entity group. + if (count($entity_groups) == 1) { + $gids = reset($entity_groups); + // Orphan node can be relate to only one node. + if (count($gids) > 1) { + continue; + } + } + $orphans[$entity_type][] = $id; + } + + if ($orphans) { + foreach ($orphans as $entity_type => $ids) { + entity_delete_multiple($entity_type, $ids); + } + } + + // Delete the OG memberships. + og_membership_delete_multiple($ids); +} + +/** + * Helper function to move orphan group-content to another group. + * + * @param $ids + * Array of OG membership IDs. + * + * @see og_membership_delete_by_group_worker() + */ +function _og_orphans_move($ids, $group_type, $gid) { + if (!og_is_group($group_type, $gid)) { + $params = array( + '@group-type' => $group_type, + '@gid' => $gid, + ); + throw new OgException(format_string('Cannot move orphan group-content to @group-type - @gid, as it is not a valid group.', $params)); + } + + $og_memberships = og_membership_load_multiple($ids); + foreach ($og_memberships as $og_membership) { + $entity_type = $og_membership->entity_type; + $id = $og_membership->etid; + if (count(og_get_entity_groups($entity_type, $id)) > 1) { + continue; + } + $og_membership->group_type = $group_type; + $og_membership->gid = $gid; + $og_membership->save(); } } +/** + * Register memberships for deletion. + * + * if the property "skip_og_membership_delete_by_group" exists on the + * entity, this function will return early, and allow other implementing + * modules to deal with the deletion logic. + * + * @param $entity_type + * The group type. + * @param $entity + * The group entity object. + */ +function og_membership_delete_by_group($entity_type, $entity) { + if (!empty($entity->skip_og_membership_delete_by_group)) { + return; + } + + list($gid) = entity_extract_ids($entity_type, $entity); + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'og_membership') + ->propertyCondition('group_type', $entity_type, '=') + ->propertyCondition('gid', $gid, '=') + ->execute(); + + if (empty($result['og_membership'])) { + return; + } + + if (variable_get('og_use_queue', FALSE)) { + $queue = DrupalQueue::get('og_membership_orphans'); + // Add item to the queue. + $data = array( + 'group_type' => $entity_type, + 'gid' => $gid, + // Allow implementing modules to determine the disposition (e.g. delete + // orphan group content). + 'orphans' => array( + 'delete' => isset($entity->og_orphans['delete']) ? $entity->og_orphans['delete'] : variable_get('og_orphans_delete', FALSE), + 'move' => isset($entity->og_orphans['move']) ? $entity->og_orphans['move'] : array(), + ), + ); + + // Exit now, as the task will be processed via queue. + return $queue->createItem($data); + } + + // No scalable solution was chosen, so just delete OG memberships. + og_membership_delete_multiple(array_keys($result['og_membership'])); +} + /** * Label callback; Return the label of OG membership entity. */ @@ -1579,8 +1807,7 @@ function og_get_all_group($group_type = 'node') { * Defaults to FALSE. */ function og_get_best_group_audience_field($entity_type, $entity, $group_type, $group_bundle, $skip_access = FALSE) { - $entity_wrapper = entity_metadata_wrapper($entity_type, $entity); - $bundle = $entity_wrapper->getBundle(); + list(,, $bundle) = entity_extract_ids($entity_type, $entity); $field_names = og_get_group_audience_fields($entity_type, $bundle); if (!$field_names) { @@ -1598,11 +1825,13 @@ function og_get_best_group_audience_field($entity_type, $entity, $group_type, $g continue; } + if (!og_check_field_cardinality($entity_type, $entity, $field_name)) { // Field reached maximum. continue; } - if (!$skip_access && !$entity_wrapper->{$field_name}->access('view')) { + + if (!$skip_access && !field_access('view', $field, $entity_type, $entity)) { // User can't access field. continue; } @@ -1612,7 +1841,7 @@ function og_get_best_group_audience_field($entity_type, $entity, $group_type, $g } /** - * Return TRUE if a field can be used and has not reached maxium values. + * Return TRUE if a field can be used and has not reached maximum values. * * @param $entity_type * The entity type. @@ -1824,9 +2053,9 @@ function og_user_access($group_type, $gid, $string, $account = NULL, $skip_alter return TRUE; } - $group = entity_load_single($group_type, $gid); // Group manager has all privileges (if variable is TRUE). - if (variable_get('og_group_manager_full_access', TRUE)) { + if (!empty($account->uid) && variable_get('og_group_manager_full_access', TRUE)) { + $group = entity_load_single($group_type, $gid); if (!empty($group->uid) && $group->uid == $account->uid) { return TRUE; } @@ -1848,8 +2077,9 @@ function og_user_access($group_type, $gid, $string, $account = NULL, $skip_alter } if (!$skip_alter && empty($perm_alter[$identifier][$account->uid][$string])) { - // Let modules alter the permissions. since $perm is static we create a - // clone of it. + // Let modules alter the permissions. since $perm is static we create + // a clone of it. + $group = !empty($group) ? $group : entity_load_single($group_type, $gid); $temp_perm = $perm[$identifier][$account->uid]; $context = array( 'string' => $string, @@ -2021,7 +2251,7 @@ function og_get_entity_groups($entity_type = 'user', $entity = NULL, $states = a */ function og_is_group_audience_field($field_name) { $field = field_info_field($field_name); - return $field['type'] == 'entityreference' && $field['settings']['handler'] == 'og'; + return $field['type'] == 'entityreference' && ($field['settings']['handler'] == 'og' || strpos($field['settings']['handler'], 'og_') === 0); } /** @@ -2042,23 +2272,12 @@ function og_get_group_audience_fields($entity_type = 'user', $bundle_name = 'use return $return[$identifier]; } $return[$identifier] = array(); - // field_info_instances() doesn't give the field type, so we have to use - // field_info_fields(). - foreach (field_info_fields() as $field_name => $field_info) { - if (!og_is_group_audience_field($field_name)) { - continue; - } - if (empty($field_info['bundles'][$entity_type])) { + foreach (field_info_instances($entity_type, $bundle_name) as $field_name => $instance) { + if (!og_is_group_audience_field($field_name)) { continue; } - - foreach ($field_info['bundles'][$entity_type] as $field_bundle) { - if ($field_bundle == $bundle_name) { - $instance_info = field_info_instance($entity_type, $field_name, $bundle_name); - $return[$identifier][$field_name] = $instance_info['label']; - } - } + $return[$identifier][$field_name] = $instance['label']; } return $return[$identifier]; } @@ -2103,15 +2322,15 @@ function og_is_group_type($entity_type, $bundle_name) { * * This is a wrapper function around og_get_group_type(). * - * @param $node_type - * The node type to be checked. + * @param $entity_type + * The entity type to be checked. */ function og_is_group_content_type($entity_type, $bundle_name) { return og_get_group_type($entity_type, $bundle_name, 'group content'); } /** - * Return all the enteties that are a group. + * Return all the entities that are a group. * * @return * Array keyed with the entity type machine name and the entity human readable @@ -2154,7 +2373,7 @@ function og_get_all_group_bundle() { * * @return * Array keyed with the entity type machine name and the entity human readable - * name as the value, or an empty array if no enteties are defined as group + * name as the value, or an empty array if no entities are defined as group * content. */ function og_get_all_group_content_entity() { @@ -2218,13 +2437,21 @@ function og_is_member($group_type, $gid, $entity_type = 'user', $entity = NULL, /** * Check if group should use default roles and permissions. * + * @param $group_type + * The entity type of the group. * @param $gid - * The group ID. + * The group ID or the group entity. + * * @return * TRUE if group should use default roles and permissions. */ -function og_is_group_default_access($entity_type, $entity) { - $wrapper = entity_metadata_wrapper($entity_type, $entity); +function og_is_group_default_access($group_type, $gid) { + if (!field_info_field(OG_DEFAULT_ACCESS_FIELD)) { + // Don't bother loading entity if field does not exist. + return TRUE; + } + + $wrapper = entity_metadata_wrapper($group_type, $gid); if (empty($wrapper->{OG_DEFAULT_ACCESS_FIELD})) { return TRUE; } @@ -2293,6 +2520,9 @@ function og_role_permissions($roles = array()) { * The group might be set to "Default access" but infact there are inactive * group roles. Thus, we are forcing the function to return the overriden * roles. see og_delete_user_roles_by_group(). + * @param $include_all + * Optional; If TRUE then the anonymous and authenticated default roles will + * be included. * * @return * An associative array with the role id as the key and the role name as @@ -2334,6 +2564,8 @@ function og_roles($group_type, $bundle, $gid = 0, $force_group = FALSE, $include * Get array of default roles, keyed by their declaring module. * * @param $include + * (optional) If TRUE also anonymous and authenticated roles will be returned. + * Defaults to TRUE. * * @return * Array of default roles, grouped by module name. @@ -2359,13 +2591,16 @@ function og_get_default_roles($include = TRUE) { /** * Get all roles of a user in a certain group. * + * @param $group_type + * The entity type of the group. * @param $gid * The group ID. * @param $uid - * The user ID. + * (optional) Integer specifying the user ID. By default an ID of current + * logged in user will be used. * @param $include - * Optional; If TRUE also anonymous or authenticated role ID will be returned. - * Defaults to TRUE. + * (optional) If TRUE also anonymous or authenticated role ID will be + * returned. Defaults to TRUE. * * @return * Array with the role IDs of the user as the key, and the role name as @@ -2427,9 +2662,16 @@ function og_get_user_roles($group_type, $gid, $uid = NULL, $include = TRUE) { * Create a stub OG role object. * * @param $name - * @param $gid + * A name of the role. * @param $group_type + * (optional) The entity type of the group. + * @param $gid + * (optional) The group ID. * @param $group_bundle + * (optional) The bundle of the group. + * + * @return + * A stub OG role object. */ function og_role_create($name, $group_type = '', $gid = 0, $group_bundle = '') { $role = new stdClass; @@ -2443,11 +2685,12 @@ function og_role_create($name, $group_type = '', $gid = 0, $group_bundle = '') { /** * Fetch a user role from database. * - * @param $role + * @param $rid * An integer with the role ID. + * * @return - * A fully-loaded role object if a role with the given name or ID - * exists, FALSE otherwise. + * A fully-loaded role object if a role with the given ID exists, + * FALSE otherwise. */ function og_role_load($rid) { return db_select('og_role', 'r') @@ -2463,6 +2706,7 @@ function og_role_load($rid) { * @param $role * A role object to modify or add. If $role->rid is not specified, a new * role will be created. + * * @return * Status constant indicating if role was created or updated. * Failure to write the user role record will return FALSE. Otherwise. @@ -2490,7 +2734,7 @@ function og_role_save($role) { /** * Delete a user role from database. * - * @param $role + * @param $rid * An integer with the role ID. */ function og_role_delete($rid) { @@ -2548,13 +2792,13 @@ function og_get_user_roles_name($rids = array()) { * * @see og_modules_uninstalled() * - * @param $module - * Array with the modules name. + * @param $modules + * Array with the module names. */ function og_permissions_delete_by_module($modules = array()) { - db_delete('og_role_permission') - ->condition('module', $modules, 'IN') - ->execute(); + db_delete('og_role_permission') + ->condition('module', $modules, 'IN') + ->execute(); } /** @@ -2566,6 +2810,7 @@ function og_permissions_delete_by_module($modules = array()) { * The bundle type. * @param $gid * The group ID. + * * @return * The newly created roles keyed by role ID and role name as the value. Or * FALSE if no roles were created. @@ -2613,6 +2858,10 @@ function og_roles_override($group_type, $bundle, $gid) { /** * Grant a group role to a user. * + * @param $group_type + * The entity type of the group. + * @param $gid + * The group ID. * @param $uid * The user ID. * @param $rid @@ -2646,6 +2895,10 @@ function og_role_grant($group_type, $gid, $uid, $rid) { /** * Revoke a group role from a user. * + * @param $group_type + * The entity type of the group. + * @param $gid + * The group ID. * @param $uid * The user ID. * @param $rid @@ -2818,7 +3071,14 @@ function og_get_default_permissions() { } /** - * Get all the modules fields that can be assigned to fieldable enteties. + * Get all the modules fields that can be assigned to fieldable entities. + * + * @param $field_name + * The field name that was registered for the definition. + * + * @return + * An array with the field and instance definitions, or FALSE if not + * found. */ function og_fields_info($field_name = NULL) { $return = &drupal_static(__FUNCTION__, array()); @@ -2828,7 +3088,11 @@ function og_fields_info($field_name = NULL) { if ($fields = module_invoke($module, 'og_fields_info')) { foreach ($fields as $key => $field) { // Add default values. - $field += array('entity type' => array()); + $field += array( + 'entity type' => array(), + 'multiple' => FALSE, + 'description' => '', + ); // Add the module information. $return[$key] = array_merge($field, array('module' => $module)); @@ -2840,7 +3104,11 @@ function og_fields_info($field_name = NULL) { drupal_alter('og_fields_info', $return); } - return empty($field_name) ? $return : $return[$field_name]; + if (!empty($field_name)) { + return !empty($return[$field_name]) ? $return[$field_name] : FALSE; + } + + return $return; } /** @@ -2946,6 +3214,7 @@ function og_get_fieldable_entity_list() { * * @param $type * The machine-readable name of the node type. + * * @return array * An array of permission names and descriptions. */ @@ -2960,10 +3229,7 @@ function og_list_permissions($type) { $perms += array( "create $type content" => array( 'title' => t('Create %type_name content', array('%type_name' => $info->name)), - // We allow the create permission only on members, as otherwise we would - // have to iterate over every single group to decide if the user has - // permissions for it. - 'roles' => array(OG_AUTHENTICATED_ROLE), + ), "update own $type content" => array( 'title' => t('Edit own %type_name content', array('%type_name' => $info->name)), @@ -2979,6 +3245,13 @@ function og_list_permissions($type) { ), ); + if (!module_exists('entityreference_prepopulate')) { + // We allow the create permission only on members, as otherwise we would + // have to iterate over every single group to decide if the user has + // permissions for it. + $perms["create $type content"]['roles'] = array(OG_AUTHENTICATED_ROLE); + } + // Add default permissions. foreach ($perms as $key => $value) { $perms[$key]['default role'] = array(OG_AUTHENTICATED_ROLE); @@ -2990,8 +3263,12 @@ function og_list_permissions($type) { /** * Return a form element with crafted links to create nodes for a group. * + * @param $group_type + * The entity type of the group. * @param $gid * The group ID. + * @param $field_name + * The group audience field name. * @param $destination * Optional; The destiantion after a node is created. Defaults to the * destination passed in the URL if exists, otherwise back to the current @@ -3060,6 +3337,9 @@ function og_node_create_links($group_type, $gid, $field_name, $destination = NUL * @param $account * Optional; The user object to fetch group memberships for. Defaults to the * acting user. + * @param $group_type + * Optional; The entity type of the groups to fetch. By default all group + * types will be fetched. * * @return * An array with the group IDs or an empty array. @@ -3166,6 +3446,25 @@ function og_features_api() { ); } +/** + * Implements hook_features_pipe_alter(). + * + * Prevent OG related fields from being piped in features, when a content + * type that has them is selected. + */ +function og_features_pipe_alter(&$pipe, $data, $export) { + if (!variable_get('og_features_ignore_og_fields', FALSE) || empty($pipe['field'])) { + return; + } + foreach ($pipe['field'] as $delta => $value) { + $args = explode('-', $value); + $field_name = $args[2]; + if (og_fields_info($field_name) || og_is_group_audience_field($field_name)) { + unset($pipe['field'][$delta]); + } + } +} + /** * Implements hook_migrate_api(). */ @@ -3175,6 +3474,14 @@ function og_migrate_api() { $migrations['OgMigrateAddFields'] = array('class_name' => 'OgMigrateAddFields'); $migrations['OgMigrateContent'] = array('class_name' => 'OgMigrateContent'); $migrations['OgMigrateUser'] = array('class_name' => 'OgMigrateUser'); + + foreach (node_type_get_names() as $bundle => $value) { + $machine_name = 'OgMigrateGroup' . ucfirst($bundle); + $migrations[$machine_name] = array( + 'class_name' => 'OgMigrateGroup', + 'bundle' => $bundle, + ); + } } elseif (db_field_exists('og_membership', 'group_type') && db_table_exists('og') && !db_table_exists('d6_og')) { $migrations['OgMigrateMembership'] = array('class_name' => 'OgMigrateMembership'); @@ -3185,29 +3492,17 @@ function og_migrate_api() { $api = array( 'api' => 2, 'migrations' => $migrations, - 'destination handlers' => array( - 'MigrateDestinationOGMembership', - ), ); return $api; } /** - * Implements hook_modules_enabled(). - * - * Register dynamic migrate plugins for upgrading from OG6. + * Implements hook_flush_caches(). */ -function og_modules_enabled($modules) { - if (!db_table_exists('d6_og')) { - return; - } - - if (!in_array('migrate', $modules) && !module_exists('migrate')) { - return; - } - - foreach (node_type_get_names() as $bundle => $value) { - // Register a dynamic migration. - Migration::registerMigration('OgMigrateGroup', 'OgMigrateGroup' . ucfirst($bundle), array('bundle' => $bundle)); - } +function og_flush_caches() { + $bins = array( + 'cache_entity_og_membership', + 'cache_entity_og_membership_type', + ); + return $bins; } diff --git a/sites/all/modules/og/og.test b/sites/all/modules/og/og.test index f64b08942af0aa101b7436e71bde59a94cfecaf9..b9fe5c0ea6768b883f61da65714f6c015d34dbc1 100644 --- a/sites/all/modules/og/og.test +++ b/sites/all/modules/og/og.test @@ -364,7 +364,7 @@ class OgMetaData extends DrupalWebTestCase { $settings = array(); $settings['type'] = 'article'; - // Create group enteties. + // Create group entities. foreach (og_group_content_states() as $state => $value) { $node = $this->drupalCreateNode($settings); @@ -458,6 +458,15 @@ class OgGroupAndUngroup extends DrupalWebTestCase { $settings['uid'] = $user2->uid; $node = $this->drupalCreateNode($settings); + // Exception on OG membership for anonymous user. + try { + og_membership_create('entity_test', $entity1->pid, 'user', 0, OG_AUDIENCE_FIELD)->save(); + $this->fail('OG membership can be created for anonymous user.'); + } + catch (OgException $e) { + $this->pass('OG membership can not be created for anonymous user.'); + } + $this->assertFalse(og_is_member('entity_test', $entity1->pid, 'node', $node), t('Node is not assigned to group1.')); $values = array('entity_type' => 'node', 'entity' => $node); og_group('entity_test', $entity1->pid, $values); @@ -928,7 +937,6 @@ class OgComplexWidgetTestCase extends DrupalWebTestCase { og_create_field(OG_AUDIENCE_FIELD, 'node', 'post', $og_field); } - /** * Test "field modes" of the OG reference widget. */ @@ -1017,8 +1025,6 @@ class OgComplexWidgetTestCase extends DrupalWebTestCase { $user1 = $this->drupalCreateUser(); $user2 = $this->drupalCreateUser(); - $type = $this->drupalCreateContentType(); - $settings = array( 'type' => 'group', OG_GROUP_FIELD . '[und][0][value]' => 1, @@ -1033,6 +1039,43 @@ class OgComplexWidgetTestCase extends DrupalWebTestCase { $this->assertTrue(og_get_entity_groups('user', $user2, array(OG_STATE_PENDING)), 'User membership was retained after user save.'); } + + /** + * Test multiple group-audience fields. + */ + function testMultipleFields() { + // Add another group-audience field. + $og_field = og_fields_info(OG_AUDIENCE_FIELD); + og_create_field('another_field', 'node', 'post', $og_field); + + $user1 = $this->drupalCreateUser(); + + // Create a group. + $settings = array( + 'type' => 'group', + OG_GROUP_FIELD . '[und][0][value]' => 1, + 'uid' => $user1->uid, + ); + $group1 = $this->drupalCreateNode($settings); + $group2 = $this->drupalCreateNode($settings); + + // Create group content. + $settings = array( + 'type' => 'post', + 'uid' => $user1->uid, + ); + $post1 = $this->drupalCreateNode($settings); + + og_group('node', $group1->nid, array('entity_type' => 'node', 'entity' => $post1, 'field_name' => OG_AUDIENCE_FIELD)); + og_group('node', $group2->nid, array('entity_type' => 'node', 'entity' => $post1, 'field_name' => 'another_field')); + + $this->drupalLogin($user1); + $this->drupalGet("node/$post1->nid/edit"); + + // Assert correct selection in both fields. + $this->assertOptionSelected('edit-og-group-ref-und-0-default', $group1->nid); + $this->assertOptionSelected('edit-another-field-und-0-default', $group2->nid); + } } /** @@ -1191,6 +1234,7 @@ class OgEntityFieldQueryTestCase extends DrupalWebTestCase { parent::setUp('og', 'entity_feature'); $user1 = $this->drupalCreateUser(); + $user2 = $this->drupalCreateUser(); $type = $this->drupalCreateContentType(); $group_type = $type->type; @@ -1273,10 +1317,17 @@ class OgEntityFieldQueryTestCase extends DrupalWebTestCase { ); og_group('node', $group2, $values); + $values = array( + 'entity_type' => 'user', + 'entity' => $user2, + ); + og_group('node', $group2, $values); + $this->group1 = $group1; $this->group2 = $group2; $this->node = $node; $this->user1 = $user1; + $this->user2 = $user2; $this->entity_test = $entity_test; } @@ -1289,7 +1340,8 @@ class OgEntityFieldQueryTestCase extends DrupalWebTestCase { * - Non-audience field first, with single group audience. * - Multiple entity types in entityCondition(). * - No entity property. - * - Non-node entity without revision table. + * - Non-node entity without revision table (e.g. entity_test). + * - Non-node entity without revision table and without bundles (e.g. user). * - Count query. */ function testEntityFieldQuery() { @@ -1370,6 +1422,19 @@ class OgEntityFieldQueryTestCase extends DrupalWebTestCase { $this->assertEqual(array_keys($result['entity_test']), array($entity_test->pid), 'Non-node entity without revision table query is correct.'); + // Non-node entity without revision table and without bundles. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'user') + ->fieldCondition('og_entity_test', 'target_id', $group2->nid) + ->execute(); + + $expected_values = array( + $this->user1->uid, + $this->user2->uid, + ); + $this->assertEqual(array_keys($result['user']), $expected_values, 'Non-node entity without revision table and without bundles query is correct.'); + // Count query. $query = new EntityFieldQuery(); $result = $query @@ -1440,10 +1505,10 @@ class OgBehaviorHandlerTestCase extends DrupalWebTestCase { // Add OG group field to the entity_test's "main" bundle. og_create_field(OG_GROUP_FIELD, 'entity_test', 'main'); - $type = $this->drupalCreateContentType(); + $type = $this->drupalCreateContentType(array('type' => 'behavior')); $this->group_content = $type->type; - // Add OG audience field to the node's "article" bundle. + // Add OG audience field to the new bundle. $og_field = og_fields_info(OG_AUDIENCE_FIELD); $og_field['field']['settings']['target_type'] = 'entity_test'; og_create_field(OG_AUDIENCE_FIELD, 'node', $type->type, $og_field); @@ -1511,4 +1576,155 @@ class OgBehaviorHandlerTestCase extends DrupalWebTestCase { $gids = og_get_entity_groups('node', $node); $this->assertEqual(array_values($gids['entity_test']), array($entity1->pid), 'Widget behavior was skipped and removed group association as expected.'); } + + /** + * Test settings the OG membership state via field values, when associating + * a new group-content to a group. + */ + function testSetStateOnInsert() { + module_enable(array('og_test')); + $permissions = array( + 'access content', + "create $this->group_content content", + 'administer group', + ); + $user1 = $this->drupalCreateUser(); + $user2 = $this->drupalCreateUser($permissions); + $user3 = $this->drupalCreateUser($permissions); + + // Create a group. + $entity1 = entity_create('entity_test', array('name' => 'main', 'uid' => $user1->uid)); + $wrapper = entity_metadata_wrapper('entity_test', $entity1); + $wrapper->{OG_GROUP_FIELD}->set(1); + $wrapper->save(); + + og_group('entity_test', $entity1, array('entity_type' => 'user', 'entity' => $user2)); + og_group('entity_test', $entity1, array('entity_type' => 'user', 'entity' => $user3)); + + // Post a node, state should be active. + $type = str_replace('_', '-', $this->group_content); + $edit = array( + 'title' => 'state-active', + 'og_group_ref[und][0][default][]' => array($entity1->pid), + ); + + $this->drupalLogin($user2); + $this->drupalPost('node/add/' . $type, $edit, t('Save')); + + $gids = og_get_entity_groups('node', 1); + $id = key($gids['entity_test']); + $og_membership = og_membership_load($id); + $this->assertEqual($og_membership->state, OG_STATE_ACTIVE, 'Memebership status is Active'); + + + // Post a node, state should be pending. + $this->drupalLogin($user3); + $edit['title'] = 'state-pending'; + $this->drupalPost('node/add/' . $type, $edit, t('Save')); + $gids = og_get_entity_groups('node', 2, array(OG_STATE_PENDING)); + $id = key($gids['entity_test']); + $og_membership = og_membership_load($id); + $this->assertEqual($og_membership->state, OG_STATE_PENDING, 'Memebership status is Active'); + } +} + +/** + * Testing for deleting orphans group content. + */ +class OgDeleteOrphansTestCase extends DrupalWebTestCase { + + public $group_type; + public $node_type; + + public static function getInfo() { + return array( + 'name' => 'OG orphan delete', + 'description' => 'Verifying for deleting orphan group content.', + 'group' => 'Organic groups', + ); + } + + function setUp() { + parent::setUp('og_test'); + + // Create a group content type. + $group = $this->drupalCreateContentType(); + og_create_field(OG_GROUP_FIELD, 'node', $group->type); + $this->group_type = $group->type; + + // Create group audience content type. + $type = $this->drupalCreateContentType(); + $this->node_type = $type->type; + + // Add OG audience field to the audience content type. + $og_field = og_fields_info(OG_AUDIENCE_FIELD); + $og_field['field']['settings']['target_type'] = 'node'; + og_create_field(OG_AUDIENCE_FIELD, 'node', $type->type, $og_field); + + // Set the setting for delete a group content when deleting group. + variable_set('og_orphans_delete', TRUE); + variable_set('og_use_queue', TRUE); + } + + /** + * Testing two things: + * When deleting a group, the node of the group will be deleted. + * Associated node with the deleted group and another group won't be deleted. + */ + function testDeleteGroup() { + // Creating two groups. + $first_group = $this->drupalCreateNode(array('type' => $this->group_type)); + $second_group = $this->drupalCreateNode(array('type' => $this->group_type)); + + // Create two nodes. + $first_node = $this->drupalCreateNode(array('type' => $this->node_type)); + og_group('node', $first_group, array('entity_type' => 'node', 'entity' => $first_node)); + og_group('node', $second_group, array('entity_type' => 'node', 'entity' => $first_node)); + + $second_node = $this->drupalCreateNode(array('type' => $this->node_type)); + og_group('node', $first_group, array('entity_type' => 'node', 'entity' => $second_node)); + + // Delete the group. + node_delete($first_group->nid); + + // Execute manually the queue worker. + $queue = DrupalQueue::get('og_membership_orphans'); + $item = $queue->claimItem(); + og_membership_orphans_worker($item->data); + + // Load the nodes we used during the test. + $first_node = node_load($first_node->nid); + $second_node = node_load($second_node->nid); + + // Verify the none orphan node wasn't deleted. + $this->assertTrue($first_node, "The second node is realted to another group and deleted."); + // Verify the orphan node deleted. + $this->assertFalse($second_node, "The orphan node deleted."); + } + + /** + * Testing the moving of the node to another group when deleting a group. + */ + function testMoveOrphans() { + // Creating two groups. + $first_group = $this->drupalCreateNode(array('type' => $this->group_type, 'title' => 'move')); + $second_group = $this->drupalCreateNode(array('type' => $this->group_type)); + + // Create a group and relate it to the first group. + $first_node = $this->drupalCreateNode(array('type' => $this->node_type)); + og_group('node', $first_group, array('entity_type' => 'node', 'entity' => $first_node)); + + // Delete the group. + node_delete($first_group->nid); + + // Execute manually the queue worker. + $queue = DrupalQueue::get('og_membership_orphans'); + $item = $queue->claimItem(); + og_membership_orphans_worker($item->data); + + // Load the node into a wrapper and verify we moved him to another group. + $wrapper = entity_metadata_wrapper('node', $first_node->nid); + + $this->assertTrue($wrapper->{OG_AUDIENCE_FIELD}->get(0)->getIdentifier() == $second_group->nid, "The node of the group moved to another group."); + } } diff --git a/sites/all/modules/og/og_access/og_access.info b/sites/all/modules/og/og_access/og_access.info index 21b4fd7b5c77e0fb4f319b3fdbc60c336ffea027..b410cadb2ac016e85e35d23208aef2c9b53cd093 100644 --- a/sites/all/modules/og/og_access/og_access.info +++ b/sites/all/modules/og/og_access/og_access.info @@ -9,9 +9,9 @@ files[] = og_access.install ; Tests files[] = og_access.test -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_access/og_access.module b/sites/all/modules/og/og_access/og_access.module index 7160144addb8208abf9f00e3860232ed26de2cd8..0666581d8e107d3e4a8d87d12662b0ea80d80fee 100644 --- a/sites/all/modules/og/og_access/og_access.module +++ b/sites/all/modules/og/og_access/og_access.module @@ -140,7 +140,7 @@ function og_access_og_fields_info() { ); $items[OG_ACCESS_FIELD] = array( 'type' => array('group'), - 'description' => t('Add Group access field to group types.'), + 'description' => t('Determine access to the group.'), // Private access can be done only on node entity. 'entity' => array('node'), 'field' => array( @@ -177,7 +177,7 @@ function og_access_og_fields_info() { ); $items[OG_CONTENT_ACCESS_FIELD] = array( 'type' => array('group content'), - 'description' => t('Add Group access field to group-content types, that may override the group settings.'), + 'description' => t('Determine access to the group content, which may override the group settings.'), // Private access can be done only on node entity. 'entity' => array('node'), 'field' => array( diff --git a/sites/all/modules/og/og_context/includes/views/handlers/og_context_plugin_access_og_perm.inc b/sites/all/modules/og/og_context/includes/views/handlers/og_context_plugin_access_og_perm.inc index 5ace9a1d28a139060a9157503230b8b235734c24..6ba0d4f51d0cbd561745b3cab08dcdf8cc0d3e9d 100644 --- a/sites/all/modules/og/og_context/includes/views/handlers/og_context_plugin_access_og_perm.inc +++ b/sites/all/modules/og/og_context/includes/views/handlers/og_context_plugin_access_og_perm.inc @@ -10,35 +10,6 @@ * current group. */ class og_context_plugin_access_og_perm extends views_plugin_access { - /** - * Determine if the current user has access or not. - */ - function access($account) { - // Attempt to get the group from the current context and determine if the - // user has the appropriate permission within the group - if ($group = og_context($this->options['group_type'])) { - return og_user_access($group['group_type'], $group['gid'], $this->options['perm'], $account); - } - return FALSE; - } - - /** - * Determine the access callback and arguments. - */ - function get_access_callback() { - if ($group = og_context($this->options['group_type'])) { - return array('og_user_access', array($group['group_type'], $group['gid'], $this->options['perm'])); - } - return FALSE; - } - - /** - * Return a string to display as the clickable title for the - * access control. - */ - function summary_title() { - return $this->options['perm']; - } /** * Retrieve the options when this is a new access @@ -48,6 +19,7 @@ class og_context_plugin_access_og_perm extends views_plugin_access { $options = parent::option_definition(); $options['perm'] = array('default' => 'edit group'); $options['group_type'] = array('default' => 'node'); + $options['group_id_arg'] = array('default' => FALSE); return $options; } @@ -78,5 +50,54 @@ class og_context_plugin_access_og_perm extends views_plugin_access { '#default_value' => $this->options['group_type'], '#description' => t('Determine what entity type that group should be of.') ); + + $current_display = $this->view->current_display; + if ($this->view->display[$current_display]->handler->has_path()) { + // Show the group ID argument position only for "Page" displays. + $form['group_id_arg'] = array( + '#type' => 'select', + '#title' => t('Argument position for group ID'), + '#default_value' => $this->options['group_id_arg'], + '#options' => array(NULL => t('None')) + range(0, 9), + '#description' => t('Group ID argument position with arg() function. e.g. if your dynamic path is "node/%/group/%/overview" and you are using the second "%" for group IDs you have to choose "3" like "arg(3)".'), + ); + } + } + + /** + * Return a string to display as the clickable title for the + * access control. + */ + function summary_title() { + $current_display = $this->view->current_display; + if ($this->options['group_id_arg'] === FALSE || $this->view->display[$current_display]->display_plugin != 'page') { + return $this->options['perm']; + } + + $params = array( + '@perm' => $this->options['perm'], + '@arg' => $this->options['group_id_arg'], + ); + + return t('@perm, getting the group ID from argument position @arg', $params); + } + + /** + * Determine if the current user has access or not. + */ + function access($account) { + // Attempt to get the group from the current context and determine if the + // user has the appropriate permission within the group + if ($group = og_context($this->options['group_type'])) { + return og_user_access($group['group_type'], $group['gid'], $this->options['perm'], $account); + } + return FALSE; + } + + /** + * Determine the access callback and arguments. + */ + function get_access_callback() { + return array('_og_context_views_page_access', array($this->options['group_type'], $this->options['perm'], $this->options['group_id_arg'])); } } diff --git a/sites/all/modules/og/og_context/og_context.api.php b/sites/all/modules/og/og_context/og_context.api.php index 1576a6c87420246d60dc9d0300d5fa696383f0e6..6738ca6a11107f7f391c83dd18c5e9b941386cdd 100644 --- a/sites/all/modules/og/og_context/og_context.api.php +++ b/sites/all/modules/og/og_context/og_context.api.php @@ -17,8 +17,7 @@ * Define context "handlers". * - name: The human readable name of the context handler. * - Description: The description of the context handler. - * - callback: The callback function that will evaluate and return the group IDs - * that it finds. + * - callback: The name of an implementation of callback_og_context_handler(). * - menu path: Optional; The menu path as retrieved from menu_get_item() that * is required for the context handler to be invoked. */ @@ -38,4 +37,28 @@ function hook_og_context_negotiation_info() { /** * @} End of "addtgrouproup hooks". - */ \ No newline at end of file + */ + +/** + * @addtgrouproup callbacks + * @{ + */ + +/** + * Evaluates and return group IDs to provide group context. + * + * Callback for hook_og_context_negotiation_info(). + * + * @return + * A nested array of group IDs, grouped first by entity type. Each value is + * a flat array of group IDs. + */ +function callback_og_context_handler() { + return array( + 'node' => array(1, 2, 3), + ); +} + +/** + * @} End of "addtgrouproup callbacks". + */ diff --git a/sites/all/modules/og/og_context/og_context.info b/sites/all/modules/og/og_context/og_context.info index 706d6d92ed1a034dd418fcdc273e36ccea7b2dfa..692650d2fef0d15d6c235bcf4238dc7d79289ee9 100644 --- a/sites/all/modules/og/og_context/og_context.info +++ b/sites/all/modules/og/og_context/og_context.info @@ -12,9 +12,9 @@ files[] = includes/views/handlers/og_context_plugin_argument_default_group_conte ; Views access plugin files[] = includes/views/handlers/og_context_plugin_access_og_perm.inc -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_context/og_context.module b/sites/all/modules/og/og_context/og_context.module index a1f8a5f6be4c647705e58e75912886c92a277559..65cd3a5bc881b61c3ab9964a035b88561f7e6ba9 100644 --- a/sites/all/modules/og/og_context/og_context.module +++ b/sites/all/modules/og/og_context/og_context.module @@ -121,7 +121,7 @@ function og_context_og_context_negotiation_info() { 'name' => t('Node'), 'description' => t("Determine context by checking if a node is a group or a group content."), 'callback' => 'og_context_handler_node', - 'menu path' => array('node/%', 'node/%/edit'), + 'menu path' => array('node/%', 'group/%/%/admin'), ); $providers['user-view'] = array( @@ -138,6 +138,13 @@ function og_context_og_context_negotiation_info() { 'menu path' => array('user/%/edit'), ); + $providers['comment'] = array( + 'name' => t('Comment'), + 'description' => t("Determine context by checking if the parent content of the comment belongs to a group"), + 'callback' => 'og_context_handler_comment', + 'menu path' => array('comment/reply/%', 'comment/%'), + ); + return $providers; } @@ -187,10 +194,14 @@ function og_context($group_type = 'node', $group = NULL) { global $user; $context = &drupal_static(__FUNCTION__, FALSE); - if (empty($group) && !empty($context) && $context['group_type'] == $group_type) { + if (empty($group) && $context !== FALSE && (empty($context)) || (!empty($context['group_type']) && $context['group_type'] == $group_type)) { return $context; } + // Set the context to array, so we can now this function has been already + // executed. + $context = array(); + if (!empty($group)) { // Set the group. list($id) = entity_extract_ids($group_type, $group); @@ -311,9 +322,14 @@ function og_context_provider_weight($provider) { /** * Determine the best matching context of a viewed page. * + * @param $group_type + * The entity type of the group. For example, "node" or "user". * @param $item * Optional; A menu item that context should be extracted from. If empty * defaults to the current menu item by using menu_get_item(). + * + * @return + * The group ID for the current context. */ function og_context_determine_context($group_type, $item = NULL) { // Enable url and node context handlers by default. @@ -404,9 +420,30 @@ function og_context_handler_url() { /** * Context handler; Get groups from existing node or ctools context. + * + * @param $node + * Optional; A node. If empty a node object will attempted to be loaded via + * menu_get_object(). */ -function og_context_handler_node() { - $node = menu_get_object('node'); +function og_context_handler_node($node = NULL) { + if (empty($node) && $node = menu_get_object()) { + return _group_context_handler_entity('node', $node); + } + + $item = menu_get_item(); + + if (empty($item['map'])) { + // User has no access to the menu item. + return; + } + + if ($item['path'] == 'node/%/group') { + $node = node_load($item['map'][1]); + } + elseif (strpos($item['path'], 'group/%/%/admin') === 0 && !empty($item['map'][1]) && $item['map'][1] == 'node') { + $node = node_load($item['map'][2]); + } + if ($node) { return _group_context_handler_entity('node', $node); } @@ -445,6 +482,29 @@ function og_context_handler_user_edit() { return _group_context_handler_entity('user'); } +/** + * Context handler; Get groups from parent of comment being added to it. + */ +function og_context_handler_comment() { + $item = menu_get_item(); + // Check if we are in comment/reply/%nid. No need to load the comment. + if (isset($item['original_map'][2]) && is_numeric($item['original_map'][2])) { + $node = node_load($item['original_map'][2]); + } + // Use the comment ID. + elseif (isset($item['original_map'][1]) && is_numeric($item['original_map'][1])) { + $cid = $item['original_map'][1]; + if (!$comment = comment_load($cid)) { + return; + } + $node = node_load($comment->nid); + } + else { + return; + } + return og_context_handler_node($node); +} + /** * Helper function to get group context from an entity. * @@ -481,3 +541,29 @@ function _group_context_handler_entity($entity_type = 'node', $entity = NULL, $p return $contexts; } + +/** + * Helper function to handle views page access. + * + * @param $group_type + * The group type. + * @param $perm + * The group permission to search for. + * @param $group_id_arg + * Optional; The position in arg() where the group ID can be found. + * + * @return + * TRUE if user is allowed access to the page. + */ +function _og_context_views_page_access($group_type, $perm, $group_id_arg = FALSE) { + if ($group_id_arg !== '') { + $gid = arg($group_id_arg); + if (is_numeric($gid)) { + return og_user_access($group_type, $gid, $perm); + } + } + elseif ($group = og_context($group_type)) { + // Try to get context. + return og_user_access($group_type, $group['gid'], $perm); + } +} diff --git a/sites/all/modules/og/og_example/og_example.info b/sites/all/modules/og/og_example/og_example.info index 02ac8feabb1da90dcff5036f965e2d54731bf36d..92b9a12bd35ff1be3cc97fc4a75693edfd3071cd 100644 --- a/sites/all/modules/og/og_example/og_example.info +++ b/sites/all/modules/og/og_example/og_example.info @@ -27,9 +27,9 @@ name = "OG example" package = "Features" php = "5.2.4" -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_example/og_example.module b/sites/all/modules/og/og_example/og_example.module index c6d58f6e4e1aef96c7dbf59c2684114da47ffde4..1fd302413207682da17068caf2f929df32184439 100644 --- a/sites/all/modules/og/og_example/og_example.module +++ b/sites/all/modules/og/og_example/og_example.module @@ -1,3 +1,12 @@ <?php include_once('og_example.features.inc'); + +/** + * Implements hook_ctools_plugin_directory(). + */ +function og_example_ctools_plugin_directory($module, $plugin) { + if ($module == 'entityreference') { + return "plugins/entityreference/$plugin"; + } +} diff --git a/sites/all/modules/og/og_example/plugins/entityreference/selection/OgExampleSelectionHandler.class.php b/sites/all/modules/og/og_example/plugins/entityreference/selection/OgExampleSelectionHandler.class.php new file mode 100644 index 0000000000000000000000000000000000000000..52ac0ddb031d1db37b8a5a3b3407b84242e6c15a --- /dev/null +++ b/sites/all/modules/og/og_example/plugins/entityreference/selection/OgExampleSelectionHandler.class.php @@ -0,0 +1,62 @@ +<?php + + +/** + * @file + * OG example selection handler. + */ + +class OgExampleSelectionHandler extends OgSelectionHandler { + + /** + * Overrides OgSelectionHandler::getInstance(). + */ + public static function getInstance($field, $instance = NULL, $entity_type = NULL, $entity = NULL) { + return new OgExampleSelectionHandler($field, $instance, $entity_type, $entity); + } + + /** + * Overrides OgSelectionHandler::buildEntityFieldQuery(). + * + * This is an example of "subgroups" (but without getting into the logic of + * sub-grouping). + * The idea here is to show we can set "My groups" and "Other groups" to + * reference different groups by different + * logic. In this example, all group nodes below node ID 5, will appear under + * "My groups", and the rest will appear under "Other groups", + * for administrators. + */ + public function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') { + $group_type = $this->field['settings']['target_type']; + + + if (empty($this->instance['field_mode']) || $group_type != 'node') { + return parent::buildEntityFieldQuery($match, $match_operator); + } + + $field_mode = $this->instance['field_mode']; + $handler = EntityReference_SelectionHandler_Generic::getInstance($this->field, $this->instance, $this->entity_type, $this->entity); + $query = $handler->buildEntityFieldQuery($match, $match_operator); + + // Show only the entities that are active groups. + $query->fieldCondition(OG_GROUP_FIELD, 'value', 1, '='); + + if ($field_mode == 'default') { + $query->propertyCondition('nid', '5', '<='); + } + else { + $query->propertyCondition('nid', '5', '>'); + } + + // FIXME: http://drupal.org/node/1325628 + unset($query->tags['node_access']); + + // FIXME: drupal.org/node/1413108 + unset($query->tags['entityreference']); + + $query->addTag('entity_field_access'); + $query->addTag('og'); + + return $query; + } +} diff --git a/sites/all/modules/og/og_example/plugins/entityreference/selection/og_example.inc b/sites/all/modules/og/og_example/plugins/entityreference/selection/og_example.inc new file mode 100644 index 0000000000000000000000000000000000000000..5f6e37c5f895a13214b59a004e915006e5cb6fbb --- /dev/null +++ b/sites/all/modules/og/og_example/plugins/entityreference/selection/og_example.inc @@ -0,0 +1,7 @@ +<?php + +$plugin = array( + 'title' => t('Organic groups example'), + 'class' => 'OgExampleSelectionHandler', +); + diff --git a/sites/all/modules/og/og_field_access/og_field_access.info b/sites/all/modules/og/og_field_access/og_field_access.info index c3c53efb4611df88f0fd2e238476205b23a7ec73..4d78c0659f0b71201524f16adf53da8fbb6575f5 100644 --- a/sites/all/modules/og/og_field_access/og_field_access.info +++ b/sites/all/modules/og/og_field_access/og_field_access.info @@ -8,9 +8,9 @@ files[] = og_field_access.module ; Tests files[] = og_field_access.test -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_register/og_register.info b/sites/all/modules/og/og_register/og_register.info index f0a964699dbac7f917618e26d473039eaf6d1fa8..383c4a6bc9c3bb568c3d811ed69a40a6ef0c0d85 100644 --- a/sites/all/modules/og/og_register/og_register.info +++ b/sites/all/modules/og/og_register/og_register.info @@ -6,9 +6,9 @@ dependencies[] = og core = 7.x files[] = og_register.module files[] = og_register.install -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_register/og_register.module b/sites/all/modules/og/og_register/og_register.module index ca7c23a5346a77f16a39ab2b74207b43dbe047f3..e547a0e56b65190b24015d7e9f0f15ee904a7a32 100644 --- a/sites/all/modules/og/og_register/og_register.module +++ b/sites/all/modules/og/og_register/og_register.module @@ -13,7 +13,9 @@ define('OG_REGISTER_FIELD', 'group_register'); /** - * Implements hook_query_TAG_alter(). + * Implements hook_query_og_alter(). + * + * @todo: Filter out groups that don't allow to subscribe, if OG-UI is enabled. */ function og_register_query_og_alter(QueryAlterableInterface $query) { global $user; @@ -45,7 +47,7 @@ function og_register_query_og_alter(QueryAlterableInterface $query) { // Search the condition that falsified the query. // @see OgHandler_base::buildEntityFieldQuery foreach ($conditions as $key => $condition) { - if (!is_array($condition)) { + if (!is_array($condition) || !is_string($condition['field'])) { continue; } if ($condition['field'] != $group_type . '.' . $entity_info['entity keys']['id']) { @@ -76,6 +78,61 @@ function og_register_query_og_alter(QueryAlterableInterface $query) { } } +/** + * Implements hook_field_attach_form(). + * + * Mark the group-audience fields as ones that are used i registration. This is used + * to later on make sure the user is registered according to the allowed permissions (i.e. with or + * without administrator approval). + */ +function og_register_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode) { + global $user; + if ($user->uid || $entity_type != 'user') { + return; + } + + if (!module_exists('og_ui')) { + return; + } + + if (!$fields = og_get_group_audience_fields()) { + return; + } + + foreach (array_keys($fields) as $field_name) { + if (empty($form[$field_name])) { + continue; + } + $form[$field_name]['#element_validate'][] = 'og_register_og_membership_state_validate'; + } +} + +/** + * Validate handler; Set the state according to the "subscribe" permissions of the group. + */ +function og_register_og_membership_state_validate($element, &$form_state) { + $langcode = $element['#language']; + $field_name = $element[$langcode]['#field_name']; + + if (empty($form_state['values'][$field_name][$langcode])) { + return; + } + + $field = field_info_field($field_name); + $group_type = $field['settings']['target_type']; + + $values = $form_state['values'][$field_name][$langcode]; + foreach ($values as &$value) { + $og_roles = og_roles($group_type, NULL, $value['target_id']); + $rid = array_search(OG_ANONYMOUS_ROLE, $og_roles); + $og_permissions = og_role_permissions(array($rid => $rid)); + // State should be pending if "subscribe without approval" is not enabled. + $value['state'] = !empty($og_permissions[$rid]['subscribe without approval']) ? OG_STATE_ACTIVE : OG_STATE_PENDING; + } + + form_set_value($element, array($langcode => $values), $form_state); +} + /** * Implements og_fields_info(). */ diff --git a/sites/all/modules/og/og_ui/includes/migrate/7000/add_field.inc b/sites/all/modules/og/og_ui/includes/migrate/7000/add_field.inc index a11f6dbed4daf63a898305dba38046305f3a7afb..a50b331b66732f0cd36b2944e0c1b140dd1a2a5b 100644 --- a/sites/all/modules/og/og_ui/includes/migrate/7000/add_field.inc +++ b/sites/all/modules/og/og_ui/includes/migrate/7000/add_field.inc @@ -5,8 +5,6 @@ * Add OG UI related fields to group node-types. */ -if (db_table_exists('d6_og')) { - class OgUiMigrateAddField extends MigrationBase { public function __construct() { @@ -45,5 +43,3 @@ class OgUiMigrateAddField extends MigrationBase { return MigrationBase::RESULT_COMPLETED; } } - -} diff --git a/sites/all/modules/og/og_ui/includes/migrate/7000/populate_field.inc b/sites/all/modules/og/og_ui/includes/migrate/7000/populate_field.inc index 6b75155db369a852caf16092a435d3918289f797..13ed128aae1900dfda7b447b452985fd4503bb81 100644 --- a/sites/all/modules/og/og_ui/includes/migrate/7000/populate_field.inc +++ b/sites/all/modules/og/og_ui/includes/migrate/7000/populate_field.inc @@ -5,8 +5,6 @@ * Upgrade group visibility (e.g. open, moderated, closed) from OG6. */ -if (db_table_exists('d6_og')) { - class OgUiPopulateField extends DynamicMigration { /** @@ -53,6 +51,3 @@ class OgUiPopulateField extends DynamicMigration { return drupal_strtolower('OgUiPopulateField' . ucfirst($this->arguments['bundle'])); } } - -} - diff --git a/sites/all/modules/og/og_ui/includes/migrate/7000/set_roles.inc b/sites/all/modules/og/og_ui/includes/migrate/7000/set_roles.inc index 3f8bb01f0ebffe5db1df88c6ff5feb77cf336992..6423fb89181c826b79859564216c953f241a0cc0 100644 --- a/sites/all/modules/og/og_ui/includes/migrate/7000/set_roles.inc +++ b/sites/all/modules/og/og_ui/includes/migrate/7000/set_roles.inc @@ -5,8 +5,6 @@ * Set permissions on group to upgrade group visibility. */ -if (db_table_exists('d6_og')) { - class OgUiSetRoles extends Migration { /** @@ -102,6 +100,3 @@ class OgUiSetRoles extends Migration { return TRUE; } } - -} - diff --git a/sites/all/modules/og/og_ui/includes/views/og_ui.views_default.inc b/sites/all/modules/og/og_ui/includes/views/og_ui.views_default.inc index bb6cc3a54bf179cea7dcb8b57455ca7d60abe29d..fbf56f41ccdfa80675675927c477ef803dd22d45 100644 --- a/sites/all/modules/og/og_ui/includes/views/og_ui.views_default.inc +++ b/sites/all/modules/og/og_ui/includes/views/og_ui.views_default.inc @@ -209,6 +209,14 @@ function og_ui_views_default_views() { $handler->display->display_options['fields']['og_membership_request']['table'] = 'field_data_og_membership_request'; $handler->display->display_options['fields']['og_membership_request']['field'] = 'og_membership_request'; $handler->display->display_options['fields']['og_membership_request']['relationship'] = 'og_membership_rel'; + /* Field: OG membership: Edit link */ + $handler->display->display_options['fields']['edit_membership']['id'] = 'edit_membership'; + $handler->display->display_options['fields']['edit_membership']['table'] = 'og_membership'; + $handler->display->display_options['fields']['edit_membership']['field'] = 'edit_membership'; + $handler->display->display_options['fields']['edit_membership']['relationship'] = 'og_membership_rel'; + $handler->display->display_options['fields']['edit_membership']['label'] = ''; + $handler->display->display_options['fields']['edit_membership']['element_label_colon'] = FALSE; + $handler->display->display_options['fields']['edit_membership']['destination'] = TRUE; /* Sort criterion: User: Name */ $handler->display->display_options['sorts']['name']['id'] = 'name'; $handler->display->display_options['sorts']['name']['table'] = 'users'; @@ -265,4 +273,4 @@ function og_ui_views_default_views() { $views[$view->name] = $view; return $views; -} \ No newline at end of file +} diff --git a/sites/all/modules/og/og_ui/og_ui.admin.inc b/sites/all/modules/og/og_ui/og_ui.admin.inc index e32c148895004f5715bc6f0cb1bd9b04861933cd..506f66382bef25535279be4fa2035e2b9e8d28bc 100644 --- a/sites/all/modules/og/og_ui/og_ui.admin.inc +++ b/sites/all/modules/og/og_ui/og_ui.admin.inc @@ -65,6 +65,38 @@ function og_ui_admin_settings($form_state) { } } + $form['og_features_ignore_og_fields'] = array( + '#type' => 'checkbox', + '#title' => t('Prevent "Features" export piping'), + '#description' => t('When exporting using Features module a content-type, this will prevent from OG related fields to be exported.'), + '#default_value' => variable_get('og_features_ignore_og_fields', FALSE), + '#access' => module_exists('features'), + ); + + $form['og_use_queue'] = array( + '#type' => 'checkbox', + '#title' => t('Use queue'), + '#description' => t("Use the core's queue process to operations such as deleting memberships when groups are deleted."), + '#default_value' => variable_get('og_use_queue', FALSE), + ); + + $form['og_orphans_delete'] = array( + '#type' => 'checkbox', + '#title' => t('Delete orphans'), + '#description' => t('Delete "Orphan" group-content (not including useres), when the group is deleted.'), + '#default_value' => variable_get('og_orphans_delete', FALSE), + '#states' => array( + 'visible' => array( + ':input[name="og_use_queue"]' => array('checked' => TRUE), + ), + ), + '#attributes' => array( + 'class' => array('entityreference-settings'), + ), + ); + + // Re-use Entity-reference CSS for indentation. + $form['#attached']['css'][] = drupal_get_path('module', 'entityreference') . '/entityreference.admin.css'; return system_settings_form($form); } @@ -100,6 +132,7 @@ function og_ui_add_users($form, &$form_state, $group_type, $gid) { og_set_breadcrumb($group_type, $gid, array(l(t('Group'), "$group_type/$gid/group"))); $group = entity_load_single($group_type, $gid); $label = entity_label($group_type, $group); + list(,, $bundle) = entity_extract_ids($group_type, $group); $form['group_type'] = array('#type' => 'value', '#value' => $group_type); $form['gid'] = array('#type' => 'value', '#value' => $gid); @@ -120,6 +153,15 @@ function og_ui_add_users($form, &$form_state, $group_type, $gid) { '#value' => OG_STATE_ACTIVE, ); + // Get all the non-default roles. + if ($og_roles = og_roles($group_type, $bundle, $gid, FALSE, FALSE)) { + $form['og_user']['roles'] = array( + '#type' => 'checkboxes', + '#options' => $og_roles, + '#title' => t('Roles'), + ); + } + $field_names = og_get_group_audience_fields(); $field_name = !empty($form_state['values']['field_name']) ? $form_state['values']['field_name'] : key($field_names); @@ -220,10 +262,106 @@ function og_ui_add_users_submit($form, &$form_state) { $og_membership->etid = $account->uid; $og_membership->state = $state; $og_membership->save(); + // Assign roles. + if (!empty($form_state['values']['roles'])) { + foreach ($form_state['values']['roles'] as $rid) { + og_role_grant($group_type, $gid, $og_membership->etid, $rid); + } + } + + $group = entity_load_single($group_type, $gid); + drupal_set_message(t('%user has been added to the group %group-title.', array('%user' => format_username($account), '%group-title' => entity_label($group_type, $group)))); +} + +/** + * Add Edit membership form. + */ +function og_ui_edit_membership($form, &$form_state, $group_type, $gid, $og_membership) { + og_set_breadcrumb($group_type, $gid, array(l(t('Group'), "$group_type/$gid/group"))); + $group = entity_load_single($group_type, $gid); + $label = entity_label($group_type, $group); + $account = user_load($og_membership->etid); + list(,, $bundle) = entity_extract_ids($group_type, $group); + // Get all the non-default roles. + $og_roles = og_roles($group_type, $bundle, $gid, FALSE, FALSE); + + $form['group_type'] = array('#type' => 'value', '#value' => $group_type); + $form['gid'] = array('#type' => 'value', '#value' => $gid); + $form['id'] = array('#type' => 'value', '#value' => $og_membership->id); + + $form['og_user'] = array( + '#type' => 'fieldset', + '#title' => t('Edit a group membership in %group', array('%group' => $label)), + ); + $form['og_user']['name'] = array( + '#type' => 'markup', + '#title' => t('User name'), + '#markup' => $account->name, + ); + if ($og_roles) { + $form['og_user']['roles'] = array( + '#type' => 'checkboxes', + '#options' => $og_roles, + '#title' => t('Roles'), + '#default_value' => array_keys(og_get_user_roles($group_type, $gid, $account->uid)), + ); + } + + // Add group membership form. + $values = array(); + + // Add group membership form. We still don't have the user or state. + $form_state['og_membership'] = $og_membership; + + $form['membership_fields'] = array( + '#prefix' => '<div id="og-ui-field-name">', + '#suffix' => '</div>', + '#tree' => TRUE, + '#parents' => array('membership_fields'), + ); + field_attach_form('og_membership', $og_membership, $form['membership_fields'], $form_state); + + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Update membership')); + + return $form; +} - drupal_set_message(t('%user has been added to group.', array('%user' => format_username($account)))); +/** + * Validate handler; Edit membership in group. + */ +function og_ui_edit_membership_validate($form, &$form_state) { + $og_membership = $form_state['og_membership']; + field_attach_form_validate('og_membership', $og_membership, $form['membership_fields'], $form_state); +} + +/** + * Submit handler; Edit membership in group. + */ +function og_ui_edit_membership_submit($form, &$form_state) { + $group_type = $form_state['values']['group_type']; + $gid = $form_state['values']['gid']; + $og_membership = $form_state['og_membership']; + field_attach_submit('og_membership', $og_membership, $form['membership_fields'], $form_state); + $og_membership->save(); + $account = user_load($og_membership->etid); + // Assign roles. + $og_roles = og_get_user_roles($group_type, $gid, $account->uid); + foreach (array_keys($og_roles) as $rid) { + if (!in_array($rid, $form_state['values']['roles'])) { + og_role_revoke($group_type, $gid, $account->uid, $rid); + } + } + if (!empty($form_state['values']['roles'])) { + foreach ($form_state['values']['roles'] as $rid) { + og_role_grant($group_type, $gid, $og_membership->etid, $rid); + } + } + drupal_set_message(t('The membership has been updated.')); } + + /** * Form builder; OG user administration page. */ @@ -717,23 +855,25 @@ function theme_og_ui_admin_permissions($variables) { } /** - * Allow site admin to add or remove group fields from fieldable enteies. + * Allow site admin to add or remove group fields from fieldable entities. */ function og_ui_field_settings($form, &$form_state) { $form = array(); $options = array(); $options = array(); - foreach (entity_get_info() as $entity_type => $entity) { - if (!empty($entity['fieldable']) && $entity_type != 'group') { - foreach($entity['bundles'] as $bundle_name => $bundle) { - // Prefix the bundle name with the entity type. - $options[$entity_type][$entity_type . '__' .$bundle_name] = check_plain($bundle['label']); - } + foreach (entity_get_info() as $entity_type => $entity_info) { + if (empty($entity_info['fieldable'])) { + continue; + } + foreach($entity_info['bundles'] as $bundle_name => $bundle) { + // Prefix the bundle name with the entity type. + $entity_name = check_plain("$entity_info[label] ($entity_type)"); + $options[$entity_name][$entity_type . ':' . $bundle_name] = filter_xss($bundle['label']); } } - $form['bundles'] = array( + $form['bundle'] = array( '#title' => t('Bundles'), '#type' => 'select', '#options' => $options, @@ -742,27 +882,65 @@ function og_ui_field_settings($form, &$form_state) { $options = array(); foreach (og_fields_info() as $field_name => $field) { foreach ($field['type'] as $type) { - $options[$type][$field_name] = filter_xss($field['instance']['label']); + $type_name = $type == 'group' ? t('Group') : t('Group content'); + $options[$type_name][$field_name] = filter_xss($field['instance']['label']); } } - $form['fields'] = array( + $selected_field_name = !empty($form_state['values']['field_type']) ? $form_state['values']['field_type'] : OG_AUDIENCE_FIELD; + $selected_og_info = og_fields_info($selected_field_name); + + $form['field_info_wrapper'] = array( + '#prefix' => '<div id="field-info-wrapper">', + '#suffix' => '</div>', + '#parents' => array('field_info_wrapper'), + '#type' => 'fieldset', + ); + + $form['field_info_wrapper']['field_type'] = array( '#title' => t('Fields'), '#type' => 'select', '#options' => $options, + '#required' => TRUE, + '#default_value' => $selected_field_name, + '#ajax' => array( + 'callback' => 'og_ui_admin_fields_ajax_callback', + 'wrapper' => 'field-info-wrapper', + ), ); - $field_enabled = array(); + $form['field_info_wrapper']['description'] = array( + '#markup' => $selected_og_info['description'], + ); - $group_fields = og_fields_info(); + if (!empty($selected_og_info['multiple'])) { + $form['field_info_wrapper']['field_name'] = array( + '#type' => 'textfield', + '#title' => t('Field name'), + '#description' => t('This field type supports adding multiple instances on the same bundle (i.e. the field name is not hardcoded).'), + '#required' => TRUE, + '#maxlength' => 32, + '#default_value' => $selected_field_name, + ); + } + else { + // Pass the field name as a value. + $form['field_name_wrapper']['field_name'] = array( + '#type' => 'value', + '#value' => $selected_field_name, + ); + } + + $field_enabled = array(); + $og_fields = og_fields_info(); - $group_fields_name = array_keys($group_fields); + $og_fields_name = array_keys($og_fields); $entity_info = entity_get_info(); // Get the fields that exist in the bundle. foreach (field_info_fields() as $field_name => $field) { - if (in_array($field_name, $group_fields_name) && !empty($field['bundles'])) { + if (in_array($field_name, $og_fields_name) && !empty($field['bundles'])) { foreach ($field['bundles'] as $entity_type => $bundles) { foreach ($bundles as $bundle) { $field_enabled[$entity_type][$bundle][] = $field_name; @@ -790,8 +968,8 @@ function og_ui_field_settings($form, &$form_state) { ); foreach ($fields as $field_name) { $options[] = array( - check_plain($group_fields[$field_name]['instance']['label']), - filter_xss($group_fields[$field_name]['description']), + check_plain($og_fields[$field_name]['instance']['label']), + filter_xss($og_fields[$field_name]['description']), l(t('Delete'), "admin/config/group/fields/$entity_type/$bundle/$field_name/delete"), ); } @@ -819,34 +997,39 @@ function og_ui_field_settings($form, &$form_state) { return $form; } +/** + * AJAX callback to to return the field name if the field supports "mulitple" + * instances, on the same bundle (e.g. OG_AUDIENCE_FIELD). + */ +function og_ui_admin_fields_ajax_callback($form, &$form_state) { + return $form['field_info_wrapper']; +} + +/** + * Validate handler; Check if field can be attached to bundle. + */ function og_ui_field_settings_validate($form, &$form_state) { - $bundle = preg_replace('/^.*__/i', '', $form_state['values']['bundles']); - $entity_type = str_replace('__' . $bundle, '', $form_state['values']['bundles']); + list($entity_type, $bundle) = explode(':', $form_state['values']['bundle']); + $field_name = $form_state['values']['field_name']; - // Check if field can be attached to entity. - $group_field = og_fields_info($form_state['values']['fields']); + $og_field = og_fields_info($form_state['values']['field_type']); $bundles = field_info_bundles($entity_type); $entity_info = entity_get_info($entity_type); $params = array( - '%field' => $group_field['instance']['label'], + '%field' => $og_field['instance']['label'], '%bundle' => $bundles[$bundle]['label'], '%entity' => $entity_info['label'], ); - if ($form_state['values']['fields'] == OG_GROUP_FIELD && $entity_type == 'group') { - form_set_error('bundles', t('It is not allowed to add the group field to the group entity.')); - } - - // Check if field doesn't exist already. - if (($field = field_info_field($form_state['values']['fields'])) && !empty($field['bundles'][$entity_type]) && in_array($bundle, drupal_map_assoc($field['bundles'][$entity_type]))) { + if (field_info_instance($entity_type, $field_name, $bundle)) { form_set_error('bundles', t('Field %field already exists in %bundle.', $params)); } // Check field can be attached to entity type. - if (!empty($group_field['entity']) && !in_array($entity_type, $group_field['entity'])) { + if (!empty($og_field['entity']) && !in_array($entity_type, $og_field['entity'])) { $items = array(); - foreach ($group_field['entity'] as $entity_type) { + foreach ($og_field['entity'] as $entity_type) { $info = entity_get_info($entity_type); $items[] = $info['label']; } @@ -855,22 +1038,30 @@ function og_ui_field_settings_validate($form, &$form_state) { } +/** + * Submit handler; Attach field can to bundle. + */ function og_ui_field_settings_submit($form, &$form_state) { - $bundle = preg_replace('/^.*__/i', '', $form_state['values']['bundles']); - $entity_type = str_replace('__' . $bundle, '', $form_state['values']['bundles']); - $field_name = $form_state['values']['fields']; + list($entity_type, $bundle) = explode(':', $form_state['values']['bundle']); + $field_name = $form_state['values']['field_name']; + $field_type = $form_state['values']['field_type']; - og_create_field($field_name, $entity_type, $bundle); + $og_field = og_fields_info($field_type); - $group_field = og_fields_info($field_name); - $bundles = field_info_bundles($entity_type); + og_create_field($field_name, $entity_type, $bundle, $og_field); $params = array( - '%field' => $group_field['instance']['label'], - '%bundle' => $bundles[$bundle]['label'], + '@field-type' => $og_field['instance']['label'], + '@field-name' => $field_name, + '@bundle' => $bundle, ); - drupal_set_message(t('Added field %field to %bundle.', $params)); + if ($field_name == $field_type) { + drupal_set_message(t('Added field @field-type to @bundle.', $params)); + } + else { + drupal_set_message(t('Added field @field-type (@field-name) to @bundle.', $params)); + } } diff --git a/sites/all/modules/og/og_ui/og_ui.info b/sites/all/modules/og/og_ui/og_ui.info index c539790e77ffa2eb6ce6cc525a064f865f4930d0..4bf39f6daad4ccaf8e2f81e307f15554afd20096 100644 --- a/sites/all/modules/og/og_ui/og_ui.info +++ b/sites/all/modules/og/og_ui/og_ui.info @@ -16,9 +16,9 @@ files[] = includes/migrate/7000/add_field.inc files[] = includes/migrate/7000/populate_field.inc files[] = includes/migrate/7000/set_roles.inc -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/og_ui/og_ui.module b/sites/all/modules/og/og_ui/og_ui.module index 677ecb66dc496bf7ebf0abe80b4d8c41eabcd155..cb830a7838ba4ca29c1b7c4b0b5bc6ff75f3cd51 100644 --- a/sites/all/modules/og/og_ui/og_ui.module +++ b/sites/all/modules/og/og_ui/og_ui.module @@ -58,6 +58,18 @@ function og_ui_menu() { 'file' => 'og_ui.admin.inc', ); + // User listing pages. + $items['group/%/%/admin/people/edit-membership/%og_membership'] = array( + 'title callback' => 'og_ui_menu_title_callback', + 'title arguments' => array('Edit membership in group @group', 1, 2), + 'type' => MENU_CALLBACK, + 'page callback' => 'drupal_get_form', + 'page arguments' => array('og_ui_edit_membership', 1, 2, 6), + 'access callback' => 'og_ui_user_access_group', + 'access arguments' => array('manage members', 1, 2), + 'file' => 'og_ui.admin.inc', + ); + // Permission administration pages. $items['group/%/%/admin/roles'] = array( 'title callback' => 'og_ui_menu_title_callback', @@ -574,7 +586,7 @@ function og_ui_field_formatter_view($entity_type, $entity, $field, $instance, $l return; } - if (!empty($entity->uid) && ($entity->uid == $user->uid)) { + if (!empty($entity->uid) && ($entity->uid == $account->uid)) { // User is the group manager. $element[0] = array('#markup' => t('You are the group manager')); return $element; @@ -595,7 +607,7 @@ function og_ui_field_formatter_view($entity_type, $entity, $field, $instance, $l } // Check if user can subscribe to the field. - if (empty($settings['field_name']) && $audience_field_name = og_get_best_group_audience_field('user', $user->uid, $entity_type, $bundle)) { + if (empty($settings['field_name']) && $audience_field_name = og_get_best_group_audience_field('user', $account, $entity_type, $bundle)) { $settings['field_name'] = $audience_field_name; } if (!$settings['field_name']) { @@ -614,7 +626,7 @@ function og_ui_field_formatter_view($entity_type, $entity, $field, $instance, $l return; } - if (!og_check_field_cardinality('user', $user->uid, $settings['field_name'])) { + if (!og_check_field_cardinality('user', $account, $settings['field_name'])) { $element[0] = array('#markup' => format_plural($field_info['cardinality'], 'You are already registered to another group', 'You are already registered to @count groups')); return $element; } @@ -668,6 +680,9 @@ function og_ui_field_formatter_view($entity_type, $entity, $field, $instance, $l $private = FALSE; $wrapper = entity_metadata_wrapper($entity_type, $entity); $field_name = $field['field_name']; + if (!$wrapper->{$field_name}->value()) { + return; + } if ($field['cardinality'] == 1) { // Single-value field. if ($wrapper->{$field_name}->entityAccess('view')) { @@ -709,6 +724,10 @@ function og_ui_field_formatter_view($entity_type, $entity, $field, $instance, $l '#type' => 'link', '#title' => $wrapper->label(), '#href' => $wrapper->url->value(), + // Add the group type and group ID, so it's easier for implementing + // modules to extend the formatter. + '#group_type' => $group_type, + '#gid' => $wrapper->getIdentifier(), ); } } @@ -1123,6 +1142,14 @@ function og_ui_migrate_api() { if (db_table_exists('d6_og')) { $migrations['OgUiMigrateAddField'] = array('class_name' => 'OgUiMigrateAddField'); $migrations['OgUiSetRoles'] = array('class_name' => 'OgUiSetRoles'); + + foreach (node_type_get_names() as $bundle => $value) { + $machine_name = 'OgUiPopulateField' . ucfirst($bundle); + $migrations[$machine_name] = array( + 'class_name' => 'OgUiPopulateField', + 'bundle' => $bundle, + ); + } } $api = array( 'api' => 2, @@ -1130,23 +1157,3 @@ function og_ui_migrate_api() { ); return $api; } - -/** - * Implements hook_modules_enabled(). - * - * Register dynamic migrate plugins for upgrading from OG6. - */ -function og_ui_modules_enabled($modules) { - if (!db_table_exists('d6_og')) { - return; - } - - if (!in_array('migrate', $modules) && !module_exists('migrate')) { - return; - } - - foreach (node_type_get_names() as $bundle => $value) { - // Register dynamic migrations. - Migration::registerMigration('OgUiPopulateField', 'OgUiPopulateField' . ucfirst($bundle), array('bundle' => $bundle)); - } -} diff --git a/sites/all/modules/og/og_ui/og_ui.pages.inc b/sites/all/modules/og/og_ui/og_ui.pages.inc index c415a5d3165a11c1d510df7d8e1cb4df761c1250..db30d4f44a387b1c3f2abae4a00e4c22ed79b651 100644 --- a/sites/all/modules/og/og_ui/og_ui.pages.inc +++ b/sites/all/modules/og/og_ui/og_ui.pages.inc @@ -41,10 +41,11 @@ function og_ui_subscribe($entity_type, $etid, $field_name = NULL) { } } - $wrapper = entity_metadata_wrapper('user', $user->uid); + $field = field_info_field($field_name); + $instance = field_info_instance('user', $field_name, 'user'); + $account = user_load($user->uid); - $field_info = field_info_field($field_name); - if (empty($field_info) || empty($wrapper->{$field_name}) || !$wrapper->{$field_name}->access('view')) { + if (empty($instance) || !field_access('view', $field, 'user', $account)) { // Field name given is incorrect, or user doesn't have access to the field. drupal_not_found(); return; @@ -63,6 +64,7 @@ function og_ui_subscribe($entity_type, $etid, $field_name = NULL) { } $redirect = FALSE; + $message = ''; $params = array(); $params['@user'] = format_username($user); @@ -86,8 +88,24 @@ function og_ui_subscribe($entity_type, $etid, $field_name = NULL) { $redirect = TRUE; } + if (!$message && $field['cardinality'] != FIELD_CARDINALITY_UNLIMITED) { + // Check if user is already registered as active or pending in the maximum + // allowed values. + $wrapper = entity_metadata_wrapper('user', $account->uid); + if ($field['cardinality'] == 1) { + $count = $wrapper->{$field_name}->value() ? 1 : 0; + } + else { + $count = $wrapper->{$field_name}->count(); + } + if ($count >= $field['cardinality']) { + $message = t('You cannot register to this group, as you have reached your maximum allowed subscriptions.'); + $redirect = TRUE; + } + } + if ($redirect) { - drupal_set_message($message); + drupal_set_message($message, 'warning'); $url = entity_uri($entity_type, $entity); drupal_goto($url['path'], $url['options']); } @@ -96,6 +114,7 @@ function og_ui_subscribe($entity_type, $etid, $field_name = NULL) { // Show the user a subscription confirmation. return drupal_get_form('og_ui_confirm_subscribe', $entity_type, $id, $user, $field_name); } + drupal_access_denied(); } /** @@ -211,4 +230,4 @@ function og_ui_confirm_unsubscribe_submit($form, &$form_state) { if (entity_access('view', $group_type, $group)) { $form_state['redirect'] = entity_uri($group_type, $group); } -} \ No newline at end of file +} diff --git a/sites/all/modules/og/og_ui/og_ui.test b/sites/all/modules/og/og_ui/og_ui.test index 728b089a85d489ad5799f25bf5017ee25c1c313a..2198e8a1b33f22cbeb3833888fde204012079dae 100644 --- a/sites/all/modules/og/og_ui/og_ui.test +++ b/sites/all/modules/og/og_ui/og_ui.test @@ -298,10 +298,6 @@ class OgUiMigrate7000TestCase extends UpgradePathTestCase { module_enable(array('og_ui', 'migrate')); - Migration::registerMigration('OgMigrateAddFields'); - Migration::registerMigration('OgUiMigrateAddField'); - Migration::registerMigration('OgUiSetRoles'); - foreach (migrate_migrations() as $migration) { $machine_name = $migration->getMachineName(); $result = $migration->processImport(); diff --git a/sites/all/modules/og/plugins/entityreference/behavior/OgBehaviorHandler.class.php b/sites/all/modules/og/plugins/entityreference/behavior/OgBehaviorHandler.class.php index 9c1499b0566c3f33640054363b75b61b2023cfff..4d6780136d6769dae793f49ace5927e17d2667ae 100644 --- a/sites/all/modules/og/plugins/entityreference/behavior/OgBehaviorHandler.class.php +++ b/sites/all/modules/og/plugins/entityreference/behavior/OgBehaviorHandler.class.php @@ -9,7 +9,7 @@ class OgBehaviorHandler extends EntityReference_BehaviorHandler_Abstract { * Implements EntityReference_BehaviorHandler_Abstract::access(). */ public function access($field, $instance) { - return $field['settings']['handler'] == 'og'; + return $field['settings']['handler'] == 'og' || strpos($field['settings']['handler'], 'og_') === 0; } /** @@ -91,8 +91,7 @@ class OgBehaviorHandler extends EntityReference_BehaviorHandler_Abstract { // User has no access to field. return; } - $diff = $this->groupAudiencegetDiff($entity_type, $entity, $field, $instance, $langcode, $items); - if (!$diff) { + if (!$diff = $this->groupAudiencegetDiff($entity_type, $entity, $field, $instance, $langcode, $items)) { return; } @@ -106,6 +105,21 @@ class OgBehaviorHandler extends EntityReference_BehaviorHandler_Abstract { og_membership_delete_multiple($diff['delete']); } + if (!$diff['insert']) { + return; + } + + // Prepare an array with the membership state, if it was provided in the widget. + $states = array(); + foreach ($items as $item) { + $gid = $item['target_id']; + if (empty($item['state']) || !in_array($gid, $diff['insert'])) { + // State isn't provided, or not an "insert" operation. + continue; + } + $states[$gid] = $item['state']; + } + foreach ($diff['insert'] as $gid) { $values = array( 'entity_type' => $entity_type, @@ -113,6 +127,10 @@ class OgBehaviorHandler extends EntityReference_BehaviorHandler_Abstract { 'field_name' => $field_name, ); + if (!empty($states[$gid])) { + $values['state'] = $states[$gid]; + } + og_group($group_type, $gid, $values); } } @@ -162,4 +180,51 @@ class OgBehaviorHandler extends EntityReference_BehaviorHandler_Abstract { return $return; } + + /** + * Implements EntityReference_BehaviorHandler_Abstract::views_data_alter(). + */ + public function views_data_alter(&$data, $field) { + // We need to override the default EntityReference table settings when OG + // behavior is being used. + if (og_is_group_audience_field($field['field_name'])) { + $entity_types = array_keys($field['bundles']); + // We need to join the base table for the entities + // that this field is attached to. + foreach ($entity_types as $entity_type) { + $entity_info = entity_get_info($entity_type); + $data['og_membership'] = array( + 'table' => array( + 'join' => array( + $entity_info['base table'] => array( + // Join entity base table on its id field with left_field. + 'left_field' => $entity_info['entity keys']['id'], + 'field' => 'etid', + 'extra' => array( + 0 => array( + 'field' => 'entity_type', + 'value' => $entity_type, + ), + ), + ), + ), + ), + // Copy the original config from the table definition. + $field['field_name'] => $data['field_data_' . $field['field_name']][$field['field_name']], + $field['field_name'] . '_target_id' => $data['field_data_' . $field['field_name']][$field['field_name'] . '_target_id'], + ); + + // Change config with settings from og_membership table. + foreach (array('filter', 'argument', 'sort') as $op) { + $data['og_membership'][$field['field_name'] . '_target_id'][$op]['field'] = 'gid'; + $data['og_membership'][$field['field_name'] . '_target_id'][$op]['table'] = 'og_membership'; + unset($data['og_membership'][$field['field_name'] . '_target_id'][$op]['additional fields']); + } + } + + // Get rid of the original table configs. + unset($data['field_data_' . $field['field_name']]); + unset($data['field_revision_' . $field['field_name']]); + } + } } diff --git a/sites/all/modules/og/plugins/entityreference/behavior/OgWidgetHandler.class.php b/sites/all/modules/og/plugins/entityreference/behavior/OgWidgetHandler.class.php index dcd04c33ce6532c427256d89f4985c5695bc3a06..85c2659d4de4692e3bf1329265835969fee8622a 100644 --- a/sites/all/modules/og/plugins/entityreference/behavior/OgWidgetHandler.class.php +++ b/sites/all/modules/og/plugins/entityreference/behavior/OgWidgetHandler.class.php @@ -6,7 +6,7 @@ class OgWidgetHandler extends EntityReference_BehaviorHandler_Abstract { public function access($field, $instance) { - return $field['settings']['handler'] == 'og' && $instance['widget']['type'] == 'og_complex'; + return ($field['settings']['handler'] == 'og' || strpos($field['settings']['handler'], 'og_') === 0) && $instance['widget']['type'] == 'og_complex'; } /** diff --git a/sites/all/modules/og/plugins/entityreference/selection/OgSelectionHandler.class.php b/sites/all/modules/og/plugins/entityreference/selection/OgSelectionHandler.class.php index b84037e9af7c607cee8ec5039b88d9ddcc3eb61f..651f6402d02f0fe07cb6af6cce3b96e2194ec071 100644 --- a/sites/all/modules/og/plugins/entityreference/selection/OgSelectionHandler.class.php +++ b/sites/all/modules/og/plugins/entityreference/selection/OgSelectionHandler.class.php @@ -105,6 +105,10 @@ class OgSelectionHandler extends EntityReference_SelectionHandler_Generic { $field_mode = $this->instance['field_mode']; $user_groups = og_get_groups_by_user(NULL, $group_type); + $user_groups = $user_groups ? $user_groups : array(); + $user_groups = array_merge($user_groups, $this->getGidsForCreate()); + + // Show the user only the groups they belong to. if ($field_mode == 'default') { if ($user_groups && !empty($this->instance) && $this->instance['entity_type'] == 'node') { @@ -165,4 +169,33 @@ class OgSelectionHandler extends EntityReference_SelectionHandler_Generic { // FIXME: Allow altering, after fixing http://drupal.org/node/1413108 // $handler->entityFieldQueryAlter($query); } + + /** + * Get group IDs from URL or OG-context, with access to create group-content. + * + * @return + * Array with group IDs a user (member or non-member) is allowed to + * create, or empty array. + */ + private function getGidsForCreate() { + if ($this->instance['entity_type'] != 'node') { + return array(); + } + + if (!module_exists('entityreference_prepopulate') || empty($this->instance['settings']['behaviors']['prepopulate'])) { + return array(); + } + + // Don't try to validate the IDs. + if (!$ids = entityreference_prepopulate_get_values($this->field, $this->instance, FALSE)) { + return array(); + } + $node_type = $this->instance['bundle']; + foreach ($ids as $delta => $id) { + if (!is_numeric($id) || !$id || !og_user_access('node', $id, "create $node_type content")) { + unset($ids[$delta]); + } + } + return $ids; + } } diff --git a/sites/all/modules/og/tests/og_test.info b/sites/all/modules/og/tests/og_test.info index f82ddb3a9b4766f5eafe4e1d5f78fd7fddd5f559..e814e91816158c223415ec49259c2be0c8480747 100644 --- a/sites/all/modules/og/tests/og_test.info +++ b/sites/all/modules/og/tests/og_test.info @@ -4,9 +4,9 @@ core = 7.x dependencies[] = og hidden = TRUE -; Information added by drupal.org packaging script on 2012-11-25 -version = "7.x-2.0-beta3+6-dev" +; Information added by drupal.org packaging script on 2013-04-22 +version = "7.x-2.2" core = "7.x" project = "og" -datestamp = "1353849566" +datestamp = "1366630883" diff --git a/sites/all/modules/og/tests/og_test.module b/sites/all/modules/og/tests/og_test.module index 2908aada38176324b08126885e468cdc5dbc2354..0db5f3dce077cf2755c8d49cec70def5ca46fa8b 100644 --- a/sites/all/modules/og/tests/og_test.module +++ b/sites/all/modules/og/tests/og_test.module @@ -8,9 +8,84 @@ /** * Implements hook_node_presave(). */ -function og_node_presave($node) { +function og_test_node_presave($node) { if (!empty($node->nid) && !empty($node->og_group_on_save)) { $values = $node->og_group_on_save; og_group($values['group_type'], $values['gid'], array('entity_type' => 'node', 'entity' => $node)); } } + +/** + * Implements hook_module_implements_alter(). + */ +function og_test_module_implements_alter(&$implementations, $hook) { + if ($hook != 'entity_delete') { + return; + } + + // Switch the orders of the implementations. + $og = $implementations['og']; + $og_test = $implementations['og_test']; + + unset($implementations['og'], $implementations['og_test']); + + $implementations['og_test'] = $og_test; + $implementations['og'] = $og; +} + +/** + * Implements hook_entity_delete(). + */ +function og_test_entity_delete($entity, $type) { + if (!og_is_group($type, $entity) || $entity->title != 'move' ) { + return; + } + + // The children nodes of the deleted group need another group to be associated + // with. Using entity field query for getting the NID of another group. + $query = new EntityFieldQuery(); + $result = $query + ->entityCondition('entity_type', 'node') + ->propertyCondition('nid', $entity->nid, '<>') + ->execute(); + + if (empty($result['node'])) { + return; + } + + $nid = reset(array_keys($result['node'])); + + $entity->og_orphans = array( + 'move' => array( + 'group_type' => 'node', + 'gid' => $nid, + ), + ); +} + +/** + * Implements hook_form_alter(). + * + * @see OgBehaviorHandlerTestCase::testSetStateOnInsert() + */ +function og_test_form_alter(&$form, $form_state) { + if (empty($form['#node_edit_form']) || $form['#bundle'] != 'behavior') { + return; + } + + $form[OG_AUDIENCE_FIELD]['#element_validate'][] = 'og_test_form_behavior_validate'; +} + +/** + * Validate handler; Add state to the field values, if title is "state-pending". +. */ +function og_test_form_behavior_validate($element, &$form_state) { + if ($form_state['values']['title'] != 'state-pending') { + return; + } + + $value = $form_state['values'][OG_AUDIENCE_FIELD]; + $value[LANGUAGE_NONE][0]['state'] = OG_STATE_PENDING; + + form_set_value($element, $value, $form_state); +}