diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php index 2260610..70aa871 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterPluginManager.php @@ -20,6 +20,13 @@ class FormatterPluginManager extends PluginManagerBase { /** + * An array of formatter options for each field type. + * + * @var array + */ + protected $formatterOptions; + + /** * Overrides Drupal\Component\Plugin\PluginManagerBase:$defaults. */ protected $defaults = array( @@ -125,4 +132,34 @@ public function prepareConfiguration($field_type, array $configuration) { return $configuration; } + /** + * Returns an array of formatter options for a field type. + * + * @param string|null $field_type + * (optional) The name of a field type, or NULL to retrieve all formatters. + * + * @return array + * If no field type is provided, returns a nested array of all formatters, + * keyed by field type. + */ + public function getFormatterOptions($field_type = NULL) { + if (!isset($this->formatterOptions)) { + $field_types = field_info_field_types(); + $options = array(); + foreach ($this->getDefinitions() as $name => $formatter) { + foreach ($formatter['field_types'] as $formatter_field_type) { + // Check that the field type exists. + if (isset($field_types[$formatter_field_type])) { + $options[$formatter_field_type][$name] = $formatter['label']; + } + } + } + $this->formatterOptions = $options; + } + if ($field_type) { + return !empty($this->formatterOptions[$field_type]) ? $this->formatterOptions[$field_type] : array(); + } + return $this->formatterOptions; + } + } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php index bb85736..129230c 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetPluginManager.php @@ -19,6 +19,13 @@ class WidgetPluginManager extends PluginManagerBase { /** + * An array of widget options for each field type. + * + * @var array + */ + protected $widgetOptions; + + /** * Overrides Drupal\Component\Plugin\PluginManagerBase:$defaults. */ protected $defaults = array( @@ -120,4 +127,39 @@ public function prepareConfiguration($field_type, array $configuration) { return $configuration; } + + /** + * Returns an array of widget type options for a field type. + * + * @param string|null $field_type + * (optional) The name of a field type, or NULL to retrieve all widget + * options. Defaults to NULL. + * + * @return array + * If no field type is provided, returns a nested array of all widget types, + * keyed by field type human name. + */ + public function getWidgetOptions($field_type = NULL) { + if (!isset($this->widgetOptions)) { + $options = array(); + $field_types = field_info_field_types(); + $widget_types = $this->getDefinitions(); + uasort($widget_types, 'drupal_sort_weight'); + foreach ($widget_types as $name => $widget_type) { + foreach ($widget_type['field_types'] as $widget_field_type) { + // Check that the field type exists. + if (isset($field_types[$widget_field_type])) { + $options[$widget_field_type][$name] = $widget_type['label']; + } + } + } + $this->widgetOptions = $options; + } + if (isset($field_type)) { + return !empty($this->widgetOptions[$field_type]) ? $this->widgetOptions[$field_type] : array(); + } + + return $this->widgetOptions; + } + } diff --git a/core/modules/field_ui/field_ui.admin.inc b/core/modules/field_ui/field_ui.admin.inc index 3ee1eaa..2804657 100644 --- a/core/modules/field_ui/field_ui.admin.inc +++ b/core/modules/field_ui/field_ui.admin.inc @@ -58,155 +58,6 @@ function field_ui_fields_list() { } /** - * Displays a message listing the inactive fields of a given bundle. - */ -function field_ui_inactive_message($entity_type, $bundle) { - $inactive_instances = field_ui_inactive_instances($entity_type, $bundle); - if (!empty($inactive_instances)) { - $field_types = field_info_field_types(); - - foreach ($inactive_instances as $field_name => $instance) { - $field = field_info_field($instance['field_name']); - $list[] = t('%field (@field_name) field requires the %field_type field type provided by %field_type_module module', array( - '%field' => $instance['label'], - '@field_name' => $instance['field_name'], - '%field_type' => isset($field_types[$field['type']]) ? $field_types[$field['type']]['label'] : $field['type'], - '%field_type_module' => $field['module'], - )); - } - drupal_set_message(t('Inactive fields are not shown unless their providing modules are enabled. The following fields are not enabled: !list', array('!list' => theme('item_list', array('items' => $list)))), 'error'); - } -} - -/** - * Determines the rendering order of an array representing a tree. - * - * Callback for array_reduce() within field_ui_table_pre_render(). - */ -function _field_ui_reduce_order($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'], '_field_ui_reduce_order')); - } - return $array; -} - -/** - * Returns the region to which a row in the 'Manage fields' screen belongs. - * - * This function is used as a #region_callback in - * Drupal\field_ui\DisplayOverview::form(). It is called during - * field_ui_table_pre_render(). - */ -function field_ui_field_overview_row_region($row) { - switch ($row['#row_type']) { - case 'field': - case 'extra_field': - return 'content'; - case 'add_new_field': - // If no input in 'label', assume the row has not been dragged out of the - // 'add new' section. - return (!empty($row['label']['#value']) ? 'content' : 'hidden'); - } -} - -/** - * Returns the region to which a row in the 'Manage display' screen belongs. - * - * This function is used as a #region_callback in - * Drupal\field_ui\FieldOverview::form(), and is called during - * field_ui_table_pre_render(). - */ -function field_ui_display_overview_row_region($row) { - switch ($row['#row_type']) { - case 'field': - case 'extra_field': - return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'content'); - } -} - -/** - * Render API callback: 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(). - */ -function field_ui_table_pre_render($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. - $function = $row['#region_callback']; - $region_name = $function($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); - $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (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], '_field_ui_reduce_order'); - } - - $elements['#attached']['js'][] = array( - 'type' => 'setting', - 'data' => array('fieldUIRowsData' => $js_settings), - ); - - return $elements; -} - -/** * Returns HTML for Field UI overview tables. * * @param $variables @@ -287,175 +138,3 @@ function theme_field_ui_table($variables) { return theme('table', $table); } - -/** - * Render API callback: Checks if a field machine name is taken. - * - * @param $value - * The machine name, not prefixed with 'field_'. - * - * @return - * Whether or not the field machine name is taken. - */ -function _field_ui_field_name_exists($value) { - // Prefix with 'field_'. - $field_name = 'field_' . $value; - - // We need to check inactive fields as well, so we can't use - // field_info_fields(). - return (bool) field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE)); -} - -/** - * Returns an array of field_type options. - */ -function field_ui_field_type_options() { - $options = &drupal_static(__FUNCTION__); - - if (!isset($options)) { - $options = array(); - $field_types = field_info_field_types(); - $field_type_options = array(); - foreach ($field_types as $name => $field_type) { - // Skip field types which have no widget types, or should not be add via - // uesr interface. - if (field_ui_widget_type_options($name) && empty($field_type['no_ui'])) { - $options[$name] = $field_type['label']; - } - } - asort($options); - } - return $options; -} - -/** - * Returns an array of widget type options for a field type. - * - * If no field type is provided, returns a nested array of all widget types, - * keyed by field type human name. - */ -function field_ui_widget_type_options($field_type = NULL, $by_label = FALSE) { - $options = &drupal_static(__FUNCTION__); - - if (!isset($options)) { - $options = array(); - $field_types = field_info_field_types(); - $widget_types = field_info_widget_types(); - uasort($widget_types, 'drupal_sort_weight'); - foreach ($widget_types as $name => $widget_type) { - foreach ($widget_type['field_types'] as $widget_field_type) { - // Check that the field type exists. - if (isset($field_types[$widget_field_type])) { - $options[$widget_field_type][$name] = $widget_type['label']; - } - } - } - } - - if (isset($field_type)) { - return !empty($options[$field_type]) ? $options[$field_type] : array(); - } - if ($by_label) { - $field_types = field_info_field_types(); - $options_by_label = array(); - foreach ($options as $field_type => $widgets) { - $options_by_label[$field_types[$field_type]['label']] = $widgets; - } - return $options_by_label; - } - return $options; -} - -/** - * Returns an array of formatter options for a field type. - * - * If no field type is provided, returns a nested array of all formatters, keyed - * by field type. - */ -function field_ui_formatter_options($field_type = NULL) { - $options = &drupal_static(__FUNCTION__); - - if (!isset($options)) { - $field_types = field_info_field_types(); - $options = array(); - foreach (field_info_formatter_types() as $name => $formatter) { - foreach ($formatter['field_types'] as $formatter_field_type) { - // Check that the field type exists. - if (isset($field_types[$formatter_field_type])) { - $options[$formatter_field_type][$name] = $formatter['label']; - } - } - } - } - - if ($field_type) { - return !empty($options[$field_type]) ? $options[$field_type] : array(); - } - return $options; -} - -/** - * Returns an array of existing fields to be added to a bundle. - */ -function field_ui_existing_field_options($entity_type, $bundle) { - $info = array(); - $field_types = field_info_field_types(); - - foreach (field_info_instances() as $existing_entity_type => $bundles) { - foreach ($bundles as $existing_bundle => $instances) { - // No need to look in the current bundle. - if (!($existing_bundle == $bundle && $existing_entity_type == $entity_type)) { - foreach ($instances as $instance) { - $field = field_info_field($instance['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']) - && !field_info_instance($entity_type, $field['field_name'], $bundle) - && (empty($field['entity_types']) || in_array($entity_type, $field['entity_types'])) - && empty($field_types[$field['type']]['no_ui'])) { - $widget = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($instance['field_name']); - $info[$instance['field_name']] = array( - 'type' => $field['type'], - 'type_label' => $field_types[$field['type']]['label'], - 'field' => $field['field_name'], - 'label' => $instance['label'], - 'widget_type' => $widget['type'], - ); - } - } - } - } - } - return $info; -} - -/** - * Extracts next redirect path from an array of multiple destinations. - * - * @see field_ui_next_destination() - */ -function field_ui_get_destinations($destinations) { - $path = array_shift($destinations); - $options = drupal_parse_url($path); - if ($destinations) { - $options['query']['destinations'] = $destinations; - } - return array($options['path'], $options); -} - -/** - * Returns the next redirect path in a multipage sequence. - */ -function field_ui_next_destination($entity_type, $bundle) { - $destinations = !empty($_REQUEST['destinations']) ? $_REQUEST['destinations'] : array(); - if (!empty($destinations)) { - unset($_REQUEST['destinations']); - return field_ui_get_destinations($destinations); - } - $admin_path = Drupal::entityManager()->getAdminPath($entity_type, $bundle); - return $admin_path . '/fields'; -} diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module index cce8734..f9aefe3 100644 --- a/core/modules/field_ui/field_ui.module +++ b/core/modules/field_ui/field_ui.module @@ -237,7 +237,6 @@ function field_ui_element_info() { return array( 'field_ui_table' => array( '#theme' => 'field_ui_table', - '#pre_render' => array('field_ui_table_pre_render'), '#regions' => array('' => array()), ), ); @@ -262,40 +261,6 @@ function field_ui_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) { } /** - * Identifies inactive fields within a bundle. - */ -function field_ui_inactive_instances($entity_type, $bundle_name = NULL) { - $params = array('entity_type' => $entity_type); - - if (empty($bundle_name)) { - $active = field_info_instances($entity_type); - $inactive = array(); - } - else { - // Restrict to the specified bundle. For consistency with the case where - // $bundle_name is NULL, the $active and $inactive arrays are keyed by - // bundle name first. - $params['bundle'] = $bundle_name; - $active = array($bundle_name => field_info_instances($entity_type, $bundle_name)); - $inactive = array($bundle_name => array()); - } - - // Iterate on existing definitions, and spot those that do not appear in the - // $active list collected earlier. - $all_instances = field_read_instances($params, array('include_inactive' => TRUE)); - foreach ($all_instances as $instance) { - if (!isset($active[$instance['bundle']][$instance['field_name']])) { - $inactive[$instance['bundle']][$instance['field_name']] = $instance; - } - } - - if (!empty($bundle_name)) { - return $inactive[$bundle_name]; - } - return $inactive; -} - -/** * Implements hook_form_FORM_ID_alter(). * * Adds a button 'Save and manage fields' to the 'Create content type' form. diff --git a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php index e6caf4e..cc699a6 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/DisplayOverview.php @@ -8,6 +8,9 @@ namespace Drupal\field_ui; use Drupal\field_ui\OverviewBase; +use Drupal\Core\Entity\EntityManager; +use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Field UI display overview form. @@ -15,6 +18,37 @@ class DisplayOverview extends OverviewBase { /** + * The formatter plugin manager. + * + * @var \Drupal\field\Plugin\Type\Formatter\FormatterPluginManager + */ + protected $formatterManager; + + /** + * Constructs a new DisplayOverview. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + * @param \Drupal\field\Plugin\Type\Formatter\FormatterPluginManager $formatter_manager + * The formatter plugin manager. + */ + public function __construct(EntityManager $entity_manager, FormatterPluginManager $formatter_manager) { + parent::__construct($entity_manager); + + $this->formatterManager = $formatter_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity'), + $container->get('plugin.manager.field.formatter') + ); + } + + /** * Implements Drupal\field_ui\OverviewBase::getRegions(). */ public function getRegions() { @@ -70,6 +104,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table = array( '#type' => 'field_ui_table', + '#pre_render' => array(array($this, 'tablePreRender')), '#tree' => TRUE, '#header' => array( t('Field'), @@ -107,7 +142,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'field', - '#region_callback' => 'field_ui_display_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), '#js_settings' => array( 'rowHandler' => 'field', 'defaultFormatter' => $field_types[$field['type']]['default_formatter'], @@ -148,7 +183,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, ), ); - $formatter_options = field_ui_formatter_options($field['type']); + $formatter_options = $this->formatterManager->getFormatterOptions($field['type']); $formatter_options['hidden'] = '<' . t('Hidden') . '>'; $table[$name]['format'] = array( 'type' => array( @@ -174,7 +209,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, // Get the corresponding formatter object. if ($display_options && $display_options['type'] != 'hidden') { - $formatter = drupal_container()->get('plugin.manager.field.formatter')->getInstance(array( + $formatter = $this->formatterManager->getInstance(array( 'instance' => $instance, 'view_mode' => $this->mode, 'configuration' => $display_options @@ -294,7 +329,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'extra_field', - '#region_callback' => 'field_ui_display_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), '#js_settings' => array('rowHandler' => 'field'), 'human_name' => array( '#markup' => check_plain($extra_field['label']), @@ -576,4 +611,22 @@ public function multistepAjax($form, &$form_state) { // Return the whole table. return $form['fields']; } + + /** + * Returns the region to which a row in the display overview belongs. + * + * @param array $row + * The row element. + * + * @return string|null + * The region name this row belongs to. + */ + public function getRowRegion($row) { + switch ($row['#row_type']) { + case 'field': + case 'extra_field': + return ($row['format']['type']['#value'] == 'hidden' ? 'hidden' : 'content'); + } + } + } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php index 44e4be7..1bef718 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldOverview.php @@ -8,6 +8,9 @@ namespace Drupal\field_ui; use Drupal\field_ui\OverviewBase; +use Drupal\Core\Entity\EntityManager; +use Drupal\field\Plugin\Type\Widget\WidgetPluginManager; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Field UI field overview form. @@ -15,6 +18,37 @@ class FieldOverview extends OverviewBase { /** + * The widget plugin manager. + * + * @var \Drupal\field\Plugin\Type\Widget\WidgetPluginManager + */ + protected $widgetManager; + + /** + * Constructs a new DisplayOverview. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + * @param \Drupal\field\Plugin\Type\Widget\WidgetPluginManager $widget_manager + * The widget plugin manager. + */ + public function __construct(EntityManager $entity_manager, WidgetPluginManager $widget_manager) { + parent::__construct($entity_manager); + + $this->widgetManager = $widget_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity'), + $container->get('plugin.manager.field.widget') + ); + } + + /** * Implements Drupal\field_ui\OverviewBase::getRegions(). */ public function getRegions() { @@ -67,6 +101,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table = array( '#type' => 'field_ui_table', + '#pre_render' => array(array($this, 'tablePreRender')), '#tree' => TRUE, '#header' => array( t('Label'), @@ -93,7 +128,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'field', - '#region_callback' => 'field_ui_field_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), 'label' => array( '#markup' => check_plain($instance['label']), ), @@ -165,7 +200,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf')), '#row_type' => 'extra_field', - '#region_callback' => 'field_ui_field_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), 'label' => array( '#markup' => check_plain($extra_field['label']), ), @@ -208,14 +243,31 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, // Additional row: add new field. $max_weight = $entity_form_display->getHighestWeight(); - $field_type_options = field_ui_field_type_options(); - $widget_type_options = field_ui_widget_type_options(NULL, TRUE); + + // Prepare the widget types to be display as options. + $widget_options = $this->widgetManager->getWidgetOptions(); + $widget_type_options = array(); + foreach ($widget_options as $field_type => $widgets) { + $widget_type_options[$field_types[$field_type]['label']] = $widgets; + } + + // Gather valid field types. + $field_type_options = array(); + foreach ($field_types as $name => $field_type) { + // Skip field types which have no widget types, or should not be added via + // user interface. + if (isset($widget_options[$name]) && empty($field_type['no_ui'])) { + $field_type_options[$name] = $field_type['label']; + } + } + asort($field_type_options); + if ($field_type_options && $widget_type_options) { $name = '_add_new_field'; $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), '#row_type' => 'add_new_field', - '#region_callback' => 'field_ui_field_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), 'label' => array( '#type' => 'textfield', '#title' => t('New field label'), @@ -265,7 +317,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, '#prefix' => '
 
', '#machine_name' => array( 'source' => array('fields', $name, 'label'), - 'exists' => '_field_ui_field_name_exists', + 'exists' => array($this, 'fieldNameExists'), 'standalone' => TRUE, 'label' => '', ), @@ -302,7 +354,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, } // Additional row: re-use existing field. - $existing_fields = field_ui_existing_field_options($this->entity_type, $this->bundle); + $existing_fields = $this->getExistingFieldOptions(); if ($existing_fields && $widget_type_options) { // Build list of options. $existing_field_options = array(); @@ -319,7 +371,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $table[$name] = array( '#attributes' => array('class' => array('draggable', 'tabledrag-leaf', 'add-new')), '#row_type' => 'add_new_field', - '#region_callback' => 'field_ui_field_overview_row_region', + '#region_callback' => array($this, 'getRowRegion'), 'label' => array( '#type' => 'textfield', '#title' => t('Existing field label'), @@ -404,7 +456,7 @@ public function buildForm(array $form, array &$form_state, $entity_type = NULL, $form['#attached']['js'][] = array( 'type' => 'setting', - 'data' => array('fields' => $js_fields, 'fieldWidgetTypes' => field_ui_widget_type_options()), + 'data' => array('fields' => $js_fields, 'fieldWidgetTypes' => $widget_options), ); // Add tabledrag behavior. @@ -466,7 +518,7 @@ protected function validateAddNew(array $form, array &$form_state) { } // Wrong widget type. elseif ($field['type']) { - $widget_types = field_ui_widget_type_options($field['type']); + $widget_types = $this->widgetManager->getWidgetOptions($field['type']); if (!isset($widget_types[$field['widget_type']])) { form_set_error('fields][_add_new_field][widget_type', t('Add new field: invalid widget.')); } @@ -509,7 +561,7 @@ protected function validateAddExisting(array $form, array &$form_state) { } // Wrong widget type. elseif ($field['field_name'] && ($existing_field = field_info_field($field['field_name']))) { - $widget_types = field_ui_widget_type_options($existing_field['type']); + $widget_types = $this->widgetManager->getWidgetOptions($existing_field['type']); if (!isset($widget_types[$field['widget_type']])) { form_set_error('fields][_add_existing_field][widget_type', t('Re-use existing field: invalid widget.')); } @@ -645,10 +697,95 @@ public function submitForm(array &$form, array &$form_state) { $destination = drupal_get_destination(); $destinations[] = $destination['destination']; unset($_GET['destination']); - $form_state['redirect'] = field_ui_get_destinations($destinations); + $path = array_shift($destinations); + $options = drupal_parse_url($path); + $options['query']['destinations'] = $destinations; + $form_state['redirect'] = array($options['path'], $options); } else { drupal_set_message(t('Your settings have been saved.')); } } + + /** + * Returns the region to which a row in the display overview belongs. + * + * @param array $row + * The row element. + * + * @return string|null + * The region name this row belongs to. + */ + public function getRowRegion($row) { + switch ($row['#row_type']) { + case 'field': + case 'extra_field': + return 'content'; + case 'add_new_field': + // If no input in 'label', assume the row has not been dragged out of the + // 'add new' section. + return (!empty($row['label']['#value']) ? 'content' : 'hidden'); + } + } + + /** + * 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 = field_info_field_types(); + + foreach (field_info_instances() as $existing_entity_type => $bundles) { + foreach ($bundles as $existing_bundle => $instances) { + // No need to look in the current bundle. + if (!($existing_bundle == $this->bundle && $existing_entity_type == $this->entity_type)) { + foreach ($instances as $instance) { + $field = field_info_field($instance['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']) + && !field_info_instance($this->entity_type, $field['field_name'], $this->bundle) + && (empty($field['entity_types']) || in_array($this->entity_type, $field['entity_types'])) + && empty($field_types[$field['type']]['no_ui'])) { + $widget = entity_get_form_display($instance['entity_type'], $instance['bundle'], 'default')->getComponent($instance['field_name']); + $info[$instance['field_name']] = array( + 'type' => $field['type'], + 'type_label' => $field_types[$field['type']]['label'], + 'field' => $field['field_name'], + 'label' => $instance['label'], + 'widget_type' => $widget['type'], + ); + } + } + } + } + } + return $info; + } + + /** + * 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; + + // We need to check inactive fields as well, so we can't use + // field_info_fields(). + return (bool) field_read_fields(array('field_name' => $field_name), array('include_inactive' => TRUE)); + } + } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php b/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php new file mode 100644 index 0000000..6e4a6a2 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/FieldUI.php @@ -0,0 +1,36 @@ +entityManager->getAdminPath($this->instance->entity_type, $this->instance->bundle); - $form_state['redirect'] = field_ui_get_destinations(array($admin_path . '/fields')); + $form_state['redirect'] = "$admin_path/fields"; // Fields are purged on cron. However field module prevents disabling modules // when field types they provided are used in a field until it is fully 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 56c20dc..da092a7 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 @@ -7,23 +7,34 @@ namespace Drupal\field_ui\Form; +use Drupal\Core\ControllerInterface; +use Drupal\Core\Entity\EntityManager; use Drupal\Core\Form\FormInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; +use Drupal\field\FieldInstanceInterface; use Drupal\field\Field; +use Drupal\field_ui\FieldUI; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Provides a form for the field settings edit page. */ -class FieldEditForm implements FormInterface { +class FieldEditForm implements FormInterface, ControllerInterface { /** * The field instance being edited. * - * @var \Drupal\field\Plugin\Core\Entity\FieldInstance + * @var \Drupal\field\FieldInstanceInterface */ protected $instance; /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** * {@inheritdoc} */ public function getFormID() { @@ -31,10 +42,31 @@ public function getFormID() { } /** + * Constructs a new FieldEditForm object. + * + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The entity manager. + */ + public function __construct(EntityManager $entity_manager) { + $this->entityManager = $entity_manager; + } + + /** * {@inheritdoc} */ - public function buildForm(array $form, array &$form_state, FieldInstance $field_instance = NULL) { + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity') + ); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, array &$form_state, FieldInstanceInterface $field_instance = NULL) { $this->instance = $form_state['instance'] = $field_instance; + form_load_include($form_state, 'inc', 'field_ui', 'field_ui.admin'); + $field = $this->instance->getField(); $form['#field'] = $field; @@ -129,7 +161,6 @@ public function validateForm(array &$form, array &$form_state) { * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { - form_load_include($form_state, 'inc', 'field_ui', 'field_ui.admin'); $form_values = $form_state['values']; $field_values = $form_values['field']; @@ -152,9 +183,13 @@ public function submitForm(array &$form, array &$form_state) { try { $field->save(); drupal_set_message(t('Updated field %label field settings.', array('%label' => $this->instance->label()))); - $form_state['redirect'] = field_ui_next_destination($this->instance->entity_type, $this->instance->bundle); + $next_destination = FieldUI::getNextDestination(); + if (empty($next_destination)) { + $next_destination = $this->entityManager->getAdminPath($this->instance->entity_type, $this->instance->bundle) . '/fields'; + } + $form_state['redirect'] = $next_destination; } - catch (Exception $e) { + catch (\Exception $e) { drupal_set_message(t('Attempt to update field %label failed: %message.', array('%label' => $this->instance->label(), '%message' => $e->getMessage())), 'error'); } } 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 d8788a7..826dc9c 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 @@ -7,49 +7,12 @@ namespace Drupal\field_ui\Form; -use Drupal\Core\Form\FormInterface; -use Drupal\Core\ControllerInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; -use Drupal\field\Plugin\Type\Widget\WidgetPluginManager; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\field\FieldInstanceInterface; /** * Provides a form for the field instance settings form. */ -class FieldInstanceEditForm implements FormInterface, ControllerInterface { - - /** - * The field instance being edited. - * - * @var \Drupal\field\Plugin\Core\Entity\FieldInstance - */ - protected $instance; - - /** - * The field widget plugin manager. - * - * @var \Drupal\field\Plugin\Type\Widget\WidgetPluginManager - */ - protected $widgetManager; - - /** - * Constructs a new FieldInstanceEditForm object. - * - * @param \Drupal\field\Plugin\Type\Widget\WidgetPluginManager $widget_manager - * The field widget plugin manager. - */ - public function __construct(WidgetPluginManager $widget_manager) { - $this->widgetManager = $widget_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.field.widget') - ); - } +class FieldInstanceEditForm extends FieldInstanceFormBase { /** * {@inheritdoc} @@ -61,8 +24,8 @@ public function getFormID() { /** * {@inheritdoc} */ - public function buildForm(array $form, array &$form_state, FieldInstance $field_instance = NULL) { - $this->instance = $form_state['instance'] = $field_instance; + public function buildForm(array $form, array &$form_state, FieldInstanceInterface $field_instance = NULL) { + parent::buildForm($form, $form_state, $field_instance); $bundle = $this->instance['bundle']; $entity_type = $this->instance['entity_type']; @@ -207,7 +170,7 @@ public function validateForm(array &$form, array &$form_state) { * {@inheritdoc} */ public function submitForm(array &$form, array &$form_state) { - form_load_include($form_state, 'inc', 'field_ui', 'field_ui.admin'); + $field = $form['#field']; $entity = $form['#entity']; $entity_form_display = $form['#entity_form_display']; @@ -240,7 +203,7 @@ public function submitForm(array &$form, array &$form_state) { drupal_set_message(t('Field %label is required and uses the "hidden" widget. You might want to configure a default value.', array('%label' => $this->instance['label'])), 'warning'); } - $form_state['redirect'] = field_ui_next_destination($this->instance['entity_type'], $this->instance['bundle']); + $form_state['redirect'] = $this->getNextDestination(); } /** diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceFormBase.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceFormBase.php new file mode 100644 index 0000000..2417292 --- /dev/null +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldInstanceFormBase.php @@ -0,0 +1,86 @@ +entityManager = $entity_manager; + $this->widgetManager = $widget_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('plugin.manager.entity'), + $container->get('plugin.manager.field.widget') + ); + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, array &$form_state, FieldInstanceInterface $field_instance = NULL) { + $this->instance = $form_state['instance'] = $field_instance; + form_load_include($form_state, 'inc', 'field_ui', 'field_ui.admin'); + } + + /** + * Returns the next redirect path in a multipage sequence. + * + * @return string|array + * Either the next path, or an array of redirect paths. + */ + protected function getNextDestination() { + $next_destination = FieldUI::getNextDestination(); + if (empty($next_destination)) { + $next_destination = $this->entityManager->getAdminPath($this->instance->entity_type, $this->instance->bundle) . '/fields'; + } + return $next_destination; + } + +} diff --git a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldWidgetTypeForm.php b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldWidgetTypeForm.php index 5558bc6..1ea5ad9 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldWidgetTypeForm.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/Form/FieldWidgetTypeForm.php @@ -7,49 +7,12 @@ namespace Drupal\field_ui\Form; -use Drupal\Core\Form\FormInterface; -use Drupal\Core\ControllerInterface; -use Drupal\field\Plugin\Core\Entity\FieldInstance; -use Drupal\field\Plugin\Type\Widget\WidgetPluginManager; -use Symfony\Component\DependencyInjection\ContainerInterface; +use Drupal\field\FieldInstanceInterface; /** * Provides a form for the widget selection form. */ -class FieldWidgetTypeForm implements FormInterface, ControllerInterface { - - /** - * The field instance being edited. - * - * @var \Drupal\field\Plugin\Core\Entity\FieldInstance - */ - protected $instance; - - /** - * The field widget plugin manager. - * - * @var \Drupal\field\Plugin\Type\Widget\WidgetPluginManager - */ - protected $widgetManager; - - /** - * Constructs a new FieldWidgetTypeForm object. - * - * @param \Drupal\field\Plugin\Type\Widget\WidgetPluginManager $widget_manager - * The field widget plugin manager. - */ - public function __construct(WidgetPluginManager $widget_manager) { - $this->widgetManager = $widget_manager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('plugin.manager.field.widget') - ); - } +class FieldWidgetTypeForm extends FieldInstanceFormBase { /** * {@inheritdoc} @@ -61,9 +24,9 @@ public function getFormID() { /** * {@inheritdoc} */ - public function buildForm(array $form, array &$form_state, FieldInstance $field_instance = NULL) { - $this->instance = $form_state['instance'] = $field_instance; - form_load_include($form_state, 'inc', 'field_ui', 'field_ui.admin'); + public function buildForm(array $form, array &$form_state, FieldInstanceInterface $field_instance = NULL) { + parent::buildForm($form, $form_state, $field_instance); + drupal_set_title($this->instance['label']); $bundle = $this->instance['bundle']; @@ -86,7 +49,7 @@ public function buildForm(array $form, array &$form_state, FieldInstance $field_ '#type' => 'select', '#title' => t('Widget type'), '#required' => TRUE, - '#options' => field_ui_widget_type_options($field['type']), + '#options' => $this->widgetManager->getWidgetOptions($field['type']), '#default_value' => $entity_form_display->getWidget($field_name)->getPluginId(), '#description' => t('The type of form element you would like to present to the user when creating this field in the %type type.', array('%type' => $bundle_label)), ); @@ -130,7 +93,7 @@ public function submitForm(array &$form, array &$form_state) { drupal_set_message(t('There was a problem changing the widget for field %label.', array('%label' => $instance['label'])), 'error'); } - $form_state['redirect'] = field_ui_next_destination($entity_type, $bundle); + $form_state['redirect'] = $this->getNextDestination(); } } diff --git a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php index 20f6c09..36048a6 100644 --- a/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php +++ b/core/modules/field_ui/lib/Drupal/field_ui/OverviewBase.php @@ -130,4 +130,98 @@ public function getRegionOptions() { 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); + $row[$cell]['#prefix'] = theme('indentation', array('size' => $depth)) . (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; + } + }