Workflow.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811
  1. <?php
  2. /**
  3. * @file
  4. * Contains workflow\includes\Entity\Workflow.
  5. * Contains workflow\includes\Entity\WorkflowController.
  6. */
  7. // Include file to avoid drush upgrade errors.
  8. include_once('WorkflowInterface.php');
  9. class Workflow extends Entity implements WorkflowInterface {
  10. public $wid = 0;
  11. public $name = '';
  12. public $tab_roles = array();
  13. public $options = array();
  14. protected $creation_sid = 0;
  15. // Attached States.
  16. public $states = NULL;
  17. public $transitions = NULL;
  18. /**
  19. * CRUD functions.
  20. */
  21. // public function __construct(array $values = array(), $entityType = NULL) {
  22. // return parent::__construct($values, $entityType);
  23. // }
  24. public function __clone() {
  25. // Clone the arrays of States and Transitions.
  26. foreach ($this->states as &$state) {
  27. $state = clone $state;
  28. }
  29. foreach ($this->transitions as &$transition) {
  30. $transition = clone $transition;
  31. }
  32. }
  33. /**
  34. * Given information, update or insert a new workflow.
  35. *
  36. * This also handles importing, rebuilding, reverting from Features,
  37. * as defined in workflow.features.inc.
  38. *
  39. * When changing this function, test with the following situations:
  40. * - maintain Workflow in Admin UI;
  41. * - clone Workflow in Admin UI;
  42. * - create/revert/rebuild Workflow with Features; @see workflow.features.inc
  43. * - save Workflow programmatically;
  44. */
  45. public function save($create_creation_state = TRUE) {
  46. // Are we saving a new Workflow?
  47. $is_new = !empty($this->is_new);
  48. // Are we rebuilding, reverting a new Workflow? @see workflow.features.inc
  49. $is_rebuild = !empty($this->is_rebuild) || !empty($this->is_reverted);
  50. if ($is_rebuild) {
  51. $this->is_rebuild = TRUE;
  52. $this->preRebuild();
  53. }
  54. $return = parent::save();
  55. // On either clone or rebuild from features.
  56. if ($is_new || $is_rebuild) {
  57. $this->rebuildInternals();
  58. if ($is_rebuild) {
  59. // The above may have marked us overridden!
  60. $this->status = ENTITY_IN_CODE;
  61. parent::save();
  62. }
  63. }
  64. // Make sure a Creation state exists.
  65. if ($is_new) {
  66. $state = $this->getCreationState();
  67. }
  68. workflow_reset_cache($this->wid);
  69. return $return;
  70. }
  71. /**
  72. * Rebuild things that get saved with this entity.
  73. */
  74. protected function preRebuild() {
  75. // Remap roles. They can come from another system with shifted role IDs.
  76. // See also https://drupal.org/node/1702626 .
  77. $this->rebuildRoles($this->tab_roles);
  78. // After update.php or import feature, label might be empty. @todo: remove in D8.
  79. if (empty($this->label)) {
  80. $this->label = $this->name;
  81. }
  82. }
  83. /**
  84. * Rebuild internals that get saved separately.
  85. */
  86. protected function rebuildInternals() {
  87. // Insert the type_map when building from Features.
  88. if (isset($this->typeMap)) {
  89. foreach ($this->typeMap as $node_type) {
  90. workflow_insert_workflow_type_map($node_type, $this->wid);
  91. }
  92. }
  93. // Index the existing states and transitions by name.
  94. $db_name_map = WorkflowState::getStates($this->wid, TRUE); // sid -> state.
  95. $db_states = array(); // name -> state.
  96. foreach ($db_name_map as $state) {
  97. $db_states[$state->getName()] = $state;
  98. }
  99. $db_transitions = array();
  100. foreach (entity_load('WorkflowConfigTransition') as $transition) {
  101. if ($transition->wid == $this->wid) {
  102. $start_name = $db_name_map[$transition->sid]->getName();
  103. $end_name = $db_name_map[$transition->target_sid]->getName();
  104. $name = WorkflowConfigTransition::machineName($start_name, $end_name);
  105. $db_transitions[$name] = $transition;
  106. }
  107. }
  108. // Update/create states.
  109. $states = isset($this->states) ? $this->states : array();
  110. $saved_states = array(); // Saved states: key -> sid.
  111. $saved_state_names = array();
  112. foreach ($states as $key => $data) {
  113. $data = (array)$data;
  114. $name = $data['name'];
  115. if (isset($db_states[$name])) {
  116. $state = $db_states[$name];
  117. }
  118. else {
  119. $state = $this->createState($name, FALSE);
  120. }
  121. $state->wid = $this->wid;
  122. $state->state = $data['state'];
  123. $state->weight = $data['weight'];
  124. $state->sysid = $data['sysid'];
  125. if (!$data['status']) {
  126. $this->rebuildStateInactive($state);
  127. }
  128. $state->status = $data['status'];
  129. $state->save();
  130. unset($db_states[$name]);
  131. $saved_states[$key] = $state;
  132. $saved_state_names[$state->sid] = $key;
  133. }
  134. // Update/create transitions.
  135. $transitions = isset($this->transitions) ? $this->transitions : array();
  136. foreach ($transitions as $name => $data) {
  137. $data = (array)$data;
  138. if (is_numeric($name)) {
  139. $start_state = $saved_states[$saved_state_names[$data['sid']]];
  140. $end_state = $saved_states[$saved_state_names[$data['target_sid']]];
  141. $name = WorkflowConfigTransition::machineName($start_state->getName(),
  142. $end_state->getName());
  143. }
  144. else {
  145. $start_state = $saved_states[$data['start_state']];
  146. $end_state = $saved_states[$data['end_state']];
  147. }
  148. if (isset($db_transitions[$name])) {
  149. $transition = $db_transitions[$name];
  150. }
  151. else {
  152. $transition = $this->createTransition($start_state->sid,
  153. $end_state->sid);
  154. }
  155. $transition->wid = $this->wid;
  156. $transition->sid = $start_state->sid;
  157. $transition->target_sid = $end_state->sid;
  158. $transition->label = $data['label'];
  159. $transition->roles = $data['roles'];
  160. $this->rebuildRoles($transition->roles);
  161. $transition->save();
  162. unset($db_transitions[$name]);
  163. }
  164. // Any states/transitions left in $db_states/transitions need deletion.
  165. foreach ($db_states as $state) {
  166. $this->rebuildStateInactive($state);
  167. $state->delete();
  168. }
  169. foreach ($db_transitions as $transition) {
  170. $transition->delete();
  171. }
  172. // Clear the caches, and set $this->states and $this->transitions.
  173. $this->states = $this->transitions = NULL;
  174. $this->getStates(TRUE, TRUE);
  175. $this->getTransitions(FALSE, array(), TRUE);
  176. }
  177. /**
  178. * Handle a state becoming inactive during a rebuild.
  179. */
  180. protected function rebuildStateInactive($state) {
  181. if (!$state->isActive()) {
  182. return;
  183. }
  184. // TODO: What should we do in this case? Is this safe?
  185. $state->deactivate(NULL);
  186. }
  187. /**
  188. * Given a wid, delete the workflow and its data.
  189. *
  190. * @deprecated: workflow_delete_workflows_by_wid() --> Workflow::delete().
  191. */
  192. public function delete() {
  193. $wid = $this->wid;
  194. // Notify any interested modules before we delete the workflow.
  195. // E.g., Workflow Node deletes the {workflow_type_map} record.
  196. module_invoke_all('workflow', 'workflow delete', $wid, NULL, NULL, FALSE);
  197. // Delete associated state (also deletes any associated transitions).
  198. foreach ($this->getStates($all = TRUE) as $state) {
  199. $state->deactivate(0);
  200. $state->delete();
  201. }
  202. // Delete the workflow.
  203. db_delete('workflows')->condition('wid', $wid)->execute();
  204. }
  205. /**
  206. * Validate the workflow. Generate a message if not correct.
  207. *
  208. * This function is used on the settings page of:
  209. * - Workflow node: workflow_admin_ui_type_map_form()
  210. * - Workflow field: WorkflowItem->settingsForm()
  211. *
  212. * @return bool
  213. * $is_valid
  214. */
  215. public function isValid() {
  216. $is_valid = TRUE;
  217. // Don't allow workflows with no states. There should always be a creation state.
  218. $states = $this->getStates($all = FALSE);
  219. if (count($states) < 1) {
  220. // That's all, so let's remind them to create some states.
  221. $message = t('Workflow %workflow has no states defined, so it cannot be assigned to content yet.',
  222. array('%workflow' => $this->getName()));
  223. drupal_set_message($message, 'warning');
  224. // Skip allowing this workflow.
  225. $is_valid = FALSE;
  226. }
  227. // Also check for transitions, at least out of the creation state. Use 'ALL' role.
  228. $transitions = $this->getTransitionsBySid($this->getCreationSid(), $roles = 'ALL');
  229. if (count($transitions) < 1) {
  230. // That's all, so let's remind them to create some transitions.
  231. $message = t('Workflow %workflow has no transitions defined, so it cannot be assigned to content yet.',
  232. array('%workflow' => $this->getName()));
  233. drupal_set_message($message, 'warning');
  234. // Skip allowing this workflow.
  235. $is_valid = FALSE;
  236. }
  237. // If the Workflow is mapped to a node type, check if workflow->options is set.
  238. if ($this->getTypeMap() && !count($this->options)) {
  239. // That's all, so let's remind them to create some transitions.
  240. $message = t('Please maintain Workflow %workflow on its <a href="@url">settings</a> page.',
  241. array(
  242. '%workflow' => $this->getName(),
  243. '@url' => url('admin/config/workflow/workflow/manage/' . $this->wid),
  244. )
  245. );
  246. drupal_set_message($message, 'warning');
  247. // Skip allowing this workflow.
  248. // $is_valid = FALSE;
  249. }
  250. return $is_valid;
  251. }
  252. /**
  253. * Returns if the Workflow may be deleted.
  254. *
  255. * @return bool $is_deletable
  256. * TRUE if a Workflow may safely be deleted.
  257. */
  258. public function isDeletable() {
  259. $is_deletable = FALSE;
  260. // May not be deleted if a TypeMap exists.
  261. if ($this->getTypeMap()) {
  262. return $is_deletable;
  263. }
  264. // May not be deleted if assigned to a Field.
  265. foreach (_workflow_info_fields() as $field) {
  266. if ($field['settings']['wid'] == $this->wid) {
  267. return $is_deletable;
  268. }
  269. }
  270. // May not be deleted if a State is assigned to a state.
  271. foreach ($this->getStates(TRUE) as $state) {
  272. if ($state->count()) {
  273. return $is_deletable;
  274. }
  275. }
  276. $is_deletable = TRUE;
  277. return $is_deletable;
  278. }
  279. /**
  280. * Property functions.
  281. */
  282. /**
  283. * Returns the workflow id.
  284. *
  285. * @return int
  286. * $wid
  287. */
  288. public function getWorkflowId() {
  289. return $this->wid;
  290. }
  291. /**
  292. * Create a new state for this workflow.
  293. *
  294. * @param string $name
  295. * The untranslated human readable label of the state.
  296. * @param bool $save
  297. * Indicator if the new state must be saved. Normally, the new State is
  298. * saved directly in the database. This is because you can use States only
  299. * with Transitions, and they rely on State IDs which are generated
  300. * magically when saving the State. But you may need a temporary state.
  301. *
  302. * @return WorkflowState
  303. */
  304. public function createState($name, $save = TRUE) {
  305. $wid = $this->wid;
  306. $state = workflow_state_load_by_name($name, $wid);
  307. if (!$state) {
  308. $state = entity_create('WorkflowState', array('name' => $name, 'state' => $name, 'wid' => $wid));
  309. if ($save) {
  310. $state->save();
  311. }
  312. }
  313. $state->setWorkflow($this);
  314. // Maintain the new object in the workflow.
  315. $this->states[$state->sid] = $state;
  316. return $state;
  317. }
  318. /**
  319. * Gets the initial state for a newly created entity.
  320. */
  321. public function getCreationState() {
  322. $sid = $this->getCreationSid();
  323. return ($sid) ? $this->getState($sid) : $this->createState(WORKFLOW_CREATION_STATE_NAME);
  324. }
  325. /**
  326. * Gets the ID of the initial state for a newly created entity.
  327. */
  328. public function getCreationSid() {
  329. if (!$this->creation_sid) {
  330. foreach ($this->getStates($all = TRUE) as $state) {
  331. if ($state->isCreationState()) {
  332. $this->creation_sid = $state->sid;
  333. }
  334. }
  335. }
  336. return $this->creation_sid;
  337. }
  338. /**
  339. * Gets the first valid state ID, after the creation state.
  340. *
  341. * Uses WorkflowState::getOptions(), because this does a access check.
  342. * The first State ID is user-dependent!
  343. */
  344. public function getFirstSid($entity_type, $entity, $field_name, $user, $force) {
  345. $creation_state = $this->getCreationState();
  346. $options = $creation_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  347. if ($options) {
  348. $keys = array_keys($options);
  349. $sid = $keys[0];
  350. }
  351. else {
  352. // This should never happen, but it did during testing.
  353. drupal_set_message(t('There are no workflow states available. Please notify your site administrator.'), 'error');
  354. $sid = 0;
  355. }
  356. return $sid;
  357. }
  358. /**
  359. * Returns the next state for the current state.
  360. *
  361. * @param string $entity_type
  362. * The type of the entity at hand.
  363. * @param object $entity
  364. * The entity at hand. May be NULL (E.g., on a Field settings page).
  365. * @param $field_name
  366. * @param $user
  367. * @param bool $force
  368. *
  369. * @return int $sid
  370. * A state ID.
  371. */
  372. public function getNextSid($entity_type, $entity, $field_name, $user, $force = FALSE) {
  373. $new_sid = workflow_node_current_state($entity, $entity_type, $field_name);
  374. if ($new_sid && $new_state = workflow_state_load_single($new_sid)) {
  375. /* @var $current_state WorkflowState */
  376. $options = $new_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  377. // Loop over every option. To find the next one.
  378. $flag = $new_state->isCreationState();
  379. foreach ($options as $sid => $name) {
  380. if ($flag) {
  381. $new_sid = $sid;
  382. break;
  383. }
  384. if ($sid == $new_state->sid) {
  385. $flag = TRUE;
  386. }
  387. }
  388. }
  389. return $new_sid;
  390. }
  391. /**
  392. * Gets all states for a given workflow.
  393. *
  394. * @param mixed $all
  395. * Indicates to which states to return.
  396. * - TRUE = all, including Creation and Inactive;
  397. * - FALSE = only Active states, not Creation;
  398. * - 'CREATION' = only Active states, including Creation.
  399. *
  400. * @return array
  401. * An array of WorkflowState objects.
  402. */
  403. public function getStates($all = FALSE, $reset = FALSE) {
  404. if ($this->states === NULL || $reset) {
  405. $this->states = $this->wid ? WorkflowState::getStates($this->wid, $reset) : array();
  406. }
  407. // Do not unset, but add to array - you'll remove global objects otherwise.
  408. $states = array();
  409. foreach ($this->states as $state) {
  410. if ($all === TRUE) {
  411. $states[$state->sid] = $state;
  412. }
  413. elseif (($all === FALSE) && ($state->isActive() && !$state->isCreationState())) {
  414. $states[$state->sid] = $state;
  415. }
  416. elseif (($all == 'CREATION') && ($state->isActive() || $state->isCreationState())) {
  417. $states[$state->sid] = $state;
  418. }
  419. }
  420. return $states;
  421. }
  422. /**
  423. * Gets a state for a given workflow.
  424. *
  425. * @param mixed $key
  426. * A state ID or state Name.
  427. *
  428. * @return WorkflowState
  429. * A WorkflowState object.
  430. */
  431. public function getState($key) {
  432. if (is_numeric($key)) {
  433. return workflow_state_load_single($key, $this->wid);
  434. }
  435. else {
  436. return workflow_state_load_by_name($key, $this->wid);
  437. }
  438. }
  439. /**
  440. * Creates a Transition for this workflow.
  441. */
  442. public function createTransition($sid, $target_sid, $values = array()) {
  443. $workflow = $this;
  444. if (is_numeric($sid) && is_numeric($target_sid)) {
  445. $values['sid'] = $sid;
  446. $values['target_sid'] = $target_sid;
  447. }
  448. else {
  449. $state = $workflow->getState($sid);
  450. $target_state = $workflow->getState($target_sid);
  451. $values['sid'] = $state->sid;
  452. $values['target_sid'] = $target_state->sid;
  453. }
  454. // First check if this transition already exists.
  455. if ($transitions = entity_load('WorkflowConfigTransition', FALSE, $values)) {
  456. $transition = reset($transitions);
  457. }
  458. else {
  459. $values['wid'] = $workflow->wid;
  460. $transition = entity_create('WorkflowConfigTransition', $values);
  461. $transition->save();
  462. }
  463. $transition->setWorkflow($this);
  464. // Maintain the new object in the workflow.
  465. $this->transitions[$transition->tid] = $transition;
  466. return $transition;
  467. }
  468. /**
  469. * Sorts all Transitions for this workflow, according to State weight.
  470. *
  471. * This is only needed for the Admin UI.
  472. */
  473. public function sortTransitions() {
  474. // Sort the transitions on state weight.
  475. usort($this->transitions, '_workflow_transitions_sort_by_weight');
  476. }
  477. /**
  478. * Loads all allowed ConfigTransitions for this workflow.
  479. *
  480. * @param mixed $tids
  481. * Array of Transitions IDs. If FALSE, show all transitions.
  482. * @param array $conditions
  483. * $conditions['sid'] : if provided, a 'from' State ID.
  484. * $conditions['target_sid'] : if provided, a 'to' state ID.
  485. * $conditions['roles'] : if provided, an array of roles, or 'ALL'.
  486. * @param bool $reset
  487. * Indicator to reset the cache.
  488. *
  489. * @return array
  490. * An array of keyed transitions.
  491. */
  492. public function getTransitions($tids = FALSE, array $conditions = array(), $reset = FALSE) {
  493. $config_transitions = array();
  494. // Get valid + creation states.
  495. $states = $this->getStates('CREATION');
  496. // Get filters on 'from' states, 'to' states, roles.
  497. $sid = isset($conditions['sid']) ? $conditions['sid'] : FALSE;
  498. $target_sid = isset($conditions['target_sid']) ? $conditions['target_sid'] : FALSE;
  499. $roles = isset($conditions['roles']) ? $conditions['roles'] : 'ALL';
  500. // Cache all transitions in the workflow.
  501. // We may have 0 transitions....
  502. if ($this->transitions === NULL) {
  503. $this->transitions = array();
  504. // Get all transitions. (Even from other workflows. :-( )
  505. $config_transitions = entity_load('WorkflowConfigTransition', $tids, array(), $reset);
  506. foreach ($config_transitions as &$config_transition) {
  507. if (isset($states[$config_transition->sid])) {
  508. $config_transition->setWorkflow($this);
  509. $this->transitions[$config_transition->tid] = $config_transition;
  510. }
  511. }
  512. $this->sortTransitions();
  513. }
  514. $config_transitions = array();
  515. foreach ($this->transitions as &$config_transition) {
  516. if (!isset($states[$config_transition->sid])) {
  517. // Not a valid transition for this workflow.
  518. }
  519. elseif ($sid && $sid != $config_transition->sid) {
  520. // Not the requested 'from' state.
  521. }
  522. elseif ($target_sid && $target_sid != $config_transition->target_sid) {
  523. // Not the requested 'to' state.
  524. }
  525. elseif ($roles == 'ALL' || $config_transition->isAllowed($roles)) {
  526. // Transition is allowed, permitted. Add to list.
  527. $config_transition->setWorkflow($this);
  528. $config_transitions[$config_transition->tid] = $config_transition;
  529. }
  530. else {
  531. // Transition is otherwise not allowed.
  532. }
  533. }
  534. return $config_transitions;
  535. }
  536. public function getTransitionsByTid($tid, $roles = '', $reset = FALSE) {
  537. $conditions = array(
  538. 'roles' => $roles,
  539. );
  540. return $this->getTransitions(array($tid), $conditions, $reset);
  541. }
  542. public function getTransitionsBySid($sid, $roles = '', $reset = FALSE) {
  543. $conditions = array(
  544. 'sid' => $sid,
  545. 'roles' => $roles,
  546. );
  547. return $this->getTransitions(FALSE, $conditions, $reset);
  548. }
  549. public function getTransitionsByTargetSid($target_sid, $roles = '', $reset = FALSE) {
  550. $conditions = array(
  551. 'target_sid' => $target_sid,
  552. 'roles' => $roles,
  553. );
  554. return $this->getTransitions(FALSE, $conditions, $reset);
  555. }
  556. /**
  557. * Get a specific transition. Therefore, use $roles = 'ALL'.
  558. */
  559. public function getTransitionsBySidTargetSid($sid, $target_sid, $roles = 'ALL', $reset = FALSE) {
  560. $conditions = array(
  561. 'sid' => $sid,
  562. 'target_sid' => $target_sid,
  563. 'roles' => $roles,
  564. );
  565. return $this->getTransitions(FALSE, $conditions, $reset);
  566. }
  567. /**
  568. * Gets a the type map for a given workflow.
  569. *
  570. * @param int $sid
  571. * A state ID.
  572. *
  573. * @return array
  574. * An array of typemaps.
  575. */
  576. public function getTypeMap() {
  577. $result = array();
  578. $type_maps = module_exists('workflownode') ? workflow_get_workflow_type_map_by_wid($this->wid) : array();
  579. foreach ($type_maps as $map) {
  580. $result[] = $map->type;
  581. }
  582. return $result;
  583. }
  584. /**
  585. * Gets a setting from the state object.
  586. */
  587. public function getSetting($key, array $field = array()) {
  588. switch ($key) {
  589. case 'watchdog_log':
  590. if (isset($this->options['watchdog_log'])) {
  591. // This is set via Node API.
  592. return $this->options['watchdog_log'];
  593. }
  594. elseif ($field) {
  595. if (isset($field['settings']['watchdog_log'])) {
  596. // This is set via Field API.
  597. return $field['settings']['watchdog_log'];
  598. }
  599. }
  600. drupal_set_message('Setting Workflow::getSetting(' . $key . ') does not exist', 'error');
  601. break;
  602. default:
  603. drupal_set_message('Setting Workflow::getSetting(' . $key . ') does not exist', 'error');
  604. }
  605. }
  606. /**
  607. * Mimics Entity API functions.
  608. */
  609. public function getName() {
  610. return $this->name;
  611. }
  612. protected function defaultLabel() {
  613. return isset($this->label) ? $this->label : '';
  614. }
  615. protected function defaultUri() {
  616. return array('path' => 'admin/config/workflow/workflow/manage/' . $this->wid);
  617. }
  618. protected function rebuildRoles(array &$roles) {
  619. $role_map = isset($this->system_roles) ? $this->system_roles : array();
  620. if (!$role_map) {
  621. return;
  622. }
  623. // See also https://drupal.org/node/1702626 .
  624. $new_roles = array();
  625. foreach ($roles as $key => $rid) {
  626. if ($rid == WORKFLOW_ROLE_AUTHOR_RID) {
  627. $new_roles[$rid] = $rid;
  628. }
  629. else {
  630. if ($role = user_role_load_by_name($role_map[$rid])) {
  631. $new_roles[$role->rid] = (int)($role->rid);
  632. }
  633. }
  634. }
  635. $roles = $new_roles;
  636. }
  637. }
  638. /**
  639. * Helper function to sort the transitions.
  640. *
  641. * @param WorkflowConfigTransition $a
  642. * @param WorkflowConfigTransition $b
  643. *
  644. * @return int
  645. */
  646. function _workflow_transitions_sort_by_weight($a, $b) {
  647. // First sort on From-State.
  648. $old_state_a = $a->getOldState();
  649. $old_state_b = $b->getOldState();
  650. if ($old_state_a->weight < $old_state_b->weight) return -1;
  651. if ($old_state_a->weight > $old_state_b->weight) return +1;
  652. // Then sort on To-State.
  653. $new_state_a = $a->getNewState();
  654. $new_state_b = $b->getNewState();
  655. if ($new_state_a->weight < $new_state_b->weight) return -1;
  656. if ($new_state_a->weight > $new_state_b->weight) return +1;
  657. return 0;
  658. }
  659. /**
  660. * Implements a controller class for Workflow.
  661. */
  662. class WorkflowController extends EntityAPIControllerExportable {
  663. // public function create(array $values = array()) { return parent::create($values); }
  664. // public function load($ids = array(), $conditions = array()) { }
  665. public function delete($ids, DatabaseTransaction $transaction = NULL) {
  666. // @todo: replace WorkflowController::delete() with parent.
  667. // @todo: throw error if not workflow->isDeletable().
  668. foreach ($ids as $wid) {
  669. if ($workflow = workflow_load($wid)) {
  670. $workflow->delete();
  671. }
  672. }
  673. $this->resetCache();
  674. }
  675. /**
  676. * Overrides DrupalDefaultEntityController::cacheGet().
  677. *
  678. * Override default function, due to Core issue #1572466.
  679. */
  680. protected function cacheGet($ids, $conditions = array()) {
  681. // Load any available entities from the internal cache.
  682. if ($ids === FALSE && !$conditions) {
  683. return $this->entityCache;
  684. }
  685. return parent::cacheGet($ids, $conditions);
  686. }
  687. /**
  688. * Overrides DrupalDefaultEntityController::cacheSet().
  689. */
  690. /*
  691. // protected function cacheSet($entities) { }
  692. // return parent::cacheSet($entities);
  693. // }
  694. */
  695. /**
  696. * Overrides DrupalDefaultEntityController::resetCache().
  697. *
  698. * Called by workflow_reset_cache, to
  699. * Reset the Workflow when States, Transitions have been changed.
  700. */
  701. // public function resetCache(array $ids = NULL) {
  702. // parent::resetCache($ids);
  703. // }
  704. /**
  705. * Overrides DrupalDefaultEntityController::attachLoad().
  706. */
  707. protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
  708. foreach ($queried_entities as $entity) {
  709. // Load the states, so they are already present on the next (cached) load.
  710. $entity->states = $entity->getStates($all = TRUE);
  711. $entity->transitions = $entity->getTransitions(FALSE);
  712. $entity->typeMap = $entity->getTypeMap();
  713. }
  714. parent::attachLoad($queried_entities, $revision_id);
  715. }
  716. }