diff --git a/core/modules/field_ui/field_ui.local_actions.yml b/core/modules/field_ui/field_ui.local_actions.yml new file mode 100644 index 0000000..2939522 --- /dev/null +++ b/core/modules/field_ui/field_ui.local_actions.yml @@ -0,0 +1,3 @@ +field_ui.add: + class: \Drupal\Core\Menu\LocalActionDefault + derivative: \Drupal\field_ui\Plugin\Derivative\FieldUiLocalAction diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index 2033cf6..eb6c68a 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -164,6 +164,7 @@ function field_ui_element_info() { */ function field_ui_entity_info(&$entity_info) { $entity_info['field_instance']['controllers']['form']['delete'] = 'Drupal\field_ui\Form\FieldDeleteForm'; + $entity_info['field_instance']['controllers']['list'] = 'Drupal\field_ui\FieldInstanceListController'; $entity_info['field_entity']['controllers']['list'] = 'Drupal\field_ui\FieldListController'; } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Controller/FieldInstanceListController.php b/core/modules/field_ui/lib/Drupal/field_ui/Controller/FieldInstanceListController.php new file mode 100644 index 0000000..3d32c17 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/Controller/FieldInstanceListController.php @@ -0,0 +1,39 @@ +entityManager()->getDefinition($entity_type); + $bundle = $request->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); + } + return $this->entityManager()->getListController('field_instance')->render($entity_type, $bundle, $request); + } + +} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php index 31a08ec..005237b 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverviewBase.php @@ -12,7 +12,6 @@ use Drupal\Core\Field\FieldTypePluginManager; use Drupal\entity\EntityDisplayBaseInterface; use Drupal\field\FieldInstanceInterface; -use Drupal\field_ui\OverviewBase; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -21,6 +20,41 @@ abstract class DisplayOverviewBase extends OverviewBase { /** + * The name of the entity type. + * + * @var string + */ + protected $entity_type = ''; + + /** + * The entity bundle. + * + * @var string + */ + protected $bundle = ''; + + /** + * The entity view or form mode. + * + * @var string + */ + protected $mode = ''; + + /** + * The admin path of the overview page. + * + * @var string + */ + protected $adminPath = NULL; + + /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManagerInterface + */ + protected $entityManager; + + /** * The widget or formatter plugin manager. * * @var \Drupal\Component\Plugin\PluginManagerBase @@ -45,8 +79,7 @@ * The widget or formatter plugin manager. */ public function __construct(EntityManagerInterface $entity_manager, FieldTypePluginManager $field_type_manager, PluginManagerBase $plugin_manager) { - parent::__construct($entity_manager); - + $this->entityManager = $entity_manager; $this->fieldTypes = $field_type_manager->getConfigurableDefinitions(); $this->pluginManager = $plugin_manager; } @@ -65,25 +98,23 @@ public static function create(ContainerInterface $container) { /** * {@inheritdoc} */ - public function getRegions() { - return array( - 'content' => array( - 'title' => $this->t('Content'), - 'invisible' => TRUE, - 'message' => $this->t('No field is displayed.') - ), - 'hidden' => array( - 'title' => $this->t('Disabled'), - 'message' => $this->t('No field is hidden.') - ), - ); - } - - /** - * {@inheritdoc} - */ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) { - parent::buildForm($form, $form_state, $entity_type, $bundle); + if (!isset($form_state['bundle'])) { + if (!$bundle) { + $entity_info = $this->entityManager->getDefinition($entity_type); + $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); + } + $form_state['bundle'] = $bundle; + } + + $this->entity_type = $entity_type; + $this->bundle = $form_state['bundle']; + $this->adminPath = $this->entityManager->getAdminPath($this->entity_type, $this->bundle); + + // When displaying the form, make sure the list of fields is up-to-date. + if (empty($form_state['post'])) { + field_info_cache_clear(); + } if (empty($this->mode)) { $this->mode = 'default'; @@ -621,6 +652,136 @@ public function multistepAjax($form, &$form_state) { } /** + * Returns the regions needed to create the overview form. + * + * @return array + * Example usage: + * @code + * return array( + * 'content' => array( + * // label for the region. + * 'title' => $this->t('Content'), + * // Indicates if the region is visible in the UI. + * 'invisible' => TRUE, + * // A message to indicate that there is nothing to be displayed in + * // the region. + * 'message' => $this->t('No field is displayed.'), + * ), + * ); + * @endcode + */ + protected function getRegions() { + return array( + 'content' => array( + 'title' => $this->t('Content'), + 'invisible' => TRUE, + 'message' => $this->t('No field is displayed.') + ), + 'hidden' => array( + 'title' => $this->t('Disabled'), + 'message' => $this->t('No field is hidden.') + ), + ); + } + + /** + * Performs pre-render tasks on field_ui_table elements. + * + * This function is assigned as a #pre_render callback in + * field_ui_element_info(). + * + * @see drupal_render(). + */ + public function tablePreRender($elements) { + $js_settings = array(); + + // For each region, build the tree structure from the weight and parenting + // data contained in the flat form structure, to determine row order and + // indentation. + $regions = $elements['#regions']; + $tree = array('' => array('name' => '', 'children' => array())); + $trees = array_fill_keys(array_keys($regions), $tree); + + $parents = array(); + $list = drupal_map_assoc(element_children($elements)); + + // Iterate on rows until we can build a known tree path for all of them. + while ($list) { + foreach ($list as $name) { + $row = &$elements[$name]; + $parent = $row['parent_wrapper']['parent']['#value']; + // Proceed if parent is known. + if (empty($parent) || isset($parents[$parent])) { + // Grab parent, and remove the row from the next iteration. + $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array(); + unset($list[$name]); + + // Determine the region for the row. + $region_name = call_user_func($row['#region_callback'], $row); + + // Add the element in the tree. + $target = &$trees[$region_name]['']; + foreach ($parents[$name] as $key) { + $target = &$target['children'][$key]; + } + $target['children'][$name] = array('name' => $name, 'weight' => $row['weight']['#value']); + + // Add tabledrag indentation to the first row cell. + if ($depth = count($parents[$name])) { + $children = element_children($row); + $cell = current($children); + $indentation = array( + '#theme' => 'indentation', + '#size' => $depth, + ); + $row[$cell]['#prefix'] = drupal_render($indentation) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : ''); + } + + // Add row id and associate JS settings. + $id = drupal_html_class($name); + $row['#attributes']['id'] = $id; + if (isset($row['#js_settings'])) { + $row['#js_settings'] += array( + 'rowHandler' => $row['#row_type'], + 'name' => $name, + 'region' => $region_name, + ); + $js_settings[$id] = $row['#js_settings']; + } + } + } + } + // Determine rendering order from the tree structure. + foreach ($regions as $region_name => $region) { + $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], array($this, 'reduceOrder')); + } + + $elements['#attached']['js'][] = array( + 'type' => 'setting', + 'data' => array('fieldUIRowsData' => $js_settings), + ); + + return $elements; + } + + /** + * Determines the rendering order of an array representing a tree. + * + * Callback for array_reduce() within self::tablePreRender(). + */ + public function reduceOrder($array, $a) { + $array = !isset($array) ? array() : $array; + if ($a['name']) { + $array[] = $a['name']; + } + if (!empty($a['children'])) { + uasort($a['children'], 'drupal_sort_weight'); + $array = array_merge($array, array_reduce($a['children'], array($this, 'reduceOrder'))); + } + return $array; + } + + /** * Returns the entity display object used by this form. * * @param string $mode diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldInstanceListController.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldInstanceListController.php new file mode 100644 index 0000000..e807d68 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldInstanceListController.php @@ -0,0 +1,196 @@ +get('entity.manager')->getStorageController($entity_type), + $container->get('module_handler'), + $container->get('field.info'), + $container->get('plugin.manager.field.field_type') + ); + } + + /** + * Constructs a new EntityListController object. + * + * @param string $entity_type + * The type of entity to be listed. + * @param array $entity_info + * An array of entity info for the entity type. + * @param \Drupal\Core\Entity\EntityStorageControllerInterface $storage + * The entity storage controller class. + * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler + * The module handler to invoke hooks on. + * @param \Drupal\field\FieldInfo $field_info + * The field info service. + * @param \Drupal\Core\Field\FieldTypePluginManager $field_type_manager + * The field type manager + */ + public function __construct($entity_type, array $entity_info, EntityStorageControllerInterface $storage, ModuleHandlerInterface $module_handler, FieldInfo $field_info, FieldTypePluginManager $field_type_manager) { + parent::__construct($entity_type, $entity_info, $storage, $module_handler); + + $this->fieldInfo = $field_info; + $this->fieldTypeManager = $field_type_manager; + } + + /** + * {inheritdoc} + */ + public function render($entity_type = NULL, $bundle = NULL, Request $request = NULl) { + $this->entityType = $entity_type; + $this->bundle = $bundle; + + $this->fieldTypes = $this->fieldTypeManager->getConfigurableDefinitions(); + $this->currentPath = $request->attributes->get('_system_path'); + + $build = array( + '#theme' => 'table', + '#header' => $this->buildHeader(), + '#title' => $this->getTitle(), + '#rows' => array(), + '#empty' => $this->t('No fields are present yet.'), + ); + foreach ($this->load() as $entity) { + if ($row = $this->buildRow($entity)) { + $build['#rows'][$entity->id()] = $row; + } + } + return $build; + } + + /** + * {@inheritdoc} + */ + public function load() { + $entities = $this->fieldInfo->getBundleInstances($this->entityType, $this->bundle); + uasort($entities, array($this->entityInfo['class'], 'sort')); + return $entities; + } + + /** + * {@inheritdoc} + */ + public function buildHeader() { + $header = array( + 'label' => $this->t('Label'), + 'machine_name' => $this->t('Machine name'), + 'field_type' => $this->t('Field type'), + ); + return $header + parent::buildHeader(); + } + + /** + * {@inheritdoc} + */ + public function buildRow(EntityInterface $field_instance) { + $row['label'] = String::checkPlain($field_instance->getFieldLabel()); + $row['machine_name'] = $field_instance->getFieldName(); + $field_type = array( + '#type' => 'link', + '#title' => $this->fieldTypes[$field_instance->getField()->getFieldType()]['label'], + '#href' => $this->currentPath . '/' . $field_instance->id() . '/field', + '#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))), + ); + $row['field_type'] = drupal_render($field_type); + + return $row += parent::buildRow($field_instance); + } + + /** + * {@inheritdoc} + */ + public function getOperations(EntityInterface $entity) { + $operations = array(); + $admin_field_path = $this->currentPath . '/' . $entity->id(); + + $operations['edit'] = array( + 'title' => $this->t('Edit'), + 'href' => $admin_field_path, + 'attributes' => array('title' => $this->t('Edit instance settings.')), + 'weight' => 10, + ); + $operations['field-settings'] = array( + 'title' => $this->t('Field settings'), + 'href' => $admin_field_path . '/field', + 'attributes' => array('title' => $this->t('Edit field settings.')), + 'weight' => 20, + ); + $operations['delete'] = array( + 'title' => $this->t('Delete'), + 'href' => "$admin_field_path/delete", + 'attributes' => array('title' => $this->t('Delete instance.')), + 'weight' => 30, + ); + + return $operations; + } + +} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php deleted file mode 100644 index 0068a44..0000000 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ /dev/null @@ -1,456 +0,0 @@ -fieldTypeManager = $field_type_manager; - $this->moduleHandler = $module_handler; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.manager'), - $container->get('plugin.manager.field.field_type'), - $container->get('module_handler') - ); - } - - /** - * {@inheritdoc} - */ - public function getRegions() { - return array( - 'content' => array( - 'title' => $this->t('Content'), - 'invisible' => TRUE, - 'rows_order' => array(), - // @todo Bring back this message in https://drupal.org/node/1963340. - //'message' => $this->t('No fields are present yet.'), - ), - ); - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'field_ui_field_overview_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) { - parent::buildForm($form, $form_state, $entity_type, $bundle); - - // Gather bundle information. - $instances = field_info_instances($this->entity_type, $this->bundle); - $field_types = $this->fieldTypeManager->getConfigurableDefinitions(); - - // Field prefix. - $field_prefix = \Drupal::config('field_ui.settings')->get('field_prefix'); - - $form += array( - '#entity_type' => $this->entity_type, - '#bundle' => $this->bundle, - '#fields' => array_keys($instances), - ); - - $table = array( - '#type' => 'field_ui_table', - '#tree' => TRUE, - '#header' => array( - $this->t('Label'), - $this->t('Machine name'), - $this->t('Field type'), - $this->t('Operations'), - ), - '#regions' => $this->getRegions(), - '#attributes' => array( - 'class' => array('field-ui-overview'), - 'id' => 'field-overview', - ), - ); - - // @todo - $form['inline_actions'] = array( - '#prefix' => '', - ); - $form['inline_actions']['add'] = array( - '#theme' => 'menu_local_action', - '#link' => array( - 'href' => $this->adminPath . '/add-field', - 'title' => t('Add field'), - ), - ); - - // Fields. - foreach ($instances as $name => $instance) { - $field = $instance->getField(); - $admin_field_path = $this->adminPath . '/fields/' . $instance->id(); - $table[$name] = array( - '#attributes' => array( - 'id' => drupal_html_class($name), - ), - 'label' => array( - '#markup' => check_plain($instance->getFieldLabel()), - ), - 'field_name' => array( - '#markup' => $instance->getFieldName(), - ), - 'type' => array( - '#type' => 'link', - '#title' => $field_types[$field->getFieldType()]['label'], - '#href' => $admin_field_path . '/field', - '#options' => array('attributes' => array('title' => $this->t('Edit field settings.'))), - ), - ); - - $links = array(); - $links['edit'] = array( - 'title' => $this->t('Edit'), - 'href' => $admin_field_path, - 'attributes' => array('title' => $this->t('Edit instance settings.')), - ); - $links['field-settings'] = array( - 'title' => $this->t('Field settings'), - 'href' => $admin_field_path . '/field', - 'attributes' => array('title' => $this->t('Edit field settings.')), - ); - $links['delete'] = array( - 'title' => $this->t('Delete'), - 'href' => "$admin_field_path/delete", - 'attributes' => array('title' => $this->t('Delete instance.')), - ); - // Allow altering the operations on this entity listing. - $this->moduleHandler->alter('entity_operation', $links, $instance); - $table[$name]['operations']['data'] = array( - '#type' => 'operations', - '#links' => $links, - ); - - if (!empty($field->locked)) { - $table[$name]['operations'] = array('#markup' => $this->t('Locked')); - $table[$name]['#attributes']['class'][] = 'menu-disabled'; - } - } - - // We can set the 'rows_order' element, needed by theme_field_ui_table(), - // here instead of a #pre_render callback because this form doesn't have the - // tabledrag behavior anymore. - foreach (element_children($table) as $name) { - $table['#regions']['content']['rows_order'][] = $name; - } - - $form['fields'] = $table; - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save')); - - return $form; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, array &$form_state) { - $this->validateAddNew($form, $form_state); - $this->validateAddExisting($form, $form_state); - } - - /** - * Validates the 'add new field' row. - * - * @param array $form - * An associative array containing the structure of the form. - * @param array $form_state - * A reference to a keyed array containing the current state of the form. - * - * @see \Drupal\field_ui\FieldOverview::validateForm() - */ - protected function validateAddNew(array $form, array &$form_state) { - $field = $form_state['values']['fields']['_add_new_field']; - - // Validate if any information was provided in the 'add new field' row. - if (array_filter(array($field['label'], $field['field_name'], $field['type']))) { - // Missing label. - if (!$field['label']) { - form_set_error('fields][_add_new_field][label', $form_state, $this->t('Add new field: you need to provide a label.')); - } - - // Missing field name. - if (!$field['field_name']) { - form_set_error('fields][_add_new_field][field_name', $form_state, $this->t('Add new field: you need to provide a field name.')); - } - // Field name validation. - else { - $field_name = $field['field_name']; - - // Add the field prefix. - $field_name = \Drupal::config('field_ui.settings')->get('field_prefix') . $field_name; - form_set_value($form['fields']['_add_new_field']['field_name'], $field_name, $form_state); - } - - // Missing field type. - if (!$field['type']) { - form_set_error('fields][_add_new_field][type', $form_state, $this->t('Add new field: you need to select a field type.')); - } - } - } - - /** - * Validates the 're-use existing field' row. - * - * @param array $form - * An associative array containing the structure of the form. - * @param array $form_state - * A reference to a keyed array containing the current state of the form. - * - * @see \Drupal\field_ui\FieldOverview::validate() - */ - protected function validateAddExisting(array $form, array &$form_state) { - // The form element might be absent if no existing fields can be added to - // this bundle. - if (isset($form_state['values']['fields']['_add_existing_field'])) { - $field = $form_state['values']['fields']['_add_existing_field']; - - // Validate if any information was provided in the - // 're-use existing field' row. - if (array_filter(array($field['label'], $field['field_name']))) { - // Missing label. - if (!$field['label']) { - form_set_error('fields][_add_existing_field][label', $form_state, $this->t('Re-use existing field: you need to provide a label.')); - } - - // Missing existing field name. - if (!$field['field_name']) { - form_set_error('fields][_add_existing_field][field_name', $form_state, $this->t('Re-use existing field: you need to select a field.')); - } - } - } - } - - /** - * Overrides \Drupal\field_ui\OverviewBase::submitForm(). - */ - public function submitForm(array &$form, array &$form_state) { - $form_values = $form_state['values']['fields']; - $destinations = array(); - - // Create new field. - if (!empty($form_values['_add_new_field']['field_name'])) { - $values = $form_values['_add_new_field']; - - $field = array( - 'name' => $values['field_name'], - 'entity_type' => $this->entity_type, - 'type' => $values['type'], - 'translatable' => $values['translatable'], - ); - $instance = array( - 'field_name' => $values['field_name'], - 'entity_type' => $this->entity_type, - 'bundle' => $this->bundle, - 'label' => $values['label'], - ); - - // Create the field and instance. - try { - $this->entityManager->getStorageController('field_entity')->create($field)->save(); - $new_instance = $this->entityManager->getStorageController('field_instance')->create($instance); - $new_instance->save(); - - // Make sure the field is displayed in the 'default' form mode (using - // default widget and settings). It stays hidden for other form modes - // until it is explicitly configured. - entity_get_form_display($this->entity_type, $this->bundle, 'default') - ->setComponent($values['field_name']) - ->save(); - - // Make sure the field is displayed in the 'default' view mode (using - // default formatter and settings). It stays hidden for other view - // modes until it is explicitly configured. - entity_get_display($this->entity_type, $this->bundle, 'default') - ->setComponent($values['field_name']) - ->save(); - - // Always show the field settings step, as the cardinality needs to be - // configured for new fields. - $destinations[] = $this->adminPath. '/fields/' . $new_instance->id() . '/field'; - $destinations[] = $this->adminPath . '/fields/' . $new_instance->id(); - - // Store new field information for any additional submit handlers. - $form_state['fields_added']['_add_new_field'] = $values['field_name']; - } - catch (\Exception $e) { - drupal_set_message($this->t('There was a problem creating field %label: !message', array('%label' => $instance->getFieldLabel(), '!message' => $e->getMessage())), 'error'); - } - } - - // Re-use existing field. - if (!empty($form_values['_add_existing_field']['field_name'])) { - $values = $form_values['_add_existing_field']; - $field_name = $values['field_name']; - $field = field_info_field($this->entity_type, $field_name); - if (!empty($field->locked)) { - drupal_set_message($this->t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error'); - } - else { - $instance = array( - 'field_name' => $field_name, - 'entity_type' => $this->entity_type, - 'bundle' => $this->bundle, - 'label' => $values['label'], - ); - - try { - $new_instance = $this->entityManager->getStorageController('field_instance')->create($instance); - $new_instance->save(); - - // Make sure the field is displayed in the 'default' form mode (using - // default widget and settings). It stays hidden for other form modes - // until it is explicitly configured. - entity_get_form_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field_name) - ->save(); - - // Make sure the field is displayed in the 'default' view mode (using - // default formatter and settings). It stays hidden for other view - // modes until it is explicitly configured. - entity_get_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field_name) - ->save(); - - $destinations[] = $this->adminPath . '/fields/' . $new_instance->id(); - // Store new field information for any additional submit handlers. - $form_state['fields_added']['_add_existing_field'] = $instance['field_name']; - } - catch (\Exception $e) { - drupal_set_message($this->t('There was a problem creating field instance %label: @message.', array('%label' => $instance->getFieldLabel(), '@message' => $e->getMessage())), 'error'); - } - } - } - - if ($destinations) { - $destination = drupal_get_destination(); - $destinations[] = $destination['destination']; - $this->getRequest()->query->remove('destination'); - $path = array_shift($destinations); - $options = drupal_parse_url($path); - $options['query']['destinations'] = $destinations; - $form_state['redirect'] = array($options['path'], $options); - } - else { - drupal_set_message($this->t('Your settings have been saved.')); - } - } - - /** - * Returns an array of existing fields to be added to a bundle. - * - * @return array - * An array of existing fields keyed by field name. - */ - protected function getExistingFieldOptions() { - $options = array(); - - // Collect candidate field instances: all instances of fields for this - // entity type that are not already present in the current bundle. - $field_map = field_info_field_map(); - $instance_ids = array(); - if (!empty($field_map[$this->entity_type])) { - foreach ($field_map[$this->entity_type] as $field_name => $data) { - if (!in_array($this->bundle, $data['bundles'])) { - $bundle = reset($data['bundles']); - $instance_ids[] = $this->entity_type . '.' . $bundle . '.' . $field_name; - } - } - } - - // Load the instances and build the list of options. - if ($instance_ids) { - $field_types = $this->fieldTypeManager->getDefinitions(); - $instances = $this->entityManager->getStorageController('field_instance')->loadMultiple($instance_ids); - foreach ($instances as $instance) { - // Do not show: - // - locked fields, - // - fields that should not be added via user interface. - $field_type = $instance->getFieldType(); - $field = $instance->getField(); - if (empty($field->locked) && empty($field_types[$field_type]['no_ui'])) { - $options[$instance->getFieldName()] = array( - 'type' => $field_type, - 'type_label' => $field_types[$field_type]['label'], - 'field' => $instance->getFieldName(), - 'label' => $instance->getFieldLabel(), - ); - } - } - } - - return $options; - } - - /** - * Checks if a field machine name is taken. - * - * @param string $value - * The machine name, not prefixed with 'field_'. - * - * @return bool - * Whether or not the field machine name is taken. - */ - public function fieldNameExists($value) { - // Prefix with 'field_'. - $field_name = 'field_' . $value; - - return (bool) field_info_field($this->entity_type, $field_name); - } - -} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldAddForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldAddForm.php index e3f5cb0..32ac16b 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldAddForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldAddForm.php @@ -9,8 +9,10 @@ use Drupal\Component\Utility\Url; use Drupal\Core\Entity\EntityManager; -use Drupal\Core\Entity\Field\FieldTypePluginManager; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldTypePluginManager; use Drupal\Core\Form\FormBase; +use Drupal\Core\Form\FormBuilderInterface; use Drupal\field\Entity\Field; use Drupal\field\FieldInfo; use Symfony\Component\DependencyInjection\ContainerInterface; @@ -32,21 +34,14 @@ class FieldAddForm extends FormBase { * * @var string */ - protected $entity_type = ''; + protected $entityType; /** * The entity bundle. * * @var string */ - protected $bundle = ''; - - /** - * The entity view or form mode. - * - * @var string - */ - protected $mode = ''; + protected $bundle; /** * The admin path of the overview page. @@ -72,24 +67,34 @@ class FieldAddForm extends FormBase { /** * The field type plugin manager. * - * @var \Drupal\Core\Entity\Field\FieldTypePluginManager + * @var \Drupal\Core\Field\FieldTypePluginManager */ protected $fieldTypePluginManager; /** + * The form builder being tested. + * + * @var \Drupal\Core\Form\FormBuilderInterface + */ + protected $formBuilder; + + /** * Constructs a new FieldAddForm object. * * @param \Drupal\Core\Entity\EntityManager $entity_manager * The entity manager. * @param \Drupal\field\FieldInfo $field_info * The field info service. - * @param \Drupal\Core\Entity\Field\FieldTypePluginManager $field_type_plugin_manager + * @param \Drupal\Core\Field\FieldTypePluginManager $field_type_plugin_manager * The field type plugin manager. + * @param \Drupal\Core\Form\FormBuilderInterface $form_builder + * The form builder. */ - public function __construct(EntityManager $entity_manager, FieldInfo $field_info, FieldTypePluginManager $field_type_plugin_manager) { + public function __construct(EntityManager $entity_manager, FieldInfo $field_info, FieldTypePluginManager $field_type_plugin_manager, FormBuilderInterface $form_builder) { $this->entityManager = $entity_manager; $this->fieldInfo = $field_info; $this->fieldTypePluginManager = $field_type_plugin_manager; + $this->formBuilder = $form_builder; } /** @@ -106,51 +111,36 @@ public static function create(ContainerInterface $container) { return new static( $container->get('entity.manager'), $container->get('field.info'), - $container->get('plugin.manager.entity.field.field_type') + $container->get('plugin.manager.field.field_type'), + $container->get('form_builder') ); } /** * {@inheritdoc} */ - public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL, $form_mode = NULL) { - $entity_info = $this->entityManager->getDefinition($entity_type); - if (!empty($entity_info['bundle_prefix'])) { - $bundle = $entity_info['bundle_prefix'] . $bundle; + public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) { + if (!isset($form_state['bundle'])) { + if (!$bundle) { + $entity_info = $this->entityManager->getDefinition($entity_type); + $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); + } + $form_state['bundle'] = $bundle; } - $this->entity_type = $entity_type; - $this->bundle = $bundle; - $this->mode = $form_mode; - $this->adminPath = $this->entityManager->getAdminPath($this->entity_type, $this->bundle); - - // Gather bundle information. - $instances = $this->fieldInfo->getBundleInstances($entity_type, $bundle); - $field_types = $this->fieldTypePluginManager->getDefinitions(); - $extra_fields = $this->fieldInfo->getBundleExtraFields($entity_type, $bundle); - $extra_fields = isset($extra_fields['form']) ? $extra_fields['form'] : array(); - - // Field prefix. - $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); + $this->entityType = $entity_type; + $this->bundle = $form_state['bundle']; + $this->adminPath = $this->entityManager->getAdminPath($this->entityType, $this->bundle); $form = array( - '#entity_type' => $this->entity_type, - '#bundle' => $this->bundle, - '#fields' => array_keys($instances), - '#extra' => array_keys($extra_fields), - '#tree' => TRUE, '#attributes' => array( - 'class' => array('field-ui-overview'), - 'id' => 'field-display-overview', + 'id' => 'field-add-wrapper', ), - // @todo Remove once _title from \Drupal\field_ui\Routing\RouteSubscriber - // will start to work. - '#title' => 'Add a new field', ); // Gather valid field types to add. $field_type_options = array(); - foreach ($field_types as $name => $field_type) { + foreach ($this->fieldTypePluginManager->getConfigurableDefinitions() as $name => $field_type) { // Skip field types which should not be added via user interface. if (empty($field_type['no_ui'])) { $field_type_options[$name] = $field_type['label']; @@ -165,7 +155,39 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, '#options' => $field_type_options, '#empty_option' => $this->t('- Select a field type -'), '#description' => $this->t('Type of data to store.'), - '#attributes' => array('class' => array('field-type-select')), + ); + + $form['label'] = array( + '#type' => 'textfield', + '#title' => $this->t('Label'), + '#size' => 15, + '#weight' => 20, + ); + + $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); + $form['field_name'] = array( + '#type' => 'machine_name', + // This field should stay LTR even for RTL languages. + '#field_prefix' => '' . $field_prefix, + '#field_suffix' => '‎', + '#size' => 15, + '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), + // Calculate characters depending on the length of the field prefix + // setting. Maximum length is 32. + '#maxlength' => Field::NAME_MAX_LENGTH - strlen($field_prefix), + '#machine_name' => array( + 'source' => array('label'), + 'exists' => array($this, 'fieldNameExists'), + ), + '#required' => FALSE, + '#weight' => 25, + ); + + // Place the 'translatable' property as an explicit value so that + // contrib modules can form_alter() the value for newly created fields. + $form['translatable'] = array( + '#type' => 'value', + '#value' => FALSE, ); // Determine which options from the 'type' field already have existing @@ -178,74 +200,41 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $states[] = array('value' => $field['type']); } - $form['new-or-existing'] = array( - '#type' => 'select', - '#title' => $this->t('New or Existing'), - '#options' => array( - $this->t('New'), - $this->t('Existing'), - ), - '#empty_option' => $this->t('- New or Existing -'), + // #states don't work if they are used on a 'radios' element so we have to + // use a wrapper. + $form['new_or_existing_wrapper'] = array( + '#type' => 'container', '#states' => array( 'visible' => array( 'select[name="type"]' => $states, ), ), ); + $form['new_or_existing_wrapper']['new_or_existing'] = array( + '#type' => 'radios', + '#options' => array( + 'new' => $this->t('Create a new field'), + 'existing' => $this->t('Create a new instance of an existing field'), + ), + '#default_value' => 'new', + '#weight' => 10, + '#ajax' => array( + 'callback' => array(get_class($this), 'updateForm'), + 'wrapper' => 'field-add-wrapper', + ), + ); - if ($field_type_options) { - $states[] = array('value' => ''); - $name = '_add_new_field'; - $form[$name] = array( - '#type' => 'container', - '#attributes' => array('class' => array('new-field-settings')), - // Only show this field if a type has been selected and 'Existing' - // has not explicitly been selected. - '#states' => array( - 'invisible' => array( - 'select[name="type"]' => $states, - 'select[name="new-or-existing"]' => array('!value' => 0), - ), - ), - 'label' => array( - '#type' => 'textfield', - '#title' => $this->t('New field label'), - '#size' => 15, - '#description' => $this->t('Label'), - ), - 'field_name' => array( - '#type' => 'machine_name', - '#title' => $this->t('New field name'), - // This field should stay LTR even for RTL languages. - '#field_prefix' => '' . $field_prefix, - '#field_suffix' => '‎', - '#size' => 15, - '#description' => $this->t('A unique machine-readable name containing letters, numbers, and underscores.'), - // Calculate characters depending on the length of the field prefix - // setting. Maximum length is 32. - '#maxlength' => Field::NAME_MAX_LENGTH - strlen($field_prefix), - '#machine_name' => array( - 'source' => array($name, 'label'), - 'exists' => array($this, 'fieldNameExists'), - 'standalone' => TRUE, - 'label' => '', - ), - '#required' => FALSE, - ), - // Place the 'translatable' property as an explicit value so that - // contrib modules can form_alter() the value for newly created fields. - 'translatable' => array( - '#type' => 'value', - '#value' => FALSE, - ), - ); - } - - // Additional row: re-use existing field. - if ($existing_fields) { - // Build list of options. + // Update the 'field_name' element if we are adding a new instance of an + // existing field. + if (isset($form_state['values']['new_or_existing']) && $form_state['values']['new_or_existing'] == 'existing') { $existing_field_options = array(); foreach ($existing_fields as $field_name => $info) { + // Show only existing fields of the same type as the one selected by the + // user. + if (isset($form_state['values']['type']) && $form_state['values']['type'] != $info['type']) { + continue; + } + $text = $this->t('@type: @field (@label)', array( '@type' => $info['type_label'], '@label' => $info['label'], @@ -255,42 +244,29 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, } asort($existing_field_options); - $name = '_add_existing_field'; - $form[$name] = array( - '#type' => 'container', - '#attributes' => array('class' => array('existing-field-settings')), - // Only show this field if 'Existing' has explicitly been selected. - '#states' => array( - 'invisible' => array( - 'select[name="new-or-existing"]' => array('!value' => 1), - ), - ), - 'field_name' => array( - '#type' => 'select', - '#title' => $this->t('Existing field to share'), - '#options' => $existing_field_options, - '#empty_option' => $this->t('- Select an existing field -'), - '#description' => $this->t('Field to share'), - '#attributes' => array('class' => array('field-select')), - ), - 'label' => array( - '#type' => 'textfield', - '#title' => $this->t('Existing field label'), - '#size' => 15, - '#description' => $this->t('Label'), - '#attributes' => array('class' => array('label-textfield')), - ), + $form['field_name'] = array( + '#type' => 'select', + '#title' => $this->t('Existing field to share'), + '#options' => $existing_field_options, + '#empty_option' => $this->t('- Select an existing field -'), + '#weight' => 15, ); } - // This key is used to store the current updated field. - $form_state += array( - 'formatter_settings_edit' => NULL, + $form['actions'] = array('#type' => 'actions'); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Add field'), + '#button_type' => 'primary', ); - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save field settings')); + return $form; + } + /** + * Handles switching the 'new_or_existing' selector. + */ + public static function updateForm($form, &$form_state) { return $form; } @@ -298,18 +274,19 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, * {@inheritdoc} */ public function validateForm(array &$form, array &$form_state) { - switch($form_state['values']['new-or-existing']) { - case 0: - $this->validateAddNew($form, $form_state); - break; - case 1: - $this->validateAddExisting($form, $form_state); - break; + $values = $form_state['values']; + + // Missing field type. + if (!$values['type']) { + $this->formBuilder->setErrorByName('type', $form_state, $this->t('You need to select a field type.')); } + + $this->validateAddNew($form, $form_state); + $this->validateAddExisting($form, $form_state); } /** - * Validates the 'add new field' row. + * Validates the 'add new field' case. * * @param array $form * An associative array containing the structure of the form. @@ -319,45 +296,34 @@ public function validateForm(array &$form, array &$form_state) { * @see \Drupal\field_ui\Form\FieldAddForm::validateForm() */ protected function validateAddNew(array $form, array &$form_state) { - $field = $form_state['values']['_add_new_field']; $values = $form_state['values']; - // Validate if any information was provided in the 'add new field' row. - if (array_filter(array($field['label'], $field['field_name'], $values['type']))) { + // Validate if any information was provided in the 'add new field' case. + if ($values['new_or_existing'] == 'new' && array_filter(array($values['label'], $values['field_name']))) { // Missing label. - if (!$field['label']) { - form_set_error('_add_new_field][label', $this->t('Add new field: you need to provide a label.')); + if (!$values['label']) { + $this->formBuilder->setErrorByName('label', $form_state, $this->t('You need to provide a label.')); } // Missing field name. - if (!$field['field_name']) { - form_set_error('_add_new_field][field_name', $this->t('Add new field: you need to provide a field name.')); + if (!$values['field_name']) { + $this->formBuilder->setErrorByName('field_name', $form_state, $this->t('You need to provide a field name.')); } // Field name validation. else { - $field_name = $field['field_name']; - - // Add the field prefix. + $field_name = $values['field_name']; $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); // Check if the field name is not longer then the max_length. if(Field::NAME_MAX_LENGTH - (strlen($field_prefix) + strlen($field_name)) < 0) { - form_set_error('_add_new_field][field_name', $this->t('New field name cannot be longer than %max_length characters but is currently %current_length characters long.', array('%max_length' => Field::NAME_MAX_LENGTH - strlen($field_prefix), 'current_length' => strlen($field_name)))); + $this->formBuilder->setErrorByName('field_name', $form_state, $this->t('New field name cannot be longer than %max_length characters but is currently %current_length characters long.', array('%max_length' => Field::NAME_MAX_LENGTH - strlen($field_prefix), 'current_length' => strlen($field_name)))); } - - $field_name = $field_prefix . $field_name; - form_set_value($form['_add_new_field']['field_name'], $field_name, $form_state); - } - - // Missing field type. - if (!$values['type']) { - form_set_error('_add_new_field][type', $this->t('Add new field: you need to select a field type.')); } } } /** - * Validates the 're-use existing field' row. + * Validates the 're-use existing field' case. * * @param array $form * An associative array containing the structure of the form. @@ -367,23 +333,18 @@ protected function validateAddNew(array $form, array &$form_state) { * @see \Drupal\field_ui\Form\FieldAddForm::validateForm() */ protected function validateAddExisting(array $form, array &$form_state) { - // The form element might be absent if no existing fields can be added to - // this bundle. - if (isset($form_state['values']['_add_existing_field'])) { - $field = $form_state['values']['_add_existing_field']; - - // Validate if any information was provided in the - // 're-use existing field' row. - if (array_filter(array($field['label'], $field['field_name']))) { - // Missing label. - if (!$field['label']) { - form_set_error('_add_existing_field][label', $this->t('Re-use existing field: you need to provide a label.')); - } + $values = $form_state['values']; - // Missing existing field name. - if (!$field['field_name']) { - form_set_error('_add_existing_field][field_name', $this->t('Re-use existing field: you need to select a field.')); - } + // Validate if any information was provided. + if ($values['new_or_existing'] == 'existing' && array_filter(array($values['label'], $values['field_name']))) { + // Missing label. + if (!$values['label']) { + $this->formBuilder->setErrorByName('label', $form_state, $this->t('You need to provide a label.')); + } + + // Missing existing field name. + if (!$values['field_name']) { + $this->formBuilder->setErrorByName('field_name', $form_state, $this->t('You need to select an existing field.')); } } } @@ -392,52 +353,40 @@ protected function validateAddExisting(array $form, array &$form_state) { * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { + $values = $form_state['values']; $destinations = array(); // Create new field. - if (!empty($form_state['values']['_add_new_field']['field_name'])) { - $values = $form_state['values']['_add_new_field']; + if ($values['new_or_existing'] == 'new') { + $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); $field = array( - 'entity_type' => $this->entity_type, - 'name' => $form_state['values']['_add_new_field']['field_name'], + 'entity_type' => $this->entityType, + 'name' => $field_prefix . $form_state['values']['field_name'], 'type' => $form_state['values']['type'], - 'translatable' => $form_state['values']['_add_new_field']['translatable'], + 'translatable' => $form_state['values']['translatable'], ); $instance = array( 'field_name' => $field['name'], - 'entity_type' => $this->entity_type, + 'entity_type' => $this->entityType, 'bundle' => $this->bundle, 'label' => $values['label'], ); // Create the field and instance. try { - $this->entityManager->getStorageController('field_entity')->create($field)->save(); + $new_field = $this->entityManager->getStorageController('field_entity')->create($field); + $new_field->save(); $new_instance = $this->entityManager->getStorageController('field_instance')->create($instance); $new_instance->save(); - // Make sure the field is displayed in the 'default' form mode (using - // default widget and settings). It stays hidden for other form modes - // until it is explicitly configured. - entity_get_form_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field['name']) - ->save(); - - // Make sure the field is displayed in the 'default' view mode (using - // default formatter and settings). It stays hidden for other view - // modes until it is explicitly configured. - entity_get_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field['name']) - ->save(); + $this->configureEntityDisplays($new_field); // Always show the field settings step, as the cardinality needs to be // configured for new fields. $destinations[] = $this->adminPath. '/fields/' . $new_instance->id() . '/field'; $destinations[] = $this->adminPath . '/fields/' . $new_instance->id(); - - // Store new field information for any additional submit handlers. - $form_state['fields_added']['_add_new_field'] = $field['name']; + $destinations[] = $this->adminPath . '/fields'; } catch (\Exception $e) { drupal_set_message($this->t('There was a problem creating field %label: !message', array('%label' => $instance['label'], '!message' => $e->getMessage())), 'error'); @@ -445,16 +394,15 @@ public function submitForm(array &$form, array &$form_state) { } // Re-use existing field. - if (!empty($form_state['values']['_add_existing_field']['field_name'])) { - $values = $form_state['values']['_add_existing_field']; - $field = $this->fieldInfo->getField($this->entity_type, $values['field_name']); + if ($values['new_or_existing'] == 'existing') { + $field = $this->fieldInfo->getField($this->entityType, $values['field_name']); if (!empty($field->locked)) { drupal_set_message($this->t('The field %label cannot be added because it is locked.', array('%label' => $values['label'])), 'error'); } else { $instance = array( 'field_name' => $field->getFieldName(), - 'entity_type' => $this->entity_type, + 'entity_type' => $this->entityType, 'bundle' => $this->bundle, 'label' => $values['label'], ); @@ -463,23 +411,10 @@ public function submitForm(array &$form, array &$form_state) { $new_instance = $this->entityManager->getStorageController('field_instance')->create($instance); $new_instance->save(); - // Make sure the field is displayed in the 'default' form mode (using - // default widget and settings). It stays hidden for other form modes - // until it is explicitly configured. - entity_get_form_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field->getFieldName()) - ->save(); - - // Make sure the field is displayed in the 'default' view mode (using - // default formatter and settings). It stays hidden for other view - // modes until it is explicitly configured. - entity_get_display($this->entity_type, $this->bundle, 'default') - ->setComponent($field->getFieldName()) - ->save(); + $this->configureEntityDisplays($field); $destinations[] = $this->adminPath . '/fields/' . $new_instance->id(); - // Store new field information for any additional submit handlers. - $form_state['fields_added']['_add_existing_field'] = $instance['field_name']; + $destinations[] = $this->adminPath . '/fields'; } catch (\Exception $e) { drupal_set_message($this->t('There was a problem creating field instance %label: @message.', array('%label' => $instance['label'], '@message' => $e->getMessage())), 'error'); @@ -502,39 +437,71 @@ public function submitForm(array &$form, array &$form_state) { } /** + * Configures the newly created field for the default view and form modes. + * + * @param FieldDefinitionInterface $field + * The field definition. + */ + protected function configureEntityDisplays(FieldDefinitionInterface $field) { + // Make sure the field is displayed in the 'default' form mode (using + // default widget and settings). It stays hidden for other form modes + // until it is explicitly configured. + entity_get_form_display($this->entityType, $this->bundle, 'default') + ->setComponent($field->getFieldName()) + ->save(); + + // Make sure the field is displayed in the 'default' view mode (using + // default formatter and settings). It stays hidden for other view + // modes until it is explicitly configured. + entity_get_display($this->entityType, $this->bundle, 'default') + ->setComponent($field->getFieldName()) + ->save(); + } + + /** * Returns an array of existing fields to be added to a bundle. * * @return array * An array of existing fields keyed by field name. */ protected function getExistingFieldOptions() { - $info = array(); - $field_types = $this->fieldTypePluginManager->getDefinitions(); - - $instances = $this->fieldInfo->getInstances($this->entity_type); - foreach ($instances as $instance) { - foreach ($instance as $field_name => $instance_settings) { - if (!($instance_settings->bundle == $this->bundle && $instance_settings->entity_type == $this->entity_type)) { - $field = $this->fieldInfo->getField($instance_settings->entity_type, $field_name); - // Don't show - // - locked fields, - // - fields already in the current bundle, - // - fields that cannot be added to the entity type, - // - fields that should not be added via user interface. - if (empty($field->locked) - && !$this->fieldInfo->getInstance($this->entity_type, $this->bundle, $field->id()) - && empty($field_types[$field->getFieldType()]['no_ui'])) { - $info[$instance_settings->getFieldName()] = array( - 'type' => $field->getFieldType(), - 'type_label' => $field_types[$field->getFieldType()]['label'], - 'field' => $field->id(), - 'label' => $instance_settings->label(), - ); - } + $options = array(); + + // Collect candidate field instances: all instances of fields for this + // entity type that are not already present in the current bundle. + $field_map = $this->fieldInfo->getFieldMap(); + $instance_ids = array(); + if (!empty($field_map[$this->entityType])) { + foreach ($field_map[$this->entityType] as $field_name => $data) { + if (!in_array($this->bundle, $data['bundles'])) { + $bundle = reset($data['bundles']); + $instance_ids[] = $this->entityType . '.' . $bundle . '.' . $field_name; } } } - return $info; + + // Load the instances and build the list of options. + if ($instance_ids) { + $field_types = $this->fieldTypePluginManager->getConfigurableDefinitions(); + $instances = $this->entityManager->getStorageController('field_instance')->loadMultiple($instance_ids); + foreach ($instances as $instance) { + // Do not show: + // - locked fields, + // - fields that should not be added via user interface. + $field_type = $instance->getFieldType(); + $field = $instance->getField(); + if (empty($field->locked) && empty($field_types[$field_type]['no_ui'])) { + $options[$instance->getFieldName()] = array( + 'type' => $field_type, + 'type_label' => $field_types[$field_type]['label'], + 'field' => $instance->getFieldName(), + 'label' => $instance->getFieldLabel(), + ); + } + } + } + + return $options; } /** @@ -550,15 +517,7 @@ public function fieldNameExists($value) { // Get the configured field prefix. $field_prefix = $this->config('field_ui.settings')->get('field_prefix'); - // We need to check inactive fields as well, so we can't use - // field_info_fields(). - return (bool) $this->entityManager - ->getStorageController('field_entity') - ->loadByProperties(array( - 'field_name' => $field_prefix . $value, - 'include_inactive' => TRUE, - 'include_deleted' => TRUE) - ); + return (bool) $this->fieldInfo->getField($this->entityType, $field_prefix . $value); } } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php index 5551232..fc1e445 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldEditForm.php @@ -160,7 +160,11 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac $form['field']['settings'] += $entity->get($field->getFieldName())->offsetGet(0)->settingsForm($form, $form_state, $field->hasData()); $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save field settings')); + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save field settings'), + '#button_type' => 'primary', + ); return $form; } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php index 8a7650f..8f9e919 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceEditForm.php @@ -149,7 +149,8 @@ public function buildForm(array $form, array &$form_state, FieldInstanceInterfac $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array( '#type' => 'submit', - '#value' => $this->t('Save settings') + '#value' => $this->t('Save settings'), + '#button_type' => 'primary', ); $form['actions']['delete'] = array( '#type' => 'submit', diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php deleted file mode 100644 index aad6599..0000000 --- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php +++ /dev/null @@ -1,231 +0,0 @@ -entityManager = $entity_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('entity.manager') - ); - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, array &$form_state, $entity_type = NULL, $bundle = NULL) { - if (!isset($form_state['bundle'])) { - if (!$bundle) { - $entity_info = $this->entityManager->getDefinition($entity_type); - $bundle = $this->getRequest()->attributes->get('_raw_variables')->get($entity_info['bundle_entity_type']); - } - $form_state['bundle'] = $bundle; - } - - $this->entity_type = $entity_type; - $this->bundle = $form_state['bundle']; - $this->adminPath = $this->entityManager->getAdminPath($this->entity_type, $this->bundle); - - // When displaying the form, make sure the list of fields is up-to-date. - if (empty($form_state['post'])) { - field_info_cache_clear(); - } - } - - /** - * Implements \Drupal\Core\Form\FormInterface::submitForm(). - */ - public function submitForm(array &$form, array &$form_state) { - } - - /** - * Get the regions needed to create the overview form. - * - * @return array - * Example usage: - * @code - * return array( - * 'content' => array( - * // label for the region. - * 'title' => $this->t('Content'), - * // Indicates if the region is visible in the UI. - * 'invisible' => TRUE, - * // A message to indicate that there is nothing to be displayed in - * // the region. - * 'message' => $this->t('No field is displayed.'), - * ), - * ); - * @endcode - */ - abstract public function getRegions(); - - /** - * Returns an associative array of all regions. - */ - public function getRegionOptions() { - $options = array(); - foreach ($this->getRegions() as $region => $data) { - $options[$region] = $data['title']; - } - return $options; - } - - /** - * Performs pre-render tasks on field_ui_table elements. - * - * This function is assigned as a #pre_render callback in - * field_ui_element_info(). - * - * @see drupal_render(). - */ - public function tablePreRender($elements) { - $js_settings = array(); - - // For each region, build the tree structure from the weight and parenting - // data contained in the flat form structure, to determine row order and - // indentation. - $regions = $elements['#regions']; - $tree = array('' => array('name' => '', 'children' => array())); - $trees = array_fill_keys(array_keys($regions), $tree); - - $parents = array(); - $list = drupal_map_assoc(element_children($elements)); - - // Iterate on rows until we can build a known tree path for all of them. - while ($list) { - foreach ($list as $name) { - $row = &$elements[$name]; - $parent = $row['parent_wrapper']['parent']['#value']; - // Proceed if parent is known. - if (empty($parent) || isset($parents[$parent])) { - // Grab parent, and remove the row from the next iteration. - $parents[$name] = $parent ? array_merge($parents[$parent], array($parent)) : array(); - unset($list[$name]); - - // Determine the region for the row. - $region_name = call_user_func($row['#region_callback'], $row); - - // Add the element in the tree. - $target = &$trees[$region_name]['']; - foreach ($parents[$name] as $key) { - $target = &$target['children'][$key]; - } - $target['children'][$name] = array('name' => $name, 'weight' => $row['weight']['#value']); - - // Add tabledrag indentation to the first row cell. - if ($depth = count($parents[$name])) { - $children = element_children($row); - $cell = current($children); - $indentation = array( - '#theme' => 'indentation', - '#size' => $depth, - ); - $row[$cell]['#prefix'] = drupal_render($indentation) . (isset($row[$cell]['#prefix']) ? $row[$cell]['#prefix'] : ''); - } - - // Add row id and associate JS settings. - $id = drupal_html_class($name); - $row['#attributes']['id'] = $id; - if (isset($row['#js_settings'])) { - $row['#js_settings'] += array( - 'rowHandler' => $row['#row_type'], - 'name' => $name, - 'region' => $region_name, - ); - $js_settings[$id] = $row['#js_settings']; - } - } - } - } - // Determine rendering order from the tree structure. - foreach ($regions as $region_name => $region) { - $elements['#regions'][$region_name]['rows_order'] = array_reduce($trees[$region_name], array($this, 'reduceOrder')); - } - - $elements['#attached']['js'][] = array( - 'type' => 'setting', - 'data' => array('fieldUIRowsData' => $js_settings), - ); - - return $elements; - } - - /** - * Determines the rendering order of an array representing a tree. - * - * Callback for array_reduce() within - * \Drupal\field_ui\OverviewBase::tablePreRender(). - */ - public function reduceOrder($array, $a) { - $array = !isset($array) ? array() : $array; - if ($a['name']) { - $array[] = $a['name']; - } - if (!empty($a['children'])) { - uasort($a['children'], 'drupal_sort_weight'); - $array = array_merge($array, array_reduce($a['children'], array($this, 'reduceOrder'))); - } - return $array; - } - -} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalAction.php b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalAction.php new file mode 100644 index 0000000..5ac6a06 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/Plugin/Derivative/FieldUiLocalAction.php @@ -0,0 +1,90 @@ +entityManager = $entity_manager; + $this->translationManager = $translation_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, $base_plugin_id) { + return new static( + $container->get('entity.manager'), + $container->get('string_translation') + ); + } + + /** + * {@inheritdoc} + */ + public function getDerivativeDefinitions(array $base_plugin_definition) { + $this->derivatives = array(); + + foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) { + if ($entity_info['fieldable'] && isset($entity_info['links']['admin-form'])) { + $this->derivatives["add_$entity_type"] = array( + 'route_name' => "field_ui.add_$entity_type", + 'title' => $this->t('Add field'), + 'appears_on' => array("field_ui.overview_$entity_type"), + ); + } + } + + foreach ($this->derivatives as &$entry) { + $entry += $base_plugin_definition; + } + + return $this->derivatives; + } + + /** + * Translates a string to the current language or to a given language. + * + * See the t() documentation for details. + */ + protected function t($string, array $args = array(), array $options = array()) { + return $this->translationManager->translate($string, $args, $options); + } + +} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php index 6fa3111..00ce3e8 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Routing/RouteSubscriber.php @@ -97,7 +97,7 @@ protected function routes(RouteCollection $collection) { $route = new Route( "$path/fields", array( - '_form' => '\Drupal\field_ui\FieldOverview', + '_content' => '\Drupal\field_ui\Controller\FieldInstanceListController::listing', '_title' => 'Manage fields', ) + $defaults, array('_permission' => 'administer ' . $entity_type . ' fields') @@ -108,7 +108,7 @@ protected function routes(RouteCollection $collection) { "$path/add-field", array( '_form' => '\Drupal\field_ui\Form\FieldAddForm', - '_title' => 'Add a new field', + '_title' => 'Add field', ) + $defaults, array('_permission' => 'administer ' . $entity_type . ' fields') ); diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php index b9ff9c0..d53dafd 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Tests/FieldUIRouteTest.php @@ -46,9 +46,7 @@ public function setUp() { */ public function testFieldUIRoutes() { $this->drupalGet('field-ui-test-no-bundle/manage/fields'); - // @todo Bring back this assertion in https://drupal.org/node/1963340. - // @see \Drupal\field_ui\FieldOverview::getRegions() - //$this->assertText('No fields are present yet.'); + $this->assertText('No fields are present yet.'); $this->drupalGet('admin/structure/types/manage/article/fields'); $this->assertTitle('Manage fields | Drupal');