You are here

workbench_workflows.module in Workbench Moderation 7.2

File

modules/workbench_workflows/workbench_workflows.module
View source
<?php

/**
 * @file workbench_workflows.module
 */
define('WORKBENCH_WORKFLOWS_STATE_UNCHANGED', -1);
define('WORKBENCH_WORKFLOWS_STATE_UNPUBLISHED', 0);
define('WORKBENCH_WORKFLOWS_STATE_PUBLISHED', 1);

/**
 * Implements hook_permissions().
 */
function workbench_workflows_permission() {
  return array(
    'administer_workbench_workflows' => array(
      'title' => t('Administer workbench moderation exportables'),
    ),
  );
}

/**
 * Implementation of hook_ctools_plugin_directory() to let the system know
 * we implement task and task_handler plugins.
 */
function workbench_workflows_ctools_plugin_directory($module, $plugin) {

  // Most of this module is implemented as an export ui plugin, and the
  // rest is in ctools/includes/workbench_states.inc
  if ($module == 'ctools' && $plugin == 'export_ui') {
    return 'plugins/' . $plugin;
  }
}

/**
 * Create an array of exportable states/events/workflows suitable for FAPI.
 *
 * @param string $type
 *   Allowed values are 'states', 'events', and 'workflows'
 * @param string $name
 *   The machine name of the exportable.
 * @param $reset
 *   A boolean for whether the static cache should be reset.
 */
function workbench_workflows_options($type = 'states', $label_key = 'admin_title', $reset = FALSE) {

  // load all objects of this type
  $exportables = workbench_workflows_load_enabled($type, $reset);
  $return = array();
  foreach ($exportables as $exportable) {
    $return[$exportable->name] = $exportable->{$label_key};
  }
  return $return;
}

/**
 * Determines if access an entity has access to a state/event/workflow
 *
 * @param $entity
 *   A Drupal entity. Currently only nodes are supported.
 * @param $exportable
 *   The machine name of an exportable or the loaded object.
 * @param $exportable_type
 *   Allowed values are 'states', 'events', and 'workflows'
 */
function workbench_workflows_exportable_access($entity, $exportable, $exportable_type = 'states') {
  $contexts = array();

  // The exportable might already be the loaded object.
  if (is_string($exportable)) {
    ctools_include('export');
    $exportable = workbench_workflows_load($exportable_type, $exportable);
  }

  // Create a CTools context.
  if (!empty($exportable->requiredcontexts[0]['name'])) {
    ctools_include('context');
    $contexts[] = ctools_context_create($exportable->requiredcontexts[0]['name'], $entity);
  }

  // Load all the contexts.
  // @todo, we may need a user context other than "currently logged-in user."
  $contexts = ctools_context_match_required_contexts($exportable->requiredcontexts, $contexts);
  $contexts = ctools_context_load_contexts($exportable, FALSE, $contexts);
  return ctools_access($exportable->access, $contexts);
}

/**
 * Load a single workbench moderation exportable.
 *
 * @param string $type
 *   Allowed values are 'states', 'events', and 'workflows'
 * @param string $name
 *   The machine name of the exportable.
 * Returns a CTools exportable object.
 */
function workbench_workflows_load($type, $name) {
  $cache =& drupal_static(__FUNCTION__, array());

  // We use array_key_exists because failed loads will be NULL and
  // isset() will try to load it again.
  if (empty($cache[$type]) || !array_key_exists($name, $cache[$type])) {
    ctools_include('export');
    $result = ctools_export_load_object('workbench_workflows_' . $type, 'names', array(
      $name,
    ));

    // If scheduling is enabled we need some transition events and a schedule
    // state. These items can be generated dynamically based on the available
    // configuration.
    if (module_exists('state_flow_schedule')) {

      // Create transition events for schedulable event.
      if ($type == 'events' && strpos($name, 'schedule_') === 0 && ($source_event = workbench_workflows_load($type, substr($name, 9))) && !empty($source_event->schedulable)) {
        $transition_event = workbench_workflows_create_scheduled_tansition_event($source_event);
        $result[$transition_event->name] = $transition_event;
      }

      // Add a schedule state.
      if ($type == 'states' && $name == 'schedule') {
        $result['schedule'] = workbench_workflows_create_schedule_state();
      }
    }
    if (isset($result[$name])) {
      $cache[$type][$name] = $result[$name];
    }
    else {
      $cache[$type][$name] = NULL;
    }
  }
  if (isset($cache[$type][$name])) {
    return $cache[$type][$name];
  }
}

