Mercurial > hg > rr-repo
diff sites/all/modules/entity/includes/entity.ui.inc @ 4:ce11bbd8f642
added modules
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Thu, 19 Sep 2013 10:38:44 +0100 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/sites/all/modules/entity/includes/entity.ui.inc Thu Sep 19 10:38:44 2013 +0100 @@ -0,0 +1,834 @@ +<?php + +/** + * @file + * Provides a controller for building an entity overview form. + */ + +/** + * Default UI controller providing admin UI. + * + * This controller suites best for managing configuration entities. + * For a controller suiting content entities, see EntityContentUIController. + */ +class EntityDefaultUIController { + + protected $entityType; + protected $entityInfo, $path; + protected $id_count; + + /** + * Defines the number of entries to show per page in overview table. + */ + public $overviewPagerLimit = 25; + + public function __construct($entity_type, $entity_info) { + $this->entityType = $entity_type; + $this->entityInfo = $entity_info; + $this->path = $this->entityInfo['admin ui']['path']; + $this->statusKey = empty($this->entityInfo['entity keys']['status']) ? 'status' : $this->entityInfo['entity keys']['status']; + } + + /** + * Provides definitions for implementing hook_menu(). + */ + public function hook_menu() { + $items = array(); + // Set this on the object so classes that extend hook_menu() can use it. + $this->id_count = count(explode('/', $this->path)); + $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object'; + $plural_label = isset($this->entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's'; + + $items[$this->path] = array( + 'title' => $plural_label, + 'page callback' => 'drupal_get_form', + 'page arguments' => array($this->entityType . '_overview_form', $this->entityType), + 'description' => 'Manage ' . $plural_label . '.', + 'access callback' => 'entity_access', + 'access arguments' => array('view', $this->entityType), + 'file' => 'includes/entity.ui.inc', + ); + $items[$this->path . '/list'] = array( + 'title' => 'List', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => -10, + ); + $items[$this->path . '/add'] = array( + 'title callback' => 'entity_ui_get_action_title', + 'title arguments' => array('add', $this->entityType), + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array($this->entityType, NULL, 'add'), + 'access callback' => 'entity_access', + 'access arguments' => array('create', $this->entityType), + 'type' => MENU_LOCAL_ACTION, + ); + $items[$this->path . '/manage/' . $wildcard] = array( + 'title' => 'Edit', + 'title callback' => 'entity_label', + 'title arguments' => array($this->entityType, $this->id_count + 1), + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array($this->entityType, $this->id_count + 1), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('update', $this->entityType, $this->id_count + 1), + ); + $items[$this->path . '/manage/' . $wildcard . '/edit'] = array( + 'title' => 'Edit', + 'load arguments' => array($this->entityType), + 'type' => MENU_DEFAULT_LOCAL_TASK, + ); + + // Clone form, a special case for the edit form. + $items[$this->path . '/manage/' . $wildcard . '/clone'] = array( + 'title' => 'Clone', + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array($this->entityType, $this->id_count + 1, 'clone'), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('create', $this->entityType), + ); + // Menu item for operations like revert and delete. + $items[$this->path . '/manage/' . $wildcard . '/%'] = array( + 'page callback' => 'drupal_get_form', + 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count + 1, $this->id_count + 2), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('delete', $this->entityType, $this->id_count + 1), + 'file' => 'includes/entity.ui.inc', + ); + + if (!empty($this->entityInfo['exportable'])) { + // Menu item for importing an entity. + $items[$this->path . '/import'] = array( + 'title callback' => 'entity_ui_get_action_title', + 'title arguments' => array('import', $this->entityType), + 'page callback' => 'drupal_get_form', + 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, NULL, 'import'), + 'access callback' => 'entity_access', + 'access arguments' => array('create', $this->entityType), + 'file' => 'includes/entity.ui.inc', + 'type' => MENU_LOCAL_ACTION, + ); + } + + if (!empty($this->entityInfo['admin ui']['file'])) { + // Add in the include file for the entity form. + foreach (array("/manage/$wildcard", "/manage/$wildcard/clone", '/add') as $path_end) { + $items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file']; + $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']); + } + } + return $items; + } + + /** + * Provides definitions for implementing hook_forms(). + * + * Use per bundle form ids if possible, such that easy per bundle alterations + * are supported too. + * + * Note that for performance reasons, this method is only invoked for forms, + * which receive the entity_type as first argument. Thus any forms added, must + * follow that pattern. + * + * @see entity_forms() + */ + public function hook_forms() { + // The overview and the operation form are implemented by the controller, + // the callback and validation + submit handlers just invoke the controller. + $forms[$this->entityType . '_overview_form'] = array( + 'callback' => 'entity_ui_overview_form', + 'wrapper_callback' => 'entity_ui_form_defaults', + ); + $forms[$this->entityType . '_operation_form'] = array( + 'callback' => 'entity_ui_operation_form', + 'wrapper_callback' => 'entity_ui_form_defaults', + ); + + // The entity form (ENTITY_TYPE_form) handles editing, adding and cloning. + // For that form, the wrapper callback entity_ui_main_form_defaults() gets + // directly invoked via entity_ui_get_form(). + // If there are bundles though, we use form ids that include the bundle name + // (ENTITY_TYPE_edit_BUNDLE_NAME_form) to enable per bundle alterations + // as well as alterations based upon the base form id (ENTITY_TYPE_form). + if (!(count($this->entityInfo['bundles']) == 1 && isset($this->entityInfo['bundles'][$this->entityType]))) { + foreach ($this->entityInfo['bundles'] as $bundle => $bundle_info) { + $forms[$this->entityType . '_edit_' . $bundle . '_form']['callback'] = $this->entityType . '_form'; + // Again the wrapper callback is invoked by entity_ui_get_form() anyway. + } + } + return $forms; + } + + /** + * Builds the entity overview form. + */ + public function overviewForm($form, &$form_state) { + // By default just show a simple overview for all entities. + $form['table'] = $this->overviewTable(); + $form['pager'] = array('#theme' => 'pager'); + return $form; + } + + /** + * Overview form validation callback. + * + * @param $form + * The form array of the overview form. + * @param $form_state + * The overview form state which will be used for validating. + */ + public function overviewFormValidate($form, &$form_state) {} + + /** + * Overview form submit callback. + * + * @param $form + * The form array of the overview form. + * @param $form_state + * The overview form state which will be used for submitting. + */ + public function overviewFormSubmit($form, &$form_state) {} + + + /** + * Generates the render array for a overview table for arbitrary entities + * matching the given conditions. + * + * @param $conditions + * An array of conditions as needed by entity_load(). + + * @return Array + * A renderable array. + */ + public function overviewTable($conditions = array()) { + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', $this->entityType); + + // Add all conditions to query. + foreach ($conditions as $key => $value) { + $query->propertyCondition($key, $value); + } + + if ($this->overviewPagerLimit) { + $query->pager($this->overviewPagerLimit); + } + + $results = $query->execute(); + + $ids = isset($results[$this->entityType]) ? array_keys($results[$this->entityType]) : array(); + $entities = $ids ? entity_load($this->entityType, $ids) : array(); + ksort($entities); + + $rows = array(); + foreach ($entities as $entity) { + $rows[] = $this->overviewTableRow($conditions, entity_id($this->entityType, $entity), $entity); + } + + $render = array( + '#theme' => 'table', + '#header' => $this->overviewTableHeaders($conditions, $rows), + '#rows' => $rows, + '#empty' => t('None.'), + ); + return $render; + } + + /** + * Generates the table headers for the overview table. + */ + protected function overviewTableHeaders($conditions, $rows, $additional_header = array()) { + $header = $additional_header; + array_unshift($header, t('Label')); + if (!empty($this->entityInfo['exportable'])) { + $header[] = t('Status'); + } + // Add operations with the right colspan. + $header[] = array('data' => t('Operations'), 'colspan' => $this->operationCount()); + return $header; + } + + /** + * Returns the operation count for calculating colspans. + */ + protected function operationCount() { + $count = 3; + $count += !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui') ? 2 : 0; + $count += !empty($this->entityInfo['exportable']) ? 1 : 0; + $count += !empty($this->entityInfo['i18n controller class']) ? 1 : 0; + return $count; + } + + /** + * Generates the row for the passed entity and may be overridden in order to + * customize the rows. + * + * @param $additional_cols + * Additional columns to be added after the entity label column. + */ + protected function overviewTableRow($conditions, $id, $entity, $additional_cols = array()) { + $entity_uri = entity_uri($this->entityType, $entity); + + $row[] = array('data' => array( + '#theme' => 'entity_ui_overview_item', + '#label' => entity_label($this->entityType, $entity), + '#name' => !empty($this->entityInfo['exportable']) ? entity_id($this->entityType, $entity) : FALSE, + '#url' => $entity_uri ? $entity_uri : FALSE, + '#entity_type' => $this->entityType), + ); + + // Add in any passed additional cols. + foreach ($additional_cols as $col) { + $row[] = $col; + } + + // Add a row for the exportable status. + if (!empty($this->entityInfo['exportable'])) { + $row[] = array('data' => array( + '#theme' => 'entity_status', + '#status' => $entity->{$this->statusKey}, + )); + } + // In case this is a bundle, we add links to the field ui tabs. + $field_ui = !empty($this->entityInfo['bundle of']) && entity_type_is_fieldable($this->entityInfo['bundle of']) && module_exists('field_ui'); + // For exportable entities we add an export link. + $exportable = !empty($this->entityInfo['exportable']); + // If i18n integration is enabled, add a link to the translate tab. + $i18n = !empty($this->entityInfo['i18n controller class']); + + // Add operations depending on the status. + if (entity_has_status($this->entityType, $entity, ENTITY_FIXED)) { + $row[] = array('data' => l(t('clone'), $this->path . '/manage/' . $id . '/clone'), 'colspan' => $this->operationCount()); + } + else { + $row[] = l(t('edit'), $this->path . '/manage/' . $id); + + if ($field_ui) { + $row[] = l(t('manage fields'), $this->path . '/manage/' . $id . '/fields'); + $row[] = l(t('manage display'), $this->path . '/manage/' . $id . '/display'); + } + if ($i18n) { + $row[] = l(t('translate'), $this->path . '/manage/' . $id . '/translate'); + } + if ($exportable) { + $row[] = l(t('clone'), $this->path . '/manage/' . $id . '/clone'); + } + + if (empty($this->entityInfo['exportable']) || !entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) { + $row[] = l(t('delete'), $this->path . '/manage/' . $id . '/delete', array('query' => drupal_get_destination())); + } + elseif (entity_has_status($this->entityType, $entity, ENTITY_OVERRIDDEN)) { + $row[] = l(t('revert'), $this->path . '/manage/' . $id . '/revert', array('query' => drupal_get_destination())); + } + else { + $row[] = ''; + } + } + if ($exportable) { + $row[] = l(t('export'), $this->path . '/manage/' . $id . '/export'); + } + return $row; + } + + + /** + * Builds the operation form. + * + * For the export operation a serialized string of the entity is directly + * shown in the form (no submit function needed). + */ + public function operationForm($form, &$form_state, $entity, $op) { + switch ($op) { + case 'revert': + $label = entity_label($this->entityType, $entity); + $confirm_question = t('Are you sure you want to revert the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label)); + return confirm_form($form, $confirm_question, $this->path); + + case 'delete': + $label = entity_label($this->entityType, $entity); + $confirm_question = t('Are you sure you want to delete the %entity %label?', array('%entity' => $this->entityInfo['label'], '%label' => $label)); + return confirm_form($form, $confirm_question, $this->path); + + case 'export': + if (!empty($this->entityInfo['exportable'])) { + $export = entity_export($this->entityType, $entity); + $form['export'] = array( + '#type' => 'textarea', + '#title' => t('Export'), + '#description' => t('For importing copy the content of the text area and paste it into the import page.'), + '#rows' => 25, + '#default_value' => $export, + ); + return $form; + } + + case 'import': + $form['import'] = array( + '#type' => 'textarea', + '#title' => t('Import'), + '#description' => t('Paste an exported %entity_type here.', array('%entity_type' => $this->entityInfo['label'])), + '#rows' => 20, + ); + $form['overwrite'] = array( + '#title' => t('Overwrite'), + '#type' => 'checkbox', + '#description' => t('If checked, any existing %entity with the same identifier will be replaced by the import.', array('%entity' => $this->entityInfo['label'])), + '#default_value' => FALSE, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Import'), + ); + return $form; + } + drupal_not_found(); + exit; + } + + /** + * Operation form validation callback. + */ + public function operationFormValidate($form, &$form_state) { + if ($form_state['op'] == 'import') { + if ($entity = entity_import($this->entityType, $form_state['values']['import'])) { + // Store the successfully imported entity in $form_state. + $form_state[$this->entityType] = $entity; + if (!$form_state['values']['overwrite']) { + // Check for existing entities with the same identifier. + $id = entity_id($this->entityType, $entity); + $entities = entity_load($this->entityType, array($id)); + if (!empty($entities)) { + $label = entity_label($this->entityType, $entity); + $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); + 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)); + } + } + } + else { + form_set_error('import', t('Import failed.')); + } + } + } + + /** + * Operation form submit callback. + */ + public function operationFormSubmit($form, &$form_state) { + $msg = $this->applyOperation($form_state['op'], $form_state[$this->entityType]); + drupal_set_message($msg); + $form_state['redirect'] = $this->path; + } + + /** + * Applies an operation to the given entity. + * + * Note: the export operation is directly carried out by the operationForm() + * method. + * + * @param string $op + * The operation (revert, delete or import). + * @param $entity + * The entity to manipulate. + * + * @return + * The status message of what has been applied. + */ + public function applyOperation($op, $entity) { + $label = entity_label($this->entityType, $entity); + $vars = array('%entity' => $this->entityInfo['label'], '%label' => $label); + $id = entity_id($this->entityType, $entity); + $edit_link = l(t('edit'), $this->path . '/manage/' . $id . '/edit'); + + switch ($op) { + case 'revert': + entity_delete($this->entityType, $id); + watchdog($this->entityType, 'Reverted %entity %label to the defaults.', $vars, WATCHDOG_NOTICE, $edit_link); + return t('Reverted %entity %label to the defaults.', $vars); + + case 'delete': + entity_delete($this->entityType, $id); + watchdog($this->entityType, 'Deleted %entity %label.', $vars); + return t('Deleted %entity %label.', $vars); + + case 'import': + // First check if there is any existing entity with the same ID. + $id = entity_id($this->entityType, $entity); + $entities = entity_load($this->entityType, array($id)); + if ($existing_entity = reset($entities)) { + // Copy DB id and remove the new indicator to overwrite the DB record. + $idkey = $this->entityInfo['entity keys']['id']; + $entity->{$idkey} = $existing_entity->{$idkey}; + unset($entity->is_new); + } + entity_save($this->entityType, $entity); + watchdog($this->entityType, 'Imported %entity %label.', $vars); + return t('Imported %entity %label.', $vars); + + default: + return FALSE; + } + } + + /** + * Entity submit builder invoked via entity_ui_form_submit_build_entity(). + * + * Extracts the form values and updates the entity. + * + * The provided implementation makes use of the helper function + * entity_form_submit_build_entity() provided by core, which already invokes + * the field API attacher for fieldable entities. + * + * @return + * The updated entity. + * + * @see entity_ui_form_submit_build_entity() + */ + public function entityFormSubmitBuildEntity($form, &$form_state) { + // Add the bundle property to the entity if the entity type supports bundles + // and the form provides a value for the bundle key. Especially new entities + // need to have their bundle property pre-populated before we invoke + // entity_form_submit_build_entity(). + if (!empty($this->entityInfo['entity keys']['bundle']) && isset($form_state['values'][$this->entityInfo['entity keys']['bundle']])) { + $form_state[$this->entityType]->{$this->entityInfo['entity keys']['bundle']} = $form_state['values'][$this->entityInfo['entity keys']['bundle']]; + } + entity_form_submit_build_entity($this->entityType, $form_state[$this->entityType], $form, $form_state); + return $form_state[$this->entityType]; + } +} + +/** + * UI controller providing UI for content entities. + * + * For a controller providing UI for bundleable content entities, see + * EntityBundleableUIController. + * For a controller providing admin UI for configuration entities, see + * EntityDefaultUIController. + */ +class EntityContentUIController extends EntityDefaultUIController { + + /** + * Provides definitions for implementing hook_menu(). + */ + public function hook_menu() { + $items = parent::hook_menu(); + $wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object'; + + // Unset the manage entity path, as the provided UI is for admin entities. + unset($items[$this->path]); + + $defaults = array( + 'file' => $this->entityInfo['admin ui']['file'], + 'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']), + ); + + // Add view, edit and delete menu items for content entities. + $items[$this->path . '/' . $wildcard] = array( + 'title callback' => 'entity_ui_get_page_title', + 'title arguments' => array('view', $this->entityType, $this->id_count), + 'page callback' => 'entity_ui_entity_page_view', + 'page arguments' => array($this->id_count), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('view', $this->entityType, $this->id_count), + ) + $defaults; + $items[$this->path . '/' . $wildcard . '/view'] = array( + 'title' => 'View', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'load arguments' => array($this->entityType), + 'weight' => -10, + ) + $defaults; + $items[$this->path . '/' . $wildcard . '/edit'] = array( + 'page callback' => 'entity_ui_get_form', + 'page arguments' => array($this->entityType, $this->id_count), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('edit', $this->entityType, $this->id_count), + 'title' => 'Edit', + 'type' => MENU_LOCAL_TASK, + 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, + ) + $defaults; + $items[$this->path . '/' . $wildcard . '/delete'] = array( + 'page callback' => 'drupal_get_form', + 'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count, 'delete'), + 'load arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('delete', $this->entityType, $this->id_count), + 'title' => 'Delete', + 'type' => MENU_LOCAL_TASK, + 'context' => MENU_CONTEXT_INLINE, + 'file' => $this->entityInfo['admin ui']['file'], + 'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']), + ) + $defaults; + + return $items; + } + + /** + * Operation form submit callback. + */ + public function operationFormSubmit($form, &$form_state) { + parent::operationFormSubmit($form, $form_state); + // The manage entity path is unset for the content entity UI. + $form_state['redirect'] = '<front>'; + } +} + +/** + * UI controller providing UI for bundleable content entities. + * + * Adds a bundle selection page to the entity/add path, analogously to the + * node/add path. + */ +class EntityBundleableUIController extends EntityContentUIController { + + /** + * Provides definitions for implementing hook_menu(). + */ + public function hook_menu() { + $items = parent::hook_menu(); + + // Extend the 'add' path. + $items[$this->path . '/add'] = array( + 'title callback' => 'entity_ui_get_action_title', + 'title arguments' => array('add', $this->entityType), + 'page callback' => 'entity_ui_bundle_add_page', + 'page arguments' => array($this->entityType), + 'access callback' => 'entity_access', + 'access arguments' => array('create', $this->entityType), + 'type' => MENU_LOCAL_ACTION, + ); + $items[$this->path . '/add/%'] = array( + 'title callback' => 'entity_ui_get_action_title', + 'title arguments' => array('add', $this->entityType, $this->id_count + 1), + 'page callback' => 'entity_ui_get_bundle_add_form', + 'page arguments' => array($this->entityType, $this->id_count + 1), + 'access callback' => 'entity_access', + 'access arguments' => array('create', $this->entityType), + ); + + if (!empty($this->entityInfo['admin ui']['file'])) { + // Add in the include file for the entity form. + foreach (array('/add', '/add/%') as $path_end) { + $items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file']; + $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']); + } + } + + return $items; + } +} + +/** + * Form builder function for the overview form. + * + * @see EntityDefaultUIController::overviewForm() + */ +function entity_ui_overview_form($form, &$form_state, $entity_type) { + return entity_ui_controller($entity_type)->overviewForm($form, $form_state); +} + +/** + * Form builder for the entity operation form. + * + * @see EntityDefaultUIController::operationForm() + */ +function entity_ui_operation_form($form, &$form_state, $entity_type, $entity, $op) { + $form_state['op'] = $op; + return entity_ui_controller($entity_type)->operationForm($form, $form_state, $entity, $op); +} + +/** + * Form wrapper the main entity form. + * + * @see entity_ui_form_defaults() + */ +function entity_ui_main_form_defaults($form, &$form_state, $entity = NULL, $op = NULL) { + // Now equals entity_ui_form_defaults() but is still here to keep backward + // compatability. + return entity_ui_form_defaults($form, $form_state, $form_state['entity_type'], $entity, $op); +} + +/** + * Clones the entity object and makes sure it will get saved as new entity. + * + * @return + * The cloned entity object. + */ +function entity_ui_clone_entity($entity_type, $entity) { + // Clone the entity and make sure it will get saved as a new entity. + $entity = clone $entity; + + $entity_info = entity_get_info($entity_type); + $entity->{$entity_info['entity keys']['id']} = FALSE; + if (!empty($entity_info['entity keys']['name'])) { + $entity->{$entity_info['entity keys']['name']} = FALSE; + } + $entity->is_new = TRUE; + + // Make sure the status of a cloned exportable is custom. + if (!empty($entity_info['exportable'])) { + $status_key = isset($entity_info['entity keys']['status']) ? $entity_info['entity keys']['status'] : 'status'; + $entity->$status_key = ENTITY_CUSTOM; + } + return $entity; +} + +/** + * Form wrapper callback for all entity ui forms. + * + * This callback makes sure the form state is properly initialized and sets + * some useful default titles. + * + * @see EntityDefaultUIController::hook_forms() + */ +function entity_ui_form_defaults($form, &$form_state, $entity_type, $entity = NULL, $op = NULL) { + $defaults = array( + 'entity_type' => $entity_type, + ); + if (isset($entity)) { + $defaults[$entity_type] = $entity; + } + if (isset($op)) { + $defaults['op'] = $op; + } + $form_state += $defaults; + if (isset($op)) { + drupal_set_title(entity_ui_get_page_title($op, $entity_type, $entity), PASS_THROUGH); + } + // Add in handlers pointing to the controller for the forms implemented by it. + if (isset($form_state['build_info']['base_form_id']) && $form_state['build_info']['base_form_id'] != $entity_type . '_form') { + $form['#validate'][] = 'entity_ui_controller_form_validate'; + $form['#submit'][] = 'entity_ui_controller_form_submit'; + } + return $form; +} + +/** + * Validation callback for forms implemented by the UI controller. + */ +function entity_ui_controller_form_validate($form, &$form_state) { + // Remove 'entity_ui_' prefix and the '_form' suffix. + $base = substr($form_state['build_info']['base_form_id'], 10, -5); + $method = $base . 'FormValidate'; + entity_ui_controller($form_state['entity_type'])->$method($form, $form_state); +} + +/** + * Submit callback for forms implemented by the UI controller. + */ +function entity_ui_controller_form_submit($form, &$form_state) { + // Remove 'entity_ui_' prefix and the '_form' suffix. + $base = substr($form_state['build_info']['base_form_id'], 10, -5); + $method = $base . 'FormSubmit'; + entity_ui_controller($form_state['entity_type'])->$method($form, $form_state); +} + +/** + * Gets the page title for the passed operation. + */ +function entity_ui_get_page_title($op, $entity_type, $entity = NULL) { + $label = entity_label($entity_type, $entity); + switch ($op) { + case 'view': + return $label; + case 'edit': + return t('Edit @label', array('@label' => $label)); + case 'clone': + return t('Clone @label', array('@label' => $label)); + case 'revert': + return t('Revert @label', array('@label' => $label)); + case 'delete': + return t('Delete @label', array('@label' => $label)); + case 'export': + return t('Export @label', array('@label' => $label)); + } + if (isset($entity)) { + list(, , $bundle) = entity_extract_ids($entity_type, $entity); + } + return entity_ui_get_action_title($op, $entity_type, $bundle); +} + +/** + * Gets the page/menu title for local action operations. + * + * @param $op + * The current operation. One of 'add' or 'import'. + * @param $entity_type + * The entity type. + * @param $bundle_name + * (Optional) The name of the bundle. May be NULL if the bundle name is not + * relevant to the current page. If the entity type has only one bundle, or no + * bundles, this will be the same as the entity type. + */ +function entity_ui_get_action_title($op, $entity_type, $bundle_name = NULL) { + $info = entity_get_info($entity_type); + switch ($op) { + case 'add': + if (isset($bundle_name) && $bundle_name != $entity_type) { + return t('Add @bundle_name @entity_type', array( + '@bundle_name' => drupal_strtolower($info['bundles'][$bundle_name]['label']), + '@entity_type' => drupal_strtolower($info['label']), + )); + } + else { + return t('Add @entity_type', array('@entity_type' => drupal_strtolower($info['label']))); + } + case 'import': + return t('Import @entity_type', array('@entity_type' => drupal_strtolower($info['label']))); + } +} + +/** + * Submit builder for the main entity form, which extracts the form values and updates the entity. + * + * This is a helper function for entities making use of the entity UI + * controller. + * + * @return + * The updated entity. + * + * @see EntityDefaultUIController::hook_forms() + * @see EntityDefaultUIController::entityFormSubmitBuildEntity() + */ +function entity_ui_form_submit_build_entity($form, &$form_state) { + return entity_ui_controller($form_state['entity_type'])->entityFormSubmitBuildEntity($form, $form_state); +} + +/** + * Validation callback for machine names of exportables. + * + * We don't allow numeric machine names, as entity_load() treats them as the + * numeric identifier and they are easily confused with ids in general. + */ +function entity_ui_validate_machine_name($element, &$form_state) { + if (is_numeric($element['#value'])) { + form_error($element, t('Machine-readable names must not consist of numbers only.')); + } +} + +/** + * Returns HTML for an entity on the entity overview listing. + * + * @ingroup themeable + */ +function theme_entity_ui_overview_item($variables) { + $output = $variables['url'] ? l($variables['label'], $variables['url']['path'], $variables['url']['options']) : check_plain($variables['label']); + if ($variables['name']) { + $output .= ' <small> (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')</small>'; + } + return $output; +} + +/** + * Page callback for viewing an entity. + * + * @param Entity $entity + * The entity to be rendered. + * + * @return array + * A renderable array of the entity in full view mode. + */ +function entity_ui_entity_page_view($entity) { + return $entity->view('full', NULL, TRUE); +}