Commit 518c4d10 authored by alexpott's avatar alexpott

Issue #1875992 by amateescu, swentel, yched: Add EntityFormDisplay objects for entity forms.

parent 750b5273
......@@ -451,6 +451,27 @@ function hook_entity_display_alter(\Drupal\entity\Plugin\Core\Entity\EntityDispl
}
}
/**
* Alters the settings used for displaying an entity form.
*
* @param \Drupal\entity\Plugin\Core\Entity\EntityFormDisplay $form_display
* The entity_form_display object that will be used to display the entity form
* components.
* @param array $context
* An associative array containing:
* - entity_type: The entity type, e.g., 'node' or 'user'.
* - bundle: The bundle, e.g., 'page' or 'article'.
* - form_mode: The form mode, e.g. 'default', 'profile', 'register'...
*/
function hook_entity_form_display_alter(\Drupal\entity\Plugin\Core\Entity\EntityFormDisplay $form_display, array $context) {
// Hide the 'user_picture' field from the register form.
if ($context['entity_type'] == 'user' && $context['form_mode'] == 'register') {
$form_display->setComponent('user_picture', array(
'type' => 'hidden',
));
}
}
/**
* Define custom entity properties.
*
......
......@@ -662,7 +662,7 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
$display = entity_create('entity_display', array(
'targetEntityType' => $entity_type,
'bundle' => $bundle,
'viewMode' => $view_mode,
'mode' => $view_mode,
));
}
......@@ -691,7 +691,7 @@ function entity_get_display($entity_type, $bundle, $view_mode) {
* @return \Drupal\entity\Plugin\Core\Entity\EntityDisplay
* The display object that should be used to render the entity.
*
* @see entity_get_render_display().
* @see entity_get_display().
*/
function entity_get_render_display(EntityInterface $entity, $view_mode) {
$entity_type = $entity->entityType();
......@@ -704,11 +704,98 @@ function entity_get_render_display(EntityInterface $entity, $view_mode) {
$render_view_mode = !empty($view_mode_settings[$view_mode]['status']) ? $view_mode : 'default';
$display = entity_get_display($entity_type, $bundle, $render_view_mode);
$display->originalViewMode = $view_mode;
$display->originalMode = $view_mode;
return $display;
}
/**
* Returns the entity_form_display object associated to a bundle and form mode.
*
* The function reads the entity_form_display object from the current
* configuration, or returns a ready-to-use empty one if configuration entry
* exists yet for this bundle and form mode. This streamlines manipulation of
* EntityFormDisplay objects by always returning a consistent object that
* reflects the current state of the configuration.
*
* Example usage:
* - Set the 'body' field to be displayed with the 'text_textarea_with_summary'
* widget and the 'field_image' field to be hidden on article nodes in the
* 'default' form mode.
* @code
* entity_get_form_display('node', 'article', 'default')
* ->setComponent('body', array(
* 'type' => 'text_textarea_with_summary',
* 'weight' => 1,
* ))
* ->setComponent('field_image', array(
* 'type' => 'hidden',
* ))
* ->save();
* @endcode
*
* @param string $entity_type
* The entity type.
* @param string $bundle
* The bundle.
* @param string $form_mode
* The form mode.
*
* @return \Drupal\entity\Plugin\Core\Entity\EntityFormDisplay
* The EntityFormDisplay object associated to the form mode.
*/
function entity_get_form_display($entity_type, $bundle, $form_mode) {
// Try loading the entity from configuration.
$entity_form_display = entity_load('entity_form_display', $entity_type . '.' . $bundle . '.' . $form_mode);
// If not found, create a fresh entity object. We do not preemptively create
// new EntityFormDisplay configuration entries for each existing entity type
// and bundle whenever a new form mode becomes available. Instead,
// configuration entries are only created when a EntityFormDisplay object is
// explicitly configured and saved.
if (!$entity_form_display) {
$entity_form_display = entity_create('entity_form_display', array(
'targetEntityType' => $entity_type,
'bundle' => $bundle,
'mode' => $form_mode,
));
}
return $entity_form_display;
}
/**
* Returns the entity_form_display object used to render an entity form.
*
* This function should only be used internally when rendering an entity form.
* When assigning suggested form display options for a component in a given form
* mode, entity_get_form_display() should be used instead, in order to avoid
* inadvertently modifying the output of other form modes that might happen to
* use the 'default' form display too. Those options will then be effectively
* applied only if the form mode is configured to use them.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity for which the form is being rendered.
* @param string $form_mode
* The form mode being rendered.
*
* @return \Drupal\entity\Plugin\Core\Entity\EntityFormDisplay
* The form display object that should be used to render the entity form.
*
* @see entity_get_form_display().
*/
function entity_get_render_form_display(EntityInterface $entity, $form_mode) {
$entity_type = $entity->entityType();
$bundle = $entity->bundle();
// @todo Form modes don't have custom settings yet, so just return the display
// for the form mode that was requested.
$form_display = entity_get_form_display($entity_type, $bundle, $form_mode);
$form_display->originalMode = $form_mode;
return $form_display;
}
/**
* Generic access callback for entity pages.
*
......
......@@ -7,6 +7,8 @@
namespace Drupal\Core\Entity;
use Drupal\entity\EntityFormDisplayInterface;
/**
* Base class for entity form controllers.
*/
......@@ -117,6 +119,20 @@ protected function init(array &$form_state) {
// module-provided form handlers there.
$form_state['controller'] = $this;
$this->prepareEntity();
// @todo Allow the usage of different form modes by exposing a hook and the
// UI for them.
$form_display = entity_get_render_form_display($this->entity, 'default');
// Let modules alter the form display.
$form_display_context = array(
'entity_type' => $this->entity->entityType(),
'bundle' => $this->entity->bundle(),
'form_mode' => 'default',
);
\Drupal::moduleHandler()->alter('entity_form_display', $form_display, $form_display_context);
$this->setFormDisplay($form_display, $form_state);
}
/**
......@@ -132,6 +148,14 @@ public function form(array $form, array &$form_state) {
if (!empty($info['fieldable'])) {
field_attach_form($entity, $form, $form_state, $this->getFormLangcode($form_state));
}
// Assign the weights configured in the form display.
foreach ($this->getFormDisplay($form_state)->getComponents() as $name => $options) {
if (isset($form[$name])) {
$form[$name]['#weight'] = $options['weight'];
}
}
if (!isset($form['langcode'])) {
// If the form did not specify otherwise, default to keeping the existing
// language of the entity or defaulting to the site default language for
......@@ -393,6 +417,21 @@ protected function prepareEntity() {
// @todo Perform common prepare operations and add a hook.
}
/**
* {@inheritdoc}
*/
public function getFormDisplay(array $form_state) {
return isset($form_state['form_display']) ? $form_state['form_display'] : NULL;
}
/**
* {@inheritdoc}
*/
public function setFormDisplay(EntityFormDisplayInterface $form_display, array &$form_state) {
$form_state['form_display'] = $form_display;
return $this;
}
/**
* Implements \Drupal\Core\Entity\EntityFormControllerInterface::getOperation().
*/
......
......@@ -8,6 +8,7 @@
namespace Drupal\Core\Entity;
use Drupal\Core\Form\BaseFormIdInterface;
use Drupal\entity\EntityFormDisplayInterface;
/**
* Defines a common interface for entity form controller classes.
......@@ -44,6 +45,30 @@ public function isDefaultFormLangcode(array $form_state);
*/
public function getOperation();
/**
* Returns the form display.
*
* @param array $form_state
* An associative array containing the current state of the form.
*
* @return \Drupal\entity\EntityFormDisplayInterface
* The current form display.
*/
public function getFormDisplay(array $form_state);
/**
* Sets the form display.
*
* Sets the form display which will be used for populating form element
* defaults.
*
* @param \Drupal\entity\EntityFormDisplayInterface $form_display
* The form display that the current form operates with.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function setFormDisplay(EntityFormDisplayInterface $form_display, array &$form_state);
/**
* Returns the form entity.
*
......
......@@ -30,6 +30,14 @@ public function form(array $form, array &$form_state) {
if (!empty($info['fieldable'])) {
field_attach_form($entity, $form, $form_state, $this->getFormLangcode($form_state));
}
// Assign the weights configured in the form display.
foreach ($this->getFormDisplay($form_state)->getComponents() as $name => $options) {
if (isset($form[$name])) {
$form[$name]['#weight'] = $options['weight'];
}
}
return $form;
}
......
......@@ -259,11 +259,19 @@ function block_update_8008() {
'entity_type' => 'custom_block',
'bundle' => 'basic',
'label' => 'Block body',
'widget' => array('type' => 'text_textarea_with_summary'),
'settings' => array('display_summary' => FALSE),
);
_update_7000_field_create_instance($body_field, $instance);
module_load_install('entity');
// Assign form settings for the 'default' form mode.
$form_display = _update_8000_entity_get_form_display('custom_block', 'basic', 'default');
$form_display->set('content.user_picture', array(
'type' => 'text_textarea_with_summary',
))
->save();
update_config_manifest_add('entity.form_display', array($form_display->get('id')));
// Initialize state for future calls.
$sandbox['last'] = 0;
$sandbox['count'] = 0;
......
......@@ -214,11 +214,17 @@ function custom_block_add_body_field($block_type_id, $label = 'Block body') {
'entity_type' => 'custom_block',
'bundle' => $block_type_id,
'label' => $label,
'widget' => array('type' => 'text_textarea_with_summary'),
'settings' => array('display_summary' => FALSE),
);
$instance = field_create_instance($instance);
// Assign widget settings for the 'default' form mode.
entity_get_form_display('custom_block', $block_type_id, 'default')
->setComponent('block_body', array(
'type' => 'text_textarea_with_summary',
))
->save();
// Assign display settings for 'default' view mode.
entity_get_display('custom_block', $block_type_id, 'default')
->setComponent('block_body', array(
......
......@@ -77,17 +77,18 @@ public function testBlockFields() {
'settings' => array(
'title' => DRUPAL_OPTIONAL,
),
'widget' => array(
'type' => 'link_default',
),
);
$display_options = array(
'type' => 'link',
'label' => 'hidden',
);
field_create_instance($this->instance);
entity_get_form_display('custom_block', 'link', 'default')
->setComponent($this->field['field_name'], array(
'type' => 'link_default',
))
->save();
entity_get_display('custom_block', 'link', 'default')
->setComponent($this->field['field_name'], $display_options)
->setComponent($this->field['field_name'], array(
'type' => 'link',
'label' => 'hidden',
))
->save();
// Create a block.
......
......@@ -365,6 +365,15 @@ function _comment_body_field_create($info) {
'required' => TRUE,
);
field_create_instance($instance);
// Assign widget settings for the 'default' form mode.
entity_get_form_display('comment', 'comment_node_' . $info->type, 'default')
->setComponent('comment_body', array(
'type' => 'text_textarea',
))
->save();
// Assign display settings for the 'default' view mode.
entity_get_display('comment', 'comment_node_' . $info->type, 'default')
->setComponent('comment_body', array(
'label' => 'hidden',
......
......@@ -134,9 +134,7 @@ function datetime_field_settings_form($field, $instance, $has_data) {
* Implements hook_field_instance_settings_form().
*/
function datetime_field_instance_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $instance['settings'];
$widget_settings = $instance['widget']['settings'];
$form['default_value'] = array(
'#type' => 'select',
......
......@@ -36,23 +36,12 @@
class DateTimeDatelistWidget extends WidgetBase {
/**
* Constructs a DateTimeDatelist Widget object.
*
* @param array $plugin_id
* The plugin_id for the widget.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance
* The field instance to which the widget is associated.
* @param array $settings
* The widget settings.
* @param int $weight
* The widget weight.
* {@inheritdoc}
*/
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings, $weight) {
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings) {
// Identify the function used to set the default value.
$instance['default_value_function'] = $this->defaultValueFunction();
parent::__construct($plugin_id, $plugin_definition, $instance, $settings, $weight);
parent::__construct($plugin_id, $plugin_definition, $instance, $settings);
}
/**
......
......@@ -30,23 +30,12 @@
class DateTimeDefaultWidget extends WidgetBase {
/**
* Constructs a DateTimeDefault Widget object.
*
* @param array $plugin_id
* The plugin_id for the widget.
* @param array $plugin_definition
* The plugin implementation definition.
* @param \Drupal\field\Plugin\Core\Entity\FieldInstance $instance
* The field instance to which the widget is associated.
* @param array $settings
* The widget settings.
* @param int $weight
* The widget weight.
* {@inheritdoc}
*/
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings, $weight) {
public function __construct($plugin_id, array $plugin_definition, FieldInstance $instance, array $settings) {
// Identify the function used to set the default value.
$instance['default_value_function'] = $this->defaultValueFunction();
parent::__construct($plugin_id, $plugin_definition, $instance, $settings, $weight);
parent::__construct($plugin_id, $plugin_definition, $instance, $settings);
}
/**
......
......@@ -57,14 +57,17 @@ function setUp() {
'field_name' => $this->field['field_name'],
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'widget' => array(
'type' => 'datetime_default',
),
'settings' => array(
'default_value' => 'blank',
),
));
entity_get_form_display($this->instance['entity_type'], $this->instance['bundle'], 'default')
->setComponent($this->field['field_name'], array(
'type' => 'datetime_default',
))
->save();
$this->display_options = array(
'type' => 'datetime_default',
'label' => 'hidden',
......@@ -217,17 +220,16 @@ function testDatelistWidget() {
field_update_field($this->field);
// Change the widget to a datelist widget.
$increment = 1;
$date_order = 'YMD';
$time_type = '12';
$this->instance['widget']['type'] = 'datetime_datelist';
$this->instance['widget']['settings'] = array(
'increment' => $increment,
'date_order' => $date_order,
'time_type' => $time_type,
);
field_update_instance($this->instance);
entity_get_form_display($this->instance['entity_type'], $this->instance['bundle'], 'default')
->setComponent($this->instance['field_name'], array(
'type' => 'datetime_datelist',
'settings' => array(
'increment' => 1,
'date_order' => 'YMD',
'time_type' => '12',
),
))
->save();
field_cache_clear();
// Display creation form.
......
......@@ -59,6 +59,20 @@ protected function init(array &$form_state, EntityInterface $entity, $field_name
$form_state['entity'] = $entity;
$form_state['field_name'] = $field_name;
// @todo Allow the usage of different form modes by exposing a hook and the
// UI for them.
$form_display = entity_get_render_form_display($entity, 'default');
// Let modules alter the form display.
$form_display_context = array(
'entity_type' => $entity->entityType(),
'bundle' => $entity->bundle(),
'form_mode' => 'default',
);
\Drupal::moduleHandler()->alter('entity_form_display', $form_display, $form_display_context);
$form_state['form_display'] = $form_display;
}
/**
......
......@@ -70,13 +70,16 @@ function createFieldWithInstance($field_name, $type, $cardinality, $label, $inst
'description' => $label,
'weight' => mt_rand(0, 127),
'settings' => $instance_settings,
'widget' => array(
);
field_create_instance($this->$instance);
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent($field_name, array(
'type' => $widget_type,
'label' => $label,
'settings' => $widget_settings,
),
);
field_create_instance($this->$instance);
))
->save();
entity_get_display('entity_test', 'entity_test', 'default')
->setComponent($field_name, array(
......
......@@ -54,14 +54,18 @@ function testEmailField() {
'field_name' => $this->field['field_name'],
'entity_type' => 'test_entity',
'bundle' => 'test_bundle',
'widget' => array(
);
field_create_instance($this->instance);
// Create a form display for the default form mode.
entity_get_form_display('test_entity', 'test_bundle', 'default')
->setComponent($this->field['field_name'], array(
'type' => 'email_default',
'settings' => array(
'placeholder' => 'example@example.com',
),
),
);
field_create_instance($this->instance);
))
->save();
// Create a display for the full view mode.
entity_get_display('test_entity', 'test_bundle', 'full')
->setComponent($this->field['field_name'], array(
......
......@@ -44,11 +44,15 @@ public function setUp() {
'entity_type' => 'entity_test',
'field_name' => 'field_email',
'bundle' => 'entity_test',
'widget' => array(
'type' => 'email_default',
),
);
field_create_instance($this->instance);
// Create a form display for the default form mode.
entity_get_form_display('entity_test', 'entity_test', 'default')
->setComponent('field_email', array(
'type' => 'email_default',
))
->save();
}
/**
......
......@@ -37,7 +37,46 @@ function _update_8000_entity_get_display($entity_type, $bundle, $view_mode) {
'uuid' => $uuid->generate(),
'targetEntityType' => $entity_type,
'bundle' => $bundle,
'viewMode' => $view_mode,
'mode' => $view_mode,
'content' => array(),
);
foreach ($properties as $key => $value) {
$config->set($key, $value);
}
return $config;
}
/**
* Returns the raw configuration object for an EntityFormDisplay entity.
*
* The function returns the existing configuration entry if it exists, or
* creates a fresh structure.
*
* @param string $entity_type
* The entity type.
* @param string $bundle
* The bundle name.
* @param string $form_mode
* The form mode.
*
* @return \Drupal\Core\Config\Config
* The configuration object.
*/
function _update_8000_entity_get_form_display($entity_type, $bundle, $form_mode) {
$id = $entity_type . '.' . $bundle . '.' . $form_mode;
$config = config("entity.form_display.$id");
if ($config->get()) {
return $config;
}
// Initialize a fresh structure.
$uuid = new Uuid();
$properties = array(
'id' => $id,
'uuid' => $uuid->generate(),
'targetEntityType' => $entity_type,
'bundle' => $bundle,
'mode' => $form_mode,
'content' => array(),
);
foreach ($properties as $key => $value) {
......
......@@ -14,20 +14,33 @@
* Implements hook_entity_bundle_rename().
*/
function entity_entity_bundle_rename($entity_type, $bundle_old, $bundle_new) {
$entity_info = entity_get_info('entity_display');
// Rename entity displays.
$entity_info = entity_get_info('entity_display');
if ($bundle_old !== $bundle_new) {
$ids = config_get_storage_names_with_prefix('entity.display.' . $entity_type . '.' . $bundle_old);
foreach ($ids as $id) {
$id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
$display = entity_load('entity_display', $id);
$new_id = $entity_type . '.' . $bundle_new . '.' . $display->viewMode;
$new_id = $entity_type . '.' . $bundle_new . '.' . $display->mode;
$display->id = $new_id;
$display->bundle = $bundle_new;
$display->save();
}
}
// Rename entity form displays.
$entity_info = entity_get_info('entity_form_display');
if ($bundle_old !== $bundle_new) {
$ids = config_get_storage_names_with_prefix('entity.form_display.' . $entity_type . '.' . $bundle_old);
foreach ($ids as $id) {
$id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
$form_display = entity_load('entity_form_display', $id);
$new_id = $entity_type . '.' . $bundle_new . '.' . $form_display->mode;
$form_display->id = $new_id;
$form_display->bundle = $bundle_new;
$form_display->save();
}
}
}
/**
......@@ -42,4 +55,11 @@ function entity_entity_bundle_delete($entity_type, $bundle) {
$id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
}
entity_delete_multiple('entity_display', $ids);
// Remove entity form displays of the deleted bundle.
$ids = config_get_storage_names_with_prefix('entity.form_display.' . $entity_type . '.' . $bundle);
foreach ($ids as &$id) {
$id = ConfigStorageController::getIDFromConfigName($id, $entity_info['config_prefix']);
}
entity_delete_multiple('entity_form_display', $ids);
}
<?php
/**
* @file
* Contains \Drupal\entity\EntityDisplayBase.
*/
namespace Drupal\entity;