/**
 * Load a single, unsanitized workbench moderation exportable label.
 *
 * @param string $type
 *   Allowed values are 'states', 'events', and 'workflows'
 * @param string $name
 *   The machine name of the exportable.
 * @param string $label_property
 *   The property on the exportable to return. Defaults to admin_title.
 * Returns an UNESCAPED string.
 */
function workbench_workflows_load_label($type, $name, $label_property = 'admin_title') {
  $exportable = workbench_workflows_load($type, $name);
  if (!empty($exportable->{$label_property})) {
    return $exportable->{$label_property};
  }
  return '';
}

/**
 * Load a single workbench moderation exportable.
 *
 * @param string $type
 *   Allowed values are 'states', 'events', and 'workflows'
 * @param string $name
 *   The machine name of the exportable.
 * @param $reset
 *   A boolean for whether the static cache should be reset.
 * Returns a CTools exportable object.
 */
function workbench_workflows_load_all($type = 'states', $reset = FALSE) {
  $cache =& drupal_static(__FUNCTION__, array());

  // We check our own private static because individual minis could have
  // been loaded prior to load all and we need to know that.
  if (empty($cache[$type]) || $reset) {

    // If reseting, also reset ctools_export_load_object_all.
    // It seems like overkill but it is the cache used by ctools_export_load_object()
    // @todo, this needs a test.
    if ($reset) {
      drupal_static_reset('ctools_export_load_object_all');
    }
    $all_loaded = TRUE;
    if ($reset) {
      $cache = array();
    }
    ctools_include('export');
    $type_exportables = ctools_export_load_object('workbench_workflows_' . $type);

    // The sort function expects a title property.
    // @todo, perhaps these objects should have those as a column. For now,
    // copy the admin_title.
    foreach ($type_exportables as $machine_name => $exportable) {
      if (empty($exportable->title) && !empty($exportable->admin_title)) {
        $type_exportables[$machine_name]->title = $exportable->admin_title;
      }

      // If scheduling is enabled we need some transition events and a schedule
      // state. These items can be generated dynamically based on the available
      // configuration.
      if (module_exists('state_flow_schedule')) {

        // Create transition events for schedulable event.
        if ($type == 'events' && !empty($exportable->schedulable)) {
          $transition_event = workbench_workflows_create_scheduled_tansition_event($type_exportables[$machine_name]);
          $type_exportables[$transition_event->name] = $transition_event;
        }

        // Add schedule state.
        if ($type == 'states') {
          $state = workbench_workflows_create_schedule_state();
          $type_exportables[$state->name] = $state;
        }
      }
    }
    uasort($type_exportables, 'ctools_plugin_sort');
    $cache[$type] = $type_exportables;
  }
  return $cache[$type];
}

/**
 * Load enabled workbench moderation exportables.
 *
 * @param string $type
 *   Allowed values are 'states', 'events', and 'workflows'
 * @param string $name
 *   The machine name of the exportable.
 * @param $reset
 *   A boolean for whether the static cache should be reset.
 * Returns a CTools exportable object.
 */
function workbench_workflows_load_enabled($type = 'states', $reset = FALSE) {
  $exportables = workbench_workflows_load_all($type, $reset);
  foreach ($exportables as $machine_name => $exportable) {
    if (!empty($exportable->disabled)) {
      unset($exportables[$machine_name]);
    }
  }
  return $exportables;
}

