entity.ui.inc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. <?php
  2. /**
  3. * @file
  4. * Provides a controller for building an entity overview form.
  5. */
  6. /**
  7. * Default controller for providing UI.
  8. */
  9. class EntityDefaultUIController {
  10. protected $entityType;
  11. protected $entityInfo, $path;
  12. /**
  13. * Defines the number of entries to show per page in overview table.
  14. */
  15. public $overviewPagerLimit = 25;
  16. public function __construct($entity_type, $entity_info) {
  17. $this->entityType = $entity_type;
  18. $this->entityInfo = $entity_info;
  19. $this->path = $this->entityInfo['admin ui']['path'];
  20. $this->statusKey = empty($this->entityInfo['entity keys']['status']) ? 'status' : $this->entityInfo['entity keys']['status'];
  21. }
  22. /**
  23. * Provides definitions for implementing hook_menu().
  24. */
  25. public function hook_menu() {
  26. $items = array();
  27. $id_count = count(explode('/', $this->path));
  28. $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
  29. $plural_label = isset($this->entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's';
  30. $items[$this->path] = array(
  31. 'title' => $plural_label,
  32. 'page callback' => 'drupal_get_form',
  33. 'page arguments' => array($this->entityType . '_overview_form', $this->entityType),
  34. 'description' => 'Manage ' . $plural_label . '.',
  35. 'access callback' => 'entity_access',
  36. 'access arguments' => array('view', $this->entityType),
  37. 'file' => 'includes/entity.ui.inc',
  38. );
  39. $items[$this->path . '/list'] = array(
  40. 'title' => 'List',
  41. 'type' => MENU_DEFAULT_LOCAL_TASK,
  42. 'weight' => -10,
  43. );
  44. $items[$this->path . '/add'] = array(
  45. 'title callback' => 'entity_ui_get_action_title',
  46. 'title arguments' => array('add', $this->entityType),
  47. 'page callback' => 'entity_ui_get_form',
  48. 'page arguments' => array($this->entityType, NULL, 'add'),
  49. 'access callback' => 'entity_access',
  50. 'access arguments' => array('create', $this->entityType),
  51. 'type' => MENU_LOCAL_ACTION,
  52. );
  53. $items[$this->path . '/manage/' . $wildcard] = array(
  54. 'title' => 'Edit',
  55. 'title callback' => 'entity_label',
  56. 'title arguments' => array($this->entityType, $id_count + 1),
  57. 'page callback' => 'entity_ui_get_form',
  58. 'page arguments' => array($this->entityType, $id_count + 1),
  59. 'load arguments' => array($this->entityType),
  60. 'access callback' => 'entity_access',
  61. 'access arguments' => array('update', $this->entityType, $id_count + 1),
  62. );
  63. $items[$this->path . '/manage/' . $wildcard . '/edit'] = array(
  64. 'title' => 'Edit',
  65. 'load arguments' => array($this->entityType),
  66. 'type' => MENU_DEFAULT_LOCAL_TASK,
  67. );
  68. // Clone form, a special case for the edit form.
  69. $items[$this->path . '/manage/' . $wildcard . '/clone'] = array(
  70. 'title' => 'Clone',
  71. 'page callback' => 'entity_ui_get_form',
  72. 'page arguments' => array($this->entityType, $id_count + 1, 'clone'),
  73. 'load arguments' => array($this->entityType),
  74. 'access callback' => 'entity_access',
  75. 'access arguments' => array('create', $this->entityType),
  76. );
  77. // Menu item for operations like revert and delete.
  78. $items[$this->path . '/manage/' . $wildcard . '/%'] = array(
  79. 'page callback' => 'drupal_get_form',
  80. 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $id_count + 1, $id_count + 2),
  81. 'load arguments' => array($this->entityType),
  82. 'access callback' => 'entity_access',
  83. 'access arguments' => array('delete', $this->entityType, $id_count + 1),
  84. 'file' => 'includes/entity.ui.inc',
  85. );
  86. if (!empty($this->entityInfo['exportable'])) {
  87. // Menu item for importing an entity.
  88. $items[$this->path . '/import'] = array(
  89. 'title callback' => 'entity_ui_get_action_title',
  90. 'title arguments' => array('import', $this->entityType),
  91. 'page callback' => 'drupal_get_form',
  92. 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, NULL, 'import'),
  93. 'access callback' => 'entity_access',
  94. 'access arguments' => array('create', $this->entityType),
  95. 'file' => 'includes/entity.ui.inc',
  96. 'type' => MENU_LOCAL_ACTION,
  97. );
  98. }
  99. if (!empty($this->entityInfo['admin ui']['file'])) {
  100. // Add in the include file for the entity form.
  101. foreach (array("/manage/$wildcard", "/manage/$wildcard/clone", '/add') as $path_end) {
  102. $items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file'];
  103. $items[$this->path . $path_end]['file path'] = isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']);
  104. }
  105. }
  106. return $items;
  107. }
  108. /**
  109. * Provides definitions for implementing hook_forms().
  110. *
  111. * Use per bundle form ids if possible, such that easy per bundle alterations
  112. * are supported too.
  113. *
  114. * Note that for performance reasons, this method is only invoked for forms,
  115. * which receive the entity_type as first argument. Thus any forms added, must
  116. * follow that pattern.
  117. *
  118. * @see entity_forms()
  119. */
  120. public function hook_forms() {
  121. // The overview and the operation form are implemented by the controller,
  122. // the callback and validation + submit handlers just invoke the controller.
  123. $forms[$this->entityType . '_overview_form'] = array(
  124. 'callback' => 'entity_ui_overview_form',
  125. 'wrapper_callback' => 'entity_ui_form_defaults',
  126. );
  127. $forms[$this->entityType . '_operation_form'] = array(
  128. 'callback' => 'entity_ui_operation_form',
  129. 'wrapper_callback' => 'entity_ui_form_defaults',
  130. );
  131. // The entity form (ENTITY_TYPE_form) handles editing, adding and cloning.
  132. // For that form, the wrapper callback entity_ui_main_form_defaults() gets
  133. // directly invoked via entity_ui_get_form().
  134. // If there are bundles though, we use form ids that include the bundle name
  135. // (ENTITY_TYPE_edit_BUNDLE_NAME_form) to enable per bundle alterations
  136. // as well as alterations based upon the base form id (ENTITY_TYPE_form).
  137. if (!(count($this->entityInfo['bundles']) == 1 && isset($this->entityInfo['bundles'][$this->entityType]))) {
  138. foreach ($this->entityInfo['bundles'] as $bundle => $bundle_info) {
  139. $forms[$this->entityType . '_edit_' . $bundle . '_form']['callback'] = $this->entityType . '_form';
  140. // Again the wrapper callback is invoked by entity_ui_get_form() anyway.
  141. }
  142. }
  143. return $forms;
  144. }
  145. /**
  146. * Builds the entity overview form.
  147. */
  148. public function overviewForm($form, &$form_state) {
  149. // By default just show a simple overview for all entities.
  150. $form['table'] = $this->overviewTable();
  151. $form['pager'] = array('#theme' => 'pager');
  152. return $form;
  153. }
  154. /**
  155. * Overview form validation callback.
  156. *
  157. * @param $form
  158. * The form array of the overview form.
  159. * @param $form_state
  160. * The overview form state which will be used for validating.
  161. */
  162. public function overviewFormValidate($form, &$form_state) {}
  163. /**
  164. * Overview form submit callback.
  165. *
  166. * @param $form
  167. * The form array of the overview form.
  168. * @param $form_state
  169. * The overview form state which will be used for submitting.
  170. */
  171. public function overviewFormSubmit($form, &$form_state) {}
  172. /**
  173. * Generates the render array for a overview table for arbitrary entities
  174. * matching the given conditions.
  175. *
  176. * @param $conditions
  177. * An array of conditions as needed by entity_load().
  178. * @return Array
  179. * A renderable array.
  180. */
  181. public function overviewTable($conditions = array()) {
  182. $query = new EntityFieldQuery();
  183. $query->entityCondition('entity_type', $this->entityType);
  184. // Add all conditions to query.
  185. foreach ($conditions as $key => $value) {
  186. $query->propertyCondition($key, $value);
  187. }
  188. if ($this->overviewPagerLimit) {
  189. $query->pager($this->overviewPagerLimit);
  190. }
  191. $results = $query->execute();
  192. $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array();
  193. $entities = $ids ? entity_load($this->entityType, $ids) : array();
  194. ksort($entities);
  195. $rows = array();
  196. foreach ($entities as $entity) {
  197. $rows[] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity);
  198. }
  199. $render = array(
  200. '#theme' => 'table',
  201. '#header' => $this->overviewTableHeaders($conditions, $rows),
  202. '#rows' => $rows,
  203. '#empty' => t('None.'),
  204. );
  205. return $render;
  206. }
  207. /**
  208. * Generates the table headers for the overview table.
  209. */
  210. protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) {
  211. $header = $additional_header;
  212. array_unshift($header, t('Label'));
  213. if (!empty($this->entityInfo['exportable'])) {
  214. $header[] = t('Status');
  215. }
  216. // Add operations with the right colspan.
  217. $header[] = array('data' => t('Operations'), 'colspan' => $this->operationCount());
  218. return $header;
  219. }
  220. /**
  221. * Returns the operation count for calculating colspans.
  222. */
  223. protected function operationCount() {
  224. $count = 3;
  225. $count += !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui') ? 2 : 0;
  226. $count += !empty($this->entityInfo['exportable']) ? 1 : 0;
  227. $count += !empty($this->entityInfo['i18n controller class']) ? 1 : 0;
  228. return $count;
  229. }
  230. /**
  231. * Generates the row for the passed entity and may be overridden in order to
  232. * customize the rows.
  233. *
  234. * @param $additional_cols
  235. * Additional columns to be added after the entity label column.
  236. */
  237. protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) {
  238. $entity_uri = entity_uri($this->entityType, $entity);
  239. $row[] = array('data' => array(
  240. '#theme' => 'entity_ui_overview_item',
  241. '#label' => entity_label($this->entityType, $entity),
  242. '#name' => !empty($this->entityInfo['exportable']) ? entity_id($this->entityType, $entity) : FALSE,
  243. '#url' => $entity_uri ? $entity_uri : FALSE,
  244. '#entity_type' => $this->entityType),
  245. );
  246. // Add in any passed additional cols.
  247. foreach ($additional_cols as $col) {
  248. $row[] = $col;
  249. }
  250. // Add a row for the exportable status.
  251. if (!empty($this->entityInfo['exportable'])) {
  252. $row[] = array('data' => array(
  253. '#theme' => 'entity_status',
  254. '#status' => $entity->{$this->statusKey},
  255. ));
  256. }
  257. // In case this is a bundle, we add links to the field ui tabs.
  258. $field_ui = !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui');
  259. // For exportable entities we add an export link.
  260. $exportable = !empty($this->entityInfo['exportable']);
  261. // If i18n integration is enabled, add a link to the translate tab.
  262. $i18n = !empty($this->entityInfo['i18n controller class']);
  263. // Add operations depending on the status.
  264. if (entity_has_status($this->entityType, $entity, ENTITY_FIXED)) {
  265. $row[] = array('data' => l(t('clone'), $this->path . '/manage/' . $id . '/clone'), 'colspan' => $this->operationCount());
  266. }
  267. else {
  268. $row[] = l(t('edit'), $this->path . '/manage/' . $id);
  269. if ($field_ui) {
  270. $row[] = l(t('manage fields'), $this->path . '/manage/' . $id . '/fields');
  271. $row[] = l(t('manage display'), $this->path . '/manage/' . $id . '/display');
  272. }
  273. if ($i18n) {
  274. $row[] = l(t('translate'), $this->path . '/manage/' . $id . '/translate');
  275. }
  276. if ($exportable) {
  277. $row[] = l(t('clone'), $this->path . '/manage/' . $id . '/clone');
  278. }
  279. if (empty($this->entityInfo['exportable']) || !entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) {
  280. $row[] = l(t('delete'), $this->path . '/manage/' . $id . '/delete', array('query' => drupal_get_destination()));
  281. }
  282. elseif (entity_has_status($this->entityType, $entity, ENTITY_OVERRIDDEN)) {
  283. $row[] = l(t('revert'), $this->path . '/manage/' . $id . '/revert', array('query' => drupal_get_destination()));
  284. }
  285. else {
  286. $row[] = '';
  287. }
  288. }
  289. if ($exportable) {
  290. $row[] = l(t('export'), $this->path . '/manage/' . $id . '/export');
  291. }
  292. return $row;
  293. }
  294. /**
  295. * Builds the operation form.
  296. *
  297. * For the export operation a serialized string of the entity is directly
  298. * shown in the form (no submit function needed).
  299. */
  300. public function operationForm($form, &$form_state, $entity, $op) {
  301. switch ($op) {
  302. case 'revert':
  303. $label = entity_label($this->entityType, $entity);
  304. $confirm_question = t('Are you sure you want to revert the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label));
  305. return confirm_form($form, $confirm_question, $this->path);
  306. case 'delete':
  307. $label = entity_label($this->entityType, $entity);
  308. $confirm_question = t('Are you sure you want to delete the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label));
  309. return confirm_form($form, $confirm_question, $this->path);
  310. case 'export':
  311. if (!empty($this->entityInfo['exportable'])) {
  312. $export = entity_export($this->entityType, $entity);
  313. $form['export'] = array(
  314. '#type' => 'textarea',
  315. '#title' => t('Export'),
  316. '#description' => t('For importing copy the content of the text area and paste it into the import page.'),
  317. '#rows' => 25,
  318. '#default_value' => $export,
  319. );
  320. return $form;
  321. }
  322. case 'import':
  323. $form['import'] = array(
  324. '#type' => 'textarea',
  325. '#title' => t('Import'),
  326. '#description' => t('Paste an exported %entity_type here.', array('%entity_type' => $this->entityInfo['label'])),
  327. '#rows' => 20,
  328. );
  329. $form['overwrite'] = array(
  330. '#title' => t('Overwrite'),
  331. '#type' => 'checkbox',
  332. '#description' => t('If checked, any existing %entity with the same identifier will be replaced by the import.', array('%entity' => $this->entityInfo['label'])),
  333. '#default_value' => FALSE,
  334. );
  335. $form['submit'] = array(
  336. '#type' => 'submit',
  337. '#value' => t('Import'),
  338. );
  339. return $form;
  340. }
  341. drupal_not_found();
  342. exit;
  343. }
  344. /**
  345. * Operation form validation callback.
  346. */
  347. public function operationFormValidate($form, &$form_state) {
  348. if ($form_state['op'] == 'import') {
  349. if ($entity = entity_import($this->entityType, $form_state['values']['import'])) {
  350. // Store the successfully imported entity in $form_state.
  351. $form_state[$this->entityType] = $entity;
  352. if (!$form_state['values']['overwrite']) {
  353. // Check for existing entities with the same identifier.
  354. $id = entity_id($this->entityType, $entity);
  355. $entities = entity_load($this->entityType, array($id));
  356. if (!empty($entities)) {
  357. $label = entity_label($this->entityType, $entity);
  358. $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label);
  359. form_set_error('import', t('Import of %entity %label failed, a %entity with the same machine name already exists. Check the overwrite option to replace it.', $vars));
  360. }
  361. }
  362. }
  363. else {
  364. form_set_error('import', t('Import failed.'));
  365. }
  366. }
  367. }
  368. /**
  369. * Operation form submit callback.
  370. */
  371. public function operationFormSubmit($form, &$form_state) {
  372. $msg = $this->applyOperation($form_state['op'], $form_state[$this->entityType]);
  373. drupal_set_message($msg);
  374. $form_state['redirect'] = $this->path;
  375. }
  376. /**
  377. * Applies an operation to the given entity.
  378. *
  379. * Note: the export operation is directly carried out by the operationForm()
  380. * method.
  381. *
  382. * @param string $op
  383. * The operation (revert, delete or import).
  384. * @param $entity
  385. * The entity to manipulate.
  386. *
  387. * @return
  388. * The status message of what has been applied.
  389. */
  390. public function applyOperation($op, $entity) {
  391. $label = entity_label($this->entityType, $entity);
  392. $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label);
  393. $id = entity_id($this->entityType, $entity);
  394. $edit_link = l(t('edit'), $this->path . '/manage/' . $id . '/edit');
  395. switch ($op) {
  396. case 'revert':
  397. entity_delete($this->entityType, $id);
  398. watchdog($this->entityType, 'Reverted %entity %label to the defaults.', $vars, WATCHDOG_NOTICE, $edit_link);
  399. return t('Reverted %entity %label to the defaults.', $vars);
  400. case 'delete':
  401. entity_delete($this->entityType, $id);
  402. watchdog($this->entityType, 'Deleted %entity %label.', $vars);
  403. return t('Deleted %entity %label.', $vars);
  404. case 'import':
  405. // First check if there is any existing entity with the same ID.
  406. $id = entity_id($this->entityType, $entity);
  407. $entities = entity_load($this->entityType, array($id));
  408. if ($existing_entity = reset($entities)) {
  409. // Copy DB id and remove the new indicator to overwrite the DB record.
  410. $idkey = $this->entityInfo['entity keys']['id'];
  411. $entity->{$idkey} = $existing_entity->{$idkey};
  412. unset($entity->is_new);
  413. }
  414. entity_save($this->entityType, $entity);
  415. watchdog($this->entityType, 'Imported %entity %label.', $vars);
  416. return t('Imported %entity %label.', $vars);
  417. default:
  418. return FALSE;
  419. }
  420. }
  421. /**
  422. * Entity submit builder invoked via entity_ui_form_submit_build_entity().
  423. *
  424. * Extracts the form values and updates the entity.
  425. *
  426. * The provided implementation makes use of the helper function
  427. * entity_form_submit_build_entity() provided by core, which already invokes
  428. * the field API attacher for fieldable entities.
  429. *
  430. * @return
  431. * The updated entity.
  432. *
  433. * @see entity_ui_form_submit_build_entity()
  434. */
  435. public function entityFormSubmitBuildEntity($form, &$form_state) {
  436. // Add the bundle property to the entity if the entity type supports bundles
  437. // and the form provides a value for the bundle key. Especially new entities
  438. // need to have their bundle property pre-populated before we invoke
  439. // entity_form_submit_build_entity().
  440. if (!empty($this->entityInfo['entity keys']['bundle']) && isset($form_state['values'][$this->entityInfo['entity keys']['bundle']])) {
  441. $form_state[$this->entityType]->{$this->entityInfo['entity keys']['bundle']} = $form_state['values'][$this->entityInfo['entity keys']['bundle']];
  442. }
  443. entity_form_submit_build_entity($this->entityType, $form_state[$this->entityType], $form, $form_state);
  444. return $form_state[$this->entityType];
  445. }
  446. }
  447. /**
  448. * Form builder function for the overview form.
  449. *
  450. * @see EntityDefaultUIController::overviewForm()
  451. */
  452. function entity_ui_overview_form($form, &$form_state, $entity_type) {
  453. return entity_ui_controller($entity_type)->overviewForm($form, $form_state);
  454. }
  455. /**
  456. * Form builder for the entity operation form.
  457. *
  458. * @see EntityDefaultUIController::operationForm()
  459. */
  460. function entity_ui_operation_form($form, &$form_state, $entity_type, $entity, $op) {
  461. $form_state['op'] = $op;
  462. return entity_ui_controller($entity_type)->operationForm($form, $form_state, $entity, $op);
  463. }
  464. /**
  465. * Form wrapper the main entity form.
  466. *
  467. * @see entity_ui_form_defaults()
  468. */
  469. function entity_ui_main_form_defaults($form, &$form_state, $entity = NULL, $op = NULL) {
  470. // Now equals entity_ui_form_defaults() but is still here to keep backward
  471. // compatability.
  472. return entity_ui_form_defaults($form, $form_state, $form_state['entity_type'], $entity, $op);
  473. }
  474. /**
  475. * Clones the entity object and makes sure it will get saved as new entity.
  476. *
  477. * @return
  478. * The cloned entity object.
  479. */
  480. function entity_ui_clone_entity($entity_type, $entity) {
  481. // Clone the entity and make sure it will get saved as a new entity.
  482. $entity = clone $entity;
  483. $entity_info = entity_get_info($entity_type);
  484. $entity->{$entity_info['entity keys']['id']} = FALSE;
  485. if (!empty($entity_info['entity keys']['name'])) {
  486. $entity->{$entity_info['entity keys']['name']} = FALSE;
  487. }
  488. $entity->is_new = TRUE;
  489. // Make sure the status of a cloned exportable is custom.
  490. if (!empty($entity_info['exportable'])) {
  491. $status_key = isset($entity_info['entity keys']['status']) ? $entity_info['entity keys']['status'] : 'status';
  492. $entity->$status_key = ENTITY_CUSTOM;
  493. }
  494. return $entity;
  495. }
  496. /**
  497. * Form wrapper callback for all entity ui forms.
  498. *
  499. * This callback makes sure the form state is properly initialized and sets
  500. * some useful default titles.
  501. *
  502. * @see EntityDefaultUIController::hook_forms()
  503. */
  504. function entity_ui_form_defaults($form, &$form_state, $entity_type, $entity = NULL, $op = NULL) {
  505. $defaults = array(
  506. 'entity_type' => $entity_type,
  507. );
  508. if (isset($entity)) {
  509. $defaults[$entity_type] = $entity;
  510. }
  511. if (isset($op)) {
  512. $defaults['op'] = $op;
  513. }
  514. $form_state += $defaults;
  515. if (isset($op)) {
  516. drupal_set_title(entity_ui_get_page_title($op, $entity_type, $entity), PASS_THROUGH);
  517. }
  518. // Add in handlers pointing to the controller for the forms implemented by it.
  519. if (isset($form_state['build_info']['base_form_id']) && $form_state['build_info']['base_form_id'] != $entity_type . '_form') {
  520. $form['#validate'][] = 'entity_ui_controller_form_validate';
  521. $form['#submit'][] = 'entity_ui_controller_form_submit';
  522. }
  523. return $form;
  524. }
  525. /**
  526. * Validation callback for forms implemented by the UI controller.
  527. */
  528. function entity_ui_controller_form_validate($form, &$form_state) {
  529. // Remove 'entity_ui_' prefix and the '_form' suffix.
  530. $base = substr($form_state['build_info']['base_form_id'], 10, -5);
  531. $method = $base . 'FormValidate';
  532. entity_ui_controller($form_state['entity_type'])->$method($form, $form_state);
  533. }
  534. /**
  535. * Submit callback for forms implemented by the UI controller.
  536. */
  537. function entity_ui_controller_form_submit($form, &$form_state) {
  538. // Remove 'entity_ui_' prefix and the '_form' suffix.
  539. $base = substr($form_state['build_info']['base_form_id'], 10, -5);
  540. $method = $base . 'FormSubmit';
  541. entity_ui_controller($form_state['entity_type'])->$method($form, $form_state);
  542. }
  543. /**
  544. * Gets the page title for the passed operation.
  545. */
  546. function entity_ui_get_page_title($op, $entity_type, $entity = NULL) {
  547. $label = entity_label($entity_type, $entity);
  548. switch ($op) {
  549. case 'edit':
  550. return t('Edit @label', array('@label' => $label));
  551. case 'clone':
  552. return t('Clone @label', array('@label' => $label));
  553. case 'revert':
  554. return t('Revert @label', array('@label' => $label));
  555. case 'delete':
  556. return t('Delete @label', array('@label' => $label));
  557. case 'export':
  558. return t('Export @label', array('@label' => $label));
  559. }
  560. return entity_ui_get_action_title($op, $entity_type);
  561. }
  562. /**
  563. * Gets the page/menu title for local action operations.
  564. */
  565. function entity_ui_get_action_title($op, $entity_type) {
  566. $info = entity_get_info($entity_type);
  567. switch ($op) {
  568. case 'add':
  569. return t('Add @entity_type', array('@entity_type' => drupal_strtolower($info['label'])));
  570. case 'import':
  571. return t('Import @entity_type', array('@entity_type' => drupal_strtolower($info['label'])));
  572. }
  573. }
  574. /**
  575. * Submit builder for the main entity form, which extracts the form values and updates the entity.
  576. *
  577. * This is a helper function for entities making use of the entity UI
  578. * controller.
  579. *
  580. * @return
  581. * The updated entity.
  582. *
  583. * @see EntityDefaultUIController::hook_forms()
  584. * @see EntityDefaultUIController::entityFormSubmitBuildEntity()
  585. */
  586. function entity_ui_form_submit_build_entity($form, &$form_state) {
  587. return entity_ui_controller($form_state['entity_type'])->entityFormSubmitBuildEntity($form, $form_state);
  588. }
  589. /**
  590. * Validation callback for machine names of exportables.
  591. *
  592. * We don't allow numeric machine names, as entity_load() treats them as the
  593. * numeric identifier and they are easily confused with ids in general.
  594. */
  595. function entity_ui_validate_machine_name($element, &$form_state) {
  596. if (is_numeric($element['#value'])) {
  597. form_error($element, t('Machine-readable names must not consist of numbers only.'));
  598. }
  599. }
  600. /**
  601. * Returns HTML for an entity on the entity overview listing.
  602. *
  603. * @ingroup themeable
  604. */
  605. function theme_entity_ui_overview_item($variables) {
  606. $output = $variables['url'] ? l($variables['label'], $variables['url']['path'], $variables['url']['options']) : check_plain($variables['label']);
  607. if ($variables['name']) {
  608. $output .= ' <small> (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')</small>';
  609. }
  610. return $output;
  611. }