You are here

workflow_fields.module in Workflow Fields 5

Same filename and directory in other branches
  1. 6 workflow_fields.module
  2. 7 workflow_fields.module

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.module
View 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("FIELD_ROLE_AUTHOR", -1);
define("FIELD_ROLE_USERREFERENCE", -2);

/**
 * Implementation of hook_help().
 */
function workflow_fields_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Add per-state CCK field settings to workflows. <em>Note: Requires both workflow.module and content.module</em>.');
  }
}

/**
 * Implementation of hook_form_alter().
 * Hook on both any CCK node form and on the workflow state form.
 *
 * @param object &$node
 * @return array
 */
function workflow_fields_form_alter($form_id, &$form) {
  if (isset($form['type']) && $form['type']['#value'] . '_node_form' == $form_id) {
    workflow_fields_node_form_alter($form_id, $form);
  }
  elseif ('workflow_state_add_form' == $form_id) {
    workflow_fields_state_form_alter($form_id, $form);
  }
}

/**
 * Alter the workflow state form.
 * Add a table listing the fields for workflow's content type.
 */
function workflow_fields_state_form_alter($form_id, &$form) {
  $wid = $form['wid']['#value'];
  $sid = $form['sid']['#value'];

  // Get all types that are mapped to this workflow.
  $types = db_query("SELECT type FROM {workflow_type_map} WHERE wid=%d", $wid);
  if (empty($types)) {
    return;
  }

  // Fix some fields if dealing with creation state.
  if (WORKFLOW_CREATION == db_result(db_query("SELECT sysid FROM {workflow_states} WHERE sid = %d", $sid))) {
    $form['sysid'] = array(
      '#type' => 'value',
      '#value' => WORKFLOW_CREATION,
    );
    $form['weight'] = array(
      '#type' => 'value',
      '#value' => WORKFLOW_CREATION_DEFAULT_WEIGHT,
    );
    $form['state'] = array(
      '#type' => 'value',
      '#value' => t('(creation)'),
    );
  }
  $form['#submit']['workflow_fields_state_form_submit'] = array();
  $form['fields']['#theme'] = 'workflow_fields_state';
  $form['fields']['#tree'] = TRUE;
  $form['fields']['types'] = array(
    '#type' => 'value',
    '#value' => array(),
  );

  // Gather role ids.
  $rids = array(
    FIELD_ROLE_AUTHOR => t('author'),
  );
  $result = db_query("SELECT r.rid, r.name FROM {role} r ORDER BY r.name");
  while ($obj = db_fetch_object($result)) {
    $rids[$obj->rid] = $obj->name;
  }

  // For each type, find out all the fields.
  while ($values = db_fetch_array($types)) {
    $type = $values['type'];
    if ($content = content_types($type)) {
      array_push($form['fields']['types']['#value'], $type);

      // Get all user reference fields.
      $userrefs = array();
      foreach ($content['fields'] as $userref) {
        if ($userref['type'] == 'userreference') {
          $userrefs[$userref['field_name']] = $userref['widget']['label'];
        }
      }

      // For each field, add checkboxes for visible and editable for all roles.
      $fields = module_invoke_all('workflow_fields', $type) + $content['fields'];
      foreach ($fields as $field) {
        $visible = array();
        $editable = array();
        $result = db_query("SELECT rid, visible, editable, visible_userref, editable_userref FROM {workflow_fields} WHERE sid=%d AND name='%s'", intval($sid), $field['field_name']);
        while ($access = db_fetch_array($result)) {
          if ($access['visible']) {
            $visible[] = $access['rid'];
          }
          if ($access['editable']) {
            $editable[] = $access['rid'];
          }
        }
        $form['fields'][$type][$field['field_name']]['visible'] = array(
          '#type' => 'checkboxes',
          '#options' => $rids,
          '#default_value' => $visible,
        );
        $form['fields'][$type][$field['field_name']]['editable'] = array(
          '#type' => 'checkboxes',
          '#options' => $rids,
          '#default_value' => $editable,
        );
        if (!empty($userrefs)) {
          $form['fields'][$type][$field['field_name']]['visible_ref'] = array(
            '#type' => 'checkbox',
            '#title' => t('user reference: '),
            '#default_value' => in_array(FIELD_ROLE_USERREFERENCE, $visible),
          );
          $form['fields'][$type][$field['field_name']]['visible_refs'] = array(
            '#type' => 'select',
            '#options' => $userrefs,
            '#default_value' => $access['visible_userref'],
          );
          $form['fields'][$type][$field['field_name']]['editable_ref'] = array(
            '#type' => 'checkbox',
            '#title' => t('user reference: '),
            '#default_value' => in_array(FIELD_ROLE_USERREFERENCE, $editable),
          );
          $form['fields'][$type][$field['field_name']]['editable_refs'] = array(
            '#type' => 'select',
            '#options' => $userrefs,
            '#default_value' => $access['editable_userref'],
          );
        }
      }
    }
    else {
      watchdog('workflow fields', t('The content type "%type" does not provide metadata information.', array(
        '%type' => $type,
      )));
    }
  }
  $form['submit']['#weight'] = 99;
}
function workflow_fields_workflow_fields($type) {
  $content = content_types($type);
  $fields = array();
  if ($content['has_title']) {
    $fields['title'] = array(
      'field_name' => 'title',
      'widget' => array(
        'label' => $content['title_label'],
      ),
    );
  }
  if ($content['has_body']) {
    $fields['body'] = array(
      'field_name' => 'body',
      'widget' => array(
        'label' => $content['body_label'],
      ),
    );
  }
  return $fields;
}
function theme_workflow_fields_state($form) {
  $header = array(
    t('Content type'),
    t('Field name'),
    t('Visible'),
    t('Editable'),
  );
  $rows = array();
  foreach ($form['types']['#value'] as $type) {
    $content = content_types($type);
    $fields = module_invoke_all('workflow_fields', $type) + $content['fields'];
    foreach ($fields as $field) {
      $rows[] = array(
        $type,
        $field['widget']['label'] . ' (' . $field['field_name'] . ')',
        drupal_render($form[$type][$field['field_name']]['visible']) . drupal_render($form[$type][$field['field_name']]['visible_ref']) . drupal_render($form[$type][$field['field_name']]['visible_refs']),
        drupal_render($form[$type][$field['field_name']]['editable']) . drupal_render($form[$type][$field['field_name']]['editable_ref']) . drupal_render($form[$type][$field['field_name']]['editable_refs']),
      );
    }
  }
  $output = theme('table', $header, $rows) . '<p />';
  return $output;
}
function workflow_fields_state_form_submit($form_id, $form_values) {
  if (isset($form_values['fields'])) {
    if (!isset($form_state['values']['sid'])) {
      $form_values['sid'] = db_result(db_query("SELECT sid FROM {workflow_states} WHERE wid=%d AND state='%s'", $form_values['wid'], $form_values['state']));
    }
    db_query("DELETE FROM {workflow_fields} WHERE sid = %d", intval($form_values['sid']));
    foreach ($form_values['fields'] as $type => $fields) {
      if ($type == 'types') {
        continue;
      }
      foreach ($fields as $key => $field) {
        foreach ($field['visible'] as $rid => $checked) {
          db_query("INSERT INTO {workflow_fields} (sid, rid, name, type, visible, editable) VALUES (%d, %d, '%s', '%s', %d, %d)", $form_values['sid'], $rid, $key, $type, (bool) $checked, (bool) $field['editable'][$rid]);
        }
        db_query("INSERT INTO {workflow_fields} (sid, rid, name, type, visible, editable, visible_userref, editable_userref) VALUES (%d, %d, '%s', '%s', %d, %d, '%s', '%s')", $form_values['sid'], FIELD_ROLE_USERREFERENCE, $key, $type, (bool) $field['visible_ref'], (bool) $field['editable_ref'], $field['visible_refs'], $field['editable_refs']);
      }
    }
  }
}

