You are here

workflow.features.inc in Workflow 7.2

Same filename and directory in other branches
  1. 7 workflow.features.inc

Provides Features integration for Workflow using the CRUD API.

As you will notice this file will only handle the <export> of Worflows, including states and transitions. The <import> is handeled magically, and all modifications are done in function Workflow::save().

File

workflow.features.inc
View source
<?php

/**
 * @file
 * Provides Features integration for Workflow using the CRUD API.
 *
 * As you will notice this file will only handle the <export> of Worflows,
 * including states and transitions. The <import> is handeled magically,
 * and all modifications are done in function Workflow::save().
 */
define('WORKFLOW_FEATURES_AUTHOR_NAME', 'workflow_features_author_name');

// Even if workflow Node is not enabled, Features may use Node API's type_maps.
require_once dirname(__FILE__) . '/workflow.node.type_map.inc';

/**
 * Default controller handling features integration.
 */
class WorkflowFeaturesController extends EntityDefaultFeaturesController {

  /**
   * Generates the result for hook_features_export().
   */
  public function export($data, &$export, $module_name = '') {
    $pipe = parent::export($data, $export, $module_name);
    foreach ($data as $workflow_name) {
      if ($workflow = workflow_load_by_name($workflow_name)) {

        // Add dependency on workflow_node.
        if (count($workflow
          ->getTypeMap())) {
          $export['dependencies']['workflownode'] = 'workflownode';
        }
      }
    }
    return $pipe;
  }

  /**
   * Generates the result for hook_features_export_render().
   *
   * This is a copy of the parent, adding 'system_roles'.
   * The workflow is imported in the target system with Workflow::save().
   */
  public function export_render($module, $data, $export = NULL) {
    $translatables = $code = array();
    $code[] = '  $workflows = array();';
    $code[] = '';
    foreach ($data as $identifier) {

      // Clone workflow to make sure changes are not propagated to original.
      if ($workflow = entity_load_single($this->type, $identifier)) {
        $this
          ->export_render_workflow($workflow, $identifier, $code);
      }
    }
    $code[] = '  return $workflows;';
    $code = implode("\n", $code);
    $hook = isset($this->info['export']['default hook']) ? $this->info['export']['default hook'] : 'default_' . $this->type;
    return array(
      $hook => $code,
    );
  }

  /**
   * Renders the provided workflow into export code.
   *
   * @param Workflow $workflow
   *   The workflow to export.
   * @param string $identifier
   *   The unique machine name for the workflow in the export.
   * @param array $code
   *   A reference to the export code array that will receive the output.
   */
  protected function export_render_workflow(Workflow $workflow, $identifier, array &$code) {

    // Make sure data is not copied to the database.
    $workflow = clone $workflow;
    $this
      ->sanitize_workflow_for_export($workflow);

    // Make sure to escape the characters \ and '.
    // The following method has the advantage, that you can export with
    // features,
    // and later import without enabling Features in the target system.
    $workflow_export = addcslashes(entity_export($this->type, $workflow, '  '), '\\\'');
    $workflow_identifier = features_var_export($identifier);
    $code[] = "  // Exported workflow: {$workflow_identifier}";
    $code[] = "  \$workflows[{$workflow_identifier}] = entity_import('{$this->type}', '" . $workflow_export . "');";
    $code[] = '';

    // Blank line
  }

  /**
   * Prepares the provided workflow for export.
   *
   * Removes serial IDs and replaces them with machine names.
   *
   * @param Workflow $workflow
   *   The workflow to sanitize. The contents of this object are modified directly.
   */
  protected function sanitize_workflow_for_export(Workflow $workflow) {

    // Eliminate serial IDs in exports to prevent "Overridden" status.
    // We use machine names instead.
    unset($workflow->wid);

    // Add roles to translate role IDs on target system.
    $permission = NULL;

    // Get system roles.
    $workflow->system_roles = workflow_get_roles($permission);

    // Only export system roles for roles used by this workflow.
    $roles = array();
    foreach ($workflow->transitions as $id => $transition) {
      foreach ($transition->roles as $rid) {
        $roles[] = $rid;
      }
    }
    $roles = array_unique($roles);
    foreach ($workflow->system_roles as $id => $system_role) {
      if (!in_array($id, $roles)) {
        unset($workflow->system_roles[$id]);
      }
    }
    $sid_to_name_map = $this
      ->pack_states($workflow);
    $this
      ->pack_transitions($workflow, $sid_to_name_map);
  }

