diff --git a/admin/group.child.entities.inc b/admin/group.child.entities.inc new file mode 100644 index 0000000..ffabe8a --- /dev/null +++ b/admin/group.child.entities.inc @@ -0,0 +1,71 @@ + 'radios', + '#title' => t('Admin group selection mode'), + '#default_value' => variable_get('group_entity_choice_admin_element', 'autocomplete'), + '#description' => t('Choose the type of form element administrators use to select which groups an entity should be in.'), + '#options' => array( + 'select' => t('Select dropdown'), + 'autocomplete' => t('Autocomplete'), + 'checkbox' => t('Checkboxes or radios'), + ), + ); + + $form['group_entity_choice_standard_element'] = array( + '#type' => 'radios', + '#title' => t('Standard user group selection mode'), + '#default_value' => variable_get('group_entity_choice_standard_element', 'select'), + '#description' => t('Choose the type of form element standard users use to select which groups an entity should be in.'), + '#options' => array( + 'select' => t('Select dropdown'), + 'autocomplete' => t('Autocomplete'), + 'checkbox' => t('Checkboxes or radios'), + ), + ); + + $options = array(); + + foreach (entity_get_info() as $entity_type => $entity) { + if($entity['group entity']) { + $options[$entity_type] = $entity['label']; + } + } + + $form['group_entity_multiple_groups'] = array( + '#type' => 'checkboxes', + '#title' => t('Entities in multiple groups'), + '#default_value' => variable_get('group_entity_multiple_groups', array()), + '#description' => t('Choose if an entity can be in multiple groups. Entities in multiple groups can be updated and deleted by users with the necessary permissions in any of the groups it is in. After changing this session you must clear the site cache. It is not recommended that you go from multiple to single on a site which has entities in multiple groups without testing.'), + '#options' => $options, + ); + + $form['#submit'][] = 'group_entity_settings_form_submit'; + + return system_settings_form($form); +} + +/** + * Form submission handler. + * + * @see gnode_settings(). + */ +function group_entity_settings_form_submit(&$form, &$form_state) { + $multiple_mode = $form_state['values']['group_entity_multiple_groups']; + + // Show a warning about caches needing to be cleared. + if ($multiple_mode !== variable_get('group_entity_multiple_groups', array())) { + drupal_set_message(t('The site cache should be cleared now because the nodes in multiple groups setting has changed. !link', array( + '!link' => l(t('Go to performance settings'), 'admin/config/development/performance'), + )), 'warning'); + } +} \ No newline at end of file diff --git a/group.install b/group.install index d4f7caf..aa627e5 100644 --- a/group.install +++ b/group.install @@ -44,7 +44,10 @@ function group_install() { function group_uninstall() { variable_del('group_admin_theme'); variable_del('group_pm_group_view_disabled'); - + variable_del('group_entity_choice_admin_element'); + variable_del('group_entity_choice_standard_element'); + variable_del('group_entity_multiple_groups'); + // Bypass entity_load() as we cannot use it here. $types = db_select('group_type', 'gt') ->fields('gt', array('name')) diff --git a/group.module b/group.module index 0e2a224..3e515f8 100644 --- a/group.module +++ b/group.module @@ -17,6 +17,7 @@ define('GROUP_LIMIT_ALL', 0x07); /** * Load our helper functions without polluting the .module file. */ +require_once 'helpers/group.child.entities.inc'; require_once 'helpers/group.entity.inc'; require_once 'helpers/group_membership.entity.inc'; require_once 'helpers/group_role.entity.inc'; diff --git a/group.router.inc b/group.router.inc index a691c15..fd46d8a 100644 --- a/group.router.inc +++ b/group.router.inc @@ -33,6 +33,17 @@ function group_menu() { 'weight' => 0, ); + + $items['admin/group/settings/entity'] = array( + 'title' => 'Group entity settings', + 'access arguments' => array('configure group module'), + 'page callback' => 'drupal_get_form', + 'page arguments' => array('group_entity_settings'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'admin/group.child.entities.inc', + 'weight' => 10, + ); + return $items; } diff --git a/helpers/group.child.entities.inc b/helpers/group.child.entities.inc new file mode 100644 index 0000000..08f11eb --- /dev/null +++ b/helpers/group.child.entities.inc @@ -0,0 +1,445 @@ +group)) { + return array(); + } + + return is_array($entity->group) ? $entity->group : array($entity->group); +} + +/** + * Get a list of all group entities an entity is within. + * + * @param $entity + * + * @return Group[] + */ +function group_entity_parent_gids($entity) { + return group_load_multiple(group_entity_group_ids($entity)); +} + +/** + * Determine if entity is in multiple mode. + * + * @return bool + * TRUE if an entity can be placed within multiple groups. + * FALSE if an entity can only be placed in a single group. + */ +function group_entity_is_multiple($entity_type) { + $values = variable_get('group_entity_multiple_groups', array()); + + if(isset($values[$entity_type]) && $values[$entity_type]) { + return GROUP_ENTITY_MULTIPLE_GROUPS_MODE; + } + else { + return GROUP_ENTITY_SINGLE_GROUPS_MODE; + } +} + +/** + * Helper function to generate a form entity element for selecting the group. + * + * @param $entity_type + * The entity type whose edit form is being generated. + * @param $entity + * The entity whose edit form is being generated. + * @param string $create_permission + * The group create permission for this entity. + * @param bool $global_create + * Whether or not the user has the global create permission for this entity. + * + * @return array + * Form API select element. + */ +function group_entity_group_settings_select_element($entity_type, $entity, $create_permission, $global_create = FALSE) { + $options = array(); + + $wrapper = entity_metadata_wrapper($entity_type, $entity); + + // Show all of the groups the user can create an entity in if it's a new + // entity or just the groups the user can administer if it's not. + foreach (group_load_multiple(group_entity_create_gids($create_permission)) as $group) { + if (!$wrapper->getIdentifier() || group_access('administer group', $group)) { + $options[$group->gid] = $group->label(); + } + } + + // Bail early if the user has no options available. + if (empty($options)) { + return array(); + } + + // If there is only one option and the user cannot create content outside of + // the group then the group control should be selected and disabled. + $disabled = (count($options) === 1 && !$global_create); + + // Retrieve the current group IDs from the entity. + $default_gids = group_entity_group_ids($entity); + + if (!$global_create && empty($default_gids) && count($options) === 1) { + $gids = array_keys($options); + $default_gids = array(key($gids)); + } + + $description = $global_create + ? t('Optionally select the group to attach this content to.') + : t('Because you are not allowed to create this content outside of a group, you are required to select one here.'); + + return array( + 'gid' => array( + '#type' => 'select', + '#title' => t('Parent group'), + '#description' => $description . '
' . group_entity_group_settings_access_warning(), + '#options' => $options, + '#required' => !$global_create, + '#multiple' => group_entity_is_multiple($entity_type), + '#default_value' => $default_gids, + '#empty_value' => '', + '#disabled' => $disabled, + ), + ); +} + +/** + * Function to generate a form entity element for checkbox selection of the group. + * + * @param $entity_type + * The entity type whose edit form is being generated. + * @param $entity + * The entity whose edit form is being generated. + * @param string $create_permission + * The group create permission for this entity. + * @param bool $global_create + * Whether or not the user has the global create permission for this entity. + * + * @return array + * Form API select element. + */ +function group_entity_group_settings_checkbox_element($entity_type, $entity, $create_permission, $global_create = FALSE) { + $element = group_entity_group_settings_select_element($entity_type, $entity, $create_permission, $global_create); + + if (empty($element)) { + return $element; + } + + if (group_entity_is_multiple($entity_type)) { + // If the entity can be in many groups then use checkboxes. + $element['gid']['#type'] = 'checkboxes'; + } + else { + // If the entity can be in only one group then use radio buttons. + $element['gid']['#type'] = 'radios'; + + // If not required add a none option. + if (empty($element['gid']['#required'])) { + $element['gid']['#options'] = array(0 => t('None')) + $element['gid']['#options']; + if (empty($element['gid']['#default_value'])) { + $element['gid']['#default_value'] = array('0'); + } + } + + // Set the default value to be a single value. + $element['gid']['#default_value'] = reset($element['gid']['#default_value']); + } + + return $element; +} + +/** + * Helper function to generate a form entity auto complete element. + * + * @param $entity_type + * The entity type whose edit form is being generated. + * @param $entity + * The entity whose edit form is being generated. + * @param string $create_permission + * The group create permission for this entity. + * @param bool $global_create + * Whether or not the user has the global create permission for this entity. + * + * @return array + * Form API select element. + */ +function group_entity_group_settings_autocomplete_element($entity_type, $entity, $create_permission, $global_create = FALSE) { + $groups = group_entity_parent_gids($entity); + + $group_names = array(); + $wrapper = entity_metadata_wrapper($entity_type, $entity); + + foreach ($groups as $gid => $group) { + // Retrieve the default value for the auto complete field. + $title = $group->label(); + $group_names[] = "$title (GID: $gid)"; + } + + $group_name = implode(', ', $group_names); + + $description = $global_create + ? t('Enter the name of the group to attach this content to. Leave blank for no group.') + : t('Because you are not allowed to create this content outside of a group, you are required to enter the name of one here.'); + + // There is a special use case where a user may edit global entities, but not + // create them and also may bypass group access. In such a case, he could move + // an ungrouped entity to a group, or leave it ungrouped. + if (!$wrapper->getIdentifier() && empty($gids) && !$global_create) { + $description = t('This entity is not attached to a group. In order to keep it that way, leave this field blank. Alternatively, you may enter the name of the group to attach this entity to.'); + $description .= '
' . t('Warning') . ': ' . t('Because you are not allowed to create this content outside of a group, you will not be able to move it back to the site wide scope.'); + $global_create = TRUE; + } + + return array( + 'group_autocomplete' => array( + '#type' => 'textfield', + '#title' => t('Parent group'), + '#autocomplete_path' => 'group/autocomplete', + '#default_value' => $group_name, + '#description' => $description . '
' . group_entity_group_settings_access_warning(), + '#required' => !$global_create, + '#multiple' => group_entity_is_multiple($entity_type), + ), + ); +} + +/** + * Retrieve the warning about entity inheriting access control of the group. + */ +function group_entity_group_settings_access_warning() { + return '' . t("By selecting a group, the entity will inherit the group's access control.") . ''; +} + +/** + * Validates and processes the 'parent group' field. + * + * @see gnode_form_node_form_alter() + */ +function group_entity_group_settings_validate($form, &$form_state) { + + $entity_type = $form_state['#group']['entity_type']; + $entity = $form_state['#group']['entity']; + $global_create = $form_state['#group']['global_create']; + + // If the user was presented with an autocomplete, we need to map the selected + // group name to a group id in the form state so the submit handler can rely + // on it being set. + if (!empty($form_state['values']['group_settings']['group_autocomplete'])) { + // Retrieve the group id from the name suffix. + if ($gids = group_autocomplete_result_gid($form_state['values']['group_settings']['group_autocomplete'])) { + $form_state['values']['group_settings']['gid'] = $gids; + } + // Throw an error if the gid could not be retrieved. + else { + form_set_error('group_settings][group_autocomplete', t('Group name not found')); + } + } + + try { + group_entity_submit_get_new_gids($entity_type, $entity, $form_state['values']['group_settings'], $global_create); + } + catch (Exception $e) { + form_set_error('group_settings', t('You cannot remove this content from all groups you are an administrator of.')); + } +} + +/** + * Helper for group entity validation and submission functions. + * + * Reads which groups the user would like the entity to be in and works out + * which it will now be in. + * + * @param $entity_type + * The type of the entity being submitted. + * @param $entity + * The entity being submitted. + * @param $form_settings + * The value of $form_state['values']['group_settings'] which contains + * the list of group ids the user would like the group to be in. + * @param bool $global_create + * Whether or not the user has global creation permissions for this entity. + * + * @return array + * List of group ids the entity will be in after form submission. + * + * @throws \Exception + * If the user tries to remove the entity from all groups an exception is + * thrown if the user does not have create access for the entity in the global + * space. + */ +function group_entity_submit_get_new_gids($entity_type, $entity, $form_settings, $global_create = FALSE) { + $can_bypass_access = user_access('bypass group access'); + $specified_gids = array(); + + $wrapper = entity_metadata_wrapper($entity_type, $entity); + + if (!empty($form_settings['gid'])) { + $specified_gids = $form_settings['gid']; + + // If we are in single mode and the result isn't an array, we cast it as + // if it were coming from a multiple option form element. + if (!is_array($specified_gids)) { + $specified_gids = array($specified_gids => $specified_gids); + } + } + + // If the user cannot bypass group access then they cannot change the groups + // the entity is in for groups they are not the administrator of. The previous + // groups for this entity were stored on the form. + if (!$can_bypass_access && !$wrapper->getIdentifier()) { + $existing_gids =$form_settings['existing_gids']; + + foreach (group_load_multiple($existing_gids) as $existing_group) { + if (!isset($specified_gids[$existing_group->gid])) { + // The user is trying to take the entity out a group. They can only do + // this if they are admin of this group. + if (!group_access('administer group', $existing_group)) { + // The user is not an administrator so keep the entity in this group. + $specified_gids[] = $existing_group->gid; + } + } + } + + // The user cannot remove the entity from all their groups. + $removed_group_ids = array_diff($existing_gids, $specified_gids); + + if (!empty($removed_group_ids)) { + // There has been a group removed, check that at least one remaining group + // the user is administer of. + $found_group = FALSE; + foreach (group_load_multiple($specified_gids) as $specified_group) { + if (group_access('administer group', $specified_group)) { + // The user is an administrator so the specified list is valid. + $found_group = TRUE; + break; + } + } + + if (!$found_group && !$global_create) { + // The user has taken the entity out of all their groups and does not have + // the global create permission for that entity type. + throw new Exception(t('You cannot remove this content from all groups you are an administrator of.')); + } + } + } + + return array_filter($specified_gids, '_group_entity_specified_groups_result_clean'); +} + +/** + * Submit function to handle adding entities to groups. + * + * 1. Bypass group access users can set any groups for this entity. + * 2. If it is a new entity, the user can add the entity to any group they have + * create access to. + * 3. If it is an existing entity, the user can change the groups an entity is + * in only for groups they are an administrator of. + * 4. If the user cannot globally create entities of this type then they must + * select at least one group. + * + * @see gnode_form_node_form_alter() + */ +function group_entity_group_settings_submit($form, &$form_state) { + $entity_type = $form_state['#group']['entity_type']; + $entity = $form_state['#group']['entity']; + $global_create = $form_state['#group']['global_create']; + $group_settings = $form_state['values']['group_settings']; + + if (!empty($form_state['values']['group_settings'])) { + $specified_gids = group_entity_submit_get_new_gids($entity_type, $entity, $group_settings, $global_create); + $entity->group = group_entity_is_multiple($entity_type) ? $specified_gids : reset($specified_gids); + } + elseif (isset($entity->group)) { + unset($entity->group); + } +} + +/** + * Retrieve all group ids a user can create an entity of a given type in. + * + * @param string $permission + * The permission to create entities of this type. + * @param object $account + * (optional) The account of the user. + * + * @return array + * An array of group ids (gids). + */ +function group_entity_create_gids($permission, $account = NULL) { + global $user; + + if (!isset($account)) { + $account = $user; + } + + // Determine whether the account could be 'anonymous' or 'outsider'. + $account_exists = (bool) $account->uid; + + // If the user can bypass group access, return all group ids. + if (user_access('bypass group access', $account)) { + return db_select('groups', 'g')->fields('g')->execute()->fetchCol(); + } + + // Otherwise, start gathering group ids. + $gids = array(); + + // Check for all group types if a non-member can create entities of the given + // entity type in them. + foreach (group_types() as $group_type) { + // Retrieve the permissions to check for creation rights. + $check_permissions = $account_exists ? $group_type->outsider_permissions : $group_type->anonymous_permissions; + + $has_access = in_array('administer group', $check_permissions); + $has_access = $has_access || in_array($permission, $check_permissions); + + // If the group type allows access to non-members, we add all of the groups + // the user is not a member of. This would be all groups in case we are + // checking for an anonymous user. + if ($has_access) { + $gids = array_merge($gids, group_non_member_gids($account->uid, $group_type->name)); + } + } + + // Add all of the user's groups in which he has creation rights. + foreach (group_load_by_member($account->uid) as $group) { + $has_access = group_access('administer group', $group, $account); + $has_access = $has_access || group_access($permission, $group, $account); + + if ($has_access) { + $gids[] = $group->gid; + } + } + + $gids = array_unique($gids); + sort($gids); + + return $gids; +} + +/** + * Array filter for results of the entity form submission. + * + * We only want real group ids in the result. + * + */ +function _group_entity_specified_groups_result_clean($value) { + return !empty($value) && is_numeric($value); +} \ No newline at end of file diff --git a/modules/gnode/gnode.admin.inc b/modules/gnode/gnode.admin.inc index 91a280e..ac74af2 100644 --- a/modules/gnode/gnode.admin.inc +++ b/modules/gnode/gnode.admin.inc @@ -11,41 +11,6 @@ * @see gnode_menu(). */ function gnode_settings($form, &$form_state) { - $form['gnode_group_choice_admin_element'] = array( - '#type' => 'radios', - '#title' => t('Admin group selection mode'), - '#default_value' => variable_get('gnode_group_choice_admin_element', 'autocomplete'), - '#description' => t('Choose the type of form element administrators use to select which groups a node should be in.'), - '#options' => array( - 'select' => t('Select dropdown'), - 'autocomplete' => t('Autocomplete'), - 'checkbox' => t('Checkboxes or radios'), - ), - ); - - $form['gnode_group_choice_standard_element'] = array( - '#type' => 'radios', - '#title' => t('Standard user group selection mode'), - '#default_value' => variable_get('gnode_group_choice_standard_element', 'select'), - '#description' => t('Choose the type of form element standard users use to select which groups a node should be in.'), - '#options' => array( - 'select' => t('Select dropdown'), - 'autocomplete' => t('Autocomplete'), - 'checkbox' => t('Checkboxes or radios'), - ), - ); - - $form['group_node_multiple_groups'] = array( - '#type' => 'radios', - '#title' => t('Nodes in multiple groups'), - '#default_value' => variable_get('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE), - '#description' => t('Choose if a node can only be in one group or can be in multiple groups. Nodes in multiple groups can be updated and deleted by users with the necessary permissions in any of the groups it is in. After changing this session you must clear the site cache. It is not recommended that you go from multiple to single on a site which has nodes in multiple groups without testing.'), - '#options' => array( - GROUP_NODE_SINGLE_GROUPS_MODE => t('A node can only be in a single group.'), - GROUP_NODE_MULTIPLE_GROUPS_MODE => t('A node can be placed into multiple groups.'), - ), - ); - $form['group_node_mode'] = array( '#type' => 'radios', '#title' => t('Group node access mode'), @@ -69,17 +34,9 @@ function gnode_settings($form, &$form_state) { */ function gnode_settings_form_submit(&$form, &$form_state) { $access_mode = $form_state['values']['group_node_mode']; - $multiple_mode = $form_state['values']['group_node_multiple_groups']; // If the access mode has changed then node access needs rebuilding. if ($access_mode !== _gnode_get_mode()) { node_access_needs_rebuild(TRUE); } - - // Show a warning about caches needing to be cleared. - if ($multiple_mode !== variable_get('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE)) { - drupal_set_message(t('The site cache should be cleared now because the nodes in multiple groups setting has changed. !link', array( - '!link' => l(t('Go to performance settings'), 'admin/config/development/performance'), - )), 'warning'); - } } diff --git a/modules/gnode/gnode.install b/modules/gnode/gnode.install index 10a320c..a4e9642 100644 --- a/modules/gnode/gnode.install +++ b/modules/gnode/gnode.install @@ -13,12 +13,3 @@ function gnode_install() { ->condition('name', 'gnode') ->execute(); } - -/** - * Implements hook_uninstall(). - */ -function gnode_uninstall() { - variable_del('gnode_group_choice_admin_element'); - variable_del('gnode_group_choice_standard_element'); - variable_del('group_node_multiple_groups'); -} diff --git a/modules/gnode/gnode.module b/modules/gnode/gnode.module index 8cd3b5b..8d7bd19 100644 --- a/modules/gnode/gnode.module +++ b/modules/gnode/gnode.module @@ -21,16 +21,6 @@ define('GROUP_NODE_SAFE_MODE', 'GROUP_NODE_SAFE_MODE'); define('GROUP_NODE_COMPLIANCE_MODE', 'GROUP_NODE_COMPLIANCE_MODE'); /** - * Mode which only allows a node to be in one group. - */ -define('GROUP_NODE_SINGLE_GROUPS_MODE', 'GROUP_NODE_SINGLE_GROUPS_MODE'); - -/** - * Mode which allows a node to be in multiple group. - */ -define('GROUP_NODE_MULTIPLE_GROUPS_MODE', 'GROUP_NODE_MULTIPLE_GROUPS_MODE'); - -/** * Load our router functions without polluting the .module file. */ require_once 'gnode.router.inc'; @@ -52,22 +42,10 @@ function _gnode_get_mode() { } /** - * Determine if gnode is in multiple mode. - * - * @return bool - * TRUE if a node can be placed within multiple groups. - * FALSE if a node can only be placed in a single group. - */ -function _gnode_is_multiple() { - $multiple_mode = variable_get('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); - return $multiple_mode === GROUP_NODE_MULTIPLE_GROUPS_MODE; -} - -/** * Implements hook_entity_info_alter(). */ function gnode_entity_info_alter(&$entity_info) { - $entity_info['node']['group entity'] = _gnode_is_multiple() ? 'multiple' : 'single'; + $entity_info['node']['group entity'] = group_entity_is_multiple('node') ? 'multiple' : 'single'; } /** @@ -290,36 +268,6 @@ function gnode_global_node_create_access($type, $account = NULL) { } /** - * Get a list of all the group ids a node is within. - * - * This normalises the group parameter on a node as this can be either a single - * value if gnode module is configured so that nodes can have only one parent or - * an array of values if a node can have multiple parents. - * - * @param $node - * - * @return int[] - */ -function gnode_node_group_ids($node) { - if (empty($node->group)) { - return array(); - } - - return is_array($node->group) ? $node->group : array($node->group); -} - -/** - * Get a list of all group entities a node is within. - * - * @param $node - * - * @return Group[] - */ -function gnode_node_groups($node) { - return group_load_multiple(gnode_node_group_ids($node)); -} - -/** * Implements hook_node_access(). * * Because the grant system for node access can quickly become very taxing on @@ -355,7 +303,7 @@ function gnode_node_access($node, $op, $account) { } // Check access within all of the node's groups. - foreach (gnode_node_groups($node) as $group) { + foreach (group_entity_parent_gids($node) as $group) { if (group_access('administer group', $group, $account)) { return NODE_ACCESS_ALLOW; } @@ -410,6 +358,12 @@ function gnode_node_add(Group $group, $node_type) { 'group' => $group->gid, ); + $global_create = gnode_global_node_create_access($node->type); + + $form_state['#group']['entity'] =& $node; + $form_state['#group']['entity_type'] = 'node'; + $form_state['#group']['global_create'] = $global_create; + // Set the page title to something different than the local action title. $message = 'Create @name in @group'; $replace = array('@name' => $types[$node_type]->name, '@group' => $group->label()); @@ -445,13 +399,18 @@ function gnode_node_add(Group $group, $node_type) { * @see gnode_node_validate() * @see gnode_node_submit() */ -function gnode_form_node_form_alter(&$form, $form_state) { +function gnode_form_node_form_alter(&$form, &$form_state) { $node = $form_state['node']; - $groups = gnode_node_groups($node); + $groups = group_entity_parent_gids($node); $gids = array_keys($groups); $can_bypass_access = user_access('bypass group access'); + $global_create = gnode_global_node_create_access($node->type); + + $form_state['#group']['entity'] =& $form_state['node']; + $form_state['#group']['entity_type'] = 'node'; + $form_state['#group']['global_create'] = $global_create; // If the user can't bypass group access, the node has an ID and does not // belong to any groups, we are on an ungrouped node's edit form and do not @@ -475,6 +434,7 @@ function gnode_form_node_form_alter(&$form, $form_state) { ), '#tree' => TRUE, '#weight' => -50, + '#element_validate' => array('group_entity_group_settings_validate'), ); // Keep track of the groups the node was in already. @@ -486,12 +446,12 @@ function gnode_form_node_form_alter(&$form, $form_state) { // Find out which form API element we should be using. $element_type = $can_bypass_access - ? variable_get('gnode_group_choice_admin_element', 'autocomplete') - : variable_get('gnode_group_choice_standard_element', 'select'); + ? variable_get('group_entity_choice_admin_element', 'autocomplete') + : variable_get('group_entity_choice_standard_element', 'select'); // Use a helper function to generate the form element. - $element_function = "_gnode_generate_node_form_group_{$element_type}_element"; - $element = function_exists($element_function) ? $element_function($node) : array(); + $element_function = "group_entity_group_settings_{$element_type}_element"; + $element = function_exists($element_function) ? $element_function('node', $node, "create $node->type content", $global_create) : array(); // If the user is not able to change the group for this node, we do not show // the group selection vertical tab. @@ -502,297 +462,11 @@ function gnode_form_node_form_alter(&$form, $form_state) { // Add the group select form element to the form. $form['group_settings'] += $element; -} -/** - * Helper function to generate a form node element for selecting the group. - * - * @param $node - * The node whose edit form is being generated. - * - * @return array - * Form API select element. - */ -function _gnode_generate_node_form_group_select_element($node) { - $options = array(); - - // Show all of the groups the user can create a node in if it's a new node or - // just the groups the user can administer if it's not. - foreach (group_load_multiple(gnode_group_node_create_gids($node->type)) as $group) { - if (empty($node->nid) || group_access('administer group', $group)) { - $options[$group->gid] = $group->label(); - } - } - - // Bail early if the user has no options available to him. - if (empty($options)) { - return array(); - } - - // Check if the user can create nodes outside of a group. - $global_node_create_access = gnode_global_node_create_access($node->type); - - // If there is only one option and the user cannot create content outside of - // the group then the group control should be selected and disabled. - $disabled = (count($options) === 1 && !$global_node_create_access); - - // Retrieve the current group IDs from the node. - $default_gids = gnode_node_group_ids($node); - - if (!$global_node_create_access && empty($default_gids) && count($options) === 1) { - $default_gids = array(key($gids)); - } - - $description = $global_node_create_access - ? t('Optionally select the group to attach this node to.') - : t('Because you are not allowed to create this content outside of a group, you are required to select one here.'); - - return array( - 'gid' => array( - '#type' => 'select', - '#title' => t('Parent group'), - '#description' => $description . '
' . _gnode_get_group_access_warning(), - '#options' => $options, - '#required' => !$global_node_create_access, - '#multiple' => _gnode_is_multiple(), - '#default_value' => $default_gids, - '#empty_value' => '', - '#disabled' => $disabled, - ), - ); -} - -/** - * Function to generate a form node element for checkbox selection of the group. - * - * @param $node - * The node whose edit form is being generated - * - * @return array - * Form API select element. - */ -function _gnode_generate_node_form_group_checkbox_element($node) { - $element = _gnode_generate_node_form_group_select_element($node); - - if (empty($element)) { - return $element; - } - - if (_gnode_is_multiple()) { - // If nodes can be in many groups then use checkboxes. - $element['gid']['#type'] = 'checkboxes'; + if (isset($form['#submit'])) { + array_unshift($form['#submit'], 'group_entity_group_settings_submit'); } else { - // If nodes can be in only one group then use radio buttons. - $element['gid']['#type'] = 'radios'; - - // If not required add a none option. - if (empty($element['gid']['#required'])) { - $element['gid']['#options'] = array(0 => t('None')) + $element['gid']['#options']; - if (empty($element['gid']['#default_value'])) { - $element['gid']['#default_value'] = array('0'); - } - } - - // Set the default value to be a single value. - $element['gid']['#default_value'] = reset($element['gid']['#default_value']); + $form['#submit'] = array('group_entity_group_settings_submit'); } - - return $element; -} - -/** - * Helper function to generate a form node auto complete element. - * - * @param $node - * The node whose edit form is being generated - * - * @return array - * Form API select element. - */ -function _gnode_generate_node_form_group_autocomplete_element($node) { - $global_node_create_access = gnode_global_node_create_access($node->type); - $groups = gnode_node_groups($node); - - $group_names = array(); - - foreach ($groups as $gid => $group) { - // Retrieve the default value for the auto complete field. - $title = $group->label(); - $group_names[] = "$title (GID: $gid)"; - } - - $group_name = implode(', ', $group_names); - - $description = $global_node_create_access - ? t('Enter the name of the group to attach this node to. Leave blank for no group.') - : t('Because you are not allowed to create this content outside of a group, you are required to enter the name of one here.'); - - // There is a special use case where a user may edit global nodes, but not - // create them and also may bypass group access. In such a case, he could move - // an ungrouped node to a group, or leave it ungrouped. - if (!empty($node->nid) && empty($gids) && !$global_node_create_access) { - $description = t('This node is not attached to a group. In order to keep it that way, leave this field blank. Alternatively, you may enter the name of the group to attach this node to.'); - $description .= '
' . t('Warning') . ': ' . t('Because you are not allowed to create this content outside of a group, you will not be able to move it back to the site wide scope.'); - $global_node_create_access = TRUE; - } - - return array( - 'group_autocomplete' => array( - '#type' => 'textfield', - '#title' => t('Parent group'), - '#autocomplete_path' => 'group/autocomplete', - '#default_value' => $group_name, - '#description' => $description . '
' . _gnode_get_group_access_warning(), - '#required' => !$global_node_create_access, - '#multiple' => _gnode_is_multiple(), - ), - ); -} - -/** - * Retrieve the warning about node inheriting access control of the group. - */ -function _gnode_get_group_access_warning() { - return '' . t("By selecting a group, the node will inherit the group's access control.") . ''; -} - -/** - * Helper for gnode_node validation and submission functions. - * - * Reads which groups the user would like the node to be in and works out which - * it will now be in. - * - * @param $node - * @param $form_settings - * The value of $form_state['values']['group_settings'] which contains - * the list of group ids the user would like the group to be in. - * - * @return array - * List of group ids the node will be in after node submission. - * - * @throws \Exception - * If the user tries to remove the node from all groups an exception is - * thrown if the user does not have create access for the node in the global - * space. - */ -function _gnode_node_submit__get_new_gids($node, $form_settings) { - $can_bypass_access = user_access('bypass group access'); - $specified_gids = array(); - - if (!empty($form_settings['gid'])) { - $specified_gids = $form_settings['gid']; - - // If we are in single mode and the result isn't an array, we cast it as - // if it were coming from a multiple option form element. - if (!is_array($specified_gids)) { - $specified_gids = array($specified_gids => $specified_gids); - } - } - - // If the user cannot bypass group access then they cannot change the groups - // the node is in for groups they are not the administrator of. The previous - // groups for this node were stored on the form. - if (!$can_bypass_access && !empty($node->nid)) { - $existing_gids =$form_settings['existing_gids']; - - foreach (group_load_multiple($existing_gids) as $existing_group) { - if (!isset($specified_gids[$existing_group->gid])) { - // The user is trying to take the node out a group. They can only do - // this if they are admin of this group. - if (!group_access('administer group', $existing_group)) { - // The user is not an administrator so keep the node in this group. - $specified_gids[] = $existing_group->gid; - } - } - } - - // The user cannot remove the node from all their groups. - $removed_group_ids = array_diff($existing_gids, $specified_gids); - - if (!empty($removed_group_ids)) { - // There has been a group removed, check that at least one remaining group - // the user is administer of. - $found_group = FALSE; - foreach (group_load_multiple($specified_gids) as $specified_group) { - if (group_access('administer group', $specified_group)) { - // The user is an administrator so the specified list is valid. - $found_group = TRUE; - break; - } - } - - if (!$found_group && !gnode_global_node_create_access($node->type)) { - // The user has taken the node out of all their groups and does not have - // the global create permission for that node type. - throw new Exception(t('You cannot remove this content from all groups you are an administrator of.')); - } - } - } - - return array_filter($specified_gids, '_gnode_specified_groups_result_clean'); -} - -/** - * Implements hook_node_validate(). - * - * Validates and processes the 'parent group' field. - * - * @see gnode_form_node_form_alter() - */ -function gnode_node_validate($node, $form, &$form_state) { - // If the user was presented with an autocomplete, we need to map the selected - // group name to a group id in the form state so the submit handler can rely - // on it being set. - if (!empty($form_state['values']['group_settings']['group_autocomplete'])) { - // Retrieve the group id from the name suffix. - if ($gids = group_autocomplete_result_gid($form_state['values']['group_settings']['group_autocomplete'])) { - $form_state['values']['group_settings']['gid'] = $gids; - } - // Throw an error if the gid could not be retrieved. - else { - form_set_error('group_settings][group_autocomplete', t('Group name not found')); - } - } - - try { - _gnode_node_submit__get_new_gids($node, $form_state['values']['group_settings']); - } - catch (Exception $e) { - form_set_error('group_settings', t('You cannot remove this content from all groups you are an administrator of.')); - } -} - -/** - * Implements hook_node_submit(). - * - * 1. Bypass group access users can set any groups for this node. - * 2. If it is a new node, the user can add the node to any group they have - * create access to. - * 3. If it is an existing node, the user can change the groups a node is in - * only for groups they are an administrator of. - * 4. If the user cannot globally create nodes of this type then they must - * select at least one of their groups. - * - * @see gnode_form_node_form_alter() - */ -function gnode_node_submit($node, $form, &$form_state) { - if (!empty($form_state['values']['group_settings'])) { - $specified_gids = _gnode_node_submit__get_new_gids($node, $form_state['values']['group_settings']); - $node->group = _gnode_is_multiple() ? $specified_gids : reset($specified_gids); - } - elseif (isset($node->group)) { - unset($node->group); - } -} - -/** - * Array filter for results of the node form submission. - * - * We only want real group ids in the result. - * - * @see gnode_node_submit(). - */ -function _gnode_specified_groups_result_clean($value) { - return !empty($value) && is_numeric($value); -} +} \ No newline at end of file diff --git a/modules/gnode/modes/gnode.compliance.inc b/modules/gnode/modes/gnode.compliance.inc index de3b34d..61e5449 100644 --- a/modules/gnode/modes/gnode.compliance.inc +++ b/modules/gnode/modes/gnode.compliance.inc @@ -201,7 +201,7 @@ function gnode_node_access_records($node) { return $grants; } - foreach (gnode_node_group_ids($node) as $gid) { + foreach (group_entity_group_ids($node) as $gid) { if ($node->status) { $grants[] = array( 'gid' => $gid, diff --git a/modules/gnode/tests/gnode.interface.test b/modules/gnode/tests/gnode.interface.test index 71a0718..85bb682 100644 --- a/modules/gnode/tests/gnode.interface.test +++ b/modules/gnode/tests/gnode.interface.test @@ -269,8 +269,8 @@ class GNodeMultipleEditInterfaceWithCheckboxTests extends GNodeMultipleEditInter public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', GROUP_NODE_SAFE_MODE); - variable_set('group_node_multiple_groups', GROUP_NODE_MULTIPLE_GROUPS_MODE); - variable_set('gnode_group_choice_standard_element', 'checkbox'); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); + variable_set('group_entity_choice_standard_element', 'checkbox'); node_access_rebuild(); } @@ -323,8 +323,8 @@ class GNodeMultipleEditInterfaceWithSelectTests extends GNodeMultipleEditInterfa public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', GROUP_NODE_SAFE_MODE); - variable_set('group_node_multiple_groups', GROUP_NODE_MULTIPLE_GROUPS_MODE); - variable_set('gnode_group_choice_standard_element', 'select'); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); + variable_set('group_entity_choice_standard_element', 'select'); node_access_rebuild(); } @@ -389,8 +389,8 @@ class GNodeMultipleEditInterfaceWithAutoCompleteTests extends GNodeMultipleEditI public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', GROUP_NODE_SAFE_MODE); - variable_set('group_node_multiple_groups', GROUP_NODE_MULTIPLE_GROUPS_MODE); - variable_set('gnode_group_choice_standard_element', 'autocomplete'); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); + variable_set('group_entity_choice_standard_element', 'autocomplete'); node_access_rebuild(); } diff --git a/modules/gnode/tests/gnode.multiple.test b/modules/gnode/tests/gnode.multiple.test index e43a936..0a77346 100644 --- a/modules/gnode/tests/gnode.multiple.test +++ b/modules/gnode/tests/gnode.multiple.test @@ -70,7 +70,7 @@ class GNodeComplianceModeMultipleNodeTests extends GNodeMultipleViewAccessTests public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', GROUP_NODE_COMPLIANCE_MODE); - variable_set('group_node_multiple_groups', GROUP_NODE_MULTIPLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } } @@ -97,7 +97,7 @@ class GNodeSafeModeMultipleNodeTests extends GNodeMultipleViewAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', GROUP_NODE_SAFE_MODE); - variable_set('group_node_multiple_groups', GROUP_NODE_MULTIPLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } } diff --git a/modules/gnode/tests/gnode.test b/modules/gnode/tests/gnode.test index 3dd91d9..15d60a3 100644 --- a/modules/gnode/tests/gnode.test +++ b/modules/gnode/tests/gnode.test @@ -179,7 +179,7 @@ abstract class GNodeWebTestBase extends DrupalWebTestCase { 'uid' => $uid, 'status' => $status, 'promote' => 1, - 'group' => _gnode_is_multiple() ? array($gid) : $gid, + 'group' => group_entity_is_multiple('node') ? array($gid) : $gid, ) ); } @@ -195,7 +195,7 @@ abstract class GNodeWebTestBase extends DrupalWebTestCase { * @throws \Exception */ public function setNodeGroups($node, $gids = array()) { - $node->group = _gnode_is_multiple() ? $gids : reset($gids); + $node->group = group_entity_is_multiple('node') ? $gids : reset($gids); node_save($node); } @@ -1000,7 +1000,7 @@ class GNodeSafeModeViewTests extends GNodeViewAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } @@ -1028,7 +1028,7 @@ class GNodeSafeModeEditTests extends GNodeEditAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } @@ -1056,7 +1056,7 @@ class GNodeSafeModeDeleteTests extends GNodeDeleteAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } @@ -1084,7 +1084,7 @@ class GNodeSafeModeCreateTests extends GNodeCreateAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } @@ -1112,7 +1112,7 @@ class GNodeSafeModeBypassTests extends GNodeBypassAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); } @@ -1140,7 +1140,7 @@ class GNodeComplianceModeViewTests extends GNodeViewAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1168,7 +1168,7 @@ class GNodeComplianceModeEditTests extends GNodeEditAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1196,7 +1196,7 @@ class GNodeComplianceModeDeleteTests extends GNodeDeleteAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1224,7 +1224,7 @@ class GNodeComplianceModeCreateTests extends GNodeCreateAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1252,7 +1252,7 @@ class GNodeComplianceModeBypassTests extends GNodeBypassAccessTests { public function setUp() { parent::setUp('node', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1287,7 +1287,7 @@ class GNodeComplianceModeViewWithAnotherModuleTests extends GNodeWebTestBase { public function setUp() { parent::setUp('node', 'node_access_test', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_COMPLIANCE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); $this->assert(count(module_implements('node_grants')) > 0, 'The number of modules which implement hook_node_grants is greater than 0.'); } @@ -1409,7 +1409,7 @@ class GNodeSafeModeViewWithAnotherModuleTests extends GNodeWebTestBase { public function setUp() { parent::setUp('node', 'node_access_test', 'entity', 'group', 'gnode'); variable_set('group_node_mode', 'GROUP_NODE_SAFE_MODE'); - variable_set('group_node_multiple_groups', GROUP_NODE_SINGLE_GROUPS_MODE); + variable_set('group_entity_multiple_groups', array('node' => TRUE)); node_access_rebuild(); }