diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 22d73fd..1ee6013 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1654,6 +1654,57 @@ function template_preprocess_field_multiple_value_form(&$variables) { } /** + * Prepares variables for individual form element templates. + * + * Default template: field-multiple-value-without-order-form.html.twig. + * + * Combines multiple values. + * + * @param array $variables + * An associative array containing: + * - element: A render element representing the form element. + */ +function template_preprocess_field_multiple_value_without_order_form(&$variables) { + $element = $variables['element']; + $variables['multiple'] = $element['#cardinality_multiple']; + + if ($variables['multiple']) { + $items = array(); + $variables['button'] = array(); + foreach (Element::children($element) as $key) { + if ($key === 'add_more') { + $variables['button'] = &$element[$key]; + } + else { + $items[$key] = &$element[$key]; + if (isset($items[$key]['_weight'])) { + $items[$key]['_weight']['#access'] = FALSE; + } + } + } + usort($items, '_field_multiple_value_form_sort_helper'); + + $variables['title'] = array(); + if (!empty($element['#title'])) { + $variables['title'] = array( + '#type' => 'label', + '#title' => $element['#title'], + '#required' => !empty($element['#required']) ? $element['#required'] : FALSE, + '#title_display' => 'before', + ); + } + $variables['items'] = $items; + $variables['description'] = $element['#description']; + } + else { + $variables['elements'] = array(); + foreach (Element::children($element) as $key) { + $variables['elements'][] = $element[$key]; + } + } +} + +/** * Prepares variables for breadcrumb templates. * * Default template: breadcrumb.html.twig. @@ -1833,5 +1884,8 @@ function drupal_common_theme() { 'field_multiple_value_form' => array( 'render element' => 'element', ), + 'field_multiple_value_without_order_form' => array( + 'render element' => 'element', + ), ); } diff --git a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php index 1032438..1e01bc7 100644 --- a/core/lib/Drupal/Core/Field/BaseFieldDefinition.php +++ b/core/lib/Drupal/Core/Field/BaseFieldDefinition.php @@ -238,6 +238,27 @@ public function setTranslatable($translatable) { /** * {@inheritdoc} */ + public function isOrderable() { + return !empty($this->definition['orderable']); + } + + /** + * Sets whether the field is orderable. + * + * @param bool $orderable + * Whether the field is orderable. + * + * @return $this + * The object itself for chaining. + */ + public function setOrderable($orderable) { + $this->definition['orderable'] = $orderable; + return $this; + } + + /** + * {@inheritdoc} + */ public function isRevisionable() { return !empty($this->definition['revisionable']); } diff --git a/core/lib/Drupal/Core/Field/FieldConfigBase.php b/core/lib/Drupal/Core/Field/FieldConfigBase.php index 0fa3ac6..1e23957 100644 --- a/core/lib/Drupal/Core/Field/FieldConfigBase.php +++ b/core/lib/Drupal/Core/Field/FieldConfigBase.php @@ -108,6 +108,15 @@ protected $required = FALSE; /** + * Flag indicating whether the field is orderable. + * + * Defaults to TRUE. + * + * @var bool + */ + protected $orderable = TRUE; + + /** * Flag indicating whether the field is translatable. * * Defaults to TRUE. @@ -337,6 +346,23 @@ public function setTranslatable($translatable) { /** * {@inheritdoc} */ + public function isOrderable() { + // Make the default state TRUE even when the cardinality doesn't allow + // multiple values. + return $this->orderable || $this->getFieldStorageDefinition()->getCardinality() == 1; + } + + /** + * {@inheritdoc} + */ + public function setOrderable($orderable) { + $this->orderable = $orderable; + return $this; + } + + /** + * {@inheritdoc} + */ public function getSettings() { return $this->settings + $this->getFieldStorageDefinition()->getSettings(); } diff --git a/core/lib/Drupal/Core/Field/WidgetBase.php b/core/lib/Drupal/Core/Field/WidgetBase.php index c99dee5..876dc0c 100644 --- a/core/lib/Drupal/Core/Field/WidgetBase.php +++ b/core/lib/Drupal/Core/Field/WidgetBase.php @@ -215,7 +215,7 @@ protected function formMultipleElements(FieldItemListInterface $items, array &$f if ($elements) { $elements += array( - '#theme' => 'field_multiple_value_form', + '#theme' => $this->fieldDefinition->isOrderable() ? 'field_multiple_value_form' : 'field_multiple_value_without_order_form', '#field_name' => $field_name, '#cardinality' => $cardinality, '#cardinality_multiple' => $this->fieldDefinition->getFieldStorageDefinition()->isMultiple(), diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml index ca182c4..786e434 100644 --- a/core/modules/field/config/schema/field.schema.yml +++ b/core/modules/field/config/schema/field.schema.yml @@ -38,6 +38,9 @@ field.storage.*.*: translatable: type: boolean label: 'Translatable' + orderable: + type: boolean + label: 'Orderable' indexes: type: sequence label: 'Indexes' diff --git a/core/modules/field/src/Entity/FieldConfig.php b/core/modules/field/src/Entity/FieldConfig.php index 8e0e0b4..d7f5e34 100644 --- a/core/modules/field/src/Entity/FieldConfig.php +++ b/core/modules/field/src/Entity/FieldConfig.php @@ -35,6 +35,7 @@ * "bundle", * "label", * "description", + * "orderable", * "required", * "translatable", * "default_value", diff --git a/core/modules/field/src/Entity/FieldStorageConfig.php b/core/modules/field/src/Entity/FieldStorageConfig.php index d4c2922..c51d866 100644 --- a/core/modules/field/src/Entity/FieldStorageConfig.php +++ b/core/modules/field/src/Entity/FieldStorageConfig.php @@ -39,6 +39,7 @@ * "module", * "locked", * "cardinality", + * "orderable", * "translatable", * "indexes", * "persist_with_no_fields", @@ -124,6 +125,15 @@ class FieldStorageConfig extends ConfigEntityBase implements FieldStorageConfigI protected $cardinality = 1; /** + * Flag indicating whether the field is orderable. + * + * Defaults to TRUE. + * + * @var int + */ + protected $orderable = TRUE; + + /** * Flag indicating whether the field is translatable. * * Defaults to TRUE. @@ -593,6 +603,13 @@ public function isTranslatable() { /** * {@inheritdoc} */ + public function isOrderable() { + return $this->orderable; + } + + /** + * {@inheritdoc} + */ public function isRevisionable() { // All configurable fields are revisionable. return TRUE; @@ -609,6 +626,14 @@ public function setTranslatable($translatable) { /** * {@inheritdoc} */ + public function setOrderable($orderable) { + $this->orderable = $orderable; + return $this; + } + + /** + * {@inheritdoc} + */ public function getProvider() { return 'field'; } diff --git a/core/modules/field_ui/src/Form/FieldConfigEditForm.php b/core/modules/field_ui/src/Form/FieldConfigEditForm.php index e2d1bce..e98d677 100644 --- a/core/modules/field_ui/src/Form/FieldConfigEditForm.php +++ b/core/modules/field_ui/src/Form/FieldConfigEditForm.php @@ -76,6 +76,16 @@ public function form(array $form, FormStateInterface $form_state) { '#weight' => -5, ); + if ($this->entity->getFieldStorageDefinition()->getCardinality() != 1) { + $form['orderable'] = array( + '#type' => 'checkbox', + '#title' => $this->t('Orderable'), + '#default_value' => $this->entity->isOrderable(), + '#weight' => -5, + '#description' => t('Orderable multiple fields widgets are in a table with drag and drop.'), + ); + } + // Create an arbitrary entity object (used by the 'default value' widget). $ids = (object) array( 'entity_type' => $this->entity->getTargetEntityTypeId(), diff --git a/core/modules/system/templates/field-multiple-value-form.html.twig b/core/modules/system/templates/field-multiple-value-form.html.twig index 1108d6e..bcc7b89 100644 --- a/core/modules/system/templates/field-multiple-value-form.html.twig +++ b/core/modules/system/templates/field-multiple-value-form.html.twig @@ -1,7 +1,7 @@ {# /** * @file - * Default theme implementation for an individual form element. + * Theme override for an individual form element. * * Available variables for all fields: * - multiple: Whether there are multiple instances of the field. @@ -11,21 +11,17 @@ * * Available variables when there are multiple fields. * - table: Table of field items. - * - description: The description element containing the following properties: - * - content: The description content of the form element. - * - attributes: HTML attributes to apply to the description container. + * - description: Description text for the form element. * - button: "Add another item" button. * * @see template_preprocess_field_multiple_value_form() - * - * @ingroup themeable */ #} {% if multiple %}
{{ table }} - {% if description.content %} - {{ description.content }}
+ {% if description %} +
{{ description }}
{% endif %} {% if button %}
{{ button }}
diff --git a/core/modules/system/templates/field-multiple-value-without-order-form.html.twig b/core/modules/system/templates/field-multiple-value-without-order-form.html.twig new file mode 100644 index 0000000..673062d --- /dev/null +++ b/core/modules/system/templates/field-multiple-value-without-order-form.html.twig @@ -0,0 +1,48 @@ +{# +/** + * @file + * Default theme implementation for an individual form element. + * + * Available variables for all fields: + * - multiple: Whether there are multiple instances of the field. + * + * Available variables for single cardinality fields: + * - elements: Form elements to be rendered. + * + * Available variables when there are multiple fields. + * - table: Table of field items. + * - description: The description element containing the following properties: + * - content: The description content of the form element. + * - attributes: HTML attributes to apply to the description container. + * - button: "Add another item" button. + * + * @see template_preprocess_field_multiple_value_form() + * + * @ingroup themeable + */ +#} +{% + set title_classes = [ + 'label', + required ? 'js-form-required', + required ? 'form-required', + ] +%} +{% if multiple %} + {% if title %} + {{ title }} + {% endif %} +
+ {{ items }} + {% if description.content %} + {{ description.content }}
+ {% endif %} + {% if button %} +
{{ button }}
+ {% endif %} + +{% else %} + {% for element in elements %} + {{ element }} + {% endfor %} +{% endif %} diff --git a/core/themes/classy/templates/form/field-multiple-value-form.html.twig b/core/themes/classy/templates/form/field-multiple-value-form.html.twig index 6ac2ddf..bcc7b89 100644 --- a/core/themes/classy/templates/form/field-multiple-value-form.html.twig +++ b/core/themes/classy/templates/form/field-multiple-value-form.html.twig @@ -11,9 +11,7 @@ * * Available variables when there are multiple fields. * - table: Table of field items. - * - description: The description element containing the following properties: - * - content: The description content of the form element. - * - attributes: HTML attributes to apply to the description container. + * - description: Description text for the form element. * - button: "Add another item" button. * * @see template_preprocess_field_multiple_value_form() @@ -22,8 +20,8 @@ {% if multiple %}
{{ table }} - {% if description.content %} - {{ description.content }}
+ {% if description %} +
{{ description }}
{% endif %} {% if button %}
{{ button }}
diff --git a/core/themes/classy/templates/form/field-multiple-value-without-order-form.html.twig b/core/themes/classy/templates/form/field-multiple-value-without-order-form.html.twig new file mode 100644 index 0000000..673062d --- /dev/null +++ b/core/themes/classy/templates/form/field-multiple-value-without-order-form.html.twig @@ -0,0 +1,48 @@ +{# +/** + * @file + * Default theme implementation for an individual form element. + * + * Available variables for all fields: + * - multiple: Whether there are multiple instances of the field. + * + * Available variables for single cardinality fields: + * - elements: Form elements to be rendered. + * + * Available variables when there are multiple fields. + * - table: Table of field items. + * - description: The description element containing the following properties: + * - content: The description content of the form element. + * - attributes: HTML attributes to apply to the description container. + * - button: "Add another item" button. + * + * @see template_preprocess_field_multiple_value_form() + * + * @ingroup themeable + */ +#} +{% + set title_classes = [ + 'label', + required ? 'js-form-required', + required ? 'form-required', + ] +%} +{% if multiple %} + {% if title %} + {{ title }} + {% endif %} +
+ {{ items }} + {% if description.content %} + {{ description.content }}
+ {% endif %} + {% if button %} +
{{ button }}
+ {% endif %} + +{% else %} + {% for element in elements %} + {{ element }} + {% endfor %} +{% endif %}