  /**
   * "Packs" the states in the provided workflow into an export-friendly format.
   *
   * @param Workflow $workflow
   *   The workflow to pack. The contents of this object are modified directly.
   *
   * @return array
   *   A map of the old state IDs to their new machine names.
   */
  protected function pack_states(Workflow $workflow) {
    $named_states = array();
    $sid_to_name_map = array();
    foreach ($workflow->states as $state) {

      /* @var WorkflowState $state */
      $name = $state
        ->getName();
      $sid_to_name_map[$state->sid] = $name;

      // Eliminate serial IDs in exports to prevent "Overridden" status.
      // We use machine names instead.
      unset($state->sid);
      unset($state->wid);
      $named_states[$name] = $state;
    }
    ksort($named_states);

    // Identify states by machine name.
    $workflow->states = $named_states;
    return $sid_to_name_map;
  }

  /**
   * "Packs" the transitions in the provided workflow into an export-friendly format.
   *
   * @param Workflow $workflow
   *   The workflow to pack. The contents of this object are modified directly.
   *
   * @param array $sid_to_name_map
   *   The map of numeric state IDs to their machine names, for remapping sid
   *   references.
   */
  protected function pack_transitions(Workflow $workflow, array $sid_to_name_map) {
    $named_transitions = array();
    foreach ($workflow->transitions as $transition) {

      /* @var WorkflowTransition $transition */
      $start_name = $sid_to_name_map[$transition->sid];
      $end_name = $sid_to_name_map[$transition->target_sid];
      $new_name = WorkflowConfigTransition::machineName($start_name, $end_name);
      $transition->name = $new_name;
      $transition->start_state = $start_name;
      $transition->end_state = $end_name;

      // Eliminate serial IDs in exports to prevent "Overridden" status.
      // We use machine names instead.
      unset($transition->wid);
      unset($transition->tid);
      unset($transition->sid);
      unset($transition->target_sid);
      $named_transitions[$new_name] = $transition;
    }
    ksort($named_transitions);

    // Identify transitions by new machine name.
    $workflow->transitions = $named_transitions;
  }

  /**
   * Revert this workflow, either creating the workflow new (if one with the
   * same machine name is not present), or updating the existing workflow.
   *
   * @param string $module
   *   The name of the feature module whose components should be reverted.
   */
  function revert($module = NULL) {

    // Loads defaults from feature code.
    $defaults = workflow_get_defaults($module);
    if (!empty($defaults)) {
      foreach ($defaults as $machine_name => $entity) {
        workflow_revert($defaults, $machine_name);
      }
    }
  }

}

/**
 * Implements hook_features_COMPONENT_alter().
 *
 * Adds the corresponding Workflow to the WorkflowField.
 */
function workflow_features_pipe_field_base_alter(&$pipe, $data, $export) {
  if (!empty($data)) {
    foreach ($data as $field_name) {

      // $info = field_info_field($field_name);
      $field = _workflow_info_field($field_name);
      if ($field['type'] == 'workflow') {

        // $field['settings']['wid'] can be numeric or named.
        $workflow = workflow_load_single($field['settings']['wid']);

        // Fields might reference missing workflows.
        if (!empty($workflow)) {
          $pipe['Workflow'][] = $workflow->name;
        }
      }
    }
  }
}

/**
 * Implements hook_features_api_alter().
 *
 * Ensures Workflow always fires last during rebuild, to ensure that roles
 * referenced by workflows to be loaded-in when features contain roles.
 */
function workflow_features_api_alter(array &$components) {

  // FIXME: Why is Workflow the only features provider with an uppercase component name?
  $component_name = 'Workflow';
  if (isset($components[$component_name])) {
    $setting = $components[$component_name];
    unset($components[$component_name]);
    $components[$component_name] = $setting;
  }
}

Functions

Namesort descending Description
workflow_features_api_alter Implements hook_features_api_alter().
workflow_features_pipe_field_base_alter Implements hook_features_COMPONENT_alter().

Constants

Namesort descending Description
WORKFLOW_FEATURES_AUTHOR_NAME @file Provides Features integration for Workflow using the CRUD API.

Classes

Namesort descending Description
WorkflowFeaturesController Default controller handling features integration.