diff --git a/README.txt b/README.txt index bfe9060..5c6db31 100644 --- a/README.txt +++ b/README.txt @@ -24,6 +24,7 @@ CONTENTS 4.1 Access schemes 4.1.1 Automated section assignment 4.1.2 Workbench Access message label +4.1.3 Allow multiple section assignments 4.2 Access sections 4.2.1 Manual section configuration 4.2.2 Automated section configuration @@ -488,6 +489,21 @@ This settings lets you change how the item is labelled. The default is "Workbench Access". You may prefer "Sections" or "Editorial Team" instead. ---- +4.1.3 Allow multiple section assignments + +The checkbox labeled 'Allow multiple section assignments' controls the behavior +of the section selection form when editing content. This optional setting is +disabled by default. + + [ ] Allow multiple section assignments + Let content be assigned to multiple sections. + +If enabled, editors will be shown a multiple select option when editing section +assignments. This configuration can be useful if you have content that spans +several parts of your organization. + + +---- 4.2 Access sections Once you have declared an access scheme, you may enable the sections for that diff --git a/includes/workbench_access_handler_field_section.inc b/includes/workbench_access_handler_field_section.inc index 96699e5..4aae24b 100644 --- a/includes/workbench_access_handler_field_section.inc +++ b/includes/workbench_access_handler_field_section.inc @@ -9,13 +9,19 @@ * @TODO: support multiple sections. */ -class workbench_access_handler_field_section extends views_handler_field { - // No query for this field. +class workbench_access_handler_field_section extends views_handler_field_prerender_list { + function init(&$view, &$options) { + parent::init($view, $options); + if ($view->base_table == 'node_revisions') { + $this->additional_fields['nid'] = array('table' => 'node_revisions', 'field' => 'nid'); + } + else { + $this->additional_fields['nid'] = array('table' => 'node', 'field' => 'nid'); + } + } + // Add the additional fields. function query() { - $active = workbench_access_get_active_tree(); - $field_table = $this->query->ensure_table($active['access_scheme']['field_table']); - // Using this field alias breaks the COUNT query. Views bug? - $field = $this->query->add_field($field_table, $active['access_scheme']['query_field'], 'workbench_access_id'); + $this->add_additional_fields(); } function click_sort($order) { @@ -44,32 +50,45 @@ class workbench_access_handler_field_section extends views_handler_field { } } - function render($values) { - global $user; - $account = $user; - if (!isset($account->workbench_access)) { - workbench_access_user_load_data($account); + function pre_render(&$values) { + // We have to find the sections assigned to each node. + $this->field_alias = $this->aliases['nid']; + $nids = array(); + foreach ($values as $result) { + if (!empty($result->{$this->aliases['nid']})) { + $nids[] = $result->{$this->aliases['nid']}; + } } - if (!isset($values->workbench_access_id)) { - return ''; + foreach ($values as $result) { + if (!empty($result->{$this->aliases['nid']})) { + $nids[$result->{$this->aliases['nid']}] = new stdClass(); + $nids[$result->{$this->aliases['nid']}]->nid = $result->{$this->aliases['nid']}; + } } + workbench_access_node_load($nids, array()); $active = workbench_access_get_active_tree(); - $name = ''; - $tree = $active['tree']; - workbench_access_build_tree($tree, array_keys($account->workbench_access)); - if (isset($account->workbench_access[$values->workbench_access_id])) { - $name = $tree[$values->workbench_access_id]['name']; - } - else { - foreach ($account->workbench_access as $id => $data) { - if (!empty($tree[$id]['children'])) { - $children = $tree[$id]['children']; - if (in_array($values->workbench_access_id, $children)) { - $name = $tree[$id]['name'] . ' > ' . $tree[$values->workbench_access_id]['name']; - } + foreach ($values as $value) { + if (isset($value->nid) && isset($nids[$value->nid]->workbench_access)) { + foreach ($nids[$value->nid]->workbench_access as $access_id) { + $value->workbench_access[] = check_plain($active['tree'][$access_id]['name']); + $this->items[$value->nid][$access_id]['value'] = check_plain($active['tree'][$access_id]['name']); } } + else { + $value->workbench_access = array(); + } + } + } + // Only render() seems to work. What gives, Views? + function render($values) { + if (!isset($values->workbench_access)) { + $values->workbench_access = array(); + } + if ($this->options['type'] == 'separator') { + return implode($this->sanitize_value($this->options['separator']), $values->workbench_access); } - return check_plain($name); + $variables['items'] = $values->workbench_access; + $variables['type'] = $this->options['type']; + return theme('item_list', $variables); } } diff --git a/includes/workbench_access_handler_filter_access.inc b/includes/workbench_access_handler_filter_access.inc index b7d982b..f93bcdc 100644 --- a/includes/workbench_access_handler_filter_access.inc +++ b/includes/workbench_access_handler_filter_access.inc @@ -7,7 +7,7 @@ * Provides a filter to display nodes by assigned section. */ -class workbench_access_handler_filter_access extends views_handler_filter { +class workbench_access_handler_filter_access extends views_handler_filter_many_to_one { function option_definition() { $options = parent::option_definition(); $options['access_id'] = array('default' => NULL); diff --git a/tests/workbench_access.test b/tests/workbench_access.test index cf689e7..a6cc7fc 100644 --- a/tests/workbench_access.test +++ b/tests/workbench_access.test @@ -8,6 +8,7 @@ class WorkbenchAccessTestCase extends DrupalWebTestCase { protected $user_list; protected $editor_role; + protected $permissions; public static function getInfo() { return array( @@ -20,7 +21,7 @@ class WorkbenchAccessTestCase extends DrupalWebTestCase { function setUp() { parent::setUp('workbench_access'); // Add an editor role. - $permissions = array( + $this->permissions = array( 'access content', 'create page content', 'edit any page content', @@ -28,7 +29,7 @@ class WorkbenchAccessTestCase extends DrupalWebTestCase { 'access workbench access by role', ); - $this->editor_role = $this->drupalCreateRole($permissions); + $this->editor_role = $this->drupalCreateRole($this->permissions); // Create some nodes and users. for ($i = 0; $i < 10; $i++) { @@ -44,6 +45,7 @@ class WorkbenchAccessTestCase extends DrupalWebTestCase { 'mail' => $this->randomName(32) . '@example.com', 'roles' => drupal_map_assoc(array(DRUPAL_AUTHENTICATED_RID, $this->editor_role)), 'status' => 1, + 'pass' => 'fubar', ); $this->user_list[] = user_save(NULL, $edit); } @@ -142,7 +144,24 @@ class WorkbenchAccessTestCase extends DrupalWebTestCase { user_role_grant_permissions($this->editor_role, array('access workbench access by role')); $account = user_load($account->uid, TRUE); $this->assertTrue(!empty($account->workbench_access['workbench_access']), t('Permission reinstated and test user assigned to a section.')); - + // Form testing in Drupal is horribly broken. + // We can confirm that a form page is loaded, but cannot perform + // any introspection on the $form array. + $account->pass_raw = 'fubar'; + $this->drupalLogin($account); + // Set the form label. + // Attempt to access edit page. + $this->drupalGet("node/$node->nid/edit"); + $this->assertResponse(200); + $this->assertRaw('Section', t('Workbench Access field was found.')); + $this->assertRaw('', t('Form presents a select list with multiple select.')); + // TODO: Test form options. } } diff --git a/workbench_access.admin.inc b/workbench_access.admin.inc index adb97e7..16f21a6 100644 --- a/workbench_access.admin.inc +++ b/workbench_access.admin.inc @@ -65,6 +65,12 @@ function workbench_access_settings_form($form, &$form_state) { '#default_value' => variable_get('workbench_access_auto_assign', 1), '#description' => t('Enable all sections automatically for the active scheme.'), ); + $form['workbench_access_allow_multiple'] = array( + '#type' => 'checkbox', + '#title' => t('Allow multiple section assignments'), + '#default_value' => variable_get('workbench_access_allow_multiple', 0), + '#description' => t('Let content be assigned to multiple sections.'), + ); $form = system_settings_form($form); $form['#validate'][] = 'workbench_access_settings_validate'; $form['#submit'][] = 'workbench_access_settings_submit'; diff --git a/workbench_access.module b/workbench_access.module index bae15df..a08050d 100644 --- a/workbench_access.module +++ b/workbench_access.module @@ -538,8 +538,12 @@ function workbench_access_node_load($nodes, $types) { } $tree = workbench_access_get_active_tree(); $result = array(); - if (!empty($tree['active'])) { - $result = db_query("SELECT nid, access_id FROM {workbench_access_node} WHERE nid IN (:nid) AND access_scheme = :access_scheme", array(':nid' => array_keys($nodes), ':access_scheme' => $scheme))->fetchAllAssoc('nid'); + if (!empty($tree['active']) && !empty($nodes)) { + $result = db_query("SELECT nid, access_id FROM {workbench_access_node} WHERE nid IN (:nid) AND access_scheme = :access_scheme", array(':nid' => array_keys($nodes), ':access_scheme' => $scheme))->fetchAll(); + } + $data = array(); + foreach ($result as $obj) { + $data[$obj->nid][$obj->access_id] = $obj->access_id; } foreach ($nodes as $node) { // Cannot load if the node has not been created yet. @@ -547,8 +551,13 @@ function workbench_access_node_load($nodes, $types) { continue; } $nodes[$node->nid]->workbench_access = array(); - if (isset($result[$node->nid]->access_id) && in_array($result[$node->nid]->access_id, array_keys($tree['active']))) { - $nodes[$node->nid]->workbench_access[$result[$node->nid]->access_id] = $result[$node->nid]->access_id; + if (empty($data[$node->nid])) { + continue; + } + foreach ($data[$node->nid] as $access_id) { + if (in_array($access_id, array_keys($tree['active']))) { + $nodes[$node->nid]->workbench_access[$access_id] = $access_id; + } } } } @@ -562,12 +571,35 @@ function workbench_access_node_insert($node) { return; } workbench_access_node_delete($node); - $record = array( - 'nid' => $node->nid, - 'access_id' => $node->workbench_access_id, - 'access_scheme' => $node->workbench_access_scheme['access_scheme'], - ); - drupal_write_record('workbench_access_node', $record); + if (empty($node->workbench_access_id)) { + return; + } + if (!is_array($node->workbench_access_id)) { + $node->workbench_access_id = array($node->workbench_access_id); + } + foreach ($node->workbench_access_id as $id) { + $record = array( + 'nid' => $node->nid, + 'access_id' => $id, + 'access_scheme' => $node->workbench_access_scheme['access_scheme'], + ); + drupal_write_record('workbench_access_node', $record); + } +} + +/** + * Implements hook_node_presave(). + */ +function workbench_access_node_presave($node) { + if (isset($node->workbench_access_id) && isset($node->workbench_access_fixed)) { + if (!is_array($node->workbench_access_id)) { + $node->workbench_access_id = array($node->workbench_access_id); + } + if (!is_array($node->workbench_access_fixed)) { + $node->workbench_access_fixed = array($node->workbench_access_fixed); + } + $node->workbench_access_id += $node->workbench_access_fixed; + } } /** @@ -1227,12 +1259,10 @@ function workbench_access_form_alter(&$form, $form_state, $form_id) { $tree = workbench_access_get_user_tree(); // Generate options so we can check for access. $options = workbench_access_options($tree, $active['active']); - // TODO: multi-select - $default = NULL; + $default = array(); if (!empty($form['#node']->workbench_access)) { - $default = current(array_keys($form['#node']->workbench_access)); + $default = array_keys($form['#node']->workbench_access); } - // If there are no options and the 'workbench_access' variable has not been set // then it seems that Workbench Access has not been configured. if (empty($options) && !variable_get('workbench_access', FALSE)) { @@ -1243,26 +1273,50 @@ function workbench_access_form_alter(&$form, $form_state, $form_id) { } // The base form element. + $multiple = variable_get('workbench_access_allow_multiple', 0); $element = array( '#type' => 'select', '#title' => t('@message_label', array('@message_label' => variable_get('workbench_access_label', 'Section'))), '#options' => $options, '#required' => TRUE, '#default_value' => $default, - '#description' => t('Select the proper editorial group for this content.'), + '#multiple' => $multiple, + '#description' => ($multiple) ? t('Select the proper editorial group(s) for this content.') : t('Select the proper editorial group for this content.'), ); // If the default is set and is not in the user's range, then pass hidden and // display a message. // TODO: $default might legitimately be zero in some edge cases. - if (!empty($default) && !isset($options[$default]) && isset($active['tree'][$default])) { - $name = check_plain($active['tree'][$default]['name']); - $element['#type'] = 'value'; - $element['#value'] = $element['#default_value']; - $form['workbench_access']['message'] = array( - '#type' => 'item', - '#title' => t('Workbench access'), - '#markup' => t('%title is assigned to the %section editorial group and may not be changed.', array('%title' => $form['#node']->title, '%section' => $name)), - ); + if (!empty($default)) { + $all = array(); + $disallowed = array(); + foreach ($default as $item) { + if (isset($active['tree'][$item]['name'])) { + $all[$active['tree'][$item]['access_id']] = check_plain($active['tree'][$item]['name']); + if (!isset($options[$item])) { + $disallowed[$active['tree'][$item]['access_id']] = check_plain($active['tree'][$item]['name']); + } + } + } + if (!empty($disallowed)) { + $diff = array_diff($all, $disallowed); + // TODO: If we toggle from single to multiple, then this can get messy. + if (empty($diff) || !variable_get('workbench_access_allow_multiple', 0)) { + $element['#type'] = 'value'; + $element['#value'] = $element['#default_value']; + $form['workbench_access']['message'] = array( + '#type' => 'item', + '#title' => t('Workbench access'), + '#markup' => t('%title is assigned to the %section editorial group(s), which may not be changed.', array('%title' => $form['#node']->title, '%section' => implode(', ', $disallowed))), + ); + } + else { + $form['workbench_access']['workbench_access_fixed'] = array( + '#type' => 'value', + '#value' => array_keys($disallowed), + ); + $element['#description'] = $element['#description'] . '
' . t('%title is also assigned to the %section editorial group(s), which may not be changed.', array('%title' => $form['#node']->title, '%section' => implode(', ', $disallowed))); + } + } } workbench_access_alter_form('node_element', $element, $form_state); $form['workbench_access']['workbench_access_id'] = $element; @@ -1513,11 +1567,14 @@ function workbench_access_workbench_block() { return array(t('@message_label: Unassigned', array('@message_label' => variable_get('workbench_access_label', 'Section')))); } else { + $names = array(); $access_type = variable_get('workbench_access', 'taxonomy'); - $access_id = current($node->workbench_access); - $section = workbench_access_load($access_type, $access_id); - return array(t('@message_label: %section', array('@message_label' => variable_get('workbench_access_label', 'Section'), '%section' => $section['name']))); - } + foreach ($node->workbench_access as $access_id) { + $section = workbench_access_load($access_type, $access_id); + $names[] = check_plain($section['name']); + } + return array(t('Workbench Access: %section', array('%section' => implode(', ', $names)))); + } } } }