/**
 * Alter the node form by hiding/disabling fields depending on the workflow state.
 * To hide a field, just unset it from the form.
 * To disable a field, replace its form element by a view-only version (by calling the 'view' CCK function).
 */
function workflow_fields_node_form_alter($form_id, &$form) {
  $node = $form['#node'];
  $sid = workflow_node_current_state($node);
  if (!is_numeric($sid)) {
    $wid = db_result(db_query("SELECT wid FROM {workflow_type_map} WHERE type = '%s'", $form['type']['#value']));
    $sid = db_result(db_query("SELECT sid FROM {workflow_states} WHERE sysid = %d AND wid = %d", WORKFLOW_CREATION, $wid));
  }

  // Check for visible/editable flags.
  if ($content = content_types($node->type)) {
    $form['sid'] = array(
      '#type' => 'value',
      '#value' => $sid,
    );
    list($visibles, $editables) = _workflow_fields_get_fields($sid, $node->type, $node);
    if (!isset($visibles)) {
      return;
    }
    $fields = module_invoke_all('workflow_fields', $node->type) + $content['fields'];
    foreach ($visibles as $key => $visible) {
      $editable = $editables[$key];
      if ($visible && $editable) {
        continue;
      }
      if (!($element =& _workflow_fields_find_element($key, $form))) {
        watchdog('workflow fields', t('Could not find field "%name" while altering the form. Proceeding to delete it from database.', array(
          '%name' => $key,
        )));
        db_query("DELETE FROM {workflow_fields} WHERE type = '%s' AND name = '%s'", $node->type, $key);
        continue;
      }
      $field = $fields[$key];
      if (!$visible || variable_get('workflow_fields_hide_read_only_when_editing', FALSE)) {
        if (isset($content['fields'][$key])) {

          // CCK?
          $element['#prefix'] = '<div style="display: none">';
          $element['#suffix'] = '</div>';
        }
        else {
          $element['#type'] = 'value';
        }
      }
      elseif (!$editable) {
        if (isset($content['fields'][$key])) {

          // CCK?
          $weight = $element['#weight'];
          $node_field = isset($node->{$key}) ? $node->{$key} : array();
          $form[$key . '_view'] = array(
            '#type' => 'markup',
            '#value' => _workflow_fields_node_view($node, $field, $node_field),
            '#weight' => $weight,
          );

          // FIXME This is hackable. I could not figure out a way to cleanly remove the fields.
          $element['#prefix'] = '<div style="display: none">';
          $element['#suffix'] = '</div>';
        }
        else {
          $element['#disabled'] = TRUE;
        }
      }
      if ($field['type'] == 'file') {
        unset($form[$key . '-attach-url']);
      }
    }
  }
}