/**
 * Creates a new transition event for scheduling.
 *
 * If an event is schedulable we need an event that doesn't do the
 * transition immediately but keeps the current state and waits until the
 * scheduled event is fired.
 * Create such a transition event based on the schedulable event.
 *
 * @param object $event
 *   Schedulable event to create transition event for.
 *
 * @return object
 *   Transition event.
 */
function workbench_workflows_create_scheduled_tansition_event($event) {

  // As this is a schedulable event, it has to accepts schedule as origin state.
  $event->origin_states += array(
    'schedule' => 'schedule',
  );
  $transition_event = clone $event;
  unset($transition_event->eventid);
  $transition_event->name = 'schedule_' . $event->name;
  $transition_event->schedulable = 0;
  $transition_event->target_state = 'schedule';
  $transition_event->export_type = EXPORT_IN_CODE;
  $transition_event->export_module = 'workbench_workflows';
  $transition_event->admin_title = 'Schedule: ' . $event->admin_title;
  $transition_event->title = $transition_event->admin_title;
  return $transition_event;
}

/**
 * Creates the state used for scheduling.
 *
 * @return object
 *   Schedule state.
 */
function workbench_workflows_create_schedule_state() {
  return (object) array(
    'name' => 'schedule',
    'title' => 'Scheduled',
    'admin_title' => 'Scheduled',
    'admin_description' => 'Event is scheduled',
    'requiredcontexts' => array(),
    'contexts' => array(),
    'relationships' => array(),
    'access' => array(),
    'weight' => 1,
  );
}

/**
 * Implements hook_menu_alter().
 */
function workbench_workflows_menu_alter(&$items) {

  // Move up the states link.
  // @todo, is there a way to set the weight at the plugin registration?
  $items['admin/config/workflow/workbench-workflows/states']['weight'] = -111;
}

/**
 * Implements hook_state_flow_plugins().
 */
function workbench_workflows_state_flow_entity_plugins() {
  module_load_include('inc', 'workbench_workflows', 'workbench_workflows.state_flow_entity');
  return _workbench_workflows_state_flow_entity_plugins();
}

/**
 * Implements hook_state_flow_machine_type_alter().
 */
function workbench_workflows_state_flow_entity_machine_type_alter(&$machine_type, $entity, $entity_type) {
  if ($entity_type == 'node') {
    module_load_include('inc', 'workbench_workflows', 'workbench_workflows.state_flow_entity');
    $workflow = workbench_workflows_select_workflow($entity);
    if (!empty($workflow->name)) {
      $machine_type = 'workbench_workflows__' . $workflow->name;
    }
    else {
      if (variable_get('workbench_workflows_default_to_ignore', FALSE)) {
        $machine_type = 'workbench_workflows_ignore';
      }
    }
  }
}

/**
 * Guard callback for the workbenchStates publish and unpublish events.
 */
function workbench_workflows_guard($event) {
  $entity = $event
    ->get_machine()
    ->get_object();

  // Check if this is prohibited based on the events rules.
  if (!workbench_workflows_exportable_access($entity, $event->name, 'events')) {
    return FALSE;
  }
  else {
    return workbench_workflows_exportable_access($entity, $event
      ->get_option('target'), 'states');
  }
}

/**
 * Implements hook_state_flow_schedule_schedulable_events_alter().
 */
function workbench_workflows_state_flow_schedule_schedulable_events_alter(&$schedulable_events) {
  $events = workbench_workflows_load_enabled('events');
  foreach ($events as $event) {
    if (!empty($event->schedulable)) {

      // Register automatically created transition events as schedulable. And
      // register the schedulable event as target event.
      $schedulable_events['schedule_' . $event->name] = $event->name;
    }
  }
}

/**
 * Implements hook_entity_revision_operation_info().
 */
