workflow.features.inc 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. <?php
  2. /**
  3. * @file
  4. * Provides Features integration for Workflow using the CRUD API.
  5. *
  6. * As you will notice this file will only handle the <export> of Worflows,
  7. * including states and transitions. The <import> is handeled magically,
  8. * and all modifications are done in function Workflow::save().
  9. */
  10. define('WORKFLOW_FEATURES_AUTHOR_NAME', 'workflow_features_author_name');
  11. // Even if workflow Node is not enabled, Features may use Node API's type_maps.
  12. require_once dirname(__FILE__) . '/workflow.node.type_map.inc';
  13. /**
  14. * Default controller handling features integration.
  15. */
  16. class WorkflowFeaturesController extends EntityDefaultFeaturesController {
  17. /**
  18. * Generates the result for hook_features_export().
  19. */
  20. public function export($data, &$export, $module_name = '') {
  21. $pipe = parent::export($data, $export, $module_name);
  22. foreach ($data as $workflow_name) {
  23. if ($workflow = workflow_load_by_name($workflow_name)) {
  24. // Add dependency on workflow_node.
  25. if (count($workflow->getTypeMap())) {
  26. $export['dependencies']['workflownode'] = 'workflownode';
  27. }
  28. // Add dependency on workflow_field.
  29. $export['features']['Workflow'][$workflow_name] = $workflow_name;
  30. }
  31. }
  32. return $pipe;
  33. }
  34. /**
  35. * Generates the result for hook_features_export_render().
  36. *
  37. * This is a copy of the parent, adding 'system_roles'.
  38. * The workflow is imported in the target system with Workflow::save().
  39. */
  40. public function export_render($module, $data, $export = NULL) {
  41. $translatables = $code = array();
  42. $code[] = ' $workflows = array();';
  43. $code[] = '';
  44. foreach ($data as $identifier) {
  45. // Clone workflow to make sure changes are not propagated to original.
  46. if ($workflow = entity_load_single($this->type, $identifier)) {
  47. $this->export_render_workflow($workflow, $identifier, $code);
  48. }
  49. }
  50. $code[] = ' return $workflows;';
  51. $code = implode("\n", $code);
  52. $hook = isset($this->info['export']['default hook']) ? $this->info['export']['default hook'] : 'default_' . $this->type;
  53. return array($hook => $code);
  54. }
  55. /**
  56. * Renders the provided workflow into export code.
  57. *
  58. * @param Workflow $workflow
  59. * The workflow to export.
  60. * @param string $identifier
  61. * The unique machine name for the workflow in the export.
  62. * @param array $code
  63. * A reference to the export code array that will receive the output.
  64. */
  65. protected function export_render_workflow(Workflow $workflow, $identifier, array &$code) {
  66. // Make sure data is not copied to the database.
  67. $workflow = clone $workflow;
  68. $this->sanitize_workflow_for_export($workflow);
  69. // Make sure to escape the characters \ and '.
  70. // The following method has the advantage, that you can export with
  71. // features,
  72. // and later import without enabling Features in the target system.
  73. $workflow_export = addcslashes(entity_export($this->type, $workflow, ' '), '\\\'');
  74. $workflow_identifier = features_var_export($identifier);
  75. $code[] = " // Exported workflow: {$workflow_identifier}";
  76. $code[] = " \$workflows[{$workflow_identifier}] = entity_import('{$this->type}', '" . $workflow_export . "');";
  77. $code[] = ''; // Blank line
  78. }
  79. /**
  80. * Prepares the provided workflow for export.
  81. *
  82. * Removes serial IDs and replaces them with machine names.
  83. *
  84. * @param Workflow $workflow
  85. * The workflow to sanitize. The contents of this object are modified directly.
  86. */
  87. protected function sanitize_workflow_for_export(Workflow $workflow) {
  88. // Eliminate serial IDs in exports to prevent "Overridden" status.
  89. // We use machine names instead.
  90. unset($workflow->wid);
  91. // Add roles to translate role IDs on target system.
  92. $permission = NULL;
  93. $workflow->system_roles = workflow_get_roles($permission);
  94. $sid_to_name_map = $this->pack_states($workflow);
  95. $this->pack_transitions($workflow, $sid_to_name_map);
  96. }
  97. /**
  98. * "Packs" the states in the provided workflow into an export-friendly format.
  99. *
  100. * @param Workflow $workflow
  101. * The workflow to pack. The contents of this object are modified directly.
  102. *
  103. * @return array
  104. * A map of the old state IDs to their new machine names.
  105. */
  106. protected function pack_states(Workflow $workflow) {
  107. $named_states = array();
  108. $sid_to_name_map = array();
  109. foreach ($workflow->states as $state) {
  110. /* @var WorkflowState $state */
  111. $name = $state->getName();
  112. $sid_to_name_map[$state->sid] = $name;
  113. // Eliminate serial IDs in exports to prevent "Overridden" status.
  114. // We use machine names instead.
  115. unset($state->sid);
  116. unset($state->wid);
  117. $named_states[$name] = $state;
  118. }
  119. ksort($named_states);
  120. // Identify states by machine name.
  121. $workflow->states = $named_states;
  122. return $sid_to_name_map;
  123. }
  124. /**
  125. * "Packs" the transitions in the provided workflow into an export-friendly format.
  126. *
  127. * @param Workflow $workflow
  128. * The workflow to pack. The contents of this object are modified directly.
  129. *
  130. * @param array $sid_to_name_map
  131. * The map of numeric state IDs to their machine names, for remapping sid
  132. * references.
  133. */
  134. protected function pack_transitions(Workflow $workflow, array $sid_to_name_map) {
  135. $named_transitions = array();
  136. foreach ($workflow->transitions as $transition) {
  137. /* @var WorkflowTransition $transition */
  138. $start_name = $sid_to_name_map[$transition->sid];
  139. $end_name = $sid_to_name_map[$transition->target_sid];
  140. $new_name = WorkflowConfigTransition::machineName($start_name, $end_name);
  141. $transition->name = $new_name;
  142. $transition->start_state = $start_name;
  143. $transition->end_state = $end_name;
  144. // Eliminate serial IDs in exports to prevent "Overridden" status.
  145. // We use machine names instead.
  146. unset($transition->wid);
  147. unset($transition->tid);
  148. unset($transition->sid);
  149. unset($transition->target_sid);
  150. $named_transitions[$new_name] = $transition;
  151. }
  152. ksort($named_transitions);
  153. // Identify transitions by new machine name.
  154. $workflow->transitions = $named_transitions;
  155. }
  156. /**
  157. * Revert this workflow, either creating the workflow new (if one with the
  158. * same machine name is not present), or updating the existing workflow.
  159. *
  160. * @param string $module
  161. * The name of the feature module whose components should be reverted.
  162. */
  163. function revert($module = NULL) {
  164. // Loads defaults from feature code.
  165. $defaults = workflow_get_defaults($module);
  166. foreach ($defaults as $machine_name => $entity) {
  167. workflow_revert($defaults, $machine_name);
  168. }
  169. }
  170. }
  171. /**
  172. * Implements hook_features_COMPONENT_alter().
  173. *
  174. * Adds the corresponding Workflow to the WorkflowField.
  175. */
  176. function workflow_features_pipe_field_base_alter(&$pipe, $data, $export) {
  177. if (!empty($data)) {
  178. foreach ($data as $field_name) {
  179. // $info = field_info_field($field_name);
  180. $field = _workflow_info_field($field_name);
  181. if ($field['type'] == 'workflow') {
  182. // $field['settings']['wid'] can be numeric or named.
  183. $workflow = workflow_load_single($field['settings']['wid']);
  184. // Fields might reference missing workflows.
  185. if (!empty($workflow)) {
  186. $pipe['Workflow'][] = $workflow->name;
  187. }
  188. }
  189. }
  190. }
  191. }
  192. /**
  193. * Implements hook_features_api_alter().
  194. *
  195. * Ensures Workflow always fires last during rebuild, to ensure that roles
  196. * referenced by workflows to be loaded-in when features contain roles.
  197. */
  198. function workflow_features_api_alter(array &$components) {
  199. // FIXME: Why is Workflow the only features provider with an uppercase component name?
  200. $component_name = 'Workflow';
  201. if (isset($components[$component_name])) {
  202. $setting = $components[$component_name];
  203. unset($components[$component_name]);
  204. $components[$component_name] = $setting;
  205. }
  206. }