workflow_fields.module in Workflow Fields 7
Same filename and directory in other branches
This module adds to workflow.module the ability to specify, for each state, which node fields should be visible and/or editable. It is a useful feature when workflows demand that certain information be hidden or read-only to certain roles.
File
workflow_fields.moduleView source
<?php
/**
* @file
* This module adds to workflow.module the ability to specify, for each state,
* which node fields should be visible and/or editable. It is a useful feature
* when workflows demand that certain information be hidden or read-only to
* certain roles.
*/
define("WORKFLOW_FIELDS_AUTHOR_ROLE", -1);
/**
* Implements hook_help().
*/
function workflow_fields_help($section) {
switch ($section) {
case 'admin/modules#description':
return t('Add per-state field settings to workflows.');
}
}
/**
* Implements hook_permission().
*/
function workflow_fields_permission() {
return array(
'bypass field restrictions' => array(
'title' => t('Bypass Field Restrictions'),
'description' => t('Ignore field restrictions configured with Workflow Fields module.'),
),
);
}
/**
* Administration form that renders a table listing of the fields for workflow's content type.
*/
function workflow_fields_form_workflow_admin_ui_form($form, &$form_state, $workflow, $sid) {
$wid = $workflow->wid;
$state = workflow_get_workflow_states_by_sid($sid);
// Make sure the state actually belongs to the workflow.
if ($state && $state->wid == $workflow->wid) {
// Create breadcrumbs.
workflow_admin_ui_breadcrumbs($workflow);
drupal_set_title(t('Fields in state:') . ' ' . $state->state);
// Get all types that are mapped to this workflow.
$types = workflow_get_workflow_type_map_by_wid($wid);
if (empty($types)) {
drupal_set_message(t('To set fields visibility you have to map the workflow to at least a content type.'));
$form['description'] = array(
'#type' => 'markup',
'#markup' => t("There isn't a content type mapped to this workflow."),
);
return $form;
}
// Form fields to identify current workflow and state.
$form['wid'] = array(
'#type' => 'value',
'#value' => $wid,
);
$form['sid'] = array(
'#type' => 'value',
'#value' => $sid,
);
$form['fields'] = array(
'#theme' => 'workflow_fields_state',
'#tree' => TRUE,
'#types' => array(
'#type' => 'value',
'#value' => array(),
),
'#weight' => 98,
);
// Gather role ids.
$rids = array(
WORKFLOW_FIELDS_AUTHOR_ROLE => t('author'),
) + user_roles();
$userrefs = array();
$results = db_query("SELECT type, name, visible_roles, editable_roles\n FROM {workflow_fields} WHERE sid = :sid", array(
':sid' => $sid,
));
$permissions = array();
foreach ($results as $row) {
$permissions[$row->type][$row->name] = array(
'visible_roles' => explode(',', $row->visible_roles),
'editable_roles' => explode(',', $row->editable_roles),
);
}
// For each type, find out all the fields.
foreach ($types as $values) {
$type = $values->type;
$form['fields']['#types']['#value'][] = $type;
// For each field, add checkboxes for visible and editable for all roles.
$fields = _workflow_fields_get_fields($type);
uasort($fields, "_workflow_fields_sort_fields_by_widget_weight");
foreach ($fields as $field_name => $field) {
if (!isset($permissions[$type][$field_name])) {
$perms = array(
'visible_roles' => array(
WORKFLOW_FIELDS_AUTHOR_ROLE,
),
'editable_roles' => array(
WORKFLOW_FIELDS_AUTHOR_ROLE,
),
);
}
else {
$perms = $permissions[$type][$field_name];
}
$form['fields'][$type][$field_name] = array(
'#field_label' => $field['label'],
'#weight' => isset($field['weight']) ? $field['weight'] : -100,
);
$form['fields'][$type][$field_name]['visible'] = array(
'#type' => 'checkboxes',
'#options' => $rids,
'#default_value' => $perms['visible_roles'],
);
$form['fields'][$type][$field_name]['editable'] = array(
'#type' => 'checkboxes',
'#options' => $rids,
'#default_value' => $perms['editable_roles'],
);
// Selection shortcuts.
$type_shortcuts = array();
$type_css = str_replace('_', '-', $type);
$field_name_css = str_replace('_', '-', $field['field_name']);
$group = $type_css . '-' . $field_name_css;
$type_shortcuts[$group]['all'] = array(
'title' => t('all'),
'href' => "javascript:Drupal.workflowFields.select('all', '{$group}');",
);
$type_shortcuts[$group]['none'] = array(
'title' => t('none'),
'href' => "javascript:Drupal.workflowFields.select('none', '{$group}');",
);
$type_shortcuts[$group]['toggle'] = array(
'title' => t('toggle'),
'href' => "javascript:Drupal.workflowFields.select('toggle', '{$group}');",
);
$visible_group = $group . '-visible';
$type_shortcuts[$group]['visible'] = array(
'title' => t('visible'),
'href' => "javascript:Drupal.workflowFields.select('toggle', '{$visible_group}');",
);
$editable_group = $group . '-editable';
$type_shortcuts[$group]['editable'] = array(
'title' => t('editable'),
'href' => "javascript:Drupal.workflowFields.select('toggle', '{$editable_group}');",
);
$form['fields'][$type][$field['field_name']]['shortcuts'] = array(
'#type' => 'value',
'#value' => $type_shortcuts,
'#theme' => 'workflow_fields_shortcuts',
'#render title' => FALSE,
);
}
}
// Selection shortcuts.
foreach (array(
'all',
'visible',
'editable',
) as $group) {
$shortcuts[$group]['all'] = array(
'title' => t($group),
'href' => "javascript:Drupal.workflowFields.select('all', '{$group}');",
);
$shortcuts[$group]['none'] = array(
'title' => t('none'),
'href' => "javascript:Drupal.workflowFields.select('none', '{$group}');",
);
$shortcuts[$group]['toggle'] = array(
'title' => t('toggle'),
'href' => "javascript:Drupal.workflowFields.select('toggle', '{$group}');",
);
foreach ($rids as $rid => $rname) {
$shortcuts[$group][$rid] = array(
'title' => t('role:') . ' ' . $rname,
'href' => "javascript:Drupal.workflowFields.select('{$rid}', '{$group}');",
);
}
if ($userrefs) {
foreach ($userrefs as $ref => $refname) {
$ref_css = str_replace('_', '-', $ref);
$shortcuts[$group][$ref] = array(
'title' => t('user:') . ' ' . $refname,
'href' => "javascript:Drupal.workflowFields.select('{$ref_css}', '{$group}');",
);
}
}
}
$form['shortcuts'] = array(
'#type' => 'value',
'#value' => $shortcuts,
'#theme' => 'workflow_fields_shortcuts',
'#render title' => TRUE,
'#weight' => 97,
);
$form['submit'] = array(
'#type' => 'submit',
'#weight' => 99,
'#value' => t('Save configuration'),
);
return $form;
}
}
/**
* Submit function for workflow state form.
*/
function workflow_fields_form_workflow_admin_ui_form_submit($form, &$form_state) {
$wid = $form_state['values']['wid'];
if (isset($form_state['values']['fields'])) {
$sid = $form_state['values']['sid'];
db_delete('workflow_fields')
->condition('sid', $sid)
->execute();
$insert = db_insert('workflow_fields')
->fields(array(
'sid',
'name',
'type',
'visible_roles',
'editable_roles',
));
foreach ($form_state['values']['fields'] as $type => $fields) {
foreach ($fields as $name => $field) {
$visible = array_filter($field['visible']);
$editable = array_filter($field['editable']);
$insert
->values(array(
'sid' => $sid,
'name' => $name,
'type' => $type,
'visible_roles' => implode(',', $visible),
'editable_roles' => implode(',', $editable),
));
}
}
$insert
->execute();
$state = workflow_get_workflow_states_by_sid($sid);
drupal_set_message(t('Changes on fields for the state <em>@state</em> made successfully.', array(
'@state' => $state->state,
)));
}
$form_state['redirect'] = 'admin/config/workflow/workflow/' . $wid;
}
/**
* Helper function for sorting fields by widget weight.
*/
function _workflow_fields_sort_fields_by_widget_weight($a, $b) {
if (!isset($a['widget']['weight'])) {
return -1;
}
if (!isset($b['widget']['weight'])) {
return 1;
}
return $a['widget']['weight'] - $b['widget']['weight'];
}
/**
* Implements hook_theme().
*/
function workflow_fields_theme() {
return array(
'workflow_fields_state' => array(
'render element' => 'element',
),
'workflow_fields_shortcuts' => array(
'render element' => 'element',
),
);
}
/**
* Theme function for workflow state form.
*/
function theme_workflow_fields_state($vars) {
$element =& $vars['element'];
$header = array(
t('Content type'),
t('Field name'),
t('Visible'),
t('Editable'),
t('Select'),
);
$rows = array();
foreach (element_children($element) as $type) {
$type_info = node_type_get_type($type);
foreach (element_children($element[$type]) as $field_name) {
$field_label = $element[$type][$field_name]['#field_label'];
$rows[] = array(
$type_info->name . '<br/>(' . $type . ')',
$field_label . '<br/>(' . $field_name . ')',
drupal_render($element[$type][$field_name]['visible']),
drupal_render($element[$type][$field_name]['editable']),
drupal_render($element[$type][$field_name]['shortcuts']),
);
}
}
$attributes = array(
'class' => array(
'workflow-fields-table',
),
);
$output = theme('table', compact('header', 'rows', 'attributes')) . '<p />';
return $output;
}
/**
* Theme function for field shortcuts on workflow state form.
*/
function theme_workflow_fields_shortcuts($vars) {
$element =& $vars['element'];
drupal_add_js(drupal_get_path('module', 'workflow_fields') . '/workflow_fields.js');
$output = '<div class="form-item">';
if ($element['#render title']) {
$output .= '<label>' . t('Select:') . '</label>';
}
foreach ($element['#value'] as $group) {
$links = array();
foreach ($group as $key => $link) {
$links[] = "<a href=\"{$link['href']}\">{$link['title']}</a>";
}
$output .= implode(', ', $links) . '<br />';
}
$output .= '</div>';
return $output;
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function workflow_fields_form_node_form_alter(&$form, $form_state, $form_id) {
$node =& $form['#node'];
$sid = workflow_node_current_state($node);
// Check for visible/editable flags.
$fields = _workflow_fields_get_fields($node->type);
$form['sid'] = array(
'#type' => 'value',
'#value' => $sid,
);
list($visibles, $editables) = _workflow_fields_compute_permissions($sid, $node->type, $node, 'edit');
foreach ($visibles as $field_name => $visible) {
$editable = $editables[$field_name];
if (!isset($fields[$field_name])) {
continue;
}
else {
$field = $fields[$field_name];
}
// Find the path to the field in the form.
if (!empty($field['path'])) {
$path = $field['path'];
if (!_workflow_fields_array_path_exists($form, $path)) {
$path = FALSE;
}
}
else {
$path = _workflow_fields_array_key_path($field_name, $form, array(
'#group_children',
));
}
if ($path === FALSE) {
continue;
}
if ($editable && $visible) {
if (isset($field['widget']['weight'])) {
$element =& _workflow_fields_array_path($form, $path);
$element['#weight'] = $field['widget']['weight'];
}
continue;
}
// Process the field.
if (isset($field['process'])) {
// Does the field need special handling?
$param_arr = array(
&$form,
$field,
$path,
$node,
$visible,
$editable,
isset($field['process arguments']) ? (array) $field['process arguments'] : array(),
);
call_user_func_array($field['process'], $param_arr);
}
else {
// Standard handling.
_workflow_fields_process_field($form, $field, $path, $node, $visible, $editable);
}
}
}
function _workflow_fields_process_field(&$form, $field, $path, $node, $visible, $editable) {
$element =& _workflow_fields_array_path($form, $path);
$parent =& _workflow_fields_array_path($form, array_slice($path, 0, -1));
if ($visible) {
// Show the read-only version of the field.
if (!isset($field['extra'])) {
// Normal field.
$renderable = field_view_field('node', $node, $field['field_name'], 'default');
$parent[$field['field_name'] . '_view'] = array(
'#type' => 'item',
'#title' => isset($element['#title']) ? $element['#title'] : '',
'#markup' => drupal_render($renderable),
'#weight' => isset($field['widget']['weight']) ? $field['widget']['weight'] : 0,
);
if (isset($form['#group_children'][$field['field_name']])) {
$form['#group_children'][$field['field_name'] . '_view'] = $form['#group_children'][$field['field_name']];
}
}
// If non field, visible but not editable... you should define process.
if (isset($parent['#type']) && $parent['#type'] == 'fieldset') {
$parent['#access'] = TRUE;
// #access of parent can be set to FALSE when all fields are disabled (by hook_field_access)
}
}
// Now hide the field.
$element['#access'] = FALSE;
}
function _workflow_fields_show_title(&$form, $field, $path, $node, $visible, $editable, $args) {
$element =& _workflow_fields_array_path($form, $path);
$parent =& _workflow_fields_array_path($form, array_slice($path, 0, -1));
$element['#access'] = FALSE;
$parent['title_view'] = array(
'#type' => 'item',
'#title' => isset($element['#title']) ? $element['#title'] : '',
'#markup' => check_plain($node->title),
'#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
);
}
/**
* Implements hook_workflow_fields().
*/
function workflow_fields_workflow_fields($type) {
$content = node_type_get_type($type);
$fields = array();
if ($content->has_title) {
$fields['title'] = array(
'label' => $content->title_label,
'process' => '_workflow_fields_show_title',
);
}
return $fields;
}
function _workflow_fields_get_fields($type, $reset = FALSE) {
static $fields = array();
if (!isset($fields[$type]) || $reset) {
$fields[$type] = module_invoke_all('workflow_fields', $type);
foreach ($fields[$type] as $key => $field) {
$fields[$type][$key]['field_name'] = $key;
$fields[$type][$key]['widget']['label'] = $field['label'];
$fields[$type][$key]['extra'] = TRUE;
}
drupal_alter('workflow_fields', $fields, $type);
$fields[$type] += field_info_instances('node', $type);
}
return $fields[$type];
}
/**
* Implements hook_workflow_operations().
*/
function workflow_fields_workflow_operations($mode, $workflow, $state = NULL) {
if ('state' == $mode && $workflow && $state->sid > 0) {
$state_name = method_exists($state, 'getName') ? $state
->getName() : $state->state;
return array(
'workflow_fields_manage' => array(
'title' => t('Workflow fields'),
'href' => 'admin/config/workflow/workflow/fields_visibility/' . $workflow->wid . '/' . $state->sid,
'attributes' => array(
'title' => t('Edit the @state fields visibility', array(
'@state' => $state_name,
)),
'alt' => t('Edit the @state fields visibility', array(
'@state' => $state_name,
)),
),
),
);
}
}
/**
* Implements hook_menu().
*/
function workflow_fields_menu() {
$items['admin/config/workflow/workflow_fields'] = array(
'title' => 'Workflow Fields settings',
'description' => 'Global settings for the behaviour of Workflow Fields.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workflow_fields_settings',
),
'access arguments' => array(
'administer workflow',
),
);
$items['admin/config/workflow/workflow/fields_visibility/%workflow/%'] = array(
'title' => 'Manage workflow fields',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'workflow_fields_form_workflow_admin_ui_form',
5,
6,
),
'access arguments' => array(
'administer workflow',
),
'type' => MENU_CALLBACK,
);
return $items;
}
function workflow_fields_settings() {
$form['workflow_fields_edit_invisible_when_editing'] = array(
'#type' => 'checkbox',
'#title' => t('Edit fields marked as "not visible" but "editable" when editing.'),
'#default_value' => variable_get('workflow_fields_edit_invisible_when_editing', TRUE),
);
$form['workflow_fields_hide_read_only_when_editing'] = array(
'#type' => 'checkbox',
'#title' => t('Hide fields marked as "not editable" when editing.'),
'#default_value' => variable_get('workflow_fields_hide_read_only_when_editing', FALSE),
);
return system_settings_form($form);
}
/**
* Return an array of rids for the current user, given a node.
*
* Include WORKFLOW_FIELDS_AUTHOR_ROLE if the current user is the node author.
*/
function _workflow_fields_compute_groups($node = NULL) {
global $user;
$groups = array_keys($user->roles);
if (isset($node) && isset($node->uid)) {
if ($user->uid == $node->uid) {
$groups[] = WORKFLOW_FIELDS_AUTHOR_ROLE;
}
}
return $groups;
}
function _workflow_fields_compute_permissions($sid, $type, $node, $op) {
$visibles = array();
$editables = array();
$groups = _workflow_fields_compute_groups($node);
$result = db_query("SELECT name, visible_roles, editable_roles\n FROM {workflow_fields} WHERE sid = :sid AND type = :type", array(
':sid' => $sid,
':type' => $type,
));
foreach ($result as $row) {
$visible_roles = explode(',', $row->visible_roles);
$editable_roles = explode(',', $row->editable_roles);
$visibles[$row->name] = user_access('bypass field restrictions') || (bool) array_intersect($groups, $visible_roles);
$editables[$row->name] = user_access('bypass field restrictions') || (bool) array_intersect($groups, $editable_roles);
if ($op == 'edit') {
if (variable_get('workflow_fields_hide_read_only_when_editing', FALSE) && !$editables[$row->name]) {
$visibles[$row->name] = FALSE;
}
if (variable_get('workflow_fields_edit_invisible_when_editing', TRUE) && $editables[$row->name]) {
$visibles[$row->name] = TRUE;
}
if (!$visibles[$row->name]) {
$editables[$row->name] = FALSE;
}
}
}
return array(
$visibles,
$editables,
);
}
function _workflow_fields_array_key_path($needle, $haystack, $forbidden = array(), $path = array()) {
foreach ($haystack as $key => $val) {
if (in_array($key, $forbidden)) {
continue;
}
if (is_array($val) && is_array($sub = _workflow_fields_array_key_path($needle, $val, $forbidden, array_merge($path, (array) $key)))) {
return $sub;
}
elseif ($key === $needle) {
return array_merge($path, (array) $key);
}
}
return FALSE;
}
function &_workflow_fields_array_path(&$array, $path) {
$offset =& $array;
if ($path) {
foreach ($path as $index) {
$offset =& $offset[$index];
}
}
return $offset;
}
function _workflow_fields_array_path_exists($array, $path) {
$offset =& $array;
if ($path) {
foreach ($path as $index) {
if (!array_key_exists($index, $offset)) {
return FALSE;
}
$offset =& $offset[$index];
}
}
return TRUE;
}
/**
* Implements hook_field_access().
*/
function workflow_fields_field_access($op, $field, $entity_type, $node, $account) {
if ($entity_type == 'node' || !$node) {
$sid = NULL;
$workflow_fields = array(
'',
);
// Workflow 2+
if (function_exists('_workflow_info_fields')) {
$workflow_fields = _workflow_info_fields($node, $entity_type);
if (0 === count($workflow_fields)) {
return TRUE;
}
}
// If there is one workflow that gives access to the field, access is granted
foreach ($workflow_fields as $workflow_field_name => $workflow_field_info) {
$sid = workflow_node_current_state($node, 'node', $workflow_field_name);
if (FALSE === $sid) {
$type_map = workflow_get_workflow_type_map_by_type($node->type);
if (!$type_map) {
return;
}
// Workflow 1.x
if (function_exists('workflow_get_creation_state_by_wid')) {
$sid = workflow_get_creation_state_by_wid($type_map->wid);
}
else {
$workflow = workflow_load($type_map->wid);
$sid = $workflow
->getCreationSid();
}
}
// Check for visible/editable flags.
list($visibles, $editables) = _workflow_fields_compute_permissions($sid, $node->type, $node, $op);
if (isset($visibles[$field['field_name']])) {
$is_accessible = $op == 'view' ? $visibles[$field['field_name']] : $visibles[$field['field_name']] && $editables[$field['field_name']];
if ($is_accessible) {
return TRUE;
}
}
}
return FALSE;
}
else {
return TRUE;
}
}
Functions
Constants
Name![]() |
Description |
---|---|
WORKFLOW_FIELDS_AUTHOR_ROLE | @file This module adds to workflow.module the ability to specify, for each state, which node fields should be visible and/or editable. It is a useful feature when workflows demand that certain information be hidden or read-only to certain roles. |