workflow.entity.inc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /**
  3. * @file
  4. * Integrates workflow with entity API.
  5. */
  6. /**
  7. * Implements hook_entity_info().
  8. *
  9. * @todo: implement hook_property_info, metadata.
  10. */
  11. function workflow_entity_info() {
  12. $entities['Workflow'] = array(
  13. 'label' => t('Workflow'),
  14. 'plural label' => t('Workflows'),
  15. 'entity class' => 'Workflow',
  16. 'controller class' => 'WorkflowController',
  17. 'metadata controller class' => 'EntityDefaultMetadataController',
  18. 'features controller class' => 'WorkflowFeaturesController',
  19. 'module' => 'workflow',
  20. 'base table' => 'workflows',
  21. // Make WorkflowTransition fieldable. The wid is the Transition Type.
  22. 'fieldable' => FALSE,
  23. 'bundle of' => 'WorkflowTransition',
  24. 'exportable' => TRUE,
  25. 'entity keys' => array(
  26. 'id' => 'wid',
  27. 'name' => 'name',
  28. ),
  29. 'label callback' => 'entity_class_label',
  30. 'uri callback' => 'entity_class_uri',
  31. // The following is added in workflow_admin_ui.module.
  32. /*
  33. // 'access callback' => 'workflow_access',
  34. // 'admin ui' => array(
  35. // 'path' => WORKFLOW_ADMIN_UI_PATH,
  36. // 'file' => 'workflow_admin_ui/workflow_admin_ui.pages.inc',
  37. // 'controller class' => 'EntityWorkflowUIController',
  38. // 'menu wildcard' => '%workflow',
  39. // ),
  40. */
  41. );
  42. $entities['WorkflowState'] = array(
  43. 'label' => t('Workflow state'),
  44. 'entity class' => 'WorkflowState',
  45. 'controller class' => 'WorkflowStateController',
  46. 'metadata controller class' => 'EntityDefaultMetadataController',
  47. // 'features controller class' => FALSE, //@todo: implement this.
  48. 'module' => 'workflow',
  49. 'base table' => 'workflow_states',
  50. 'fieldable' => FALSE,
  51. 'exportable' => FALSE,
  52. 'entity keys' => array(
  53. 'id' => 'sid',
  54. ),
  55. 'label callback' => 'entity_class_label',
  56. 'uri callback' => 'entity_class_uri',
  57. );
  58. $entities['WorkflowConfigTransition'] = array(
  59. 'label' => t('Workflow config transition'),
  60. 'entity class' => 'WorkflowConfigTransition',
  61. // Add controller Class. 'Workflow' class is the de-facto controller.
  62. 'controller class' => 'WorkflowConfigTransitionController',
  63. 'metadata controller class' => 'EntityDefaultMetadataController',
  64. 'base table' => 'workflow_transitions',
  65. 'exportable' => FALSE,
  66. 'module' => 'workflow',
  67. 'entity keys' => array(
  68. 'id' => 'tid',
  69. 'status' => 'status',
  70. ),
  71. 'label callback' => 'entity_class_label',
  72. // 'uri callback' => 'entity_class_uri',
  73. /*
  74. 'view modes' => array(
  75. 'full' => array(
  76. 'label' => t('Full'),
  77. 'custom settings' => TRUE,
  78. ),
  79. ),
  80. 'views controller class' => 'EntityDefaultViewsController',
  81. 'access callback' => 'workflow_tab_access', // @todo: use to-be workflow_access here. Access to Tab <> access to workflow.
  82. */
  83. );
  84. // The Controller class of Transitions and ScheduledTransition is shared.
  85. $entities['WorkflowTransition'] = array(
  86. 'label' => t('Workflow executed transition'),
  87. 'entity class' => 'WorkflowTransition',
  88. 'controller class' => 'WorkflowTransitionController',
  89. 'metadata controller class' => 'EntityDefaultMetadataController',
  90. // Do not use 'extra fields controller class'. Use hook_field_extra_fields instead.
  91. // 'extra fields controller class' => 'EntityDefaultExtraFieldsController',
  92. 'views controller class' => 'EntityDefaultViewsController',
  93. 'rules controller class' => 'EntityDefaultRulesController',
  94. 'features controller class' => FALSE,
  95. 'base table' => 'workflow_node_history',
  96. 'fieldable' => TRUE,
  97. // Bundles are defined by the $types below.
  98. 'bundles' => array(),
  99. // Bundle keys tell the FieldAPI how to extract information from the bundle objects.
  100. 'bundle keys' => array(
  101. 'bundle' => 'wid',
  102. ),
  103. // 'admin ui' => array(
  104. // 'path' => WORKFLOW_ADMIN_UI_PATH . '/manage/%workflow_transition/fields2',
  105. // 'menu wildcard' => '%workflow_transition',
  106. // 'controller class' => 'EntityDefaultUIController',
  107. // ),
  108. 'entity keys' => array(
  109. 'id' => 'hid',
  110. 'bundle'=> 'wid',
  111. ),
  112. 'label callback' => 'entity_class_label',
  113. 'module' => 'workflow',
  114. );
  115. // Add bundle info but bypass entity_load() as we cannot use it here.
  116. // (The bundle-setup is copied from the D7 entityform module.)
  117. // Get all workflows, keyed by wid.
  118. // Use try..catch.. to workaround #1311820, @see #2484325.
  119. try {
  120. // Add bundle info but bypass entity_load() as we cannot use it here.
  121. // (The bundle-setup is copied from the D7 entityform module.)
  122. // Get all workflows, keyed by wid.
  123. $types = db_select('workflows', 'w')
  124. ->fields('w')
  125. ->execute()
  126. ->fetchAllAssoc('wid');
  127. }
  128. catch (PDOException $ex) {
  129. watchdog(
  130. 'workflow',
  131. 'Failed to retrieve workflow types: %exception',
  132. array('%exception' => (string)$ex),
  133. WATCHDOG_ERROR);
  134. $types = array();
  135. }
  136. // Build an array of bundles.
  137. foreach ($types as $wid => $info) {
  138. $entities['WorkflowTransition']['bundles'][$wid] = array(
  139. 'label' => $info->label,
  140. 'admin' => array(
  141. 'path' => WORKFLOW_ADMIN_UI_PATH . '/manage/%workflow',
  142. 'real path' => WORKFLOW_ADMIN_UI_PATH . "/manage/$wid",
  143. 'bundle argument' => 5,
  144. // 'access callback' => 'workflow_access',
  145. ),
  146. );
  147. }
  148. $entities['WorkflowScheduledTransition'] = array(
  149. 'label' => t('Workflow scheduled transition'),
  150. 'entity class' => 'WorkflowScheduledTransition',
  151. 'controller class' => 'WorkflowTransitionController',
  152. 'metadata controller class' => 'EntityDefaultMetadataController',
  153. 'views controller class' => 'EntityDefaultViewsController',
  154. 'rules controller class' => 'EntityDefaultRulesController',
  155. 'features controller class' => FALSE,
  156. 'base table' => 'workflow_scheduled_transition',
  157. 'entity keys' => array(
  158. 'id' => 'tid',
  159. ),
  160. 'label callback' => 'entity_class_label',
  161. 'module' => 'workflow',
  162. );
  163. return $entities;
  164. }
  165. /**
  166. * Does NOT implement hook_entity_property_info().
  167. *
  168. * By NOT implementing this hook, but using hook_entity_property_info_alter(),
  169. * the system itself will generate the most viable options.
  170. * They can then be tweaked with the _alter() function.
  171. *
  172. * @see https://www.drupal.org/node/1208874
  173. */
  174. // function workflow_entity_property_info() {
  175. // $info = array();
  176. // $return $info;
  177. // }
  178. /**
  179. * Implements hook_entity_property_info_alter().
  180. *
  181. * N.B. Keep the following functions aligned when changing properties:
  182. * - workflow_tokens()
  183. * - workflow_entity_property_info_alter()
  184. * - workflow_views_views_data_alter()
  185. */
  186. function workflow_entity_property_info_alter(&$info) {
  187. // Properties for Entites. @todo wrapper: not only nodes.
  188. // $info['node']['properties']['workflows'] = array(
  189. // 'type' => 'list<Workflow>',
  190. // 'label' => t("Workflow"),
  191. // 'description' => t("The workflow of the entity."),
  192. // 'getter callback' => '_workflow_metadata_workflow_get_properties',
  193. // 'entity token' => FALSE,
  194. // );
  195. // Properties for Workflow.
  196. $info['Workflow']['properties']['wid']['label'] = 'Workflow ID';
  197. $info['Workflow']['properties']['options']['label'] = t('Workflow options');
  198. $info['Workflow']['properties']['options']['getter callback'] = '_workflow_metadata_workflow_get_properties';
  199. $info['Workflow']['properties']['states'] = array(
  200. 'type' => 'list<WorkflowState>',
  201. 'label' => 'States',
  202. 'description' => 'States of the Workflow.',
  203. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  204. // 'options list' => 'entity_metadata_user_roles',
  205. // 'required' => TRUE/FALSE,
  206. // 'entity views field' => TRUE/FALSE,
  207. // 'entity token' => TRUE/FALSE,
  208. );
  209. $info['Workflow']['properties']['transitions'] = array(
  210. 'type' => 'list<WorkflowConfigTransition>',
  211. 'label' => 'Transitions',
  212. 'description' => 'Transitions of the Workflow.',
  213. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  214. );
  215. unset($info['Workflow']['properties']['tab_roles']['description']);
  216. if (!isset($info['Workflow']['properties']['tab_roles']) || !is_array($info['Workflow']['properties']['tab_roles'])) {
  217. $info['Workflow']['properties']['tab_roles'] = array();
  218. }
  219. $info['Workflow']['properties']['tab_roles'] += array(
  220. 'type' => 'list<integer>',
  221. 'label' => t("User roles"),
  222. 'description' => t("The roles that can access the Workflow History tab."),
  223. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  224. );
  225. // Properties for WorkflowState.
  226. $info['WorkflowState']['properties']['wid']['type'] = 'Workflow';
  227. //
  228. $info['WorkflowState']['properties']['label'] = $info['WorkflowState']['properties']['state'];
  229. $info['WorkflowState']['properties']['label']['label'] = t("Label");
  230. $info['WorkflowState']['properties']['label']['description'] = t("The label of the state.");
  231. $info['WorkflowState']['properties']['label']['required'] = TRUE;
  232. unset($info['WorkflowState']['properties']['state']);
  233. // Properties for WorkflowConfigTransition.
  234. $info['WorkflowConfigTransition']['properties']['workflow'] = array(
  235. 'type' => 'Workflow',
  236. 'label' => t("Workflow"),
  237. 'description' => t("The workflow of the transition."),
  238. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  239. 'entity token' => FALSE,
  240. );
  241. $info['WorkflowConfigTransition']['properties']['wid'] = array(
  242. 'type' => 'integer',
  243. 'label' => t("Workflow ID"),
  244. 'description' => t("The workflow ID of the transition."),
  245. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  246. 'entity token' => FALSE,
  247. );
  248. // @todo: Unify ID's to old_sid, new_sid.
  249. $info['WorkflowConfigTransition']['properties']['sid']['description'] = 'The ID of old state.';
  250. $info['WorkflowConfigTransition']['properties']['target_sid']['description'] = 'The ID of new state.';
  251. // Unify objects to old_state, new_state.
  252. $info['WorkflowConfigTransition']['properties']['old_state'] = array(
  253. 'type' => 'WorkflowState',
  254. 'label' => t('Old state'),
  255. 'schema field' => 'sid',
  256. 'description' => t("The old state."),
  257. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  258. );
  259. $info['WorkflowConfigTransition']['properties']['new_state'] = array(
  260. 'type' => 'WorkflowState',
  261. 'label' => t('New state'),
  262. 'schema field' => 'target_sid',
  263. 'description' => t("The new state."),
  264. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  265. );
  266. $info['WorkflowConfigTransition']['properties']['roles']['type'] = 'list<integer>';
  267. $info['WorkflowConfigTransition']['properties']['roles']['description'] = t("The roles that may execute the transition.");
  268. // Properties for WorkflowTransition.
  269. $info['WorkflowTransition']['properties']['label'] = $info['WorkflowConfigTransition']['properties']['label'];
  270. $info['WorkflowTransition']['properties']['workflow'] = $info['WorkflowConfigTransition']['properties']['workflow'];
  271. $info['WorkflowTransition']['properties']['wid'] = $info['WorkflowConfigTransition']['properties']['wid'];
  272. $info['WorkflowTransition']['properties']['hid']['label'] = 'Transition ID';
  273. // @todo: Unify ID's to old_sid, new_sid.
  274. $info['WorkflowTransition']['properties']['old_sid']['description'] = 'The ID of old state.';
  275. $info['WorkflowTransition']['properties']['sid']['description'] = 'The ID of new state.';
  276. // Unify objects to old_state, new_state.
  277. $info['WorkflowTransition']['properties']['old_state'] = $info['WorkflowConfigTransition']['properties']['old_state'];
  278. $info['WorkflowTransition']['properties']['new_state'] = $info['WorkflowConfigTransition']['properties']['new_state'];
  279. $info['WorkflowTransition']['properties']['user'] = array(
  280. 'type' => 'user',
  281. 'label' => t('User'),
  282. 'schema field' => 'uid',
  283. 'description' => t('The user who triggered the transition.'),
  284. );
  285. unset($info['WorkflowTransition']['properties']['stamp']);
  286. $info['WorkflowTransition']['properties']['timestamp'] = array(
  287. 'type' => 'date',
  288. 'label' => t('Timestamp'),
  289. 'schema field' => 'stamp',
  290. 'description' => t('The date, time the transition was executed.'),
  291. );
  292. $info['WorkflowTransition']['properties']['entity'] = array(
  293. 'type' => 'entity',
  294. 'label' => t('Entity'),
  295. 'description' => t("The Entity that this state change applies to."),
  296. 'getter callback' => '_workflow_metadata_workflow_get_properties',
  297. );
  298. // Properties for WorkflowScheduledTransition.
  299. $info['WorkflowScheduledTransition']['properties']['scheduled']['type'] = 'date';
  300. $info['WorkflowScheduledTransition']['properties']['workflow'] = $info['WorkflowTransition']['properties']['workflow'];
  301. // @todo: Unify ID's to old_sid, new_sid.
  302. $info['WorkflowScheduledTransition']['properties']['old_sid']['description'] = 'The ID of old state.';
  303. $info['WorkflowScheduledTransition']['properties']['sid']['description'] = 'The ID of new state.';
  304. // Unify objects to old_state, new_state.
  305. $info['WorkflowScheduledTransition']['properties']['old_state'] = $info['WorkflowTransition']['properties']['old_state'];
  306. $info['WorkflowScheduledTransition']['properties']['old_state']['schema field'] = 'old_sid';
  307. $info['WorkflowScheduledTransition']['properties']['new_state'] = $info['WorkflowTransition']['properties']['new_state'];
  308. $info['WorkflowScheduledTransition']['properties']['new_state']['schema field'] = 'new_sid';
  309. $info['WorkflowScheduledTransition']['properties']['user'] = $info['WorkflowTransition']['properties']['user'];
  310. }
  311. /**
  312. * Getter callback for Workflow defined in hook_entity_property_info_alter.
  313. */
  314. function _workflow_metadata_workflow_get_properties($entity, array $options, $name, $entity_type, $property) {
  315. switch ($name) {
  316. // // The workflows of a normal entity.
  317. // case 'workflows':
  318. // $workflow = _workflow_get_workflow_creation_sid($entity_type, $entity, $field_name);
  319. // return $workflow;
  320. // return $entity->getWorkflow();
  321. // The workflows of a Workflow entity.
  322. case 'workflow':
  323. return $entity->getWorkflow();
  324. case 'states':
  325. return $entity->getStates();
  326. case 'transitions':
  327. // @todo: for some reason, getTransitions() gives no result.
  328. return $entity->getTransitions();
  329. case 'old_state':
  330. case 'old-state':
  331. return $entity->getOldState();
  332. case 'new_state':
  333. case 'new-state':
  334. return $entity->getNewState();
  335. case 'tab_roles':
  336. // @todo: for some reason, Tab_roles gives no result.
  337. // Code copied from 'user' entity.
  338. return isset($entity->tab_roles) ? array_keys($entity->tab_roles) : array();
  339. case 'langcode':
  340. // Gets the language code of a Workflow Field, hence its State, Transitions.
  341. // '$property' is $field_name.
  342. $langcode = LANGUAGE_NONE;
  343. $wrapper = entity_metadata_wrapper($entity_type, $entity);
  344. if (!$property || !method_exists($wrapper, $property)) {
  345. // Workflow_node. Translations are not supported.
  346. }
  347. else {
  348. // Get language code for a field or property.
  349. // getPropertyLanguage() may return NULL if no language is set,
  350. // or may not exist on properties.
  351. if (isset($wrapper->{$property}) && method_exists($wrapper->{$property}, 'getPropertyLanguage')) {
  352. if (!$langcode = $wrapper->{$property}->getPropertyLanguage()) {
  353. $langcode = LANGUAGE_NONE;
  354. }
  355. }
  356. }
  357. return $langcode;
  358. case 'entity':
  359. $entity_wrapper = entity_metadata_wrapper($entity->entity_type, $entity->nid);
  360. return $entity_wrapper;
  361. // The following properties need more love. Also test their tokens!
  362. case 'options':
  363. return 'n/a';
  364. }
  365. }
  366. /**
  367. * Implements hook_field_extra_fields().
  368. *
  369. * We do not use 'extra fields controller class', which only adds 'display'
  370. * fields, not 'form' fields. Use hook_field_extra_fields instead.
  371. *
  372. * hook_field_extra_fields() is invoked by _field_extra_fields_pre_render(), which is a pre-render function used by field_attach_form(), and field_attach_view().
  373. * @see https://api.drupal.org/api/drupal/modules!field!field.api.php/function/hook_field_extra_fields/7
  374. * @see http://drupal.stackexchange.com/questions/10527/how-should-i-use-hook-field-extra-fields
  375. *
  376. */
  377. function workflow_field_extra_fields() {
  378. $extra = array();
  379. $entity_type = 'WorkflowTransition';
  380. $info = entity_get_info($entity_type);
  381. foreach ($info['bundles'] as $bundle => $bundle_info) {
  382. $extra[$entity_type][$bundle] = array(
  383. 'form' => array(
  384. 'workflow_sid' => array(
  385. 'widget-type' => 'select',
  386. 'label' => t('State Id'),
  387. 'description' => t('Target state Id'),
  388. 'weight' => -4,
  389. // 'edit' => 'test', // (optional) String containing markup (normally a link) used as the element's 'edit' operation in the administration interface. Only for 'form' context.
  390. // 'delete' => 'test', // (optional) String containing markup (normally a link) used as the element's 'delete' operation in the administration interface. Only for 'form' context.
  391. ),
  392. 'scheduled_container' => array(
  393. 'label' => t('Scheduling'),
  394. 'description' => t('Schedule checkbox & date/time'),
  395. 'weight' => -4,
  396. ),
  397. 'workflow_comment' => array(
  398. 'label' => t('Comment'),
  399. 'description' => t('Comment text area'),
  400. 'weight' => -4,
  401. ),
  402. ),
  403. 'display' => array(
  404. )
  405. );
  406. }
  407. return $extra;
  408. }
  409. /**
  410. * Entity loader for Workflow.
  411. *
  412. * Also used as Menu wild card loader {wildcard_name}_load for '%workflow'.
  413. *
  414. * @see http://www.phpgainers.com/content/creating-menu-wildcard-loader-function-drupal-7
  415. * @todo D8: deprecated in favour of workflow_load_single(), not needed for menu.
  416. *
  417. * $id can be numeric ID ('wid') or machine name ('name').
  418. * Caveat: this only works for entities with EntityAPIControllerExportable. #1741956
  419. */
  420. function workflow_load($id) {
  421. // Some Admin UI menu page loaders pass the $wid as string, not int.
  422. // @see workflow_admin_ui_edit_form_validate().
  423. $workflow = entity_load_single('Workflow', $id);
  424. return $workflow;
  425. }
  426. function workflow_load_by_name($name) {
  427. $workflows = entity_load_multiple_by_name('Workflow', array($name));
  428. return reset($workflows);
  429. }
  430. function workflow_load_multiple_by_name($names = FALSE) {
  431. $workflows = entity_load_multiple_by_name('Workflow', $names);
  432. return $workflows;
  433. }
  434. function workflow_load_single($id) {
  435. return entity_load_single('Workflow', $id);
  436. }
  437. function workflow_load_multiple($ids = FALSE, $reset = FALSE) {
  438. return entity_load('Workflow', $ids, array(), $reset);
  439. }
  440. function workflow_create($name) {
  441. // @todo: avoid double names in db-table, to get rid of this line of code.
  442. $workflow = workflow_load_by_name($name);
  443. if (!$workflow) {
  444. $workflow = entity_create('Workflow', array('name' => $name));
  445. }
  446. return $workflow;
  447. }
  448. /**
  449. * Helper function, to get the label of a given workflow.
  450. *
  451. * @see workflow_get_sid_label($sid)
  452. * @see workflow_get_wid_label($wid)
  453. */
  454. function workflow_label($workflow) {
  455. if ($workflow === FALSE) {
  456. $output = t('No workflow');
  457. }
  458. elseif ($workflow) {
  459. $output = $workflow->label();
  460. }
  461. else {
  462. // E.g., NULL.
  463. $output = t('Unknown workflow');
  464. }
  465. return $output;
  466. }
  467. /**
  468. * Reset the Workflow when States, Transitions have been changed.
  469. */
  470. function workflow_reset_cache($wid) {
  471. $ids = array($wid);
  472. entity_get_controller('Workflow')->resetCache($ids);
  473. }
  474. /**
  475. * CRUD for WorkflowState.
  476. */
  477. function workflow_state_load($sid) {
  478. return WorkflowState::load($sid);
  479. }
  480. function workflow_state_load_single($sid) {
  481. return WorkflowState::load($sid);
  482. }
  483. function workflow_state_load_multiple($wid = 0, $reset = FALSE) {
  484. return WorkflowState::getStates($wid, $reset);
  485. }
  486. function workflow_state_load_by_name($name, $wid) {
  487. return WorkflowState::loadByName($name, $wid);
  488. }
  489. /**
  490. * Load WorkflowTransitions, most recent first.
  491. *
  492. * @param string $field_name
  493. * Optional. Can be NULL, if you want to load any field.
  494. *
  495. * @deprecated: workflow_get_workflow_node_history_by_nid() --> workflow_transition_load_single()
  496. * @deprecated: workflow_get_recent_node_history() --> workflow_transition_load_single()
  497. */
  498. function workflow_transition_load_multiple($entity_type, array $entity_ids, $field_name = '', $limit = NULL, $langcode = '') {
  499. return WorkflowTransition::loadMultiple($entity_type, $entity_ids, $field_name, $limit, $langcode);
  500. }
  501. /**
  502. * Load WorkflowTransitions, most recent first.
  503. *
  504. * @return WorkflowTransition
  505. * object representing one row from the {workflow_node_history} table.
  506. */
  507. function workflow_transition_load_single($entity_type, $entity_id, $field_name = '', $langcode = '') {
  508. $limit = 1;
  509. if ($transitions = workflow_transition_load_multiple($entity_type, array($entity_id), $field_name, $limit, $langcode)) {
  510. return reset($transitions);
  511. }
  512. return NULL;
  513. }
  514. /**
  515. * Load function belonging to the menu option 'workflow-comment/%'.
  516. *
  517. * Maps to this function just like 'node/%node' maps to node_load().
  518. *
  519. * @param int $hid
  520. * The ID of the workflow state transition record to load.
  521. *
  522. * @return WorkflowTransition
  523. * object representing one row from the {workflow_node_history} table.
  524. */
  525. function workflow_transition_load($hid) {
  526. return entity_load_single('WorkflowTransition', $hid);
  527. }