/**
 * Return an array of rids for the current user, given a node.
 * Include FIELD_ROLE_AUTHOR 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) && $user->uid == $node->uid) {
    array_push($groups, FIELD_ROLE_AUTHOR);

    // add 'author' group.
  }
  return $groups;
}

/**
 * Workflow hook to allow other modules to add workflow operations.
 */
function workflow_fields_workflow_operations($mode, $wid, $sid = 0) {
  switch ($mode) {
    case 'workflow':
      break;
    case 'state':
      if (WORKFLOW_CREATION == db_result(db_query("SELECT sysid FROM {workflow_states} WHERE wid = %d AND sid = %d", $wid, $sid))) {

        // (creation) state
        return array(
          'workflow_creation_edit' => array(
            'title' => t('Edit'),
            'href' => "admin/build/workflow/state/{$wid}/{$sid}",
          ),
        );
      }
      break;
  }
}

/**
 * Render a single field.
 * This function is a copy of cck/content.module:_content_field_view.
 */
function _workflow_fields_node_view($node, $field, $node_field) {
  $field_types = _content_field_types();
  $teaser = FALSE;
  $page = FALSE;
  $context = 'full';
  $formatter = isset($field['display_settings'][$context]['format']) ? $field['display_settings'][$context]['format'] : 'default';
  $value = '';
  if ($formatter != 'hidden') {
    if (content_handle('field', 'view', $field) == CONTENT_CALLBACK_CUSTOM) {
      $module = $field_types[$field['type']]['module'];
      $function = $module . '_field';
      if (function_exists($function)) {
        $value = $function('view', $node, $field, $node_field, $teaser, $page);
      }
    }
    else {
      foreach ($node_field as $delta => $item) {
        $node_field[$delta]['view'] = content_format($field, $item, $formatter, $node);
      }
      $value = theme('field', $node, $field, $node_field, $teaser, $page);
    }
  }
  return $value;
}
function workflow_fields_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  if ($op == 'view') {
    $sid = workflow_node_current_state($node);
    list($visibles, $editables) = _workflow_fields_get_fields($sid, $node->type, $node);
    if (!isset($visibles)) {
      return;
    }
    foreach ($visibles as $key => $visible) {
      if (!$visible) {
        $node->content[$key]['#access'] = FALSE;
      }
    }
  }
}
function workflow_fields_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/workflow_fields',
      'title' => t('Workflow Fields settings'),
      'description' => t('Global settings for the behaviour of Workflow Fields.'),
      'callback' => 'drupal_get_form',
      'callback arguments' => array(
        '_workflow_fields_settings',
      ),
      'access' => user_access('administer workflow'),
    );
  }
  return $items;
}
function _workflow_fields_settings() {
  $form['workflow_fields_hide_read_only_when_editing'] = array(
    '#type' => 'checkbox',
    '#title' => t('Hide read-only fields when editing'),
    '#default_value' => variable_get('workflow_fields_hide_read_only_when_editing', FALSE),
    '#description' => t('Check this box to hide read-only fields from the editing form.'),
  );
  return system_settings_form($form);
}
function theme__workflow_fields_settings($form) {
  $output = '';
  $output .= drupal_render($form);
  $output .= '<p class="cvs-version">$Id$</p>';
  return $output;
}
function _workflow_fields_get_fields($sid, $type, $node) {
  global $user;
  $result = db_query("SELECT * FROM {workflow_fields} WHERE sid = %d AND type = '%s' AND rid IN (%d, %s)", $sid, $type, FIELD_ROLE_USERREFERENCE, implode(_workflow_fields_compute_groups($node), ','));

  // Compute the OR of permissions among all groups that this user belongs to.
  while ($row = db_fetch_array($result)) {
    if ($row['rid'] != FIELD_ROLE_USERREFERENCE) {
      $visibles[$row['name']] |= $row['visible'];
      $editables[$row['name']] |= $row['editable'];
    }
    else {
      $visibles[$row['name']] |= $row['visible'] && isset($node->{$row['visible_userref']}) && $node->{$row['visible_userref']}[0]['uid'] == $user->uid;
      $editables[$row['name']] |= $row['editable'] && isset($node->{$row['editable_userref']}) && $node->{$row['editable_userref']}[0]['uid'] == $user->uid;
    }
  }
  return array(
    $visibles,
    $editables,
  );
}
function &_workflow_fields_find_element($key, &$form) {
  if (array_key_exists($key, $form)) {
    $ret =& $form[$key];
    return $ret;
  }
  foreach ($form as $k => $v) {
    if (is_array($v)) {
      $ret =& _workflow_fields_find_element($key, $form[$k]);
      if ($ret) {
        return $ret;
      }
    }
  }
  return FALSE;
}

Functions

Namesort descending Description
theme_workflow_fields_state
theme__workflow_fields_settings
workflow_fields_form_alter Implementation of hook_form_alter(). Hook on both any CCK node form and on the workflow state form.
workflow_fields_help Implementation of hook_help().
workflow_fields_menu
workflow_fields_nodeapi
workflow_fields_node_form_alter Alter the node form by hiding/disabling fields depending on the workflow state. To hide a field, just unset it from the form. To disable a field, replace its form element by a view-only version (by calling the 'view' CCK function).
workflow_fields_state_form_alter Alter the workflow state form. Add a table listing the fields for workflow's content type.
workflow_fields_state_form_submit
workflow_fields_workflow_fields
workflow_fields_workflow_operations Workflow hook to allow other modules to add workflow operations.
_workflow_fields_compute_groups Return an array of rids for the current user, given a node. Include FIELD_ROLE_AUTHOR if the current user is the node author.
_workflow_fields_find_element
_workflow_fields_get_fields
_workflow_fields_node_view Render a single field. This function is a copy of cck/content.module:_content_field_view.
_workflow_fields_settings

Constants

Namesort descending Description
FIELD_ROLE_AUTHOR @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.
FIELD_ROLE_USERREFERENCE