Commit 1e904916 authored by jsacksick's avatar jsacksick Committed by bojanz
Browse files

Issue #2838457 by jsacksick, Pancho, bojanz: Re-enable the default value...

Issue #2838457 by jsacksick, Pancho, bojanz: Re-enable the default value functionality for Address fields
parent dd634b18
<?php <?php
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\Sql\SqlContentEntityStorageException; use Drupal\Core\Entity\Sql\SqlContentEntityStorageException;
use Drupal\Core\Utility\UpdateException; use Drupal\Core\Utility\UpdateException;
...@@ -199,3 +200,48 @@ function address_update_8103() { ...@@ -199,3 +200,48 @@ function address_update_8103() {
$config_factory->getEditable($name)->delete(); $config_factory->getEditable($name)->delete();
} }
} }
/**
* Transfers the default country code widget setting to the field config.
*/
function address_update_8104() {
$entity_field_manager = \Drupal::service('entity_field.manager');
$entity_field_map = $entity_field_manager->getFieldMapByFieldType('address');
foreach ($entity_field_map as $entity_type_id => $fields) {
foreach ($fields as $field_name => $field_info) {
foreach ($field_info['bundles'] as $bundle) {
$field = FieldConfig::loadByName($entity_type_id, $bundle, $field_name);
if (!$field) {
// This is a base field, nothing can be done.
continue 2;
}
$form_display = EntityFormDisplay::load("$entity_type_id.$bundle.default");
$address_component = $form_display ? $form_display->getComponent($field_name) : NULL;
if (!$form_display || !$address_component) {
// The bundle doesn't have a form display, or the field is hidden.
continue;
}
if (empty($address_component['settings']['default_country'])) {
// No default country has been specified.
continue;
}
$default_country = $address_component['settings']['default_country'];
if ($default_country == 'site_default') {
$default_country = \Drupal::config('system.date')->get('country.default');
}
// Remove the setting from the widget.
$address_component['settings'] = [];
$form_display->setComponent($field_name, $address_component);
$form_display->save();
// Set the default country on the field.
if ($default_country) {
$field->setDefaultValue([
['country_code' => $default_country],
]);
$field->save();
}
}
}
}
}
...@@ -5,24 +5,6 @@ ...@@ -5,24 +5,6 @@
* Provides functionality for handling postal addresses. * Provides functionality for handling postal addresses.
*/ */
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Removes the default values form from the field settings page.
* Users expect to use the default value form to predefine only certain values
* on the widget, but Drupal expects the default value to be complete, and used
* whenever an actual address isn't provided. Therefore it's preferable to
* hide this functionality and implement our own via custom widget settings.
*/
function address_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state) {
$field = $form_state->getFormObject()->getEntity();
if ($field->getType() == 'address') {
$form['default_value']['#access'] = FALSE;
}
}
/** /**
* Implements hook_theme(). * Implements hook_theme().
*/ */
......
...@@ -144,10 +144,6 @@ field.field_settings.address_zone: ...@@ -144,10 +144,6 @@ field.field_settings.address_zone:
field.widget.settings.address_default: field.widget.settings.address_default:
type: mapping type: mapping
label: 'Default address widget settings' label: 'Default address widget settings'
mapping:
default_country:
type: string
label: 'Default country'
field.widget.settings.address_zone_default: field.widget.settings.address_zone_default:
type: mapping type: mapping
......
...@@ -28,6 +28,9 @@ final class AddressEvents { ...@@ -28,6 +28,9 @@ final class AddressEvents {
/** /**
* Name of the event fired when altering initial values. * Name of the event fired when altering initial values.
* *
* @deprecated No longer fired since 1.5. Use hook_field_widget_form_alter()
* to change the address #default_value instead.
*
* @Event * @Event
* *
* @see \Drupal\address\Event\InitialValuesEvent * @see \Drupal\address\Event\InitialValuesEvent
......
...@@ -8,8 +8,8 @@ use Symfony\Component\EventDispatcher\Event; ...@@ -8,8 +8,8 @@ use Symfony\Component\EventDispatcher\Event;
/** /**
* Defines the initial values event. * Defines the initial values event.
* *
* @see \Drupal\address\Event\AddressEvents * @deprecated No longer fired since 1.5. Use hook_field_widget_form_alter()
* @see \Drupal\address\Plugin\Field\FieldWidget\AddressDefaultWidget::getInitialValues() * to change the address #default_value instead.
*/ */
class InitialValuesEvent extends Event { class InitialValuesEvent extends Event {
......
...@@ -22,7 +22,8 @@ use Drupal\Core\TypedData\DataDefinition; ...@@ -22,7 +22,8 @@ use Drupal\Core\TypedData\DataDefinition;
* description = @Translation("An entity field containing a postal address"), * description = @Translation("An entity field containing a postal address"),
* category = @Translation("Address"), * category = @Translation("Address"),
* default_widget = "address_default", * default_widget = "address_default",
* default_formatter = "address_default" * default_formatter = "address_default",
* list_class = "\Drupal\address\Plugin\Field\FieldType\AddressFieldItemList"
* ) * )
*/ */
class AddressItem extends FieldItemBase implements AddressInterface { class AddressItem extends FieldItemBase implements AddressInterface {
......
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
namespace Drupal\address\Plugin\Field\FieldWidget; namespace Drupal\address\Plugin\Field\FieldWidget;
use CommerceGuys\Addressing\Country\CountryRepositoryInterface; use CommerceGuys\Addressing\Country\CountryRepositoryInterface;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\InitialValuesEvent;
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Field\FieldItemListInterface; use Drupal\Core\Field\FieldItemListInterface;
...@@ -12,6 +10,7 @@ use Drupal\Core\Field\WidgetBase; ...@@ -12,6 +10,7 @@ use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldDefinitionInterface; use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface; use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\ConstraintViolationInterface; use Symfony\Component\Validator\ConstraintViolationInterface;
...@@ -95,98 +94,12 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI ...@@ -95,98 +94,12 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI
); );
} }
/**
* {@inheritdoc}
*/
public static function defaultSettings() {
return [
'default_country' => NULL,
] + parent::defaultSettings();
}
/**
* {@inheritdoc}
*/
public function settingsForm(array $form, FormStateInterface $form_state) {
$country_list = $this->countryRepository->getList();
$element = [];
$element['default_country'] = [
'#type' => 'select',
'#title' => $this->t('Default country'),
'#options' => ['site_default' => $this->t('- Site default -')] + $country_list,
'#default_value' => $this->getSetting('default_country'),
'#empty_value' => '',
];
return $element;
}
/**
* {@inheritdoc}
*/
public function settingsSummary() {
$default_country = $this->getSetting('default_country');
if (empty($default_country)) {
$default_country = $this->t('None');
}
elseif ($default_country == 'site_default') {
$default_country = $this->t('Site default');
}
else {
$country_list = $this->countryRepository->getList();
$default_country = $country_list[$default_country];
}
$summary = [];
$summary['default_country'] = $this->t('Default country: @country', ['@country' => $default_country]);
return $summary;
}
/**
* Gets the initial values for the widget.
*
* This is a replacement for the disabled default values functionality.
*
* @see address_form_field_config_edit_form_alter()
*
* @return array
* The initial values, keyed by property.
*/
protected function getInitialValues() {
$default_country = $this->getSetting('default_country');
// Resolve the special site_default option.
if ($default_country == 'site_default') {
$default_country = $this->configFactory->get('system.date')->get('country.default');
}
$initial_values = [
'country_code' => $default_country,
'administrative_area' => '',
'locality' => '',
'dependent_locality' => '',
'postal_code' => '',
'sorting_code' => '',
'address_line1' => '',
'address_line2' => '',
'organization' => '',
'given_name' => '',
'additional_name' => '',
'family_name' => '',
];
// Allow other modules to alter the values.
$event = new InitialValuesEvent($initial_values, $this->fieldDefinition);
$this->eventDispatcher->dispatch(AddressEvents::INITIAL_VALUES, $event);
$initial_values = $event->getInitialValues();
return $initial_values;
}
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) { public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$item = $items[$delta]; $item = $items[$delta];
$value = $item->getEntity()->isNew() ? $this->getInitialValues() : $item->toArray(); $value = $item->toArray();
// Calling initializeLangcode() every time, and not just when the field // Calling initializeLangcode() every time, and not just when the field
// is empty, ensures that the langcode can be changed on subsequent // is empty, ensures that the langcode can be changed on subsequent
// edits (because the entity or interface language changed, for example). // edits (because the entity or interface language changed, for example).
...@@ -203,6 +116,10 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI ...@@ -203,6 +116,10 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI
'#available_countries' => $item->getAvailableCountries(), '#available_countries' => $item->getAvailableCountries(),
'#field_overrides' => $item->getFieldOverrides(), '#field_overrides' => $item->getFieldOverrides(),
]; ];
// Make sure no properties are required on the default value widget.
if ($this->isDefaultValueWidget($form_state)) {
$element['address']['#after_build'][] = [get_class($this), 'makeFieldsOptional'];
}
return $element; return $element;
} }
...@@ -226,4 +143,16 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI ...@@ -226,4 +143,16 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI
return $new_values; return $new_values;
} }
/**
* Form API callback: Makes all address field properties optional.
*/
public static function makeFieldsOptional(array $element, FormStateInterface $form_state) {
foreach (Element::getVisibleChildren($element) as $key) {
if (!empty($element[$key]['#required'])) {
$element[$key]['#required'] = FALSE;
}
}
return $element;
}
} }
...@@ -4,7 +4,6 @@ namespace Drupal\address_test\EventSubscriber; ...@@ -4,7 +4,6 @@ namespace Drupal\address_test\EventSubscriber;
use Drupal\address\Event\AddressEvents; use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\AvailableCountriesEvent; use Drupal\address\Event\AvailableCountriesEvent;
use Drupal\address\Event\InitialValuesEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AddressTestEventSubscriber implements EventSubscriberInterface { class AddressTestEventSubscriber implements EventSubscriberInterface {
...@@ -14,42 +13,9 @@ class AddressTestEventSubscriber implements EventSubscriberInterface { ...@@ -14,42 +13,9 @@ class AddressTestEventSubscriber implements EventSubscriberInterface {
*/ */
public static function getSubscribedEvents() { public static function getSubscribedEvents() {
$events[AddressEvents::AVAILABLE_COUNTRIES][] = ['onAvailableCountries']; $events[AddressEvents::AVAILABLE_COUNTRIES][] = ['onAvailableCountries'];
$events[AddressEvents::INITIAL_VALUES][] = ['onInitialValues'];
return $events; return $events;
} }
/**
* Generates a set of available countries.
*
* @return array
* The countries.
*/
public function getAvailableCountries() {
return ['AU' => 'AU', 'BR' => 'BR', 'CA' => 'CA', 'GB' => 'GB', 'JP' => 'JP'];
}
/**
* Generate a set of initial values.
*
* @return array
* The initial values.
*/
public function getInitialValues() {
return [
'country_code' => 'AU',
'administrative_area' => 'NSW',
'locality' => 'Sydney',
'dependent_locality' => '',
'postal_code' => '2000',
'sorting_code' => '',
'address_line1' => 'Some address',
'address_line2' => 'Some street',
'organization' => 'Some Organization',
'given_name' => 'John',
'family_name' => 'Smith',
];
}
/** /**
* Alters the available countries. * Alters the available countries.
* *
...@@ -61,13 +27,13 @@ class AddressTestEventSubscriber implements EventSubscriberInterface { ...@@ -61,13 +27,13 @@ class AddressTestEventSubscriber implements EventSubscriberInterface {
} }
/** /**
* Alters the initial values. * Generates a set of available countries.
* *
* @param \Drupal\address\Event\InitialValuesEvent $event * @return array
* The initial values event. * The countries.
*/ */
public function onInitialValues(InitialValuesEvent $event) { public function getAvailableCountries() {
$event->setInitialValues($this->getInitialValues()); return ['AU' => 'AU', 'BR' => 'BR', 'CA' => 'CA', 'GB' => 'GB', 'US' => 'US'];
} }
} }
...@@ -118,6 +118,11 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -118,6 +118,11 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
'field_storage' => $field_storage, 'field_storage' => $field_storage,
'bundle' => 'article', 'bundle' => 'article',
'label' => 'Address', 'label' => 'Address',
'default_value' => [
[
'country_code' => 'US',
],
],
]); ]);
$this->field->save(); $this->field->save();
...@@ -135,9 +140,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -135,9 +140,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
} }
$this->formDisplay->setComponent($this->field->getName(), [ $this->formDisplay->setComponent($this->field->getName(), [
'type' => 'address_default', 'type' => 'address_default',
'settings' => [
'default_country' => 'US',
],
])->save(); ])->save();
$this->nodeAddUrl = 'node/add/article'; $this->nodeAddUrl = 'node/add/article';
...@@ -153,7 +155,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -153,7 +155,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
* *
* Checked: * Checked:
* - required/optional status. * - required/optional status.
* - default_country widget setting.
* - available_countries instance setting. * - available_countries instance setting.
*/ */
public function testCountries() { public function testCountries() {
...@@ -262,7 +263,45 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -262,7 +263,45 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
} }
/** /**
* Tests the initial values and available countries alter events. * Tests the default value functionality.
*/
public function testDefaultValue() {
$this->drupalGet($this->fieldConfigUrl);
// Confirm that the US is selected by default.
$this->assertSession()->fieldValueEquals('default_value_input[field_address][0][address][country_code]', 'US');
// Confirm that it is possible to switch the country to France.
$this->getSession()->getPage()->fillField('default_value_input[field_address][0][address][country_code]', 'FR');
$this->waitForAjaxToFinish();
$this->assertSession()->fieldNotExists('default_value_input[field_address][0][address][administrative_area]');
// Confirm that it is possible to fill-in only certain fields.
$edit = [
'default_value_input[field_address][0][address][given_name]' => 'John',
'default_value_input[field_address][0][address][family_name]' => 'Smith',
];
$this->submitForm($edit, t('Save settings'));
$this->assertSession()->pageTextContains('Saved Address configuration.');
$this->container->get('entity_type.manager')->getStorage('field_config')->resetCache();
$this->field = FieldConfig::load($this->field->id());
$default_value = $this->field->getDefaultValueLiteral();
$expected_default_value = [
'country_code' => 'FR',
'given_name' => 'John',
'family_name' => 'Smith',
];
$this->assertCount(1, $default_value);
$this->assertEquals($expected_default_value, array_filter($default_value[0]));
// Confirm that the default value is used on the node form.
$this->drupalGet($this->nodeAddUrl);
$this->assertSession()->fieldValueEquals('field_address[0][address][country_code]', 'FR');
$this->assertSession()->fieldValueEquals('field_address[0][address][given_name]', 'John');
$this->assertSession()->fieldValueEquals('field_address[0][address][family_name]', 'Smith');
$this->assertSession()->fieldValueEquals('field_address[0][address][postal_code]', '');
}
/**
* Tests the alter events.
*/ */
public function testEvents() { public function testEvents() {
$field_name = $this->field->getName(); $field_name = $this->field->getName();
...@@ -271,21 +310,14 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -271,21 +310,14 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
self::$modules[] = 'address_test'; self::$modules[] = 'address_test';
$this->container->get('module_installer')->install(self::$modules); $this->container->get('module_installer')->install(self::$modules);
$this->container = $this->kernel->rebuildContainer(); $this->container = $this->kernel->rebuildContainer();
// Get available countries and initial values from module's event subscriber.
// Confirm that the list of available countries was altered.
$subscriber = \Drupal::service('address_test.event_subscriber'); $subscriber = \Drupal::service('address_test.event_subscriber');
$available_countries = array_keys($subscriber->getAvailableCountries()); $available_countries = array_keys($subscriber->getAvailableCountries());
$initial_values = $subscriber->getInitialValues();
// Access the content add form and test the list of countries.
$this->drupalGet($this->nodeAddUrl); $this->drupalGet($this->nodeAddUrl);
$this->assertOptions($field_name . '[0][address][country_code]', $available_countries, 'Available countries set in the event subscriber are present in the widget.'); $this->assertOptions($field_name . '[0][address][country_code]', $available_countries);
// Test the values of the fields.
foreach ($initial_values as $key => $value) { // Confirm that counties for Great Britain were added.
if ($value) {
$name = $field_name . '[0][address][' . $key . ']';
$this->assertSession()->fieldValueEquals($name, $value);
}
}
// Test the GB counties.
$expected_counties = [ $expected_counties = [
'Anglesey', 'Blaenau Gwent', 'Bridgend', 'Caerphilly', 'Cardiff', 'Anglesey', 'Blaenau Gwent', 'Bridgend', 'Caerphilly', 'Cardiff',
'Carmarthenshire', 'Ceredigion', 'Conwy', 'Denbighshire', 'Flintshire', 'Carmarthenshire', 'Ceredigion', 'Conwy', 'Denbighshire', 'Flintshire',
...@@ -299,6 +331,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -299,6 +331,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
$this->assertSession()->pageTextContains(t('County')); $this->assertSession()->pageTextContains(t('County'));
$this->assertSession()->fieldExists($field_name . '[0][address][administrative_area]'); $this->assertSession()->fieldExists($field_name . '[0][address][administrative_area]');
$this->assertOptions($field_name . '[0][address][administrative_area]', $expected_counties); $this->assertOptions($field_name . '[0][address][administrative_area]', $expected_counties);
// Uninstall and remove the address_test module. // Uninstall and remove the address_test module.
$this->container->get('module_installer')->uninstall(['address_test']); $this->container->get('module_installer')->uninstall(['address_test']);
$this->container = $this->kernel->rebuildContainer(); $this->container = $this->kernel->rebuildContainer();
...@@ -533,7 +566,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase { ...@@ -533,7 +566,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
$valid = FALSE; $valid = FALSE;
} }
$this->assertNotEmpty($valid, $message); $this->assertTrue($valid, $message);
} }
/** /**
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment