diff --git a/config/schema/address.schema.yml b/config/schema/address.schema.yml index e706c72..a9643b8 100644 --- a/config/schema/address.schema.yml +++ b/config/schema/address.schema.yml @@ -151,6 +151,11 @@ field.widget.settings.address_default: wrapper_type: type: string label: Wrapper type + preferred_countries: + type: sequence + label: 'Preferred countries' + sequence: + type: string field.widget.settings.address_zone_default: type: mapping @@ -183,6 +188,12 @@ views.field.subdivision: views.filter.country: type: views.filter.in_operator label: 'Country' + mapping: + preferred_countries: + type: sequence + label: 'Preferred countries' + sequence: + type: string views.filter.country_code: type: views.filter.country diff --git a/src/Element/Address.php b/src/Element/Address.php index a928eeb..9922933 100644 --- a/src/Element/Address.php +++ b/src/Element/Address.php @@ -44,6 +44,7 @@ use Drupal\Core\Render\Element\FormElement; * AddressField::POSTAL_CODE => FieldOverride::OPTIONAL, * ], * '#available_countries' => ['DE', 'FR'], + * '#preferred_countries' => ['DE'], * ]; * @endcode * @@ -61,6 +62,8 @@ class Address extends FormElement { '#available_countries' => [], // FieldOverride constants keyed by AddressField constants. '#field_overrides' => [], + // List of country codes. Listed countries will be at the top of select. + '#preferred_countries' => [], '#input' => TRUE, '#multiple' => FALSE, @@ -178,6 +181,7 @@ class Address extends FormElement { '#type' => 'address_country', '#title' => t('Country'), '#available_countries' => $element['#available_countries'], + '#preferred_countries' => $element['#preferred_countries'], '#default_value' => $element['#default_value']['country_code'], '#required' => $element['#required'], '#limit_validation_errors' => [], diff --git a/src/Element/Country.php b/src/Element/Country.php index 6fda484..3f76220 100644 --- a/src/Element/Country.php +++ b/src/Element/Country.php @@ -2,6 +2,7 @@ namespace Drupal\address\Element; +use Drupal\address\PreferredCountriesTrait; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Render\Element\FormElement; @@ -14,6 +15,7 @@ use Drupal\Core\Render\Element\FormElement; * '#type' => 'address_country', * '#default_value' => 'DE', * '#available_countries' => ['DE', 'FR'], + * '#preferred_countries' => ['DE'], * ]; * @endcode * @@ -21,6 +23,8 @@ use Drupal\Core\Render\Element\FormElement; */ class Country extends FormElement { + use PreferredCountriesTrait; + /** * {@inheritdoc} */ @@ -29,6 +33,8 @@ class Country extends FormElement { return [ // List of country codes. If empty, all countries will be available. '#available_countries' => [], + // List of country codes. Listed countries will be at the top of select. + '#preferred_countries' => [], '#input' => TRUE, '#multiple' => FALSE, @@ -80,6 +86,14 @@ class Country extends FormElement { } $element['#tree'] = TRUE; + + // Rebuild country options if any preferred countries have been selected. + $preferred_countries = $element['#preferred_countries'] ?? []; + $preferred_countries = array_combine($preferred_countries, $preferred_countries); + $country_options = empty($preferred_countries) + ? $country_list + : self::rebuildCountryList($country_list, $preferred_countries); + // Hide the dropdown when there is only one possible value. if (count($country_list) == 1 && $element['#required']) { $element['country_code'] = [ @@ -93,7 +107,7 @@ class Country extends FormElement { '#title' => $element['#title'], '#title_display' => $element['#title_display'], '#description_display' => $element['#description_display'], - '#options' => $country_list, + '#options' => $country_options, '#default_value' => $element['#default_value'], '#required' => $element['#required'], '#limit_validation_errors' => [], diff --git a/src/Plugin/Field/FieldWidget/AddressDefaultWidget.php b/src/Plugin/Field/FieldWidget/AddressDefaultWidget.php index 7cc303d..dfe2149 100644 --- a/src/Plugin/Field/FieldWidget/AddressDefaultWidget.php +++ b/src/Plugin/Field/FieldWidget/AddressDefaultWidget.php @@ -94,11 +94,12 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI ); } - /** + /** * {@inheritdoc} */ public static function defaultSettings() { return [ + 'preferred_countries' => [], 'wrapper_type' => 'fieldset', ] + parent::defaultSettings(); } @@ -107,14 +108,32 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI * {@inheritdoc} */ public function settingsForm(array $form, FormStateInterface $form_state) { - return [ - 'wrapper_type' => [ - '#type' => 'radios', - '#title' => $this->t('Wrapper type'), - '#options' => $this->wrapperTypeOptions(), - '#default_value' => $this->getSetting('wrapper_type'), - ], + $element = []; + + $country_list = $this->countryRepository->getList(); + $available_countries = $this->getFieldSetting('available_countries'); + if (!empty($available_countries)) { + $country_list = array_intersect_key($country_list, $available_countries); + } + + $element['preferred_countries'] = [ + '#type' => 'select', + '#title' => $this->t('Preferred countries'), + '#options' => $country_list, + '#default_value' => $this->getSetting('preferred_countries'), + '#empty_value' => '', + '#multiple' => TRUE, + '#size' => 10, + ]; + + $element['wrapper_type'] = [ + '#type' => 'radios', + '#title' => $this->t('Wrapper type'), + '#options' => $this->wrapperTypeOptions(), + '#default_value' => $this->getSetting('wrapper_type'), ]; + + return $element; } /** @@ -122,9 +141,26 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI */ public function settingsSummary() { $summary = parent::settingsSummary(); + + // Add summary for preferred countries setting. + $preferred_countries = $this->getSetting('preferred_countries'); + if (!empty($preferred_countries)) { + $country_list = $this->countryRepository->getList(); + foreach ($preferred_countries as &$preferred_country) { + $preferred_country = $country_list[$preferred_country]; + } + $summary[] = $this->formatPlural(count($preferred_countries), + 'Preferred country: @countries', + 'Preferred countries: @countries', + ['@countries' => implode(', ', $preferred_countries)] + ); + } + + // Add summary for wrapper type setting. $summary[] = $this->t('Wrapper type: @wrapper_type', [ '@wrapper_type' => $this->wrapperTypeOptions()[$this->getSetting('wrapper_type')], ]); + return $summary; } @@ -170,6 +206,7 @@ class AddressDefaultWidget extends WidgetBase implements ContainerFactoryPluginI '#required' => $required, '#available_countries' => $item->getAvailableCountries(), '#field_overrides' => $item->getFieldOverrides(), + '#preferred_countries' => $this->getSetting('preferred_countries'), ]; // Make sure no properties are required on the default value widget. if ($this->isDefaultValueWidget($form_state)) { diff --git a/src/Plugin/Field/FieldWidget/CountryDefaultWidget.php b/src/Plugin/Field/FieldWidget/CountryDefaultWidget.php index 6a2b1e3..ff0152f 100644 --- a/src/Plugin/Field/FieldWidget/CountryDefaultWidget.php +++ b/src/Plugin/Field/FieldWidget/CountryDefaultWidget.php @@ -68,6 +68,65 @@ class CountryDefaultWidget extends WidgetBase implements ContainerFactoryPluginI ); } + /** + * {@inheritdoc} + */ + public static function defaultSettings() { + return [ + 'preferred_countries' => [], + ] + parent::defaultSettings(); + } + + /** + * {@inheritdoc} + */ + public function settingsForm(array $form, FormStateInterface $form_state) { + $element = []; + + $country_list = $this->countryRepository->getList(); + $available_countries = $this->getFieldSetting('available_countries'); + if (!empty($available_countries)) { + $country_list = array_intersect_key($country_list, $available_countries); + } + + $element['preferred_countries'] = [ + '#type' => 'select', + '#title' => $this->t('Preferred countries'), + '#options' => $country_list, + '#default_value' => $this->getSetting('preferred_countries'), + '#empty_value' => '', + '#multiple' => TRUE, + '#size' => 10, + ]; + + return $element; + } + + /** + * {@inheritdoc} + */ + public function settingsSummary() { + $summary = []; + + // Display summary for preferred countries setting. + $preferred_countries = $this->getSetting('preferred_countries'); + if (!empty($preferred_countries)) { + if (!isset($country_list)) { + $country_list = $this->countryRepository->getList(); + } + foreach ($preferred_countries as &$preferred_country) { + $preferred_country = $country_list[$preferred_country]; + } + $summary['preferred_countries'] = $this->formatPlural(count($preferred_countries), + 'Preferred country: @countries', + 'Preferred countries: @countries', + ['@countries' => implode(', ', $preferred_countries)] + ); + } + + return $summary; + } + /** * {@inheritdoc} */ @@ -76,6 +135,7 @@ class CountryDefaultWidget extends WidgetBase implements ContainerFactoryPluginI '#type' => 'address_country', '#default_value' => $items[$delta]->value, '#available_countries' => $items[$delta]->getAvailableCountries(), + '#preferred_countries' => $this->getSetting('preferred_countries'), ] + $element; return $element; diff --git a/src/Plugin/views/filter/Country.php b/src/Plugin/views/filter/Country.php index 2462f97..13f0f7d 100644 --- a/src/Plugin/views/filter/Country.php +++ b/src/Plugin/views/filter/Country.php @@ -2,6 +2,9 @@ namespace Drupal\address\Plugin\views\filter; +use Drupal\address\PreferredCountriesTrait; +use Drupal\Core\Form\FormStateInterface; + /** * Filter by country. * @@ -11,6 +14,8 @@ namespace Drupal\address\Plugin\views\filter; */ class Country extends CountryAwareInOperatorBase { + use PreferredCountriesTrait; + /** * {@inheritdoc} */ @@ -22,4 +27,50 @@ class Country extends CountryAwareInOperatorBase { return $this->valueOptions; } + /** + * {@inheritdoc} + */ + public function buildExposedForm(&$form, FormStateInterface $form_state) { + parent::buildExposedForm($form, $form_state); + + // We're implementing preferred_countries and the optgroup support only here + // in the exposed filter select, so as not to break the admin UI default + // value form's checkboxes, which don't handle nested arrays properly. + $preferred_countries = $this->options['preferred_countries']; + if (!empty($preferred_countries)) { + $form_key = $this->options['expose']['identifier']; + $form[$form_key]['#options'] = self::rebuildCountryList($form[$form_key]['#options'], $preferred_countries); + } + } + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['preferred_countries'] = ['default' => []]; + return $options; + } + + /** + * {@inheritdoc} + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + $form['preferred_countries'] = [ + '#type' => 'select', + '#title' => $this->t('Preferred countries'), + '#options' => $this->getAvailableCountries(), + '#multiple' => TRUE, + '#default_value' => $this->options['preferred_countries'], + '#states' => [ + 'visible' => [ + ':input[name="options[expose_button][checkbox][checkbox]"]' => [ + 'checked' => TRUE, + ], + ], + ], + ]; + } + }