workbench_access.module in Workbench Access 7
Same filename and directory in other branches
Workbench Access module file.
File
workbench_access.moduleView source
<?php
/**
* @file
* Workbench Access module file.
*/
/**
* Implements hook_init().
*/
function workbench_access_init() {
// Even though we cache, we still need the include, or else our hooks don't
// run properly. See http://drupal.org/node/1356272
$scheme = variable_get('workbench_access');
workbench_access_load_include($scheme);
}
/**
* Implements hook_menu().
*/
function workbench_access_menu() {
$items['workbench_access/autocomplete/%/%'] = array(
'page callback' => 'workbench_access_autocomplete',
'page arguments' => array(
2,
3,
),
'access arguments' => array(
'assign workbench access',
),
'type' => MENU_CALLBACK,
'file' => 'workbench_access.pages.inc',
);
if (!variable_get('workbench_access_custom_form', 1) || variable_get('workbench_access') != 'taxonomy') {
$items['workbench_access/taxonomy_autocomplete'] = array(
'title' => 'Autocomplete taxonomy',
'page callback' => 'workbench_access_taxonomy_autocomplete',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'file' => 'taxonomy.workbench_access.inc',
'file path' => drupal_get_path('module', 'workbench_access') . '/modules',
);
}
$items['admin/config/workbench/access'] = array(
'title' => 'Workbench Access',
'description' => 'Workbench access control settings',
'page callback' => 'workbench_access_editors',
'access arguments' => array(
'assign workbench access',
),
'file' => 'workbench_access.admin.inc',
);
$items['admin/config/workbench/access/editors'] = array(
'title' => 'Editors',
'description' => 'The editor assignment settings.',
'page callback' => 'workbench_access_editors',
'access arguments' => array(
'assign workbench access',
),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
'file' => 'workbench_access.admin.inc',
);
$items['admin/config/workbench/access/roles'] = array(
'title' => 'Roles',
'description' => 'Role settings.',
'page callback' => 'workbench_access_roles',
'access arguments' => array(
'assign workbench access',
),
'type' => MENU_LOCAL_TASK,
'weight' => -8,
'file' => 'workbench_access.admin.inc',
);
$items['admin/config/workbench/access/sections'] = array(
'title' => 'Sections',
'description' => 'Define content sections for the Workbench Access module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_access_section_form',
),
'access arguments' => array(
'administer workbench access',
),
'type' => MENU_LOCAL_TASK,
'weight' => -5,
'file' => 'workbench_access.admin.inc',
);
$items['admin/config/workbench/access/settings'] = array(
'title' => 'Settings',
'description' => 'Settings for the Workbench Access module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_access_settings_form',
),
'access arguments' => array(
'administer workbench access',
),
'type' => MENU_LOCAL_TASK,
'file' => 'workbench_access.admin.inc',
);
$items['admin/config/workbench/access/install'] = array(
'title' => 'Install',
'description' => 'Installs a test vocabulary for the Workbench Access module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_access_install_form',
'admin/config/workbench/access/settings',
),
'access arguments' => array(
'administer workbench access',
),
'type' => MENU_CALLBACK,
'file' => 'workbench_access.admin.inc',
);
$items['admin/workbench/sections'] = array(
'title' => 'My sections',
'page callback' => 'workbench_access_sections',
'access arguments' => array(
'access workbench',
),
'type' => MENU_LOCAL_TASK,
'weight' => 5,
'file' => 'workbench_access.admin.inc',
);
$items['user/%user/sections'] = array(
'title' => 'Sections',
'description' => 'Assign users to sections for the Workbench Access module.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workbench_access_user_form',
1,
),
'access callback' => 'workbench_access_assign_user',
'access arguments' => array(
1,
),
'type' => MENU_LOCAL_TASK,
'weight' => 1,
'file' => 'workbench_access.admin.inc',
);
// Provide a content tab if not using workbench.
if (!module_exists('workbench') && module_exists('views')) {
$items['user/%user/workbench_access'] = array(
'title' => 'Content',
'access arguments' => array(
1,
),
'access callback' => 'workbench_access_views_access',
'page callback' => 'views_embed_view',
'page arguments' => array(
'workbench_access_content',
'default',
),
'description' => 'View content assigned to my sections.',
'type' => MENU_LOCAL_TASK,
'weight' => 2,
);
}
return $items;
}
/**
* Implements hook_menu_alter().
*
* If Workbench is disabled, modify the menu.
*/
function workbench_access_menu_alter(&$items) {
// Hide our taxonomy view pages.
$items['taxonomy/term/%taxonomy_term']['access callback'] = 'workbench_access_taxonomy_page_access';
$items['taxonomy/term/%taxonomy_term']['access arguments'] = array(
2,
);
// It is possible that another module added this item.
if (isset($items['admin/config/workbench'])) {
return;
}
// Add a top-level menu item if one does not exist.
$items['admin/config/workbench'] = array(
'title' => 'Workbench',
'description' => 'Workbench configuration',
'page callback' => 'system_admin_menu_block_page',
'access arguments' => array(
'administer site configuration',
),
'position' => 'right',
'file' => 'system.admin.inc',
'file path' => drupal_get_path('module', 'system'),
);
}
/**
* Custom access callback for taxonomy/term/%.
*
* Hide the vocabulary created by Workbench Access unless the
* user has specific permission to view these pages.
*
* @param $term
* The taxonomy term being viewed.
*
* @return
* Booelan TRUE or FALSE.
*/
function workbench_access_taxonomy_page_access($term) {
if (!user_access('access content') || $term->vocabulary_machine_name == 'workbench_access' && !user_access('view workbench taxonomy pages')) {
return FALSE;
}
return TRUE;
}
/**
* Access callback for content user tab.
*/
function workbench_access_views_access($account) {
global $user;
if ($user->uid != $account->uid) {
return FALSE;
}
if (user_access('access workbench access by role')) {
return TRUE;
}
return FALSE;
}
/**
* Access callback for user sections tab.
*/
function workbench_access_assign_user($account) {
// Can the current user assign editors and is
// the account being viewed eligible.
if (user_access('assign workbench access') && user_access('access workbench access by role', $account)) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_permission().
*/
function workbench_access_permission() {
$permissions = array(
'administer workbench access' => array(
'title' => t('Administer Workbench Access settings'),
),
'assign workbench access' => array(
'title' => t('Assign users to Workbench Access sections'),
),
'access workbench access by role' => array(
'title' => t('Allow all members of this role to be assigned to Workbench Access sections'),
),
'batch update workbench access' => array(
'title' => t('Batch update section assignments for content'),
),
'view workbench access information' => array(
'title' => t('View Workbench Access information'),
),
'view workbench taxonomy pages' => array(
'title' => t('View taxonomy term pages for Workbench Access vocabulary'),
),
);
return $permissions;
}
/**
* Implements hook_admin_paths().
*/
function workbench_access_admin_paths() {
$paths = array(
'user/*/sections' => TRUE,
'user/*/workbench_access' => TRUE,
);
return $paths;
}
/**
* Implements hook_hook_info().
*
* Allows the use of $module.workbench_access.inc files.
*/
function workbench_access_hook_info() {
$hooks = array();
$items = array(
'workbench_access_info',
'workbench_access_configuration',
'workbench_access_node_element',
'workbench_access_save',
'workbench_access_delete',
'workbench_access_save_user',
'workbench_access_delete_user',
'workbench_access_save_role',
'workbench_access_delete_role',
'workbench_access_user_alter',
);
foreach ($items as $item) {
$hooks[$item]['group'] = 'workbench_access';
}
return $hooks;
}
/**
* Implements hook_theme().
*/
function workbench_access_theme($existing, $type, $theme, $path) {
$themes = array(
'workbench_access_editor_form' => array(
'render element' => 'form',
'file' => 'workbench_access.admin.inc',
),
);
return $themes;
}
/**
* Implements hook_views_api().
*/
function workbench_access_views_api() {
return array(
'api' => 2.0,
);
}
/**
* Implements hook_views_default_views().
*/
function workbench_access_views_default_views() {
$return = array();
// Find all the files in the directory with the correct extension.
$files = file_scan_directory(drupal_get_path('module', 'workbench_access') . "/views", "/.view.inc/");
foreach ($files as $path => $file) {
require $path;
if (isset($view)) {
$return[$view->name] = $view;
}
}
return $return;
}
/**
* Implements hook_field_extra_fields()
*/
function workbench_access_field_extra_fields() {
// Not using a custom form element?
if (!variable_get('workbench_access_custom_form', 1)) {
return;
}
$extra = array();
foreach (node_type_get_names() as $name => $value) {
if (variable_get('workbench_access_node_type_' . $name, 1)) {
$extra['node'][$name]['form']['workbench_access'] = array(
'label' => t('Workbench Access'),
'description' => t('Workbench Access settings.'),
'weight' => 0,
);
}
}
return $extra;
}
/**
* Load a Workbench Access module file, or all files.
*
* @param $module
* The name of the module file to load.
*
* @TODO: Allow loading from outside the module directory. However,
* this should be convered by the magic loading from hook_hook_info() and
* the registry system.
*/
function workbench_access_load_include($module = NULL) {
if (!is_null($module)) {
$file = DRUPAL_ROOT . DIRECTORY_SEPARATOR . drupal_get_path('module', 'workbench_access') . '/modules/' . $module . '.workbench_access.inc';
if (file_exists($file)) {
include_once $file;
}
else {
watchdog('workbench_access', 'Failed to load required include file %file', array(
'%file' => $file,
), WATCHDOG_ERROR);
}
return;
}
// Load all includes.
foreach (file_scan_directory(DRUPAL_ROOT . DIRECTORY_SEPARATOR . drupal_get_path('module', 'workbench_access') . '/modules', '/.inc$/') as $file) {
include_once $file->uri;
}
}
/**
* Implements hook_node_access().
*
* Enforces our access rules when users try to edit/delete a node.
*/
function workbench_access_node_access($node, $op, $account) {
// View step. We ignore for published nodes.
if ($op == 'view' && $node->status) {
return NODE_ACCESS_IGNORE;
}
// If not configured, do nothing.
$tree = workbench_access_get_active_tree();
if (empty($tree['active'])) {
return NODE_ACCESS_IGNORE;
}
// If disabled for this content type, do nothing.
if (!is_object($node)) {
$type = $node;
}
else {
$type = $node->type;
}
if (!variable_get('workbench_access_node_type_' . $type, 1)) {
return NODE_ACCESS_IGNORE;
}
// Ignore nodes where workbench section is not set.
// This is for sites that installed Workbench Access on top of existing
// content. See http://drupal.org/node/1359552.
if ($op != 'create' && empty($node->workbench_access)) {
return NODE_ACCESS_IGNORE;
}
// Now check the user account.
if (!isset($account->workbench_access)) {
$account = user_load($account->uid);
}
// Create step. User must be assigned to a section to create content.
// Note that we do not currently enforce complex rules here.
if ($op == 'create' && !empty($account->workbench_access)) {
return NODE_ACCESS_IGNORE;
}
// Load the current scheme.
workbench_access_load_include(variable_get('workbench_access'));
// Get the access rules for this node.
$result = FALSE;
// Always default to FALSE.
if (!empty($node->workbench_access) && !empty($account->workbench_access)) {
// In the case of "preview" the property may be a string. Not sure why.
// See https://drupal.org/node/1935190.
if (!is_array($node->workbench_access)) {
$node->workbench_access = array(
$node->workbench_access,
);
}
$result = workbench_access_check($op, $type, array_filter($node->workbench_access), $account->workbench_access);
}
// The user must be allowed to perform this action by core node module.
// All we do is issue an ignore response, indicating that some other module
// may grants access. If we ever support complex data rules, this may change.
if ($result !== FALSE) {
return NODE_ACCESS_IGNORE;
}
return NODE_ACCESS_DENY;
}
/**
* Given a node, return its access rules.
*
* @param $node
* The node being requested.
*
* @return
* An array of access_ids.
*/
function workbench_access_get_node_tree($node) {
if (empty($node->workbench_access)) {
return array();
}
$access = array();
foreach ($node->workbench_access as $access_id => $info) {
$access[$access_id] = $access_id;
}
return $access;
}
/**
* Check to see if a user can access the node for this operation.
*
* @param $op
* The operation being performed.
* @param $type
* The node type being requested.
* @param $access_ids
* The access_id array for this node.
* @param $account_access
* The access rules for the user performing the action.
*
* @return
* The id of the rule that grants access, or FALSE if none do.
*/
function workbench_access_check($op, $type, $access_ids, $account_access) {
foreach ($access_ids as $access_id) {
if ($id = workbench_access_in_tree($access_id, $account_access)) {
if (in_array('all', $account_access[$id][$op]) || in_array($type, $account_access[$id][$op])) {
return $id;
}
}
}
return FALSE;
}
/**
* Check to see if an access check is in a given hierarchy.
*
* @param $access_ids
* The access_id array for this node.
* @param $account_access
* The access rules for the user performing the action.
*
* @return
* The id of the rule that grants access, or FALSE if none do.
*
* @see workbench_access_check()
*/
function workbench_access_in_tree($access_id, $account_access) {
// Simple equivalence check. If this passes, no need for complexity.
if (isset($account_access[$access_id])) {
return $access_id;
}
$tree = workbench_access_get_access_tree(array_keys($account_access));
foreach ($tree as $id => $info) {
$data = array_flip($info);
if (isset($data[$access_id])) {
return $id;
}
}
return FALSE;
}
/**
* Get the access hierarchy for a user.
*
* @param $account_access
* The access rules for the user performing the action.
*
* @return
* An array of access rules.
*/
function workbench_access_get_access_tree($account_access = array()) {
$trees =& drupal_static(__FUNCTION__);
if (empty($account_access)) {
$account = $GLOBALS['user'];
workbench_access_user_load_data($account);
$account_access = array_keys($account->workbench_access);
}
$key = implode($account_access);
if (isset($trees[$key])) {
return $trees[$key];
}
$trees[$key] = array();
if (empty($account_access)) {
return $trees[$key];
}
$access_scheme = db_select('workbench_access', 'wa')
->addTag('workbench_access')
->fields('wa', array(
'access_id',
'access_type',
'access_scheme',
'access_type_id',
))
->condition('wa.access_id', $account_access, 'IN')
->condition('wa.access_type', variable_get('workbench_access'))
->execute()
->fetchAllAssoc('access_id', PDO::FETCH_ASSOC);
foreach ($access_scheme as $id => $info) {
$trees[$key][$id] = workbench_access_tree($info, TRUE);
}
return $trees[$key];
}
/**
* Return the access tree for a rule set.
*
* @see hook_workbench_access_info()
*
* @param $info
* The rule information.
* @param $keys
* Boolean value to return only array keys, or all data.
*
* @return
* An array of access_ids or a data array.
*/
function workbench_access_tree($info, $keys = FALSE) {
$function = $info['access_type'] . '_workbench_access_tree';
if (function_exists($function)) {
return $function($info, $keys);
}
return array();
}
/**
* Load the active tree.
*/
function workbench_access_get_active_tree() {
$scheme = variable_get('workbench_access');
if (!$scheme) {
return FALSE;
}
workbench_access_load_include($scheme);
$access_tree =& drupal_static(__FUNCTION__);
if (!isset($access_tree)) {
// Now check the cache.
$cache = cache_get('workbench_access_tree', 'cache_bootstrap');
if (isset($cache->data)) {
$access_tree = $cache->data;
}
if (isset($access_tree['access_scheme'])) {
return $access_tree;
}
// Retrieve and cache data.
$func = $scheme . '_workbench_access_info';
$info = $func();
$data = $info[$scheme];
$stored = workbench_access_get_ids_by_scheme($data);
$tree = workbench_access_tree($data);
workbench_access_build_tree($tree);
// Ensure that we have no orphaned ids.
$active = array();
foreach ($stored as $access_id => $access_scheme) {
if (isset($tree[$access_id])) {
$active[$access_id] = $access_scheme;
}
}
$access_tree = array(
'access_scheme' => $data,
'tree' => $tree,
'active' => $active,
);
cache_set('workbench_access_tree', $access_tree, 'cache_bootstrap');
}
return $access_tree;
}
/**
* Implements hook_node_load().
*/
function workbench_access_node_load($nodes, $types) {
$scheme =& drupal_static(__FUNCTION__);
if (!isset($scheme)) {
$scheme = variable_get('workbench_access');
}
$tree = workbench_access_get_active_tree();
$result = array();
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 or if it is
// not under access control.
if (empty($node->nid) || isset($node->type) && !variable_get('workbench_access_node_type_' . $node->type, 1)) {
continue;
}
$nodes[$node->nid]->workbench_access = array();
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;
}
}
}
}
/**
* Implements hook_node_insert().
*/
function workbench_access_node_insert($node) {
// Clear the old records for this node.
workbench_access_node_delete($node);
// Make sure we have processed new data.
workbench_access_node_presave($node);
// If nothing to set, we are done.
if (empty($node->workbench_access)) {
return;
}
// Ensure we have an array, as the form can allow multiple selects or single.
if (!is_array($node->workbench_access)) {
$node->workbench_access = array(
$node->workbench_access,
);
}
// Load the active tree to get the access scheme data and validate that
// the selections are actually present.
$active = workbench_access_get_active_tree();
foreach ($node->workbench_access as $id) {
// Prevent false records from being saved accidentally.
if (isset($active['active'][$id])) {
$record = array(
'nid' => $node->nid,
'access_id' => $id,
'access_scheme' => $active['access_scheme']['access_scheme'],
);
drupal_write_record('workbench_access_node', $record);
}
}
// Clear static caches for tokens.
drupal_static_reset('_workbench_access_get_node_section_names');
}
/**
* Implements hook_node_presave().
*/
function workbench_access_node_presave($node) {
// We must have a node id to continue. @see workbench_access_node_insert().
if (empty($node->nid)) {
return;
}
// hook_node_operations() does not retuen a full node.
// See https://www.drupal.org/node/2895338.
if (!isset($node->type)) {
$query = db_query('SELECT type FROM {node} WHERE nid = :nid', array(
':nid' => $node->nid,
));
$node->type = $query
->fetchField();
}
// Only run this on enabled node types.
if (!isset($node->type) || !variable_get('workbench_access_node_type_' . $node->type, 1)) {
return;
}
// Did we use the default field form?
workbench_access_prepare_field_save($node);
if (isset($node->workbench_access_fields)) {
$node->workbench_access = array();
foreach ($node->workbench_access_fields as $field) {
if (!is_array($node->{$field})) {
$node->workbench_access[] = $node->{$field};
}
else {
if (!empty($node->workbench_access_language)) {
$lang = field_language('node', $node, $field);
$data = isset($node->{$field}[$lang]) ? $node->{$field}[$lang] : array();
}
else {
$data = array(
$node->{$field},
);
}
foreach ($data as $value) {
if (isset($value[$node->workbench_access_column])) {
// Handle disabled menu links.
if (!isset($value['enabled']) || !empty($value['enabled'])) {
$node->workbench_access[] = $value[$node->workbench_access_column];
}
}
}
}
}
}
// Ensure that the data format is consistent and that items that cannot
// be changed by this editor are preserved.
if (isset($node->workbench_access) && isset($node->workbench_access_fixed)) {
if (!is_array($node->workbench_access)) {
$node->workbench_access = array(
$node->workbench_access,
);
}
if (!is_array($node->workbench_access_fixed)) {
$node->workbench_access_fixed = array(
$node->workbench_access_fixed,
);
}
$node->workbench_access += $node->workbench_access_fixed;
}
// Due to form handling, it is possible that we have duplicate entries.
if (isset($node->workbench_access) && is_array($node->workbench_access)) {
$node->workbench_access = array_unique($node->workbench_access);
}
}
/**
* Prepares a node for saving in the event we did not come from a form.
*/
function workbench_access_prepare_field_save($node) {
// If using a default form element, or the form data is already present, then
// we do not need this step.
if (variable_get('workbench_access_custom_form', 1)) {
return;
}
$tree = workbench_access_get_active_tree();
$scheme = $tree['access_scheme'];
$node->workbench_access_language = $scheme['translatable'];
$node->workbench_access_column = $scheme['storage_column'];
// Find the fields to watch for data.
if (empty($node->workbench_access_fields) && !empty($scheme['form_field'])) {
$node->workbench_access_fields[] = $scheme['form_field'];
}
elseif (isset($node->type)) {
$fields = workbench_access_get_assigned_fields($node->type);
foreach ($fields as $field => $info) {
$node->workbench_access_fields[] = $field;
}
}
if (!isset($node->workbench_access_fields)) {
return;
}
// Make sure that the required fields are present, even if they're empty.
foreach ($node->workbench_access_fields as $field) {
if (!isset($node->{$field})) {
if ($field == 'menu') {
menu_node_prepare($node);
if (!empty($node->menu)) {
// menu_node_prepare() expects FAPI to mark the link enabled.
// If it's not marked enabled, when we execute node_save(),
// menu_node_save() would delete it. Avoid this:
$node->menu['enabled'] = empty($node->menu['mlid']) ? 0 : 1;
}
}
else {
$node->{$field} = array();
}
}
}
}
/**
* Implements hook_node_update().
*/
function workbench_access_node_update($node) {
workbench_access_node_insert($node);
}
/**
* Implements hook_node_delete().
*/
function workbench_access_node_delete($node) {
db_delete('workbench_access_node')
->condition('nid', $node->nid)
->execute();
}
/**
* Implements hook_user_load().
*/
function workbench_access_user_load($users) {
foreach ($users as $uid => $account) {
workbench_access_user_load_data($account);
}
drupal_static_reset('workbench_access_get_user_tree');
}
/**
* Load the access data for this user.
*
* @param $account
* The user account object.
*
* @return
* No return. Add the workbench_access attribute by reference.
*/
function workbench_access_user_load_data($account) {
$access = array();
$access_scheme = variable_get('workbench_access');
$active = workbench_access_get_active_tree();
// There must be active sections, and the user must be allowed to use one.
if (!empty($active['tree']) && user_access('access workbench access by role', $account)) {
// Get the user's assigned access sections.
$query = db_select('workbench_access_user', 'wau')
->addTag('workbench_access_user')
->fields('wau', array(
'access_id',
))
->condition('wau.uid', $account->uid)
->condition('wau.access_scheme', $access_scheme);
$result = $query
->execute()
->fetchAll();
$items = array();
foreach ($result as $data) {
$items[$data->access_id] = $data;
}
// Add roles.
$query = db_select('workbench_access_role', 'war')
->addTag('workbench_access_role')
->fields('war', array(
'access_id',
))
->condition('war.rid', array_keys($account->roles), 'IN')
->condition('war.access_scheme', $access_scheme);
$result = $query
->execute()
->fetchAll();
$account->workbench_access_by_role = array();
foreach ($result as $data) {
// If the role is set it matches a role the user has and does not need to
// be added again, but we should still track it.
if (!isset($items[$data->access_id])) {
$items[$data->access_id] = $data;
}
// Identify the access-by-role features. These can be duplicated for
// users with multiple roles.
if (!isset($account->workbench_access_by_role[$data->access_id])) {
$account->workbench_access_by_role[$data->access_id] = $data->access_id;
}
}
foreach ($items as $item) {
if (!in_array($item->access_id, array_keys($active['tree']))) {
continue;
}
// Get the permissions for those sections.
// @TODO: complex permission handling.
$access[$item->access_id] = array(
'view' => array(
'all',
),
'create' => array(
'all',
),
'update' => array(
'all',
),
'delete' => array(
'all',
),
'preview' => array(
'all',
),
'revise' => array(
'all',
),
'publish' => array(
'all',
),
);
}
}
// Allow modules to alter the default behavior.
drupal_alter('workbench_access_user', $access, $account);
$account->workbench_access = $access;
}
/**
* Build an access tree for a user account.
*
* @param $account
* An optional account object.
*
* @return
* An access tree representing the user's active sections.
*/
function workbench_access_get_user_tree($account = NULL) {
$accounts =& drupal_static(__FUNCTION__, array());
if (is_null($account)) {
global $user;
$account = $user;
}
if (isset($accounts[$account->uid])) {
return $accounts[$account->uid];
}
// Make sure we prepared the user.
if (!isset($account->workbench_access)) {
workbench_access_user_load_data($account);
}
// Prepare the form element.
$active = workbench_access_get_active_tree();
$tree = $active['tree'];
// We should never get this far, really.
if (empty($account->workbench_access)) {
$tree = array();
}
else {
workbench_access_build_tree($tree, array_keys($account->workbench_access));
}
// Set the static lookup.
$accounts[$account->uid] = $tree;
return $accounts[$account->uid];
}
/**
* Rebuild the section access tables.
*
* @param $access_scheme
* The access scheme to use.
* @param $sections
* The sections to add to the table.
*/
function workbench_access_rebuild_scheme($access_scheme, $sections = array()) {
// Check to see if any sections have been removed.
$access_ids = workbench_access_get_ids_by_scheme($access_scheme, TRUE);
$removed = array_diff($access_ids, array_keys($sections));
foreach ($removed as $access_id) {
$record = $access_scheme;
$record['access_id'] = $access_id;
if (isset($sections[$access_id]['access_type_id'])) {
$record['access_type_id'] = $sections[$access_id]['access_type_id'];
}
workbench_access_section_delete($record);
}
// Add the new sections.
$added = array_diff(array_keys($sections), $access_ids);
foreach ($added as $access_id) {
$record = $access_scheme;
$record['access_id'] = $access_id;
if (isset($sections[$access_id]['access_type_id'])) {
$record['access_type_id'] = $sections[$access_id]['access_type_id'];
}
workbench_access_section_save($record);
}
}
/**
* Save an access section to the {workbench_access} table.
*
* @param $section
* The access scheme to save. Follows the format of hook_workbench_access_info().
*
* @see hook_workbench_access_section_save()
*/
function workbench_access_section_save($section) {
// Reset the tree.
workbench_access_reset_tree();
// Write the record.
drupal_write_record('workbench_access', $section);
// Notify other modules.
module_invoke_all('workbench_access_save', $section);
}
/**
* Delete an access section from the {workbench_access} table.
*
* Also removes user access permissions from {workbench_access_user}.
*
* @param $section
* The access scheme to delete. Follows the format of hook_workbench_access_info().
*
* @see hook_workbench_access_section_delete()
*/
function workbench_access_section_delete($section) {
// Reset the tree.
workbench_access_reset_tree();
// Notify other modules.
module_invoke_all('workbench_access_delete', $section);
// Now clean up.
db_delete('workbench_access')
->condition('access_id', $section['access_id'])
->condition('access_scheme', $section['access_scheme'])
->execute();
db_delete('workbench_access_node')
->condition('access_id', $section['access_id'])
->condition('access_scheme', $section['access_scheme'])
->execute();
db_delete('workbench_access_user')
->condition('access_id', $section['access_id'])
->condition('access_scheme', $section['access_scheme'])
->execute();
db_delete('workbench_access_role')
->condition('access_id', $section['access_id'])
->condition('access_scheme', $section['access_scheme'])
->execute();
}
/**
* Reset tree data stored in statics.
*
* Necessary when rebuilding the active tree settings.
*/
function workbench_access_reset_tree() {
cache_clear_all('workbench_access_tree', 'cache_bootstrap');
drupal_static_reset('workbench_access_get_access_tree');
drupal_static_reset('workbench_access_get_active_tree');
drupal_static_reset('workbench_access_get_ids_by_scheme');
drupal_static_reset('workbench_access_get_user_tree');
drupal_static_reset('workbench_access_user_load_data');
drupal_static_reset('workbench_access_node_load');
drupal_static_reset('workbench_access_get_roles');
}
/**
* Given an access scheme, return all active sections.
*
* This function will either return an array of section ids, or an associative
* array of access_id keys and the access scheme data as the value.
*
* @param $access_scheme
* The active access scheme.
* @param $keys
* Boolean value to return only array keys, or all data.
*
* @return
* An array of access_ids or a data array.
*/
function workbench_access_get_ids_by_scheme($access_scheme, $keys = FALSE) {
$data =& drupal_static(__FUNCTION__);
// If no access types are active, this fails. But return an array.
if (empty($access_scheme['access_type_id'])) {
$data = array();
}
if (!isset($data)) {
$data = db_select('workbench_access', 'wa')
->addTag('workbench_access')
->fields('wa', array(
'access_id',
'access_scheme',
'access_type',
'access_type_id',
))
->condition('wa.access_scheme', $access_scheme['access_scheme'])
->condition('wa.access_type_id', $access_scheme['access_type_id'])
->execute()
->fetchAllAssoc('access_id', PDO::FETCH_ASSOC);
}
if ($keys) {
return array_keys($data);
}
return $data;
}
/**
* Rebuild the user access tables.
*
* @param $uid
* The user id being updated.
* @param $sections
* An array of section ids to set for the user.
*/
function workbench_access_rebuild_user($uid, $sections = array()) {
// Get the user and scheme data.
$account = user_load($uid);
$user_sections = array_keys($account->workbench_access);
$access_scheme = variable_get('workbench_access');
// Check to see if any sections have been removed.
$removed = array_diff($user_sections, $sections);
foreach ($removed as $access_id) {
workbench_access_user_section_delete($uid, $access_id, $access_scheme);
}
// Add the new sections.
$added = array_diff($sections, $user_sections);
foreach ($added as $access_id) {
workbench_access_user_section_save($uid, $access_id, $access_scheme);
}
}
/**
* Save a user access record and notify other modules.
*
* @param $uid
* The active user id.
* @param $access_id
* The access id to store.
* @param $access_scheme
* The active access scheme
*
* @see hook_workbench_access_save_user()
*/
function workbench_access_user_section_save($uid, $access_id, $access_scheme) {
// Add the access section.
$record['uid'] = $uid;
$record['access_id'] = $access_id;
$record['access_scheme'] = $access_scheme;
drupal_write_record('workbench_access_user', $record);
// Clear static caches.
drupal_static_reset('workbench_access_user_load_data');
drupal_static_reset('_workbench_access_get_user_section_names');
// Notify other modules the sections have changed for the user.
$account = user_load($uid, TRUE);
module_invoke_all('workbench_access_save_user', $account, $access_id, $access_scheme);
}
/**
* Deletes an access rule from the {workbench_access_user} table.
*
* @param $uid
* The active user id.
* @param $access_id
* The active access id.
* @param $access_scheme
* The active access scheme.
*
* @see hook_workbench_access_section_delete()
*/
function workbench_access_user_section_delete($uid, $access_id, $access_scheme) {
// Remove the access section.
db_delete('workbench_access_user')
->condition('access_id', $access_id)
->condition('access_scheme', $access_scheme)
->condition('uid', $uid)
->execute();
// Clear static caches.
drupal_static_reset('workbench_access_user_load_data');
drupal_static_reset('_workbench_access_get_user_section_names');
// Notify other modules the sections have changed for the user.
$account = user_load($uid, TRUE);
module_invoke_all('workbench_access_delete_user', $account, $access_id, $access_scheme);
}
/**
* Save a role access record and notify other modules.
*
* @param $rid
* The active role id.
* @param $access_id
* The access id to store.
* @param $access_scheme
* The active access scheme
*
* @see hook_workbench_access_save_role()
*/
function workbench_access_role_section_save($rid, $access_id, $access_scheme) {
$record['rid'] = $rid;
$record['access_id'] = $access_id;
$record['access_scheme'] = $access_scheme;
drupal_write_record('workbench_access_role', $record);
$role = user_role_load($rid, TRUE);
module_invoke_all('workbench_access_save_role', $role, $access_id, $access_scheme);
}
/**
* Deletes an access rule from the {workbench_access_user} table.
*
* @param $uid
* The active user id.
* @param $access_id
* The active access id.
* @param $access_scheme
* The active access scheme.
*
* @see hook_workbench_access_section_delete()
*/
function workbench_access_role_section_delete($rid, $access_id, $access_scheme) {
// Notify other modules.
$role = user_role_load($rid, TRUE);
module_invoke_all('workbench_access_delete_role', $role, $access_id, $access_scheme);
// Clean up.
db_delete('workbench_access_role')
->condition('access_id', $access_id)
->condition('access_scheme', $access_scheme)
->condition('rid', $rid)
->execute();
}
/**
* Implements hook_user_delete().
*
* On user delete, remove access rules. Note that we do not fire our
* own hooks here, as other modules need to be smart enough to
* handle this operation.
*/
function workbench_access_user_delete($account) {
db_delete('workbench_access_user')
->condition('uid', $account->uid)
->execute();
}
/**
* Implements hook_user_role_delete().
*
* On role delete, remove access rules.
*/
function workbench_access_user_role_delete($role) {
db_delete('workbench_access_role')
->condition('rid', $role->rid)
->execute();
}
/**
* Defines configuration options for the default access scheme.
*
* @see workbench_access_workbench_access_info()
*/
function workbench_access_configuration(&$form, &$form_state) {
$options = array();
$vocabularies = taxonomy_get_vocabularies();
foreach ($vocabularies as $vid => $vocabulary) {
$options[$vid] = $vocabulary->name;
}
$form['workbench_access_info'] = array(
'#type' => 'fieldset',
'#title' => t('Default scheme settings'),
'#states' => array(
'visible' => array(
':input[name=workbench_access]' => array(
'value' => 'workbench_access',
),
),
),
);
$form['workbench_access_info']['workbench_access_vid'] = array(
'#type' => 'radios',
'#title' => t('Editorial vocabulary'),
'#description' => t('Select the vocabulary to be used for access control. <strong>Warning: changing this value in production may disrupt your workflow.</strong>'),
'#options' => $options,
'#default_value' => variable_get('workbench_access_vid', 0),
'#states' => array(
'visible' => array(
':input[name=workbench_access]' => array(
'value' => 'workbench_access',
),
),
),
);
}
/**
* Build a hierarchy defined by an access control schema.
*
* Note that unlike taxonomy_build_tree() and similar, the child
* items are expressly listed as an array of the parent, for easier
* checking later.
*
* @param &$tree
* The hierarchy array, passed by reference.
* @param $sections
* An optional array of sections to limit the return set.
* @param $depth
* Internal depth marker, used for recursive array processing. Do not use.
*
* @return
* An array of items within the given access scheme.
*/
function workbench_access_build_tree(&$tree, $sections = NULL, $depth = -1) {
static $max_depth;
if (!isset($max_depth)) {
$max_depth = 0;
}
if ($depth > $max_depth) {
if (empty($sections)) {
// Reset max_depth static in case we get a new function call.
$max_depth = 0;
return;
}
else {
$new_tree = array();
$sections = array_flip($sections);
// Find all the children of the active sections.
foreach ($tree as $access_id => $data) {
if (isset($sections[$access_id])) {
$new_tree[$access_id] = $tree[$access_id];
if (isset($tree[$access_id]['children'])) {
foreach ($tree[$access_id]['children'] as $id) {
$new_tree[$id] = $tree[$id];
}
}
}
}
// Remove sections that should not be shown.
foreach ($tree as $access_id => $data) {
if (!isset($new_tree[$access_id])) {
unset($tree[$access_id]);
}
}
// Reset max_depth static in case we get a new function call.
$max_depth = 0;
return;
}
}
$depth++;
foreach ($tree as $id => $item) {
if ($item['depth'] > $max_depth) {
$max_depth = $item['depth'];
}
// Set the parentage of this item.
if ($depth == 0 && !empty($item['parent']) && isset($tree[$item['parent']])) {
$tree[$item['parent']]['children'][$id] = $id;
}
elseif ($item['depth'] > 0 && !empty($item['children']) && isset($tree[$item['parent']]['children'])) {
foreach ($item['children'] as $child_id) {
if (!isset($child_id, $tree[$item['parent']]['children'][$child_id])) {
$tree[$item['parent']]['children'][$child_id] = $child_id;
}
}
}
}
workbench_access_build_tree($tree, $sections, $depth);
}
/**
* Build form options from a tree.
*
* @param $tree
* The current access tree.
* @param $active
* An array of active sections, used as a filter.
*
* @return
* An array of options, suitable for use in a form.
*/
function workbench_access_options($tree, $active) {
$used = array();
$parent = 0;
$base_depth = 0;
$options = array();
if (empty($tree) || empty($active)) {
return $options;
}
$tree_keys = array_keys($tree);
$active_keys = array_flip(array_keys($active));
foreach ($tree as $section) {
if (in_array($section['access_id'], $used) || !isset($active_keys[$section['access_id']])) {
continue;
}
// Nest the children so the user understands the hierarchy.
if ($section['depth'] == 0 || !isset($tree[$section['parent']])) {
$parent = $section['name'];
$base_depth = $section['depth'];
}
$options[$section['access_id']] = str_repeat('-', $section['depth'] - $base_depth) . ' ' . $section['name'];
$used[] = $section['access_id'];
}
return $options;
}
/**
* Build an array of form options for the currently active workbench access tree.
*
* @return
* An array of options, suitable for use in a form.
*/
function workbench_access_active_options() {
$active = workbench_access_get_active_tree();
$tree = workbench_access_get_user_tree();
return workbench_access_options($tree, $active['active']);
}
/**
* Ensure the proper action for our content form.
*/
function workbench_access_form_views_exposed_form_alter(&$form, &$form_state) {
if ($form['#id'] == 'views-exposed-form-workbench-access-content-default') {
$form['#action'] = url($_GET['q']);
}
}
/**
* Given an access id and scheme, load the object with its data.
*
* @param $scheme
* The access scheme to check, which is an array including at least an
* access_type and access_type_id.
*
* @return $data
* A data array containing basic information, name, descrption, access_id
* and active state. Returns an empty array on failure.
*/
function workbench_access_load_access_info($scheme) {
$function = $scheme['access_type'] . '_workbench_access_load';
if (function_exists($function)) {
$data = $function($scheme);
$data['active'] = workbench_access_is_active_id($scheme['access_type'], $data['access_id']);
return $data;
}
return array();
}
/**
* Return information for an access id.
*
* @param $access_type
* The scheme type.
* @param $access_type_id
* The access type id.
*
* @return
* Information for the access id.
*/
function workbench_access_load($access_type, $access_type_id) {
workbench_access_load_include($access_type);
$scheme = array(
'access_type' => $access_type,
'access_id' => $access_type_id,
);
return workbench_access_load_access_info($scheme);
}
/**
* Return information for an access id.
*
* @param $access_type
* The scheme type.
* @param $access_type_id
* The access type id.
*
* @return
* Boolean TRUE or FALSE.
*/
function workbench_access_is_active_id($access_type, $access_type_id) {
$active = workbench_access_get_active_tree();
if (isset($active[$access_type_id]) && $active[$access_type_id]['access_type'] != $access_type) {
return TRUE;
}
return FALSE;
}
/**
* Implements hook_form_alter().
*
* Alter the options when presenting a node form. Also fire any extension
* hooks registered with hook_workbench_access_FORM_ID_alter().
*
*/
function workbench_access_form_alter(&$form, $form_state, $form_id) {
global $user;
// Make sure we prepared the user.
if (!isset($user->workbench_access)) {
workbench_access_user_load_data($user);
}
// Fire any active internal module hooks for non-node forms.
if (empty($form['#node_edit_form'])) {
workbench_access_alter_form($form_id, $form, $form_state);
return;
}
// Fire the node form alter, if there are configured sections.
if (!workbench_access_allow_form_alter()) {
return;
}
// Do not fire if this content type is not under our control.
if (!variable_get('workbench_access_node_type_' . $form['#node']->type, 1)) {
return;
}
// Determine which form element to target.
if (variable_get('workbench_access_custom_form', 1)) {
// Provide a form element.
workbench_access_node_form_element($form, $form_state);
}
else {
workbench_access_default_form_element($form, $form_state);
}
}
/**
* Call the alter hook for the active schema.
*/
function workbench_access_alter_form($hook, &$form, &$form_state) {
$active = workbench_access_get_active_tree();
// Determine the proper field to edit.
$function = $active['access_scheme']['access_type'] . '_' . 'workbench_access_' . $hook . '_alter';
// We don't do a module_invoke or drupal_alter() here so we can target just the active scheme.
if (function_exists($function)) {
$function($form, $form_state, $active);
}
// Now let other modules act.
drupal_alter('workbench_access_' . $hook, $form, $form_state, $active);
}
/**
* Check to see if the module is configured and we can run our form alter.
*
* TODO: Make this a generic function, which is called elsewhere.
*/
function workbench_access_allow_form_alter() {
$active = workbench_access_get_active_tree();
$ids = !empty($active) ? array_filter($active['access_scheme']['access_type_id']) : NULL;
if (!empty($ids)) {
return TRUE;
}
return FALSE;
}
/**
* Provides a module-specific form for access control.
*/
function workbench_access_node_form_element(&$form, $form_state) {
global $user;
// Prepare the form element.
$active = workbench_access_get_active_tree();
if (empty($active['active'])) {
drupal_set_message(workbench_access_sections_needed_message(), 'warning');
return;
}
// Set default form options.
$default = array();
if (!empty($form['#node']->workbench_access)) {
// In preview mode, this value can be a string.
// See http://drupal.org/node/1935190.
if (is_array($form['#node']->workbench_access)) {
$current = array_keys($form['#node']->workbench_access);
}
else {
$current = array(
$form['#node']->workbench_access,
);
}
foreach ($current as $access_id) {
if (isset($active['active'][$access_id])) {
$default[] = $access_id;
}
}
}
$options = array();
$multiple = variable_get('workbench_access_allow_multiple', 0);
// Force user selection if not set. See http://drupal.org/node/1348626.
if (empty($multiple) && empty($default)) {
$options[NULL] = t('- Select a value -');
}
// Get the user options.
$options += workbench_access_active_options();
// 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)) {
$message = workbench_access_configuration_needed_message();
// Using 'error' instead of warning because the user should not have gotten this far
// without configuring Workbench Access.
drupal_set_message($message, 'error', FALSE);
}
// The base form element.
$element = array(
'#type' => 'select',
'#title' => t('@message_label', array(
'@message_label' => variable_get('workbench_access_label', 'Section'),
)),
'#options' => $options,
'#required' => TRUE,
'#default_value' => $default,
'#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)) {
$all = array();
$disallowed = array();
foreach ($default as $item) {
if (isset($active['tree'][$item]['name'])) {
$all[$active['tree'][$item]['access_id']] = $active['tree'][$item]['name'];
if (!isset($options[$item])) {
$disallowed[$active['tree'][$item]['access_id']] = $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'] . '<br />' . 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'] = $element;
}
/**
* Find and alter the native form element for node editing.
*
* FieldAPI makes this much harder than it needs to be.
*/
function workbench_access_default_form_element(&$form, &$form_state) {
// Try to find the form element(s) to target.
workbench_access_find_form_elements($form);
if (empty($form['workbench_access_fields']['#value'])) {
drupal_set_message(workbench_access_configuration_needed_message(), 'warning');
}
else {
// Generate options so we can check for access.
$options = workbench_access_active_options();
// Call the function.
$func = $form['workbench_access_fields']['#access_type'] . '_workbench_access_default_form_alter';
if (function_exists($func)) {
$func($form, $form_state, $options);
}
}
}
/**
* Find form elements that control access.
*
* @see workbench_access_get_assigned_fields()
*/
function workbench_access_find_form_elements(&$form) {
$active = workbench_access_get_active_tree();
$form['workbench_access_fields'] = array(
'#type' => 'value',
'#value' => array(),
'#access_type' => $active['access_scheme']['access_type'],
);
// Find the menu form, which is easy.
if ($active['access_scheme']['access_type'] == 'menu') {
$form['workbench_access_fields']['#value'][] = 'menu';
}
else {
$type = $form['#node']->type;
$fields = workbench_access_get_assigned_fields($type);
foreach ($fields as $field => $info) {
if (!empty($info['instance_info']['workbench_access_field'])) {
$form['workbench_access_fields']['#value'][] = $field;
}
}
}
}
/**
* Custom form validation handler.
*
* This function ensures that our handler is loaded properly.
*/
function workbench_access_autocomplete_validate($element, &$form_state) {
$active = workbench_access_get_active_tree();
$scheme = $active['access_scheme'];
$function = 'workbench_access_' . $scheme . '_autocomplete_validate';
if (function_exists($function)) {
$function($element, $form_state);
}
}
/**
* Return the text directing admins to Workbench Access configuration.
*/
function workbench_access_configuration_needed_message() {
return t('You must <a href="@settings">configure Workbench Access settings</a> before editorial access control will be enforced.', array(
'@settings' => url('admin/config/workbench/access/settings'),
));
}
/**
* Return text directing admins to section configuration.
*/
function workbench_access_sections_needed_message() {
return t('There are no active <a href="!url">editorial sections</a> for Workbench Access. Editorial access control will not be enforced.', array(
'!url' => url('admin/config/workbench/access/sections'),
));
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a sections form to native vocabulary editing.
*/
function workbench_access_form_taxonomy_form_vocabulary_alter(&$form, &$form_state, $form_id) {
if (!user_access('administer workbench access') || variable_get('workbench_access') != 'taxonomy') {
return;
}
$vocabulary = $form['#vocabulary'];
$active = array_filter(variable_get('workbench_access_taxonomy', array()));
if (!in_array($vocabulary->machine_name, $active)) {
// TODO: allow this to be made a category.
return;
}
$access_id = isset($vocabulary->machine_name) ? $vocabulary->machine_name : NULL;
workbench_access_edit_form_alter($form, $access_id, 'vocabulary');
}
/**
* Submit callback for vocabulary forms.
*/
function workbench_access_vocabulary_submit($form, &$form_state) {
$values = $form_state['values'];
if (!isset($values['workbench_access'])) {
return;
}
$section = array(
'access_id' => $values['machine_name'],
'access_type' => 'taxonomy',
'access_scheme' => 'taxonomy',
'access_type_id' => $values['machine_name'],
);
workbench_access_edit_form_submit($values, $section, $values['name']);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add a sections form to native term editing.
*/
function workbench_access_form_taxonomy_form_term_alter(&$form, &$form_state, $form_id) {
if (!user_access('administer workbench access') || variable_get('workbench_access') != 'taxonomy' || isset($form_state['confirm_delete'])) {
return;
}
// On delete, the term may be an object. Be consistent, core!
$term = (array) $form['#term'];
$active = array_filter(variable_get('workbench_access_taxonomy', array()));
if (!in_array($term['vocabulary_machine_name'], $active)) {
return;
}
$access_id = isset($term['tid']) ? $term['tid'] : NULL;
workbench_access_edit_form_alter($form, $access_id, 'term');
}
/**
* Implements hook_form_FORM_ID_alter().
*
* The term overview page does not fire hook_taxonomy_term_update().
*/
function workbench_access_form_taxonomy_overview_terms_alter(&$form, &$form_state, $form_id) {
if (variable_get('workbench_access') != 'taxonomy') {
return;
}
$form['#submit'][] = 'workbench_access_form_taxonomy_overview_terms_submit';
}
/**
* Updates taxonomy access tree on overview save.
*/
function workbench_access_form_taxonomy_overview_terms_submit($form, &$form_state) {
workbench_access_reset_tree();
}
/**
* Generic form for adding a section checkbox to another form.
*
* @param &$form
* The form being altered.
* @param $access_id
* The access_id defined by the form.
* @param $type
* The type of entity being saved (term, vocabulary, menu_link, etc.)
*/
function workbench_access_edit_form_alter(&$form, $access_id, $type) {
$active = workbench_access_get_active_tree();
$form['workbench_access'] = array(
'#type' => 'checkbox',
'#title' => t('Workbench Access editorial section'),
'#default_value' => variable_get('workbench_access_auto_assign', 1) == 1 ? 1 : isset($active['active'][$access_id]),
'#disabled' => variable_get('workbench_access_auto_assign', 1),
'#description' => t('Enable this !type as an active editorial section.', array(
'!type' => str_replace('_', ' ', $type),
)),
'#weight' => 1,
);
$form['workbench_access_exists'] = array(
'#type' => 'value',
'#value' => isset($active['active'][$access_id]),
);
// If auto-assign is OFF, then add a submit handler.
// See workbench_access_taxonomy_term_insert().
if (!variable_get('workbench_access_auto_assign', 1)) {
$form['#submit'][] = 'workbench_access_' . $type . '_submit';
}
}
/**
* Submit callback for term forms.
*/
function workbench_access_term_submit($form, &$form_state) {
$values = $form_state['values'];
if (!isset($values['workbench_access'])) {
return;
}
$section = array(
'access_id' => $values['tid'],
'access_type' => 'taxonomy',
'access_scheme' => 'taxonomy',
'access_type_id' => $values['vocabulary_machine_name'],
);
workbench_access_edit_form_submit($values, $section, $values['name']);
}
/**
* Generic submit handler for adding sections to forms.
*
* @param $values
* The values passed through the form.
* @param $section
* The section defined by this item.
* @param $name
* The human-readable name of this item.
*/
function workbench_access_edit_form_submit($values, $section, $name) {
if (!empty($values['workbench_access'])) {
if (empty($values['workbench_access_exists'])) {
workbench_access_section_save($section);
drupal_set_message(t('Added %section to active editorial groups.', array(
'%section' => $name,
)));
}
}
else {
workbench_access_section_delete($section);
drupal_set_message(t('Deleted %section from active editorial groups.', array(
'%section' => $name,
)));
}
}
/**
* Implements hook_form_alter().
*
* Add a sections form.
*/
function workbench_access_form_menu_edit_menu_alter(&$form, &$form_state, $form_id) {
if (!user_access('administer workbench access') || variable_get('workbench_access') != 'menu') {
return;
}
$menu = $form['menu_name']['#default_value'];
$active = array_filter(variable_get('workbench_access_menu', array()));
if (!in_array($menu, $active)) {
// TODO: allow this to be made a category.
return;
}
$access_id = $menu;
workbench_access_edit_form_alter($form, $access_id, 'menu');
}
/**
* Submit callback for menu forms.
*/
function workbench_access_menu_submit($form, &$form_state) {
$values = $form_state['values'];
if (!isset($values['workbench_access'])) {
return;
}
$section = array(
'access_id' => $values['menu_name'],
'access_type' => 'menu',
'access_scheme' => 'menu',
'access_type_id' => $values['menu_name'],
);
workbench_access_edit_form_submit($values, $section, $values['title']);
}
/**
* Implements hook_form_alter().
*
* Add a sections form.
*/
function workbench_access_form_menu_edit_item_alter(&$form, &$form_state, $form_id) {
if (!user_access('administer workbench access') || variable_get('workbench_access') != 'menu') {
return;
}
$menu = $form['original_item']['#value']['menu_name'];
$active = array_filter(variable_get('workbench_access_menu', array()));
if (!in_array($menu, $active)) {
return;
}
$access_id = $form['mlid']['#value'];
workbench_access_edit_form_alter($form, $access_id, 'menu_link');
}
/**
* Submit callback for menu_link forms.
*/
function workbench_access_menu_link_submit($form, &$form_state) {
$values = $form_state['values'];
if (!isset($values['workbench_access'])) {
return;
}
$section = array(
'access_id' => $values['mlid'],
'access_type' => 'menu',
'access_scheme' => 'menu',
'access_type_id' => $values['menu_name'],
);
workbench_access_edit_form_submit($values, $section, $values['link_title']);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add access rules to node types.
*/
function workbench_access_form_node_type_form_alter(&$form, $form_state) {
$form['workflow']['workbench_access_node_type'] = array(
'#title' => t('Enforce Workbench Access control'),
'#type' => 'checkbox',
'#default_value' => variable_get('workbench_access_node_type_' . $form['#node_type']->type, 1),
'#description' => t('Use Workbench Access to enforce editorial control to all content of this type.'),
);
}
/**
* Implements hook_block_view_workbench_block_alter().
*
* Show the editorial status of this node.
*/
function workbench_access_workbench_block() {
// Add editing information to this page (if it's a node).
if ($node = menu_get_object()) {
if (user_access('view workbench access information')) {
if (!variable_get('workbench_access_node_type_' . $node->type, 1)) {
return array(
t('@message_label: <em>@type pages are not under access control</em>', array(
'@message_label' => variable_get('workbench_access_label', 'Section'),
'@type' => node_type_get_name($node->type),
)),
);
}
elseif (empty($node->workbench_access)) {
return array(
t('@message_label: <em>Unassigned</em>', array(
'@message_label' => variable_get('workbench_access_label', 'Section'),
)),
);
}
else {
$names = array();
$access_type = variable_get('workbench_access');
foreach ($node->workbench_access as $access_id) {
$section = workbench_access_load($access_type, $access_id);
$names[] = $section['name'];
}
return array(
t('@message_label: %section', array(
'@message_label' => variable_get('workbench_access_label', 'Section'),
'%section' => implode(', ', $names),
)),
);
}
}
}
}
/**
* Implements hook_node_operations().
*/
function workbench_access_node_operations($form = array(), $form_state = array()) {
if (!user_access('batch update workbench access')) {
return;
}
$active = workbench_access_get_active_tree();
if (!$active) {
return;
}
$tree = $active['tree'];
workbench_access_build_tree($tree);
$options = workbench_access_options($tree, $active['active']);
if (empty($options)) {
return;
}
$operations = array(
'workbench_access' => array(
'label' => t('Editorial section'),
),
);
foreach ($options as $key => $value) {
$operations['workbench_access-' . $key] = array(
'label' => $value,
'callback' => 'workbench_access_mass_update',
'callback arguments' => array(
'access_id' => $key,
'access_scheme' => $active['access_scheme'],
),
);
}
return $operations;
}
/**
* Mass update callback for hook_node_operations().
*
* @param $nodes
* The nodes to be edited.
* @param $access_id
* The new access id to set.
* @param $access_scheme
* An array representing the active access scheme.
*/
function workbench_access_mass_update($nodes, $access_id, $access_scheme) {
foreach ($nodes as $nid) {
$data = array(
'nid' => $nid,
'workbench_access' => $access_id,
);
$node = (object) $data;
workbench_access_node_update($node);
}
drupal_set_message(t('Editorial sections have been updated.'));
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Helper function to nest the node operations form properly.
*/
function workbench_access_form_node_admin_content_alter(&$form, &$form_state) {
if (empty($form['admin']['options']['operation']['#options'])) {
return;
}
$options =& $form['admin']['options']['operation']['#options'];
foreach ($form['admin']['options']['operation']['#options'] as $key => $value) {
if ($key == 'workbench_access') {
unset($options[$key]);
$item = t('Set editorial section');
$options[$item] = array();
}
if (substr($key, 0, 17) == 'workbench_access-') {
unset($options[$key]);
$options[$item][$key] = $value;
}
}
}
/**
* Fetch all user roles with the a specific permission.
*
* This is a wrapper for user_roles() since it may exclude user roles that have
* a permission assigned to the 'authenticated user' role.
*
* @param $permission
* The perimission to return active roles.
*
* @return
* An array of allowed roles, in the format rid => name.
*/
function workbench_access_get_roles($permission) {
$roles =& drupal_static(__FUNCTION__, array());
if (!isset($roles[$permission])) {
// Build the list of user roles that can be assigned workbench access.
$roles[$permission] = user_roles(FALSE, $permission);
if (isset($roles[$permission][DRUPAL_AUTHENTICATED_RID])) {
// Because the permission may only be granted for the 'authenticated user'
// role, manually add in all non-anonymous roles in that case.
$roles[$permission] += user_roles(TRUE);
}
}
return $roles[$permission];
}
/**
* Tests content types for proper field configuration.
*
* @return
* An array of node types that are _not_ configured properly.
*/
function workbench_access_check_access_fields() {
$broken = array();
$active = workbench_access_get_active_tree();
if (variable_get('workbench_access_custom_form', 1) || !empty($active['access_scheme']['form_field'])) {
return $broken;
}
$types = node_type_get_types();
foreach ($types as $type => $data) {
$fields = workbench_access_get_assigned_fields($type);
$check = FALSE;
if (variable_get('workbench_access_node_type_' . $type, 1)) {
foreach ($fields as $field) {
if (!empty($field['instance_info']['workbench_access_field'])) {
$check = TRUE;
}
}
}
else {
$check = TRUE;
}
if (!$check) {
$broken[] = $type;
}
}
return $broken;
}
/**
* Load callback to load information about an access scheme.
*/
function workbench_access_access_scheme_load($scheme) {
workbench_access_load_include();
$info = module_invoke_all('workbench_access_info');
return isset($info[$scheme]) ? $info[$scheme] : FALSE;
}
/**
* Finds fields associated with a content type.
*
* @param $type
* The content type machine name.
* @param $all
* Boolean flag to return all possible fields for this content type. If set
* to FALSE, then only configured access fields are returned.
*
* @see workbench_access_get_available_fields().
*
* @return
* An array of field data that matches the current access scheme.
*/
function workbench_access_get_assigned_fields($type, $all = FALSE) {
$matches = array();
$fields = field_info_instances('node', $type);
$scheme = variable_get('workbench_access');
$settings = variable_get('workbench_access_' . $scheme, array());
foreach ($fields as $field => $info) {
$data = field_info_field($field);
if ($data['module'] == $scheme) {
foreach ($data['settings']['allowed_values'] as $key => $value) {
// Currently, only works for taxonomy.
$instance = field_info_instance('node', $field, $type);
if (!empty($settings[$value['vocabulary']]) && ($all || !empty($instance['workbench_access_field']))) {
$data['instance_info'] = $info;
$matches[$field] = $data;
}
}
}
}
return $matches;
}
/**
* Finds fields that may be associated with a content type.
*
* @param $type
* The content type machine name.
*
* @return
* An array of field data that matches the current access scheme.
*/
function workbench_access_get_available_fields($type) {
$all = TRUE;
return workbench_access_get_assigned_fields($type, $all);
}
/**
* Determines is the current path is an autocomplete request.
*
* @return
* TRUE or FALSE, indicating that we should alter the query.
*/
function workbench_access_is_active_autocomplete() {
$result =& drupal_static(__FUNCTION__);
if (isset($result)) {
return $result;
}
// Default to FALSE.
$result = FALSE;
// Are we running an autocomplete check?
// For some reason, calling menu_get_item() on a taxonomy page causes
// infinite recursion. So we have to simulate that function.
// See http://drupal.org/node/1187424#comment-5194746 for the report.
$page_arguments = array();
for ($i = 0; $i < 4; $i++) {
$page_arguments[$i] = arg($i);
}
$path = $page_arguments[0] . '/' . $page_arguments[1];
// If the path is wrong, or the arguments are not provided, then we cannot
// safely alter the term query.
if ($path != 'workbench_access/taxonomy_autocomplete' || empty($page_arguments[2]) || empty($page_arguments[3])) {
return $result;
}
// Check the field.
$field = $page_arguments[2];
$node_type = $page_arguments[3];
$fields = workbench_access_get_assigned_fields($node_type);
if (isset($fields[$field])) {
$result = TRUE;
}
return $result;
}
/**
* Given a change in source tree keys, update affected records.
*
* @param string $find
* The id to find.
* @param string $replace
* The id to replace.
* @param string $scheme
* The access scheme to check. Optional.
*/
function workbench_access_update_records($find, $replace, $scheme = NULL) {
if (empty($scheme)) {
$scheme = variable_get('workbench_access');
}
// Update the base table.
db_update('workbench_access')
->fields(array(
'access_id' => $replace,
))
->condition('access_scheme', $scheme)
->condition('access_id', $find)
->execute();
db_update('workbench_access')
->fields(array(
'access_type_id' => $replace,
))
->condition('access_scheme', $scheme)
->condition('access_type_id', $find)
->execute();
// Update each sub-table.
foreach (array(
'node',
'role',
'user',
) as $table) {
db_update('workbench_access_' . $table)
->fields(array(
'access_id' => $replace,
))
->condition('access_id', $find)
->condition('access_scheme', $scheme)
->execute();
}
// Update the section variable.
$active = variable_get('workbench_access_' . $scheme, array());
foreach ($active as $key => $value) {
if ($key == $find) {
$new[$replace] = empty($value) ? 0 : $replace;
}
else {
$new[$key] = $value;
}
}
variable_set('workbench_access_' . $scheme, $new);
drupal_set_message(t('Editorial sections have been updated.'));
}
Functions
Name | Description |
---|---|
workbench_access_access_scheme_load | Load callback to load information about an access scheme. |
workbench_access_active_options | Build an array of form options for the currently active workbench access tree. |
workbench_access_admin_paths | Implements hook_admin_paths(). |
workbench_access_allow_form_alter | Check to see if the module is configured and we can run our form alter. |
workbench_access_alter_form | Call the alter hook for the active schema. |
workbench_access_assign_user | Access callback for user sections tab. |
workbench_access_autocomplete_validate | Custom form validation handler. |
workbench_access_build_tree | Build a hierarchy defined by an access control schema. |
workbench_access_check | Check to see if a user can access the node for this operation. |
workbench_access_check_access_fields | Tests content types for proper field configuration. |
workbench_access_configuration | Defines configuration options for the default access scheme. |
workbench_access_configuration_needed_message | Return the text directing admins to Workbench Access configuration. |
workbench_access_default_form_element | Find and alter the native form element for node editing. |
workbench_access_edit_form_alter | Generic form for adding a section checkbox to another form. |
workbench_access_edit_form_submit | Generic submit handler for adding sections to forms. |
workbench_access_field_extra_fields | Implements hook_field_extra_fields() |
workbench_access_find_form_elements | Find form elements that control access. |
workbench_access_form_alter | Implements hook_form_alter(). |
workbench_access_form_menu_edit_item_alter | Implements hook_form_alter(). |
workbench_access_form_menu_edit_menu_alter | Implements hook_form_alter(). |
workbench_access_form_node_admin_content_alter | Implements hook_form_FORM_ID_alter(). |
workbench_access_form_node_type_form_alter | Implements hook_form_FORM_ID_alter(). |
workbench_access_form_taxonomy_form_term_alter | Implements hook_form_FORM_ID_alter(). |
workbench_access_form_taxonomy_form_vocabulary_alter | Implements hook_form_FORM_ID_alter(). |
workbench_access_form_taxonomy_overview_terms_alter | Implements hook_form_FORM_ID_alter(). |
workbench_access_form_taxonomy_overview_terms_submit | Updates taxonomy access tree on overview save. |
workbench_access_form_views_exposed_form_alter | Ensure the proper action for our content form. |
workbench_access_get_access_tree | Get the access hierarchy for a user. |
workbench_access_get_active_tree | Load the active tree. |
workbench_access_get_assigned_fields | Finds fields associated with a content type. |
workbench_access_get_available_fields | Finds fields that may be associated with a content type. |
workbench_access_get_ids_by_scheme | Given an access scheme, return all active sections. |
workbench_access_get_node_tree | Given a node, return its access rules. |
workbench_access_get_roles | Fetch all user roles with the a specific permission. |
workbench_access_get_user_tree | Build an access tree for a user account. |
workbench_access_hook_info | Implements hook_hook_info(). |
workbench_access_init | Implements hook_init(). |
workbench_access_in_tree | Check to see if an access check is in a given hierarchy. |
workbench_access_is_active_autocomplete | Determines is the current path is an autocomplete request. |
workbench_access_is_active_id | Return information for an access id. |
workbench_access_load | Return information for an access id. |
workbench_access_load_access_info | Given an access id and scheme, load the object with its data. |
workbench_access_load_include | Load a Workbench Access module file, or all files. |
workbench_access_mass_update | Mass update callback for hook_node_operations(). |
workbench_access_menu | Implements hook_menu(). |
workbench_access_menu_alter | Implements hook_menu_alter(). |
workbench_access_menu_link_submit | Submit callback for menu_link forms. |
workbench_access_menu_submit | Submit callback for menu forms. |
workbench_access_node_access | Implements hook_node_access(). |
workbench_access_node_delete | Implements hook_node_delete(). |
workbench_access_node_form_element | Provides a module-specific form for access control. |
workbench_access_node_insert | Implements hook_node_insert(). |
workbench_access_node_load | Implements hook_node_load(). |
workbench_access_node_operations | Implements hook_node_operations(). |
workbench_access_node_presave | Implements hook_node_presave(). |
workbench_access_node_update | Implements hook_node_update(). |
workbench_access_options | Build form options from a tree. |
workbench_access_permission | Implements hook_permission(). |
workbench_access_prepare_field_save | Prepares a node for saving in the event we did not come from a form. |
workbench_access_rebuild_scheme | Rebuild the section access tables. |
workbench_access_rebuild_user | Rebuild the user access tables. |
workbench_access_reset_tree | Reset tree data stored in statics. |
workbench_access_role_section_delete | Deletes an access rule from the {workbench_access_user} table. |
workbench_access_role_section_save | Save a role access record and notify other modules. |
workbench_access_sections_needed_message | Return text directing admins to section configuration. |
workbench_access_section_delete | Delete an access section from the {workbench_access} table. |
workbench_access_section_save | Save an access section to the {workbench_access} table. |
workbench_access_taxonomy_page_access | Custom access callback for taxonomy/term/%. |
workbench_access_term_submit | Submit callback for term forms. |
workbench_access_theme | Implements hook_theme(). |
workbench_access_tree | Return the access tree for a rule set. |
workbench_access_update_records | Given a change in source tree keys, update affected records. |
workbench_access_user_delete | Implements hook_user_delete(). |
workbench_access_user_load | Implements hook_user_load(). |
workbench_access_user_load_data | Load the access data for this user. |
workbench_access_user_role_delete | Implements hook_user_role_delete(). |
workbench_access_user_section_delete | Deletes an access rule from the {workbench_access_user} table. |
workbench_access_user_section_save | Save a user access record and notify other modules. |
workbench_access_views_access | Access callback for content user tab. |
workbench_access_views_api | Implements hook_views_api(). |
workbench_access_views_default_views | Implements hook_views_default_views(). |
workbench_access_vocabulary_submit | Submit callback for vocabulary forms. |
workbench_access_workbench_block | Implements hook_block_view_workbench_block_alter(). |