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