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

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
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Sql\SqlContentEntityStorage;
use Drupal\Core\Entity\Sql\SqlContentEntityStorageException;
use Drupal\Core\Utility\UpdateException;
......@@ -199,3 +200,48 @@ function address_update_8103() {
$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 @@
* 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().
*/
......
......@@ -144,10 +144,6 @@ field.field_settings.address_zone:
field.widget.settings.address_default:
type: mapping
label: 'Default address widget settings'
mapping:
default_country:
type: string
label: 'Default country'
field.widget.settings.address_zone_default:
type: mapping
......
......@@ -28,6 +28,9 @@ final class AddressEvents {
/**
* 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
*
* @see \Drupal\address\Event\InitialValuesEvent
......
......@@ -8,8 +8,8 @@ use Symfony\Component\EventDispatcher\Event;
/**
* Defines the initial values event.
*
* @see \Drupal\address\Event\AddressEvents
* @see \Drupal\address\Plugin\Field\FieldWidget\AddressDefaultWidget::getInitialValues()
* @deprecated No longer fired since 1.5. Use hook_field_widget_form_alter()
* to change the address #default_value instead.
*/
class InitialValuesEvent extends Event {
......
......@@ -22,7 +22,8 @@ use Drupal\Core\TypedData\DataDefinition;
* description = @Translation("An entity field containing a postal address"),
* category = @Translation("Address"),
* 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 {
......
......@@ -3,8 +3,6 @@
namespace Drupal\address\Plugin\Field\FieldWidget;
use CommerceGuys\Addressing\Country\CountryRepositoryInterface;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\InitialValuesEvent;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Field\FieldItemListInterface;
......@@ -12,6 +10,7 @@ use Drupal\Core\Field\WidgetBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Field\FieldDefinitionInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\Render\Element;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Validator\ConstraintViolationInterface;
......@@ -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}
*/
public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
$item = $items[$delta];
$value = $item->getEntity()->isNew() ? $this->getInitialValues() : $item->toArray();
$value = $item->toArray();
// Calling initializeLangcode() every time, and not just when the field
// is empty, ensures that the langcode can be changed on subsequent
// edits (because the entity or interface language changed, for example).
......@@ -203,6 +116,10 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI
'#available_countries' => $item->getAvailableCountries(),
'#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;
}
......@@ -226,4 +143,16 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI
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;
use Drupal\address\Event\AddressEvents;
use Drupal\address\Event\AvailableCountriesEvent;
use Drupal\address\Event\InitialValuesEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class AddressTestEventSubscriber implements EventSubscriberInterface {
......@@ -14,42 +13,9 @@ class AddressTestEventSubscriber implements EventSubscriberInterface {
*/
public static function getSubscribedEvents() {
$events[AddressEvents::AVAILABLE_COUNTRIES][] = ['onAvailableCountries'];
$events[AddressEvents::INITIAL_VALUES][] = ['onInitialValues'];
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.
*
......@@ -61,13 +27,13 @@ class AddressTestEventSubscriber implements EventSubscriberInterface {
}
/**
* Alters the initial values.
* Generates a set of available countries.
*
* @param \Drupal\address\Event\InitialValuesEvent $event
* The initial values event.
* @return array
* The countries.
*/
public function onInitialValues(InitialValuesEvent $event) {
$event->setInitialValues($this->getInitialValues());
public function getAvailableCountries() {
return ['AU' => 'AU', 'BR' => 'BR', 'CA' => 'CA', 'GB' => 'GB', 'US' => 'US'];
}
}
......@@ -118,6 +118,11 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
'field_storage' => $field_storage,
'bundle' => 'article',
'label' => 'Address',
'default_value' => [
[
'country_code' => 'US',
],
],
]);
$this->field->save();
......@@ -135,9 +140,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
}
$this->formDisplay->setComponent($this->field->getName(), [
'type' => 'address_default',
'settings' => [
'default_country' => 'US',
],
])->save();
$this->nodeAddUrl = 'node/add/article';
......@@ -153,7 +155,6 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
*
* Checked:
* - required/optional status.
* - default_country widget setting.
* - available_countries instance setting.
*/
public function testCountries() {
......@@ -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() {
$field_name = $this->field->getName();
......@@ -271,21 +310,14 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
self::$modules[] = 'address_test';
$this->container->get('module_installer')->install(self::$modules);
$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');
$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->assertOptions($field_name . '[0][address][country_code]', $available_countries, 'Available countries set in the event subscriber are present in the widget.');
// Test the values of the fields.
foreach ($initial_values as $key => $value) {
if ($value) {
$name = $field_name . '[0][address][' . $key . ']';
$this->assertSession()->fieldValueEquals($name, $value);
}
}
// Test the GB counties.
$this->assertOptions($field_name . '[0][address][country_code]', $available_countries);
// Confirm that counties for Great Britain were added.
$expected_counties = [
'Anglesey', 'Blaenau Gwent', 'Bridgend', 'Caerphilly', 'Cardiff',
'Carmarthenshire', 'Ceredigion', 'Conwy', 'Denbighshire', 'Flintshire',
......@@ -299,6 +331,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
$this->assertSession()->pageTextContains(t('County'));
$this->assertSession()->fieldExists($field_name . '[0][address][administrative_area]');
$this->assertOptions($field_name . '[0][address][administrative_area]', $expected_counties);
// Uninstall and remove the address_test module.
$this->container->get('module_installer')->uninstall(['address_test']);
$this->container = $this->kernel->rebuildContainer();
......@@ -533,7 +566,7 @@ class AddressDefaultWidgetTest extends WebDriverTestBase {
$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