Workflow.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  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->tab_roles = $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. if (isset($saved_states[$data['sid']]) && isset($saved_states[$data['target_sid']])) {
  140. $start_state = $saved_states[$data['sid']]; // #2876303
  141. $end_state = $saved_states[$data['target_sid']]; // #2876303
  142. }
  143. else {
  144. $start_state = $this->getState($data['sid']);
  145. $end_state = $this->getState($data['target_sid']);
  146. }
  147. }
  148. else {
  149. $start_state = $saved_states[$data['start_state']];
  150. $end_state = $saved_states[$data['end_state']];
  151. }
  152. $name = WorkflowConfigTransition::machineName($start_state->getName(), $end_state->getName());
  153. if (isset($db_transitions[$name])) {
  154. $transition = $db_transitions[$name];
  155. }
  156. else {
  157. $transition = $this->createTransition($start_state->sid, $end_state->sid);
  158. }
  159. $transition->wid = $this->wid;
  160. $transition->sid = $start_state->sid;
  161. $transition->target_sid = $end_state->sid;
  162. $transition->label = $data['label'];
  163. $transition->roles = $this->rebuildRoles($data['roles']);
  164. $transition->save();
  165. unset($db_transitions[$name]);
  166. }
  167. // Any states/transitions left in $db_states/transitions need deletion.
  168. foreach ($db_states as $state) {
  169. $this->rebuildStateInactive($state);
  170. $state->delete();
  171. }
  172. foreach ($db_transitions as $transition) {
  173. $transition->delete();
  174. }
  175. // Clear the caches, and set $this->states and $this->transitions.
  176. $this->states = $this->transitions = NULL;
  177. $this->getStates(TRUE, TRUE);
  178. $this->getTransitions(FALSE, array(), TRUE);
  179. }
  180. /**
  181. * Handle a state becoming inactive during a rebuild.
  182. */
  183. protected function rebuildStateInactive($state) {
  184. if (!$state->isActive()) {
  185. return;
  186. }
  187. // TODO: What should we do in this case? Is this safe?
  188. $state->deactivate(NULL);
  189. }
  190. /**
  191. * Given a wid, delete the workflow and its data.
  192. *
  193. * @deprecated: workflow_delete_workflows_by_wid() --> Workflow::delete().
  194. */
  195. public function delete() {
  196. $wid = $this->wid;
  197. // Notify any interested modules before we delete the workflow.
  198. // E.g., Workflow Node deletes the {workflow_type_map} record.
  199. module_invoke_all('workflow', 'workflow delete', $wid, NULL, NULL, FALSE);
  200. // Delete associated state (also deletes any associated transitions).
  201. foreach ($this->getStates($all = TRUE) as $state) {
  202. $state->deactivate(0);
  203. $state->delete();
  204. }
  205. // Delete the workflow.
  206. db_delete('workflows')->condition('wid', $wid)->execute();
  207. }
  208. /**
  209. * Validate the workflow. Generate a message if not correct.
  210. *
  211. * This function is used on the settings page of:
  212. * - Workflow node: workflow_admin_ui_type_map_form()
  213. * - Workflow field: WorkflowItem->settingsForm()
  214. *
  215. * @return bool
  216. * $is_valid
  217. */
  218. public function isValid() {
  219. $is_valid = TRUE;
  220. $workflow_name = $this->getName();
  221. $wid = $this->getWorkflowId();
  222. // Don't allow workflows with no states. There should always be a creation state.
  223. $states = $this->getStates($all = FALSE);
  224. if (count($states) < 1) {
  225. // That's all, so let's remind them to create some states.
  226. $message = t('Workflow %workflow has no states defined, so it cannot be assigned to content yet.',
  227. array('%workflow' => $workflow_name));
  228. drupal_set_message($message, 'warning');
  229. // Skip allowing this workflow.
  230. $is_valid = FALSE;
  231. }
  232. // Also check for transitions, at least out of the creation state. Use 'ALL' role.
  233. $transitions = $this->getTransitionsBySid($this->getCreationSid(), $roles = 'ALL');
  234. if (count($transitions) < 1) {
  235. // That's all, so let's remind them to create some transitions.
  236. $message = t('Workflow %workflow has no transitions defined, so it cannot be assigned to content yet.',
  237. array('%workflow' => $workflow_name));
  238. drupal_set_message($message, 'warning');
  239. // Skip allowing this workflow.
  240. $is_valid = FALSE;
  241. }
  242. // If the Workflow is mapped to a node type, check if workflow->options is set.
  243. if ($this->getTypeMap() && !count($this->options)) {
  244. // That's all, so let's remind them to create some transitions.
  245. $message = t('Please maintain Workflow %workflow on its <a href="@url">settings</a> page.',
  246. array(
  247. '%workflow' => $this->getName(),
  248. '@url' => url(WORKFLOW_ADMIN_UI_PATH . "/manage/$wid"),
  249. )
  250. );
  251. drupal_set_message($message, 'warning');
  252. // Skip allowing this workflow.
  253. // $is_valid = FALSE;
  254. }
  255. return $is_valid;
  256. }
  257. /**
  258. * Returns if the Workflow may be deleted.
  259. *
  260. * @return bool $is_deletable
  261. * TRUE if a Workflow may safely be deleted.
  262. */
  263. public function isDeletable() {
  264. $is_deletable = FALSE;
  265. // May not be deleted if a TypeMap exists.
  266. if ($this->getTypeMap()) {
  267. return $is_deletable;
  268. }
  269. // May not be deleted if assigned to a Field.
  270. foreach (_workflow_info_fields() as $field) {
  271. if ($field['settings']['wid'] == $this->wid) {
  272. return $is_deletable;
  273. }
  274. }
  275. // May not be deleted if a State is assigned to a state.
  276. foreach ($this->getStates(TRUE) as $state) {
  277. if ($state->count()) {
  278. return $is_deletable;
  279. }
  280. }
  281. $is_deletable = TRUE;
  282. return $is_deletable;
  283. }
  284. /**
  285. * Property functions.
  286. */
  287. /**
  288. * Returns the workflow id.
  289. *
  290. * @return int
  291. * $wid
  292. */
  293. public function getWorkflowId() {
  294. return $this->wid;
  295. }
  296. /**
  297. * Create a new state for this workflow.
  298. *
  299. * @param string $name
  300. * The untranslated human readable label of the state.
  301. * @param bool $save
  302. * Indicator if the new state must be saved. Normally, the new State is
  303. * saved directly in the database. This is because you can use States only
  304. * with Transitions, and they rely on State IDs which are generated
  305. * magically when saving the State. But you may need a temporary state.
  306. *
  307. * @return WorkflowState
  308. */
  309. public function createState($name, $save = TRUE) {
  310. $wid = $this->wid;
  311. $state = workflow_state_load_by_name($name, $wid);
  312. if (!$state) {
  313. $state = entity_create('WorkflowState', array('name' => $name, 'state' => $name, 'wid' => $wid));
  314. if ($save) {
  315. $state->save();
  316. }
  317. }
  318. $state->setWorkflow($this);
  319. // Maintain the new object in the workflow.
  320. $this->states[$state->sid] = $state;
  321. return $state;
  322. }
  323. /**
  324. * Gets the initial state for a newly created entity.
  325. */
  326. public function getCreationState() {
  327. $sid = $this->getCreationSid();
  328. return ($sid) ? $this->getState($sid) : $this->createState(WORKFLOW_CREATION_STATE_NAME);
  329. }
  330. /**
  331. * Gets the ID of the initial state for a newly created entity.
  332. */
  333. public function getCreationSid() {
  334. if (!$this->creation_sid) {
  335. foreach ($this->getStates($all = TRUE) as $state) {
  336. if ($state->isCreationState()) {
  337. $this->creation_sid = $state->sid;
  338. }
  339. }
  340. }
  341. return $this->creation_sid;
  342. }
  343. /**
  344. * Gets the first valid state ID, after the creation state.
  345. *
  346. * Uses WorkflowState::getOptions(), because this does a access check.
  347. * The first State ID is user-dependent!
  348. */
  349. public function getFirstSid($entity_type, $entity, $field_name, $user, $force) {
  350. $creation_state = $this->getCreationState();
  351. $options = $creation_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  352. if ($options) {
  353. $keys = array_keys($options);
  354. $sid = $keys[0];
  355. }
  356. else {
  357. // This should never happen, but it did during testing.
  358. drupal_set_message(t('There are no workflow states available. Please notify your site administrator.'), 'error');
  359. $sid = 0;
  360. }
  361. return $sid;
  362. }
  363. /**
  364. * Returns the next state for the current state.
  365. *
  366. * @param string $entity_type
  367. * The type of the entity at hand.
  368. * @param object $entity
  369. * The entity at hand. May be NULL (E.g., on a Field settings page).
  370. * @param $field_name
  371. * @param $user
  372. * @param bool $force
  373. *
  374. * @return int $sid
  375. * A state ID.
  376. */
  377. public function getNextSid($entity_type, $entity, $field_name, $user, $force = FALSE) {
  378. $new_sid = workflow_node_current_state($entity, $entity_type, $field_name);
  379. if ($new_sid && $new_state = workflow_state_load_single($new_sid)) {
  380. /* @var $current_state WorkflowState */
  381. $options = $new_state->getOptions($entity_type, $entity, $field_name, $user, $force);
  382. // Loop over every option. To find the next one.
  383. $flag = $new_state->isCreationState();
  384. foreach ($options as $sid => $name) {
  385. if ($flag) {
  386. $new_sid = $sid;
  387. break;
  388. }
  389. if ($sid == $new_state->sid) {
  390. $flag = TRUE;
  391. }
  392. }
  393. }
  394. return $new_sid;
  395. }
  396. /**
  397. * Gets all states for a given workflow.
  398. *
  399. * @param mixed $all
  400. * Indicates to which states to return.
  401. * - TRUE = all, including Creation and Inactive;
  402. * - FALSE = only Active states, not Creation;
  403. * - 'CREATION' = only Active states, including Creation.
  404. *
  405. * @return array
  406. * An array of WorkflowState objects.
  407. */
  408. public function getStates($all = FALSE, $reset = FALSE) {
  409. if ($this->states === NULL || $reset) {
  410. $this->states = $this->wid ? WorkflowState::getStates($this->wid, $reset) : array();
  411. }
  412. // Do not unset, but add to array - you'll remove global objects otherwise.
  413. $states = array();
  414. foreach ($this->states as $state) {
  415. if ($all === TRUE) {
  416. $states[$state->sid] = $state;
  417. }
  418. elseif (($all === FALSE) && ($state->isActive() && !$state->isCreationState())) {
  419. $states[$state->sid] = $state;
  420. }
  421. elseif (($all == 'CREATION') && ($state->isActive() || $state->isCreationState())) {
  422. $states[$state->sid] = $state;
  423. }
  424. }
  425. return $states;
  426. }
  427. /**
  428. * Gets a state for a given workflow.
  429. *
  430. * @param mixed $key
  431. * A state ID or state Name.
  432. *
  433. * @return WorkflowState
  434. * A WorkflowState object.
  435. */
  436. public function getState($key) {
  437. if (is_numeric($key)) {
  438. return workflow_state_load_single($key, $this->wid);
  439. }
  440. else {
  441. return workflow_state_load_by_name($key, $this->wid);
  442. }
  443. }
  444. /**
  445. * Creates a Transition for this workflow.
  446. */
  447. public function createTransition($sid, $target_sid, $values = array()) {
  448. $workflow = $this;
  449. if (is_numeric($sid) && is_numeric($target_sid)) {
  450. $values['sid'] = $sid;
  451. $values['target_sid'] = $target_sid;
  452. }
  453. else {
  454. $state = $workflow->getState($sid);
  455. $target_state = $workflow->getState($target_sid);
  456. $values['sid'] = $state->sid;
  457. $values['target_sid'] = $target_state->sid;
  458. }
  459. // First check if this transition already exists.
  460. if ($transitions = entity_load('WorkflowConfigTransition', FALSE, $values)) {
  461. $transition = reset($transitions);
  462. }
  463. else {
  464. $values['wid'] = $workflow->wid;
  465. $transition = entity_create('WorkflowConfigTransition', $values);
  466. $transition->save();
  467. }
  468. $transition->setWorkflow($this);
  469. // Maintain the new object in the workflow.
  470. $this->transitions[$transition->tid] = $transition;
  471. return $transition;
  472. }
  473. /**
  474. * Sorts all Transitions for this workflow, according to State weight.
  475. *
  476. * This is only needed for the Admin UI.
  477. */
  478. public function sortTransitions() {
  479. // Sort the transitions on state weight.
  480. usort($this->transitions, '_workflow_transitions_sort_by_weight');
  481. }
  482. /**
  483. * @inheritdoc
  484. */
  485. public function getTransitions($tids = FALSE, array $conditions = array(), $reset = FALSE) {
  486. $config_transitions = array();
  487. // Get valid + creation states.
  488. $states = $this->getStates('CREATION');
  489. // Get filters on 'from' states, 'to' states, roles.
  490. $sid = isset($conditions['sid']) ? $conditions['sid'] : FALSE;
  491. $target_sid = isset($conditions['target_sid']) ? $conditions['target_sid'] : FALSE;
  492. $roles = isset($conditions['roles']) ? $conditions['roles'] : 'ALL';
  493. // Cache all transitions in the workflow.
  494. // We may have 0 transitions....
  495. if ($this->transitions === NULL) {
  496. $this->transitions = array();
  497. // Get all transitions. (Even from other workflows. :-( )
  498. $config_transitions = entity_load('WorkflowConfigTransition', $tids, array(), $reset);
  499. foreach ($config_transitions as &$config_transition) {
  500. if (isset($states[$config_transition->sid])) {
  501. $config_transition->setWorkflow($this);
  502. $this->transitions[$config_transition->tid] = $config_transition;
  503. }
  504. }
  505. $this->sortTransitions();
  506. }
  507. $config_transitions = array();
  508. foreach ($this->transitions as &$config_transition) {
  509. if (!isset($states[$config_transition->sid])) {
  510. // Not a valid transition for this workflow.
  511. }
  512. elseif ($sid && $sid != $config_transition->sid) {
  513. // Not the requested 'from' state.
  514. }
  515. elseif ($target_sid && $target_sid != $config_transition->target_sid) {
  516. // Not the requested 'to' state.
  517. }
  518. elseif ($roles == 'ALL' || $config_transition->isAllowed($roles)) {
  519. // Transition is allowed, permitted. Add to list.
  520. $config_transition->setWorkflow($this);
  521. $config_transitions[$config_transition->tid] = $config_transition;
  522. }
  523. else {
  524. // Transition is otherwise not allowed.
  525. }
  526. }
  527. return $config_transitions;
  528. }
  529. public function getTransitionsByTid($tid, $roles = '', $reset = FALSE) {
  530. $conditions = array(
  531. 'roles' => $roles,
  532. );
  533. return $this->getTransitions(array($tid), $conditions, $reset);
  534. }
  535. public function getTransitionsBySid($sid, $roles = '', $reset = FALSE) {
  536. $conditions = array(
  537. 'sid' => $sid,
  538. 'roles' => $roles,
  539. );
  540. return $this->getTransitions(FALSE, $conditions, $reset);
  541. }
  542. public function getTransitionsByTargetSid($target_sid, $roles = '', $reset = FALSE) {
  543. $conditions = array(
  544. 'target_sid' => $target_sid,
  545. 'roles' => $roles,
  546. );
  547. return $this->getTransitions(FALSE, $conditions, $reset);
  548. }
  549. /**
  550. * Get a specific transition. Therefore, use $roles = 'ALL'.
  551. */
  552. public function getTransitionsBySidTargetSid($sid, $target_sid, $roles = 'ALL', $reset = FALSE) {
  553. $conditions = array(
  554. 'sid' => $sid,
  555. 'target_sid' => $target_sid,
  556. 'roles' => $roles,
  557. );
  558. return $this->getTransitions(FALSE, $conditions, $reset);
  559. }
  560. /**
  561. * Gets a the type map for a given workflow.
  562. *
  563. * @param int $sid
  564. * A state ID.
  565. *
  566. * @return array
  567. * An array of typemaps.
  568. */
  569. public function getTypeMap() {
  570. $result = array();
  571. $type_maps = module_exists('workflownode') ? workflow_get_workflow_type_map_by_wid($this->wid) : array();
  572. foreach ($type_maps as $map) {
  573. $result[] = $map->type;
  574. }
  575. return $result;
  576. }
  577. /**
  578. * Gets a setting from the state object.
  579. */
  580. public function getSetting($key, array $field = array()) {
  581. switch ($key) {
  582. case 'watchdog_log':
  583. if (isset($this->options['watchdog_log'])) {
  584. // This is set via Node API.
  585. return $this->options['watchdog_log'];
  586. }
  587. elseif ($field) {
  588. if (isset($field['settings']['watchdog_log'])) {
  589. // This is set via Field API.
  590. return $field['settings']['watchdog_log'];
  591. }
  592. }
  593. drupal_set_message('Setting Workflow::getSetting(' . $key . ') does not exist', 'error');
  594. break;
  595. default:
  596. drupal_set_message('Setting Workflow::getSetting(' . $key . ') does not exist', 'error');
  597. }
  598. }
  599. /**
  600. * Mimics Entity API functions.
  601. */
  602. public function getName() {
  603. return $this->name;
  604. }
  605. protected function defaultLabel() {
  606. return isset($this->label) ? t('@label', array('@label' => $this->label)) : '';
  607. }
  608. protected function defaultUri() {
  609. $wid = $this->wid;
  610. return array('path' => WORKFLOW_ADMIN_UI_PATH . "/manage/$wid");
  611. }
  612. protected function rebuildRoles(array $roles) {
  613. $new_roles = array();
  614. // @todo: importing Roles is incomplete when user language is not English.
  615. // function user_roles() translates DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID
  616. $role_map = workflow_get_roles(NULL);
  617. // See also https://drupal.org/node/1702626 .
  618. foreach ($roles as $key => $rid) {
  619. if ($rid == WORKFLOW_ROLE_AUTHOR_RID) {
  620. $new_roles[$rid] = $rid;
  621. }
  622. else {
  623. if ($role = user_role_load_by_name($role_map[$rid])) {
  624. $new_roles[$role->rid] = (int)($role->rid);
  625. }
  626. }
  627. }
  628. return $new_roles;
  629. }
  630. }
  631. /**
  632. * Helper function to sort the transitions.
  633. *
  634. * @param WorkflowConfigTransition $a
  635. * @param WorkflowConfigTransition $b
  636. *
  637. * @return int
  638. */
  639. function _workflow_transitions_sort_by_weight($a, $b) {
  640. // First sort on From-State.
  641. $old_state_a = $a->getOldState();
  642. $old_state_b = $b->getOldState();
  643. if ($old_state_a->weight < $old_state_b->weight) return -1;
  644. if ($old_state_a->weight > $old_state_b->weight) return +1;
  645. // Then sort on To-State.
  646. $new_state_a = $a->getNewState();
  647. $new_state_b = $b->getNewState();
  648. if ($new_state_a->weight < $new_state_b->weight) return -1;
  649. if ($new_state_a->weight > $new_state_b->weight) return +1;
  650. return 0;
  651. }
  652. /**
  653. * Implements a controller class for Workflow.
  654. */
  655. class WorkflowController extends EntityAPIControllerExportable {
  656. // public function create(array $values = array()) { return parent::create($values); }
  657. // public function load($ids = array(), $conditions = array()) { }
  658. public function delete($ids, DatabaseTransaction $transaction = NULL) {
  659. // @todo: replace WorkflowController::delete() with parent.
  660. // @todo: throw error if not workflow->isDeletable().
  661. foreach ($ids as $wid) {
  662. if ($workflow = workflow_load($wid)) {
  663. $workflow->delete();
  664. }
  665. }
  666. $this->resetCache();
  667. }
  668. /**
  669. * Overrides DrupalDefaultEntityController::cacheGet().
  670. *
  671. * Override default function, due to Core issue #1572466.
  672. */
  673. protected function cacheGet($ids, $conditions = array()) {
  674. // Load any available entities from the internal cache.
  675. if ($ids === FALSE && !$conditions) {
  676. return $this->entityCache;
  677. }
  678. return parent::cacheGet($ids, $conditions);
  679. }
  680. /**
  681. * Overrides DrupalDefaultEntityController::cacheSet().
  682. */
  683. /*
  684. // protected function cacheSet($entities) { }
  685. // return parent::cacheSet($entities);
  686. // }
  687. */
  688. /**
  689. * Overrides DrupalDefaultEntityController::resetCache().
  690. *
  691. * Called by workflow_reset_cache, to
  692. * Reset the Workflow when States, Transitions have been changed.
  693. */
  694. // public function resetCache(array $ids = NULL) {
  695. // parent::resetCache($ids);
  696. // }
  697. /**
  698. * Overrides DrupalDefaultEntityController::attachLoad().
  699. */
  700. protected function attachLoad(&$queried_entities, $revision_id = FALSE) {
  701. foreach ($queried_entities as $entity) {
  702. // Load the states, so they are already present on the next (cached) load.
  703. $entity->states = $entity->getStates($all = TRUE);
  704. $entity->transitions = $entity->getTransitions(FALSE);
  705. $entity->typeMap = $entity->getTypeMap();
  706. }
  707. parent::attachLoad($queried_entities, $revision_id);
  708. }
  709. }