function workbench_workflows_entity_revision_operation_info() {
  $info = array();
  $events = workbench_workflows_options('events');
  foreach ($events as $event => $label) {
    $info['node']['workbench_moderation_' . $event] = array(
      'label' => t('Moderate to @label', array(
        '@label' => $label,
      )),
      'access callback' => TRUE,
      'callback' => 'workbench_workflows_revision_operation_process',
      'file' => __FILE__,
    );
  }
  return $info;
}

/**
 * Implements hook_entity_revision_operation_access().
 */
function workbench_workflows_entity_revision_operation_access($operation, $entity_type, $entity, $account) {
  if (strpos($operation['operation'], 'workbench_moderation_') !== FALSE && !empty($entity) && $entity_type == 'node') {
    $machine = state_flow_entity_load_state_machine($entity, $entity_type);
    $events = $machine
      ->get_available_events_options(TRUE);
    $event = substr($operation['operation'], 21);

    // Make sure this is a valid transition.
    if (!isset($events[$event])) {
      return FALSE;
    }
  }
}

/**
 * Revision operation process callback for scheduled moderation state changes.
 */
function workbench_workflows_revision_operation_process($entity, $operation) {

  // Extract the transition to state from the operation key: 'workbench_moderation_state'.
  $event = substr($operation->operation, 21);
  $machine = state_flow_entity_load_state_machine($entity, $operation->entity_type);
  if (!empty($machine) && !$machine
    ->ignore()) {
    $machine
      ->fire_event($event);
  }
}

/**
 * Implements hook_entity_revision_operation_result_state().
 */
function workbench_workflows_entity_revision_operation_result_state($operation, $entity) {
  $event_name = substr($operation->operation, 21);
  $event = workbench_workflows_load('events', $event_name);
  if ($event) {
    $state = workbench_workflows_load('states', $event->target_state);
    if (isset($state->entity_state_change) && $state->entity_state_change != WORKBENCH_WORKFLOWS_STATE_UNCHANGED) {

      // Return TRUE for accessible if the status is published.
      return $state->entity_state_change == WORKBENCH_WORKFLOWS_STATE_PUBLISHED;
    }

    // Return the unchanged status.
    return (bool) $entity->status;
  }
}

Functions

Namesort descending Description
workbench_workflows_create_scheduled_tansition_event Creates a new transition event for scheduling.
workbench_workflows_create_schedule_state Creates the state used for scheduling.
workbench_workflows_ctools_plugin_directory Implementation of hook_ctools_plugin_directory() to let the system know we implement task and task_handler plugins.
workbench_workflows_entity_revision_operation_access Implements hook_entity_revision_operation_access().
workbench_workflows_entity_revision_operation_info Implements hook_entity_revision_operation_info().
workbench_workflows_entity_revision_operation_result_state Implements hook_entity_revision_operation_result_state().
workbench_workflows_exportable_access Determines if access an entity has access to a state/event/workflow
workbench_workflows_guard Guard callback for the workbenchStates publish and unpublish events.
workbench_workflows_load Load a single workbench moderation exportable.
workbench_workflows_load_all Load a single workbench moderation exportable.
workbench_workflows_load_enabled Load enabled workbench moderation exportables.
workbench_workflows_load_label Load a single, unsanitized workbench moderation exportable label.
workbench_workflows_menu_alter Implements hook_menu_alter().
workbench_workflows_options Create an array of exportable states/events/workflows suitable for FAPI.
workbench_workflows_permission Implements hook_permissions().
workbench_workflows_revision_operation_process Revision operation process callback for scheduled moderation state changes.
workbench_workflows_state_flow_entity_machine_type_alter Implements hook_state_flow_machine_type_alter().
workbench_workflows_state_flow_entity_plugins Implements hook_state_flow_plugins().
workbench_workflows_state_flow_schedule_schedulable_events_alter Implements hook_state_flow_schedule_schedulable_events_alter().

Constants