WorkflowTransitionForm.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. <?php
  2. /**
  3. * @file
  4. * Contains \workflow\Form\WorkflowTransitionForm.
  5. */
  6. /**
  7. * Provides a Transition Form to be used in the Workflow Widget.
  8. */
  9. class WorkflowTransitionForm { // extends FormBase {
  10. /**
  11. * The Workflow Transition storage.
  12. */
  13. protected $field;
  14. protected $instance;
  15. protected $entity;
  16. /**
  17. * Constructs a WorkflowTransitionForm object.
  18. * @param array $field
  19. * @param array $instance
  20. * @param $entity_type
  21. * @param $entity
  22. */
  23. public function __construct(array $field, array $instance, $entity_type, $entity) {
  24. $this->field = $field; // TODO : needed?
  25. $this->instance = $instance; // TODO : needed?
  26. $this->entity = $entity; // TODO : needed?
  27. $this->entity_type = $entity_type; // TODO : needed?
  28. }
  29. /**
  30. * {@inheritdoc}
  31. */
  32. public function getFormId() {
  33. $field = $this->field;
  34. // No entity may be set on VBO form.
  35. $entity_id = ($this->entity) ? entity_id($this->entity_type, $this->entity) : '';
  36. // The field is not set when editing a stand alone Transition.
  37. $field_id = isset($field['id']) ? $field['id'] : '';
  38. return implode('_', array('workflow_transition_form', $this->entity_type, $entity_id, $field_id));
  39. }
  40. /**
  41. * {@inheritdoc}
  42. *
  43. * @param array $form
  44. * @param array $form_state
  45. * @param WorkflowTransition
  46. * The Transition to be edited, created.
  47. *
  48. * @return
  49. * The enhanced form structure.
  50. */
  51. public function buildForm(array $form, array &$form_state) {
  52. global $user;
  53. /* @var $transition WorkflowTransition */
  54. $transition = NULL;
  55. if (isset($form_state['WorkflowTransition'])) {
  56. // If provided, get data from WorkflowTransition.
  57. // This happens when calling entity_ui_get_form(), like in the
  58. // WorkflowTransition Comment Edit form.
  59. $transition = $form_state['WorkflowTransition'];
  60. $field_name = $transition->field_name;
  61. $workflow = $transition->getWorkflow();
  62. $wid = $transition->wid;
  63. $entity = $this->entity = $transition->getEntity();
  64. $entity_type = $this->entity_type = $transition->entity_type;
  65. // Figure out the $entity's bundle and id.
  66. list(, , $entity_bundle) = entity_extract_ids($entity_type, $entity);
  67. $entity_id = entity_id($entity_type, $entity);
  68. // Show the current state and the Workflow form to allow state changing.
  69. // N.B. This part is replicated in hook_node_view, workflow_tab_page, workflow_vbo, transition_edit.
  70. // @todo: support multiple workflows per entity.
  71. // For workflow_tab_page with multiple workflows, use a separate view. See [#2217291].
  72. $field = _workflow_info_field($field_name, $workflow);
  73. $instance = $this->instance + field_info_instance($entity_type, $field_name, $entity_bundle);
  74. }
  75. else {
  76. // Get data from normal parameters.
  77. $entity = $this->entity;
  78. $entity_type = $this->entity_type;
  79. $entity_id = ($entity) ? entity_id($entity_type, $entity) : 0;
  80. $field = $this->field;
  81. $field_name = $field['field_name'];
  82. $instance = $this->instance;
  83. // $field['settings']['wid'] can be numeric or named.
  84. // $wid may not be specified.
  85. $wid = $field['settings']['wid'];
  86. $workflow = workflow_load_single($wid);
  87. }
  88. $force = FALSE;
  89. // Get values.
  90. // Current sid and default value may differ in a scheduled transition.
  91. // Set 'grouped' option. Only valid for select list and undefined/multiple workflows.
  92. $settings_options_type = $field['settings']['widget']['options'];
  93. $grouped = ($settings_options_type == 'select');
  94. if ($transition) {
  95. // If a Transition is passed as parameter, use this.
  96. if ($transition->isExecuted()) {
  97. // We are editing an existing/executed/not-scheduled transition.
  98. // Only the comments may be changed!
  99. // Fetch the old state for the formatter on top of form.
  100. $current_state = $transition->getOldState();
  101. $current_sid = $current_state->sid;
  102. // The states may not be changed anymore.
  103. $new_state = $transition->getNewState();
  104. $options = array($new_state->sid => $new_state->label());
  105. // We need the widget to edit the comment.
  106. $show_widget = TRUE;
  107. }
  108. else {
  109. $current_state = $transition->getOldState();
  110. $current_sid = $current_state->sid;
  111. $options = $current_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  112. $show_widget = $current_state->showWidget($entity_type, $entity, $field_name, $user, $force);
  113. }
  114. $default_value = $transition->new_sid;
  115. }
  116. elseif (!$entity) {
  117. // Sometimes, no entity is given. We encountered the following cases:
  118. // - the Field settings page,
  119. // - the VBO action form;
  120. // - the Advance Action form on admin/config/system/actions;
  121. // If so, show all options for the given workflow(s).
  122. $options = workflow_get_workflow_state_names($wid, $grouped, $all = FALSE);
  123. $show_widget = TRUE;
  124. $default_value = $current_sid = isset($items[0]['value']) ? $items[0]['value'] : '0';
  125. }
  126. else {
  127. $current_sid = workflow_node_current_state($entity, $entity_type, $field_name);
  128. if ($current_state = workflow_state_load_single($current_sid)) {
  129. /* @var $current_state WorkflowTransition */
  130. $options = $current_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  131. $show_widget = $current_state->showWidget($entity_type, $entity, $field_name, $user, $force);
  132. $default_value = !$current_state->isCreationState() ? $current_sid : $workflow->getFirstSid($entity_type, $entity, $field_name, $user, FALSE);
  133. }
  134. else {
  135. // We are in trouble! A message is already set in workflow_node_current_state().
  136. $options = array();
  137. $show_widget = FALSE;
  138. $default_value = $current_sid;
  139. }
  140. // Get the scheduling info. This may change the $default_value on the Form.
  141. // Read scheduled information, only if an entity exists.
  142. // Technically you could have more than one scheduled, but this will only add the soonest one.
  143. foreach (WorkflowScheduledTransition::load($entity_type, $entity_id, $field_name, 1) as $transition) {
  144. $default_value = $transition->new_sid;
  145. break;
  146. }
  147. }
  148. // Prepare a new transition, if still not provided.
  149. if (!$transition) {
  150. $transition = new WorkflowTransition(array(
  151. 'old_sid' => $default_value,
  152. 'stamp' => REQUEST_TIME,
  153. ));
  154. }
  155. // Fetch the form ID. This is unique for each entity, to allow multiple form per page (Views, etc.).
  156. // Make it uniquer by adding the field name, or else the scheduling of
  157. // multiple workflow_fields is not independent of each other.
  158. // IF we are truly on a Transition form (so, not a Node Form with widget)
  159. // then change the form id, too.
  160. $form_id = $this->getFormId();
  161. if (!isset($form_state['build_info']['base_form_id'])) {
  162. // Strange: on node form, the base_form_id is node_form,
  163. // but on term form, it is not set.
  164. // In both cases, it is OK.
  165. }
  166. else {
  167. if ($form_state['build_info']['base_form_id'] == 'workflow_transition_wrapper_form') {
  168. $form_state['build_info']['base_form_id'] = 'workflow_transition_form';
  169. }
  170. if ($form_state['build_info']['base_form_id'] == 'workflow_transition_form') {
  171. $form_state['build_info']['form_id'] = $form_id;
  172. }
  173. }
  174. $workflow_label = $workflow ? $workflow->label() : '';
  175. // Change settings locally.
  176. if (!$field_name) {
  177. // This is a Workflow Node workflow. Set widget options as in v7.x-1.2
  178. if ($form_state['build_info']['base_form_id'] == 'node_form') {
  179. $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_node']) ? $workflow->options['comment_log_node'] : 1; // vs. ['comment_log_tab'];
  180. $field['settings']['widget']['current_status'] = TRUE;
  181. }
  182. else {
  183. $field['settings']['widget']['comment'] = isset($workflow->options['comment_log_tab']) ? $workflow->options['comment_log_tab'] : 1; // vs. ['comment_log_node'];
  184. $field['settings']['widget']['current_status'] = TRUE;
  185. }
  186. }
  187. // Capture settings to format the form/widget.
  188. $settings_title_as_name = !empty($field['settings']['widget']['name_as_title']);
  189. $settings_fieldset = isset($field['settings']['widget']['fieldset']) ? $field['settings']['widget']['fieldset'] : 0;
  190. $settings_options_type = $field['settings']['widget']['options'];
  191. // The scheduling info can be hidden via field settings, ...
  192. // You may not schedule an existing Transition.
  193. // You must have the correct permission.
  194. $settings_schedule = !empty($field['settings']['widget']['schedule']) && !$transition->isExecuted() && user_access('schedule workflow transitions');
  195. if ($settings_schedule) {
  196. if (isset($form_state['step']) && ($form_state['step'] == 'views_bulk_operations_config_form')) {
  197. // On VBO 'modify entity values' form, leave field settings.
  198. $settings_schedule = TRUE;
  199. }
  200. else {
  201. // ... and cannot be shown on a Content add page (no $entity_id),
  202. // ...but can be shown on a VBO 'set workflow state to..'page (no entity).
  203. $settings_schedule = !($entity && !$entity_id);
  204. }
  205. }
  206. $settings_schedule_timezone = !empty($field['settings']['widget']['schedule_timezone']);
  207. // Show comment, when both Field and Instance allow this.
  208. $settings_comment = $field['settings']['widget']['comment'];
  209. // Save the current value of the node in the form, for later Workflow-module specific references.
  210. // We add prefix, since #tree == FALSE.
  211. $element['workflow']['workflow_entity'] = array(
  212. '#type' => 'value',
  213. '#value' => $this->entity,
  214. );
  215. $element['workflow']['workflow_entity_type'] = array(
  216. '#type' => 'value',
  217. '#value' => $this->entity_type,
  218. );
  219. $element['workflow']['workflow_field'] = array(
  220. '#type' => 'value',
  221. '#value' => $field,
  222. );
  223. $element['workflow']['workflow_instance'] = array(
  224. '#type' => 'value',
  225. '#value' => $instance,
  226. );
  227. // Save the form_id, so the form values can be retrieved in submit function.
  228. $element['workflow']['form_id'] = array(
  229. '#type' => 'value',
  230. '#value' => $form_id,
  231. );
  232. // Save the hid, when editing an existing transition.
  233. $element['workflow']['workflow_hid'] = array(
  234. '#type' => 'hidden',
  235. '#value' => $transition->hid,
  236. );
  237. // Add the default value in the place where normal fields
  238. // have it. This is to cater for 'preview' of the entity.
  239. $element['#default_value'] = $default_value;
  240. // Decide if we show a widget or a formatter.
  241. // There is no need for a widget when the only option is the current sid.
  242. // Show state formatter before the rest of the form,
  243. // when transition is scheduled or widget is hidden.
  244. if ( (!$show_widget) || $transition->isScheduled() || $transition->isExecuted()) {
  245. $form['workflow_current_state'] = workflow_state_formatter($entity_type, $entity, $field, $instance, $current_sid);
  246. // Set a proper weight, which works for Workflow Options in select list AND action buttons.
  247. $form['workflow_current_state']['#weight'] = -0.005;
  248. }
  249. // Add class following node-form pattern (both on form and container).
  250. $workflow_type_id = ($workflow) ? $workflow->getName() : 'none'; // No workflow on New Action form.
  251. $element['workflow']['#attributes']['class'][] = 'workflow-transition-container';
  252. $element['workflow']['#attributes']['class'][] = 'workflow-transition-' . $workflow_type_id . '-container';
  253. // Add class for D7-backwards compatibility (only on container).
  254. $element['workflow']['#attributes']['class'][] = 'workflow-form-container';
  255. if (!$show_widget) {
  256. // Show no widget.
  257. $element['workflow']['workflow_sid']['#type'] = 'value';
  258. $element['workflow']['workflow_sid']['#value'] = $default_value;
  259. $element['workflow']['workflow_sid']['#options'] = $options; // In case action buttons need them.
  260. $form += $element;
  261. return $form; // <-- exit.
  262. }
  263. else {
  264. // Prepare a UI wrapper. This might be a fieldset or a container.
  265. if ($settings_fieldset == 0) { // Use 'container'.
  266. $element['workflow'] += array(
  267. '#type' => 'container',
  268. );
  269. }
  270. else {
  271. $element['workflow'] += array(
  272. '#type' => 'fieldset',
  273. '#title' => $workflow_label,
  274. '#collapsible' => TRUE,
  275. '#collapsed' => ($settings_fieldset == 1) ? FALSE : TRUE,
  276. );
  277. }
  278. // The 'options' widget. May be removed later if 'Action buttons' are chosen.
  279. // The help text is not available for container. Let's add it to the
  280. // State box.
  281. $help_text = isset($instance['description']) ? $instance['description'] : '';
  282. $element['workflow']['workflow_sid'] = array(
  283. '#type' => $settings_options_type,
  284. '#title' => $settings_title_as_name ? t('Change @name state', array('@name' => $workflow_label)) : t('Target state'),
  285. '#access' => TRUE,
  286. '#options' => $options,
  287. // '#name' => $workflow_label,
  288. // '#parents' => array('workflow'),
  289. '#default_value' => $default_value,
  290. '#description' => t('@help', array('@help' => $help_text)),
  291. );
  292. }
  293. // Display scheduling form, but only if entity is being edited and user has
  294. // permission. State change cannot be scheduled at entity creation because
  295. // that leaves the entity in the (creation) state.
  296. if ($settings_schedule == TRUE) {
  297. if (variable_get('configurable_timezones', 1) && $user->uid && drupal_strlen($user->timezone)) {
  298. $timezone = $user->timezone;
  299. }
  300. else {
  301. $timezone = variable_get('date_default_timezone', 0);
  302. }
  303. $timezones = drupal_map_assoc(timezone_identifiers_list());
  304. $timestamp = $transition->getTimestamp();
  305. $hours = (!$transition->isScheduled()) ? '00:00' : format_date($timestamp, 'custom', 'H:i', $timezone);
  306. // Add a container, so checkbox and time stay together in extra fields.
  307. $element['workflow']['workflow_scheduling'] = array(
  308. '#type' => 'container',
  309. '#tree' => TRUE,
  310. );
  311. $element['workflow']['workflow_scheduling']['scheduled'] = array(
  312. '#type' => 'radios',
  313. '#title' => t('Schedule'),
  314. '#options' => array(
  315. '0' => t('Immediately'),
  316. '1' => t('Schedule for state change'),
  317. ),
  318. '#default_value' => $transition->isScheduled() ? '1' : '0',
  319. '#attributes' => array(
  320. // 'id' => 'scheduled_' . $form_id,
  321. 'class' => array(drupal_html_class('scheduled_' . $form_id)),
  322. ),
  323. );
  324. $element['workflow']['workflow_scheduling']['date_time'] = array(
  325. '#type' => 'fieldset',
  326. '#title' => t('At'),
  327. '#attributes' => array('class' => array('container-inline')),
  328. '#prefix' => '<div style="margin-left: 1em;">',
  329. '#suffix' => '</div>',
  330. '#states' => array(
  331. //'visible' => array(':input[id="' . 'scheduled_' . $form_id . '"]' => array('value' => '1')),
  332. 'visible' => array('input.' . drupal_html_class('scheduled_' . $form_id) => array('value' => '1')),
  333. ),
  334. );
  335. $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_date'] = array(
  336. '#type' => 'date',
  337. '#default_value' => array(
  338. 'day' => date('j', $timestamp),
  339. 'month' => date('n', $timestamp),
  340. 'year' => date('Y', $timestamp),
  341. ),
  342. );
  343. $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_hour'] = array(
  344. '#type' => 'textfield',
  345. '#title' => t('Time'),
  346. '#maxlength' => 7,
  347. '#size' => 6,
  348. '#default_value' => $hours,
  349. '#element_validate' => array('_workflow_transition_form_element_validate_time'),
  350. );
  351. $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_timezone'] = array(
  352. '#type' => $settings_schedule_timezone ? 'select' : 'hidden',
  353. '#title' => t('Time zone'),
  354. '#options' => $timezones,
  355. '#default_value' => array($timezone => $timezone),
  356. );
  357. $element['workflow']['workflow_scheduling']['date_time']['workflow_scheduled_help'] = array(
  358. '#type' => 'item',
  359. '#prefix' => '<br />',
  360. '#description' => t('Please enter a time.
  361. If no time is included, the default will be midnight on the specified date.
  362. The current time is: @time.', array('@time' => format_date(REQUEST_TIME, 'custom', 'H:i', $timezone))
  363. ),
  364. );
  365. }
  366. $element['workflow']['workflow_comment'] = array(
  367. '#type' => 'textarea',
  368. '#required' => $settings_comment == '2',
  369. '#access' => $settings_comment !='0', // Align with action buttons.
  370. '#title' => t('Workflow comment'),
  371. '#description' => t('A comment to put in the workflow log.'),
  372. '#default_value' => $transition->comment,
  373. '#rows' => 2,
  374. );
  375. // Add the fields and extra_fields from the WorkflowTransition.
  376. // Because we have a 'workflow' wrapper, it doesn't work flawlessly.
  377. field_attach_form('WorkflowTransition', $transition, $element['workflow'], $form_state);
  378. // Undo the following elements from field_attach_from. They mess up $this->getTransition().
  379. // - '#parents' corrupts the Defaultwidget.
  380. unset($element['workflow']['#parents']);
  381. // - '#pre_render' adds the exra_fields from workflow_field_extra_fields().
  382. // That doesn't work, since 'workflow' is not of #type 'form', but
  383. // 'container' or 'fieldset', and must be executed separately,.
  384. $element['workflow']['#pre_render'] = array_diff( $element['workflow']['#pre_render'], array('_field_extra_fields_pre_render') );
  385. // Add extra fields.
  386. $rescue_value = $element['workflow']['#type'];
  387. $element['workflow']['#type'] = 'form';
  388. $element['workflow'] = _field_extra_fields_pre_render($element['workflow']);
  389. $element['workflow']['#type'] = $rescue_value;
  390. // Finally, add Submit buttons/Action buttons.
  391. // Either a default 'Submit' button is added, or a button per permitted state.
  392. if ($settings_options_type == 'buttons') {
  393. // How do action buttons work? See also d.o. issue #2187151.
  394. // Create 'action buttons' per state option. Set $sid property on each button.
  395. // 1. Admin sets ['widget']['options']['#type'] = 'buttons'.
  396. // 2. This function formElement() creates 'action buttons' per state option;
  397. // sets $sid property on each button.
  398. // 3. User clicks button.
  399. // 4. Callback _workflow_transition_form_validate_buttons() sets proper State.
  400. // 5. Callback _workflow_transition_form_validate_buttons() sets Submit function.
  401. // @todo: this does not work yet for the Add Comment form.
  402. // Performance: inform workflow_form_alter() to do its job.
  403. _workflow_use_action_buttons(TRUE);
  404. // Hide the options box. It will be replaced by action buttons.
  405. $element['workflow']['workflow_sid']['#type'] = 'select';
  406. $element['workflow']['workflow_sid']['#access'] = FALSE;
  407. }
  408. // Some forms (Term) do not have 'base_form_id' set.
  409. if (isset($form_state['build_info']['base_form_id']) && $form_state['build_info']['base_form_id'] == 'workflow_transition_form') {
  410. // Add action buttons on WorkflowTransitionForm (history tab, formatter)
  411. // but not on Entity form, and not if action_buttons is selected.
  412. // you can explicitly NOT add a submit button, e.g., on VBO page.
  413. if ($instance['widget']['settings']['submit_function'] !== '') {
  414. // @todo D8: put buttons outside of 'workflow' element, in the standard location.
  415. $element['workflow']['actions']['#type'] = 'actions';
  416. $element['workflow']['actions']['submit'] = array(
  417. '#type' => 'submit',
  418. // '#access' => TRUE,
  419. '#value' => t('Update workflow'),
  420. '#weight' => -5,
  421. // '#submit' => array( isset($instance['widget']['settings']['submit_function']) ? $instance['widget']['settings']['submit_function'] : NULL),
  422. // '#executes_submit_callback' => TRUE,
  423. '#attributes' => array('class' => array('form-save-default-button')),
  424. );
  425. // The 'add submit' can explicitly set by workflowfield_field_formatter_view(),
  426. // to add the submit button on the Content view page and the Workflow history tab.
  427. // Add a submit button, but only on Entity View and History page.
  428. // Add the submit function only if one provided. Set the submit_callback accordingly.
  429. if (!empty($instance['widget']['settings']['submit_function'])) {
  430. $element['workflow']['actions']['submit']['#submit'] = array($instance['widget']['settings']['submit_function']);
  431. }
  432. else {
  433. // '#submit' Must be empty, or else the submit function is not called.
  434. // $element['workflow']['actions']['submit']['#submit'] = array();
  435. }
  436. }
  437. }
  438. /*
  439. $submit_functions = empty($instance['widget']['settings']['submit_function']) ? array() : array($instance['widget']['settings']['submit_function']);
  440. if ($settings_options_type == 'buttons' || $submit_functions) {
  441. }
  442. else {
  443. // In some cases, no submit callback function is specified. This is
  444. // explicitly done on e.g., the node edit form, because the workflow form
  445. // is 'just a field'.
  446. // So, no Submit button is to be shown.
  447. }
  448. */
  449. $form += $element;
  450. // Add class following node-form pattern (both on form and container).
  451. $workflow_type_id = ($workflow) ? $workflow->getName() : 'none'; // No workflow on New Action form.
  452. $form['#attributes']['class'][] = 'workflow-transition-form';
  453. $form['#attributes']['class'][] = 'workflow-transition-' . $workflow_type_id . '-form';
  454. return $form;
  455. }
  456. /**
  457. * {@inheritdoc}
  458. */
  459. public function validateForm(array &$form, array $form_state) {
  460. }
  461. /**
  462. * {@inheritdoc}
  463. */
  464. public function submitForm(array &$form, array &$form_state, array &$items) {
  465. // $items is a D7 parameter.
  466. // @todo: clean this code up. It is the result of gluing code together.
  467. global $user; // @todo #2287057: verify if submit() really is only used for UI. If not, $user must be passed.
  468. $entity = $this->entity;
  469. $entity_type = $this->entity_type;
  470. $field = $this->field;
  471. $field_name = $field['field_name'];
  472. // Retrieve the data from the form.
  473. if (isset($form_state['values']['workflow_field'])) {
  474. // If $entity filled: We are on a Entity View page or Workflow History Tab page.
  475. // If $entity empty: We are on an Advanced Action page.
  476. // $field = $form_state['values']['workflow_field'];
  477. // $instance = $form_state['values']['workflow_instance'];
  478. // $entity_type = $form_state['values']['workflow_entity_type'];
  479. // $entity = $form_state['values']['workflow_entity'];
  480. // $field_name = $field['field_name'];
  481. }
  482. elseif (isset($form_state['triggering_element'])) {
  483. // We are on an Entity/Node/Comment Form page (add/edit).
  484. $field_name = $form_state['triggering_element']['#workflow_field_name'];
  485. }
  486. else {
  487. // We are on an Entity/Comment Form page (add/edit).
  488. }
  489. // Determine if the transition is forced.
  490. // This can be set by a 'workflow_vbo action' in an additional form element.
  491. $force = isset($form_state['input']['workflow_force']) ? $form_state['input']['workflow_force'] : FALSE;
  492. // Set language. Multi-language is not supported for Workflow Node.
  493. $langcode = _workflow_metadata_workflow_get_properties($entity, array(), 'langcode', $entity_type, $field_name);
  494. if (!$entity) {
  495. // E.g., on VBO form.
  496. }
  497. elseif ($field_name) {
  498. // Save the entity, but only if we were not in edit mode.
  499. // Perhaps there is a better way, but for now we use 'changed' property.
  500. // Also test for 'is_new'. When Migrating content, the 'changed' property may be set externally.
  501. // Caveat: Some entities do not have 'changed' property set.
  502. if ((!empty($entity->is_new)) || (isset($entity->changed) && $entity->changed == REQUEST_TIME)) {
  503. // N.B. ONLY for Nodes!
  504. // We are in add/edit mode. No need to save the entity explicitly.
  505. // // Add the $form_state to the $items, so we can do a getTransition() later on.
  506. // $items[0]['workflow'] = $form_state['input'];
  507. // // Create a Transition. The Widget knows if it is scheduled.
  508. // $widget = new WorkflowDefaultWidget($field, $instance, $entity_type, $entity);
  509. // $new_sid = $widget->submit($form, $form_state, $items, $force);
  510. }
  511. elseif (isset($form_state['input'])) {
  512. // Save $entity, but only if sid has changed.
  513. // Use field_attach_update for this? Save always?
  514. $entity->{$field_name}[$langcode][0]['workflow'] = $form_state['input'];
  515. // @todo & totest: Save ony the field, not the complete entity.
  516. // workflow_entity_field_save($entity_type, $entity, $field_name, $langcode, FALSE);
  517. entity_save($entity_type, $entity);
  518. return; // <-- exit!
  519. }
  520. elseif ($entity_type == 'node') {
  521. // N.B. ONLY for Nodes!
  522. // We are saving a node from a comment.
  523. $entity->{$field_name}[$langcode] = $items;
  524. // @todo & totest: Save ony the field, not the complete entity.
  525. // workflow_entity_field_save($entity_type, $entity, $field_name, $langcode, FALSE);
  526. entity_save($entity_type, $entity);
  527. return; // <-- exit!
  528. }
  529. else {
  530. // We are saving a non-node from an entity form.
  531. $entity->{$field_name}[$langcode] = $items;
  532. }
  533. }
  534. else {
  535. // For a Node API form, only contrib fields need to be filled.
  536. // No updating of the node itself.
  537. // (Unless we need to record the timestamp.)
  538. // Add the $form_state to the $items, so we can do a getTransition() later on.
  539. $items[0]['workflow'] = $form_state['input'];
  540. // // Create a Transition. The Widget knows if it is scheduled.
  541. // $widget = new WorkflowDefaultWidget($field, $instance, $entity_type, $entity);
  542. // $new_sid = $widget->submit($form, $form_state, $items, $force);
  543. }
  544. // Extract the data from $items, depending on the type of widget.
  545. // @todo D8: use MassageFormValues($values, $form, $form_state).
  546. $old_sid = workflow_node_previous_state($entity, $entity_type, $field_name);
  547. if (!$old_sid) {
  548. // At this moment, $old_sid should have a value. If the content does not
  549. // have a state yet, old_sid contains '(creation)' state. But if the
  550. // content is not associated to a workflow, old_sid is now 0. This may
  551. // happen in workflow_vbo, if you assign a state to non-relevant nodes.
  552. $entity_id = entity_id($entity_type, $entity);
  553. drupal_set_message(t('Error: content @id has no workflow attached. The data is not saved.', array('@id' => $entity_id)), 'error');
  554. // The new state is still the previous state.
  555. $new_sid = $old_sid;
  556. return $new_sid;
  557. }
  558. // Now, save/execute the transition.
  559. $transition = $this->getTransition($old_sid, $items, $field_name, $user, $form, $form_state);
  560. // Try to execute the transition. Return $old_sid when error.
  561. if (!$transition) {
  562. // This should only happen when testing/developing.
  563. drupal_set_message(t('Error: the transition from @old_sid to @new_sid could not be generated.',
  564. array('@old_sid' => $old_sid, '@new_sid' => $new_sid)), 'error');
  565. // The current value is still the previous state.
  566. $new_sid = $old_sid;
  567. }
  568. elseif ($transition->isScheduled() || $transition->isExecuted()) {
  569. // A scheduled or executed transition must only be saved to the database.
  570. // The entity is not changed.
  571. $force = $force || $transition->isForced();
  572. $transition->save();
  573. // The current value is still the previous state.
  574. $new_sid = $old_sid;
  575. }
  576. elseif (!$transition->isScheduled()) {
  577. // Now the data is captured in the Transition, and before calling the
  578. // Execution, restore the default values for Workflow Field.
  579. // For instance, workflow_rules evaluates this.
  580. if ($field_name) {
  581. // $items = array();
  582. // $items[0]['value'] = $old_sid;
  583. // $entity->{$field_name}[$transition->language] = $items;
  584. }
  585. // It's an immediate change. Do the transition.
  586. // - validate option; add hook to let other modules change comment.
  587. // - add to history; add to watchdog
  588. // Return the new State ID. (Execution may fail and return the old Sid.)
  589. $force = $force || $transition->isForced();
  590. $new_sid = $transition->execute($force);
  591. }
  592. // The entity is still to be saved, so set to a 'normal' value.
  593. if ($field_name) {
  594. $items = array();
  595. $items[0]['value'] = $new_sid;
  596. $entity->{$field_name}[$transition->language] = $items;
  597. }
  598. return $new_sid;
  599. }
  600. /**
  601. * Extract WorkflowTransition or WorkflowScheduledTransition from the form.
  602. *
  603. * This merely extracts the transition from the form/widget. No validation.
  604. *
  605. * @param $old_sid
  606. * @param array $items
  607. * @param $field_name
  608. * @param \stdClass $user
  609. *
  610. * @return \WorkflowScheduledTransition|\WorkflowTransition|null
  611. */
  612. public function getTransition($old_sid, array $items, $field_name, stdClass $user, array &$form = array(), array &$form_state = array()) {
  613. $entity_type = $this->entity_type;
  614. $entity = $this->entity;
  615. // $entity_id = entity_id($entity_type, $entity);
  616. $field_name = !empty($this->field) ? $this->field['field_name'] : '';
  617. if (isset($items[0]['transition'])) {
  618. // a complete transition was already passed on.
  619. $transition = $items[0]['transition'];
  620. }
  621. else {
  622. // Get the new Transition properties. First the new State ID.
  623. if (isset($items[0]['workflow']['workflow_sid'])) {
  624. // We have shown a workflow form.
  625. $new_sid = $items[0]['workflow']['workflow_sid'];
  626. }
  627. elseif (isset($items[0]['value'])) {
  628. // We have shown a core options widget (radios, select).
  629. $new_sid = $items[0]['value'];
  630. }
  631. else {
  632. // This may happen if only 1 option is left, and a formatter is shown.
  633. $state = workflow_state_load_single($old_sid);
  634. if (!$state->isCreationState()) {
  635. $new_sid = $old_sid;
  636. }
  637. else {
  638. // This only happens on workflows, when only one transition from
  639. // '(creation)' to another state is allowed.
  640. /* @var $workflow Workflow */
  641. $workflow = $state->getWorkflow();
  642. $new_sid = $workflow->getFirstSid($this->entity_type, $this->entity, $field_name, $user, FALSE);
  643. }
  644. }
  645. // If an existing Transition has been edited, $hid is set.
  646. $hid = isset($items[0]['workflow']['workflow_hid']) ? $items[0]['workflow']['workflow_hid'] : '';
  647. // Get the comment.
  648. $comment = isset($items[0]['workflow']['workflow_comment']) ? $items[0]['workflow']['workflow_comment'] : '';
  649. // Remember, the workflow_scheduled element is not set on 'add' page.
  650. $scheduled = !empty($items[0]['workflow']['workflow_scheduling']['scheduled']);
  651. if ($hid) {
  652. // We are editing an existing transition. Only comment may be changed.
  653. $transition = workflow_transition_load($hid);
  654. $transition->comment = $comment;
  655. }
  656. elseif (!$scheduled) {
  657. $transition = new WorkflowTransition();
  658. $transition->setValues($entity_type, $entity, $field_name, $old_sid, $new_sid, $user->uid, REQUEST_TIME, $comment);
  659. }
  660. else {
  661. // Schedule the time to change the state.
  662. // If Field Form is used, use plain values;
  663. // If Node Form is used, use fieldset 'date_time'.
  664. $schedule = isset($items[0]['workflow']['workflow_scheduling']['date_time']) ? $items[0]['workflow']['workflow_scheduling']['date_time'] : $items[0]['workflow'];
  665. if (!isset($schedule['workflow_scheduled_hour'])) {
  666. $schedule['workflow_scheduled_hour'] = '00:00';
  667. }
  668. $scheduled_date_time
  669. = $schedule['workflow_scheduled_date']['year']
  670. . substr('0' . $schedule['workflow_scheduled_date']['month'], -2, 2)
  671. . substr('0' . $schedule['workflow_scheduled_date']['day'], -2, 2)
  672. . ' '
  673. . $schedule['workflow_scheduled_hour']
  674. . ' '
  675. . $schedule['workflow_scheduled_timezone'];
  676. if ($timestamp = strtotime($scheduled_date_time)) {
  677. $transition = new WorkflowScheduledTransition();
  678. $transition->setValues($entity_type, $entity, $field_name, $old_sid, $new_sid, $user->uid, $timestamp, $comment);
  679. }
  680. else {
  681. $transition = NULL;
  682. }
  683. }
  684. }
  685. return $transition;
  686. }
  687. }