Commit a6141ad5 authored by bojanz's avatar bojanz
Browse files

Issue #2862113: Remove the zone config entity & plugins

parent aa0456a4
......@@ -17,12 +17,6 @@ function address_requirements($phase) {
'severity' => REQUIREMENT_ERROR,
];
}
if (!class_exists('\CommerceGuys\Zone\Repository\ZoneRepository')) {
$requirements['zone_library'] = [
'description' => t('Address requires the commerceguys/zone library.'),
'severity' => REQUIREMENT_ERROR,
];
}
}
return $requirements;
......@@ -190,3 +184,18 @@ function address_update_8101() {
// Used by address_post_update_convert_names_subdivisions.
\Drupal::state()->set('address_8101_processed', $processed_fields);
}
/**
* Remove the stored zones.
*/
function address_update_8103() {
// Clear the caches to ensure the entity type is gone.
\Drupal::entityTypeManager()->clearCachedDefinitions();
\Drupal::service('entity_type.repository')->clearCachedDefinitions();
// Remove the underlying config.
$config_factory = \Drupal::configFactory();
$names = $config_factory->listAll('address.zone.');
foreach ($names as $name) {
$config_factory->getEditable($name)->delete();
}
}
entity.zone.add_form:
route_name: entity.zone.add_form
title: 'Add a new zone'
appears_on:
- entity.zone.collection
entity.zone.collection:
title: 'Zones'
route_name: entity.zone.collection
parent: system.admin_config_regional
description: 'Administer zones.'
'administer zones':
title: 'Administer zones'
'restrict access': TRUE
address.zone_member:
label: Address zone member
plugin_manager_service_id: plugin.manager.address.zone_member
plugin_definition_decorator_class: \Drupal\address\PluginDefinition\ZoneMemberPluginDefinitionDecorator
plugin_configuration_schema_id: address.zone_member.[plugin_id]
# edit_form and delete_form routes are generated by DefaultHtmlRouteProvider.
entity.zone.collection:
path: '/admin/config/regional/zones'
defaults:
_entity_list: 'zone'
_title: 'Zones'
options:
_admin_route: TRUE
requirements:
_permission: 'administer zones'
entity.zone.add_form:
path: '/admin/config/regional/zones/add'
defaults:
_entity_form: 'zone.add'
_title: 'Add a new zone'
options:
_admin_route: TRUE
requirements:
_permission: 'administer zones'
......@@ -11,19 +11,6 @@ services:
class: Drupal\address\Repository\SubdivisionRepository
arguments: ['@address.address_format_repository', '@event_dispatcher', '@cache.data']
address.zone_repository:
class: Drupal\address\Repository\ZoneRepository
arguments: ['@entity.manager']
address.postal_label_formatter:
class: CommerceGuys\Addressing\Formatter\PostalLabelFormatter
arguments: ['@address.address_format_repository', '@address.country_repository', '@address.subdivision_repository']
address.zone_matcher:
class: CommerceGuys\Zone\Matcher\ZoneMatcher
arguments: ['@address.zone_repository']
plugin.manager.address.zone_member:
class: Drupal\address\ZoneMemberManager
parent: default_plugin_manager
arguments: ['@uuid']
address.zone.*:
type: config_entity
label: 'Zone'
mapping:
id:
type: string
label: 'ID'
name:
type: label
label: 'Name'
scope:
type: string
label: 'Scope'
priority:
type: integer
label: 'Priority'
members:
type: sequence
label: 'Members'
sequence:
type: address.zone_member.[plugin]
address.zone_member:
type: mapping
mapping:
id:
type: string
label: 'ID'
name:
type: label
label: 'Name'
weight:
type: integer
label: 'Weight'
plugin:
type: string
label: 'Plugin'
address.zone_member.*:
type: ignore
address.zone_member.country:
type: address.zone_member
label: 'Zone member (Country)'
mapping:
country_code:
type: string
label: 'Country code'
administrative_area:
type: string
label: 'Administrative area'
locality:
type: string
label: 'Locality'
dependent_locality:
type: string
label: 'Dependent locality'
included_postal_codes:
type: string
label: 'Included postal codes'
excluded_postal_codes:
type: string
label: 'Excluded postal codes'
address.zone_member.eu:
type: address.zone_member
label: 'Zone member (EU)'
address.zone_member.zone:
type: address.zone_member
label: 'Zone member (Zone)'
mapping:
zone:
type: string
label: 'Zone ID'
field.value.address:
type: mapping
label: 'Default value'
......
<?php
namespace Drupal\address\Annotation;
use Drupal\Component\Annotation\Plugin;
/**
* Defines a zone member annotation object.
*
* Plugin Namespace: Plugin\ZoneMember.
*
* @Annotation
*/
class ZoneMember extends Plugin {
/**
* The plugin ID.
*
* @var string
*/
public $id;
/**
* The human-readable name of the plugin.
*
* @ingroup plugin_translatable
*
* @var \Drupal\Core\Annotation\Translation
*/
public $name;
}
<?php
namespace Drupal\address\Plugin\ZoneMember;
namespace Drupal\address\Element;
use CommerceGuys\Addressing\AddressInterface;
use CommerceGuys\Addressing\AddressFormat\AddressField;
use CommerceGuys\Addressing\AddressFormat\AddressFormat;
use CommerceGuys\Addressing\AddressFormat\AddressFormatRepositoryInterface;
use CommerceGuys\Addressing\Country\CountryRepositoryInterface;
use CommerceGuys\Addressing\Subdivision\SubdivisionRepositoryInterface;
use CommerceGuys\Zone\PostalCodeHelper;
use Drupal\address\FieldHelper;
use Drupal\address\LabelHelper;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Render\Element\FormElement;
/**
* Matches a country, its subdivisions, and its postal codes.
* Provides a zone territory form element.
*
* @ZoneMember(
* id = "country",
* name = @Translation("Country"),
* )
* Usage example:
* @code
* $form['territory'] = [
* '#type' => 'zone_territory',
* '#default_value' => [
* 'country_code' => 'US',
* 'administrative_area' => 'CA',
* 'included_postal_codes' => '94043',
* ],
* ];
* @endcode
*
* @FormElement("zone_territory")
*/
class ZoneMemberCountry extends ZoneMemberBase implements ContainerFactoryPluginInterface {
/**
* The address format repository.
*
* @var \CommerceGuys\Addressing\Repository\AddressFormatRepositoryInterface
*/
protected $addressFormatRepository;
/**
* The country repository.
*
* @var \CommerceGuys\Addressing\Country\CountryRepositoryInterface
*/
protected $countryRepository;
/**
* The subdivision repository.
*
* @var \CommerceGuys\Addressing\Repository\SubdivisionRepositoryInterface
*/
protected $subdivisionRepository;
/**
* Constructs a new ZoneMemberCountry object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The pluginId for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \CommerceGuys\Addressing\Repository\AddressFormatRepositoryInterface $address_format_repository
* The address format repository.
* @param \CommerceGuys\Addressing\Country\CountryRepositoryInterface $country_repository
* The country repository.
* @param \CommerceGuys\Addressing\Repository\SubdivisionRepositoryInterface $subdivision_repository
* The subdivision repository.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, AddressFormatRepositoryInterface $address_format_repository, CountryRepositoryInterface $country_repository, SubdivisionRepositoryInterface $subdivision_repository) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->addressFormatRepository = $address_format_repository;
$this->countryRepository = $country_repository;
$this->subdivisionRepository = $subdivision_repository;
}
class ZoneTerritory extends FormElement {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('address.address_format_repository'),
$container->get('address.country_repository'),
$container->get('address.subdivision_repository')
);
public function getInfo() {
$class = get_class($this);
return [
'#input' => TRUE,
'#multiple' => FALSE,
'#default_value' => NULL,
'#process' => [
[$class, 'processTerritory'],
[$class, 'processGroup'],
],
'#pre_render' => [
[$class, 'groupElements'],
[$class, 'preRenderGroup'],
],
'#after_build' => [
[$class, 'clearValues'],
],
'#theme_wrappers' => ['container'],
];
}
/**
* {@inheritdoc}
*/
public function defaultConfiguration() {
return [
'country_code' => '',
'administrative_area' => '',
'locality' => '',
'dependent_locality' => '',
'included_postal_codes' => '',
'excluded_postal_codes' => '',
] + parent::defaultConfiguration();
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if (is_array($input)) {
return $input;
}
else {
if (!is_array($element['#default_value'])) {
$element['#default_value'] = [];
}
// Initialize properties.
$properties = [
'country_code',
'administrative_area', 'locality', 'dependent_locality',
'included_postal_codes', 'excluded_postal_codes',
];
foreach ($properties as $property) {
if (!isset($element['#default_value'][$property])) {
$element['#default_value'][$property] = NULL;
}
}
return $element['#default_value'];
}
}
/**
* {@inheritdoc}
* Processes the zone territory form element.
*
* @param array $element
* The form element to process.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param array $complete_form
* The complete form structure.
*
* @return array
* The processed element.
*
* @throws \InvalidArgumentException
* Thrown when #available_countries or #used_fields is malformed.
*/
public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
$form = parent::buildConfigurationForm($form, $form_state);
$values = $form_state->getUserInput();
if ($values) {
$values += $this->defaultConfiguration();
}
else {
$values = $this->configuration;
public static function processTerritory(array &$element, FormStateInterface $form_state, array &$complete_form) {
$id_prefix = implode('-', $element['#parents']);
$wrapper_id = Html::getUniqueId($id_prefix . '-ajax-wrapper');
$country_list = \Drupal::service('address.country_repository')->getList();
$value = $element['#value'];
if (empty($value['country_code']) && $element['#required']) {
// Fallback to the first country in the list if the default country
// is empty even though the field is required.
$value['country_code'] = key($country_list);
}
$wrapper_id = Html::getUniqueId('zone-members-ajax-wrapper');
$form += [
$element = [
'#tree' => TRUE,
'#prefix' => '<div id="' . $wrapper_id . '">',
'#suffix' => '</div>',
'#after_build' => [
[get_class($this), 'clearValues'],
],
// Pass the id along to other methods.
'#wrapper_id' => $wrapper_id,
];
$form['country_code'] = [
] + $element;
$element['country_code'] = [
'#type' => 'select',
'#title' => $this->t('Country'),
'#options' => $this->countryRepository->getList(),
'#default_value' => $values['country_code'],
'#title' => t('Country'),
'#options' => $country_list,
'#default_value' => $value['country_code'],
'#required' => $element['#required'],
'#limit_validation_errors' => [],
'#required' => TRUE,
'#ajax' => [
'callback' => [get_class($this), 'ajaxRefresh'],
'callback' => [get_called_class(), 'ajaxRefresh'],
'wrapper' => $wrapper_id,
],
'#weight' => -100,
];
if (!empty($values['country_code'])) {
$address_format = $this->addressFormatRepository->get($values['country_code']);
$form = $this->buildSubdivisionElements($form, $values, $address_format);
$form = $this->buildPostalCodeElements($form, $values, $address_format);
if (!$element['#required']) {
$element['country_code']['#empty_value'] = '';
}
if (!empty($value['country_code'])) {
/** @var \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format */
$address_format = \Drupal::service('address.address_format_repository')->get($value['country_code']);
$element = static::buildSubdivisionElements($element, $value, $address_format);
$element = static::buildPostalCodeElements($element, $value, $address_format);
}
return $form;
return $element;
}
/**
* Builds the subdivision form elements.
*
* @param array $form
* The form.
* @param array $values
* The form values.
* @param array $element
* The existing form element array.
* @param array $value
* The element value.
* @param \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format
* The address format for the selected country.
*
* @return array
* The form with the added subdivision elements.
*/
protected function buildSubdivisionElements(array $form, array $values, AddressFormat $address_format) {
protected static function buildSubdivisionElements(array $element, array $value, AddressFormat $address_format) {
$depth = $address_format->getSubdivisionDepth();
if ($depth === 0) {
// No predefined data found.
return $form;
return $element;
}
$labels = LabelHelper::getFieldLabels($address_format);
......@@ -171,78 +165,79 @@ class ZoneMemberCountry extends ZoneMemberBase implements ContainerFactoryPlugin
foreach ($subdivision_fields as $index => $field) {
$property = FieldHelper::getPropertyName($field);
$parent_property = $index ? FieldHelper::getPropertyName($subdivision_fields[$index - 1]) : 'country_code';
if ($parent_property && empty($values[$parent_property])) {
if ($parent_property && empty($value[$parent_property])) {
// No parent value selected.
break;
}
$parents[] = $values[$parent_property];
$subdivisions = $this->subdivisionRepository->getList($parents);
$parents[] = $value[$parent_property];
$subdivisions = \Drupal::service('address.subdivision_repository')->getList($parents);
if (empty($subdivisions)) {
break;
}
$form[$property] = [
$element[$property] = [
'#type' => 'select',
'#title' => $labels[$field],
'#options' => $subdivisions,
'#default_value' => $values[$property],
'#default_value' => $value[$property],
'#empty_option' => $this->t('- All -'),
];
if ($current_depth < $depth) {
$form[$property]['#ajax'] = [
'callback' => [get_class($this), 'ajaxRefresh'],
'wrapper' => $form['#wrapper_id'],
$element[$property]['#ajax'] = [
'callback' => [get_called_class(), 'ajaxRefresh'],
'wrapper' => $element['#wrapper_id'],
];
}
$current_depth++;
}
return $form;
return $element;
}
/**
* Builds the postal code form elements.
*
* @param array $form
* The form.
* @param array $values
* The form values.
* @param array $element
* The existing form element array.
* @param array $value
* The element value.
* @param \CommerceGuys\Addressing\AddressFormat\AddressFormat $address_format
* The address format for the selected country.
*
* @return array
* The form with the added postal code elements.
*/
protected function buildPostalCodeElements(array $form, array $values, AddressFormat $address_format) {
protected static function buildPostalCodeElements(array $element, array $value, AddressFormat $address_format) {
if (!in_array(AddressField::POSTAL_CODE, $address_format->getUsedFields())) {
// The address format doesn't use a postal code field.
return $form;
return $element;
}
$form['included_postal_codes'] = [
$element['included_postal_codes'] = [
'#type' => 'textfield',
'#title' => $this->t('Included postal codes'),
'#description' => $this->t('A regular expression ("/(35|38)[0-9]{3}/") or comma-separated list, including ranges ("98, 100:200")'),
'#default_value' => $values['included_postal_codes'],
'#default_value' => $value['included_postal_codes'],
];
$form['excluded_postal_codes'] = [
$element['excluded_postal_codes'] = [
'#type' => 'textfield',
'#title' => $this->t('Excluded postal codes'),
'#description' => $this->t('A regular expression ("/(35|38)[0-9]{3}/") or comma-separated list, including ranges ("98, 100:200")'),
'#default_value' => $values['excluded_postal_codes'],
'#default_value' => $value['excluded_postal_codes'],
];
return $form;
return $element;
}
/**
* Ajax callback.
*/
public static function ajaxRefresh(array $form, FormStateInterface $form_state) {
$parents = $form_state->getTriggeringElement()['#parents'];
array_pop($parents);
return NestedArray::getValue($form, $parents);
$country_element = $form_state->getTriggeringElement();
$address_element = NestedArray::getValue($form, array_slice($country_element['#array_parents'], 0, -1));
return $address_element;
}
/**
......@@ -260,7 +255,9 @@ class ZoneMemberCountry extends ZoneMemberBase implements ContainerFactoryPlugin
$triggering_element_name = end($triggering_element['#parents']);
if ($triggering_element_name == 'country_code') {
$keys = ['dependent_locality', 'locality', 'administrative_area'];
$keys = [
'dependent_locality', 'locality', 'administrative_area',
];
$input = &$form_state->getUserInput();
foreach ($keys as $key) {
$parents = array_merge($element['#parents'], [$key]);
......@@ -272,50 +269,4 @@ class ZoneMemberCountry extends ZoneMemberBase implements ContainerFactoryPlugin
return $element;
}
/**
* {@inheritdoc}
*/
public function submitConfigurationForm(array &$form, FormStateInterface $form_state) {
parent::submitConfigurationForm($form, $form_state);
if (!$form_state->getErrors()) {
$this->configuration['country_code'] = $form_state->getValue('country_code');
$this->configuration['administrative_area'] = $form_state->getValue('administrative_area');
$this->configuration['locality'] = $form_state->getValue('locality');