diff --git a/og.module b/og.module index c5e4e17..701fcfc 100644 --- a/og.module +++ b/og.module @@ -780,8 +780,16 @@ function og_field_attach_form($entity_type, $entity, &$form, &$form_state, $lang return; } $primary_ids = $wrapper->$primary_field->value(array('identifier' => TRUE)); - if (!$primary_ids) { - continue; + + if (!empty($field['settings']['handler_settings']['force_optional_primary']) && user_access('administer group')) { + // Set required field to not required for administrator. + $form_primary = &$form[$primary_field][LANGUAGE_NONE]; + + if ($form_primary['#required']) { + $form_primary['#required'] = FALSE; + // Need to add a "None" option now that the field is not required. + $form_primary['#options'] = array('_none' => t('- None -')) + $form_primary['#options']; + } } // Intersect the IDs from the primary field, with the referencable IDs @@ -890,7 +898,7 @@ function og_form_secondary_field_validate($form, &$form_state) { $items = FALSE; field_default_extract_form_values($entity_type, $entity, $primary_field, $primary_instance, $langcode, $items, $form, $form_state); foreach ($items as $item) { - if (!empty($item['target_id'])) { + if (!empty($item['target_id']) && $item['target_id'] != '_none') { ++$count; } } @@ -947,10 +955,15 @@ function og_entity_presave($entity, $entity_type) { continue; } - if (!$wrapper->{$field_name}->value(array('identifier' => TRUE))) { + if (!$new_ids = $wrapper->{$field_name}->value(array('identifier' => TRUE))) { continue; } + if (!is_array($new_ids)) { + // Entity metadta returned a single ID or no ID. + $new_ids = array($new_ids); + } + $primary_field = $fields_info[$field_name]['settings']['handler_settings']['primary_field']; $primary_field_info = field_info_field($primary_field); $existing_ids = $wrapper->{$primary_field}->value(array('identifier' => TRUE)); @@ -958,7 +971,7 @@ function og_entity_presave($entity, $entity_type) { // Entity metadta returned a single ID. $existing_ids = array($existing_ids); } - foreach ($wrapper->{$field_name}->value(array('identifier' => TRUE)) as $id) { + foreach ($new_ids as $id) { // TODO: Move this check to Entity API. if (!in_array($id, $existing_ids)) { if ($primary_field_info['cardinality'] == 1) { diff --git a/og.test b/og.test index d3aef1a..0d52e1c 100644 --- a/og.test +++ b/og.test @@ -1,5 +1,27 @@ xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option)); + return $this->assertTrue(empty($elements), $message ? $message : t("Option @option doesn't exist in field @id.", array('@option' => $option, '@id' => $id)), t('Browser')); + } + + /** + * Helper function to assert an option exists in select field. + */ + protected function assertOption($id, $option, $message = '') { + $elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option)); + return $this->assertTrue(!empty($elements), $message ? $message : t("Option @option exists in field @id.", array('@option' => $option, '@id' => $id)), t('Browser')); + } +} + class OgAccess extends DrupalWebTestCase { @@ -866,8 +888,7 @@ class OgMigrate7000TestCase extends UpgradePathTestCase { /** * Test primary/second field in UI, see http://drupal.org/node/1519702. */ -class OgPrimarySecondaryField extends DrupalWebTestCase { - +class OgPrimarySecondaryField extends OGAssertOptionsTestCase { public static function getInfo() { return array( 'name' => 'OG primary/ secondary field', @@ -1061,15 +1082,130 @@ class OgPrimarySecondaryField extends DrupalWebTestCase { $this->assertOptionSelected($field_id, $this->group1->nid, t('Group1 is selected in group audience field (Edit any content).')); } +} + + + +/** + * Test second field forcing primary field to optional in UI. + * See: http://drupal.org/node/1580814 + */ +class OgSecondaryForcePrimaryOptField extends OGAssertOptionsTestCase { + + public static function getInfo() { + return array( + 'name' => 'OG secondary field force primary optional', + 'description' => 'Test secondary field forcing primary field optional in UI.', + 'group' => 'Organic groups', + ); + } + /** - * Helper function to assert an option doesn't exist in select field + * - Set Page content type as group type. + * - Set Article content type as group content type. + * - Set OG_AUDIENCE_FIELD on Article to be single select + required. + * - Set OG_AUDIENCE_OTHER_GROUPS_FIELD to use "force_optional_primary". + * - Create a group and one article which belongs to it. + * - Create an admin user who is NOT a member of group. */ - protected function assertNoOption($id, $option, $message = '') { - $elements = $this->xpath('//select[@id=:id]//option[@value=:option]', array(':id' => $id, ':option' => $option)); - return $this->assertTrue(empty($elements), $message ? $message : t("Option @option doesn't exist in field @id.", array('@option' => $option, '@id' => $id)), t('Browser')); + function setUp() { + parent::setUp('og'); + + // Add OG group field to a the node's "page" bundle. + og_create_field(OG_GROUP_FIELD, 'node', 'page'); + + // Add OG audience field to the node's "article" bundle. + $og_field = og_fields_info(OG_AUDIENCE_FIELD); + $og_field['field']['settings']['target_type'] = 'node'; + $og_field['field']['cardinality'] = 1; + $og_field['field']['required'] = TRUE; + og_create_field(OG_AUDIENCE_FIELD, 'node', 'article', $og_field); + + // Add a secondary field to the node's "article" bundle. + $og_field['instance']['label'] = t('Other groups'); + $og_field['instance']['widget']['type'] = 'options_select'; + $og_field['field']['cardinality'] = 1; + $og_field['field']['required'] = TRUE; + $og_field['field']['settings']['handler_settings']['reference_type'] = 'other_groups'; + $og_field['field']['settings']['handler_settings']['primary_field'] = OG_AUDIENCE_FIELD; + $og_field['field']['settings']['handler_settings']['hide_secondary_field'] = TRUE; + $og_field['field']['settings']['handler_settings']['force_optional_primary'] = TRUE; + og_create_field(OG_AUDIENCE_OTHER_GROUPS_FIELD, 'node', 'article', $og_field); + + // Create a group manager. + $this->group_manager = $this->drupalCreateUser(array('access content', 'create page content', 'edit own page content', 'edit own article content')); + + // Create an admin user. + $this->admin_user = $this->drupalCreateUser(array('administer group', 'access content', 'create page content', 'edit any page content', 'edit own page content', 'edit any article content', 'edit own article content')); + + // Create group nodes. + $settings = array( + 'type' => 'page', + OG_GROUP_FIELD . '[und][0][value]' => 1, + 'uid' => $this->group_manager->uid + ); + $this->group = $this->drupalCreateNode($settings); + + // Change permissions to authenticated member. + $roles = array_flip(og_roles('node', 'page', $this->group->nid)); + $permissions = array( + 'create article content' => 1, + 'update any article content' => 1, + ); + og_role_change_permissions($roles[OG_AUTHENTICATED_ROLE], $permissions); + + + // Create article node to add to group. + $settings = array( + 'type' => 'article', + 'uid' => $this->group_manager->uid, + ); + $this->group_content = $this->drupalCreateNode($settings); + + // Add node to group + $values = array( + 'entity_type' => 'node', + 'entity' => $this->group_content, + ); + og_group('node', $this->group->nid, $values); + + // Ensure Node access strict variable is FALSE, + // so we can test that an admin user who is not a member + // of the group can edit the article content. + variable_set('og_node_access_strict', FALSE); } + + /** + * Test primary/secondary fields on node edit for admin + * (non-member) user. + * + * Primary field should display no groups and only -None- option, + * since the admin user is not a member of any group. + * The secondary field should display the Page group we created. + * + */ + function testGroupFields() { + $this->drupalLogin($this->admin_user); + + $this->drupalGet('node/' . $this->group_content->nid . '/edit'); + + // Check primary field. + $instance = field_info_instance('node', OG_AUDIENCE_FIELD, 'article'); + $this->assertText($instance['label'], t('Primary field label exists.')); + $langcode = LANGUAGE_NONE; + $field_id = drupal_html_id("edit-" . OG_AUDIENCE_FIELD . "-$langcode"); + $this->assertOption($field_id, '_none', t('"None" option appears in primary field.')); + $this->assertNoOption($field_id, $this->group->nid, t("Group doesn't appear in primary field.")); + + // Check secondary field. + $instance = field_info_instance('node', OG_AUDIENCE_OTHER_GROUPS_FIELD, 'article'); + $this->assertText($instance['label'], t('Secondary field label exists.')); + $field_id = drupal_html_id("edit-".OG_AUDIENCE_OTHER_GROUPS_FIELD."-$langcode"); + $this->assertOptionSelected($field_id, $this->group->nid, t('Group appears in secondary field and is selected.')); + } } + /** * Test the revocation of group roles. */ diff --git a/plugins/entityreference/selection/OgSelectionHandler.class.php b/plugins/entityreference/selection/OgSelectionHandler.class.php index 4a4f934..1af4156 100644 --- a/plugins/entityreference/selection/OgSelectionHandler.class.php +++ b/plugins/entityreference/selection/OgSelectionHandler.class.php @@ -44,6 +44,7 @@ class OgSelectionHandler extends EntityReference_SelectionHandler_Generic { 'reference_type' => 'my_groups', 'primary_field' => FALSE, 'hide_secondary_field' => TRUE, + 'force_optional_primary' => TRUE, ); $form['target_bundles'] = array( @@ -125,6 +126,19 @@ class OgSelectionHandler extends EntityReference_SelectionHandler_Generic { ), ), ); + + $form['force_optional_primary'] = array( + '#type' => 'checkbox', + '#title' => t('Force optional primary field'), + '#description' => t("Force the primary field to be optional when the secondary field is shown. If the primary field is requrired, then this will allow privileged users to edit group content for groups they're not members of -- i.e., they can leave the primary field blank."), + '#default_value' => $settings['force_optional_primary'], + '#states' => array( + 'invisible' => array( + ':input[name="field[settings][handler_settings][primary_field]"]' => array('value' => 0), + ), + ), + ); + form_load_include($form_state, 'php', 'og', '/plugins/selection/og.class'); }