From 26b2d1698df9baf0382d75e54284f410234d9a5f Mon Sep 17 00:00:00 2001 From: Jen Lampton Date: Tue, 19 Jan 2016 23:13:46 -0800 Subject: [PATCH] Issue #10: Provie a state-and-address combo views handler. --- addressfield.module | 1 + css/addressfield-views.css | 4 + js/addressfield-views.js | 36 ++++ views/addressfield.views.inc | 25 ++- ...eld_views_handler_argument_countryname.inc | 2 +- ...eld_views_handler_filter_country_admin.inc | 185 ++++++++++++++++++ 6 files changed, 248 insertions(+), 5 deletions(-) create mode 100644 css/addressfield-views.css create mode 100644 js/addressfield-views.js create mode 100644 views/addressfield_views_handler_filter_country_admin.inc diff --git a/addressfield.module b/addressfield.module index 809a6ef..64ba7d6 100644 --- a/addressfield.module +++ b/addressfield.module @@ -24,6 +24,7 @@ function addressfield_autoload_info() { 'addressfield_views_handler_field_administrative_area' => 'views/addressfield_views_handler_field_administrative_area.inc', // Filters. 'addressfield_views_handler_filter_country' => 'views/addressfield_views_handler_filter_country.inc', + 'addressfield_views_handler_filter_country_admin' => 'views/addressfield_views_handler_filter_country_admin.inc', // Arguments. 'addressfield_views_handler_argument_countryname' => 'views/addressfield_views_handler_argument_countryname.inc', ); diff --git a/css/addressfield-views.css b/css/addressfield-views.css new file mode 100644 index 0000000..eb8a147 --- /dev/null +++ b/css/addressfield-views.css @@ -0,0 +1,4 @@ +/* align the widgets right/left of eachother */ +.views-widget-filter-field_address_full_country_admin .form-item { + display: inline-block; +} diff --git a/js/addressfield-views.js b/js/addressfield-views.js new file mode 100644 index 0000000..46ee4aa --- /dev/null +++ b/js/addressfield-views.js @@ -0,0 +1,36 @@ +/** + * @file addressfield-views.js + * Provides show/hide feature for Country + Admin area views hander. + */ +(function ($) { + + Backdrop.behaviors.addressfieldOptgroupSelect = { + attach: function(context) { + // Save a complete set of states in memory. + var $states = $(context).find('.addressfield-views-admin-area').once('addressfield-state'); + if ($states.length === 0) { + return; + } + + // Save a list of all country administrative areas. + var areas = {}; + $states.find('optgroup').each(function() { + areas[this.label] = $(this).children(); + $(this).detach(); + }); + + // Whenver the country changes... + $states.closest('.views-widget').find('.addressfield-views-country').change(function() { + var val = $(this).find('option:selected').val(); + $states.empty().append(areas[val]); + if ($states.children().length) { + $states.closest('.form-item').show(); + } + else { + $states.closest('.form-item').hide(); + } + }).triggerHandler("change"); + } + } + +}) (jQuery); diff --git a/views/addressfield.views.inc b/views/addressfield.views.inc index 70cab6e..005a10b 100644 --- a/views/addressfield.views.inc +++ b/views/addressfield.views.inc @@ -31,6 +31,7 @@ function addressfield_field_views_data($field) { $implemented = array( 'country' => 'addressfield_views_handler_field_country', 'countryname' => 'addressfield_views_handler_argument_countryname', + 'country_admin' => 'addressfield_views_handler_filter_country_admin', 'administrative_area' => 'addressfield_views_handler_field_administrative_area', 'sub_administrative_area' => 'views_handler_field', 'dependent_locality' => 'views_handler_field', @@ -80,6 +81,20 @@ function addressfield_field_views_data($field) { unset($table[$field['field_name'] . '_countryname']['sort']); } + // Add a country and administrative area handler. + if (isset($table[$field['field_name'] . '_country']) && isset($table[$field['field_name'] . '_administrative_area'])) { + // Clone the country handler. + $table[$field['field_name'] . '_country_admin'] = $table[$field['field_name'] . '_country']; + + // Set our own filter handler. + $table[$field['field_name'] . '_country_admin']['filter']['handler'] = 'addressfield_views_handler_filter_country_admin'; + + // Remove argument, field, and sort handlers. + unset($table[$field['field_name'] . '_country_admin']['argument']); + unset($table[$field['field_name'] . '_country_admin']['field']); + unset($table[$field['field_name'] . '_country_admin']['sort']); + } + // Iterate over the addressfield components. foreach ($table as $column_name => &$column) { if (empty($column['field']) && isset($valid_columns[$column_name])) { @@ -104,19 +119,21 @@ function addressfield_field_views_data($field) { $column['title'] = $title; $column['title short'] = $title; } - // The country name column is a virtual column - it's not in the - // database - so handle it separately. + // The country name column is a virtual column. Handle it separately. elseif ($property === 'countryname') { $property_label = t('Country name'); - $title = t('@field-label - @property-label', array( '@field-label' => $field_label, '@property-label' => $property_label, )); - $column['title'] = $title; $column['title short'] = $title; } + // The Country & Admin area column is also a virtual column. + elseif ($property === 'country_admin') { + $column['title'] = t('@field-label - Country and Administrative area (i.e. State / Province)', array('@field-label' => $field_label)); + $column['title short'] = t('@field-label - Country & Admin area (i.e. State / Province)', array('@field-label' => $field_label)); + } } } } diff --git a/views/addressfield_views_handler_argument_countryname.inc b/views/addressfield_views_handler_argument_countryname.inc index e7e468a..34a5715 100644 --- a/views/addressfield_views_handler_argument_countryname.inc +++ b/views/addressfield_views_handler_argument_countryname.inc @@ -56,7 +56,7 @@ class addressfield_views_handler_argument_countryname extends views_handler_argu '#type' => 'item', '#prefix' => '

', '#suffix' => '

', - '#markup' => t('Due to the way that Drupal Core maps country abbreviations to country names, this filter assumes that the value passed in via the URL is written in the same language that the request is being executed with.'), + '#markup' => t('Due to the way country abbreviations are mapped to country names, this filter assumes that the value passed in via the URL is written in the same language as the request that is being executed.'), ); } diff --git a/views/addressfield_views_handler_filter_country_admin.inc b/views/addressfield_views_handler_filter_country_admin.inc new file mode 100644 index 0000000..c536c26 --- /dev/null +++ b/views/addressfield_views_handler_filter_country_admin.inc @@ -0,0 +1,185 @@ +view = &$view; + + $this->country_field = $this->definition['additional fields'][0]; + $this->admin_field = $this->definition['additional fields'][1]; + } + + /** + * Override value options. + */ + function get_value_options() { + $this->value_title = t('Country'); + $field = field_info_field($this->definition['field_name']); + $this->value_options = _addressfield_country_options_list($field); + } + + /** + * Override value_form() to provide two selects: country & admin area. + * + * This should be overridden by all child classes and it must + * define $form['value'] + * + * @see options_form() + */ + function value_form(&$form, &$form_state) { + $path = backdrop_get_path('module', 'addressfield'); + $form['value'] = array( + '#type' => 'container', + '#attached' => array( + 'css' => array($path . '/css/addressfield-views.css'), + 'js' => array($path . '/js/addressfield-views.js'), + ), + ); + + // Since multiple values is not allowed... + $filters = explode('-', $this->value[0]); + $default_country = (isset($filters[0])) ? $filters[0] : array(); + $default_admin = (isset($filters[1])) ? $filters[1] : array(); + + $id = $this->options['expose']['identifier']; + $field = field_info_field($this->definition['field_name']); + $countries = _addressfield_country_options_list($field); + $form['value'][$id . '-country'] = array( + '#type' => 'select', + '#title' => t('Country'), + '#options' => $countries, + '#default_value' => $default_country, + '#attributes' => array('class' => array('addressfield-views-country')), + ); + + $admin_areas = array('' => t('Please select')); + module_load_include('inc', 'addressfield', 'addressfield.administrative_areas'); + foreach ($countries as $code => $name) { + if ($code != 'All') { + $areas = array('all' => t('- Any -')); + $all_areas = addressfield_get_administrative_areas($code); + // Limit options to values currently in use. + $result = db_query("SELECT DISTINCT($this->admin_field) AS administrative_area FROM {$this->definition['table']} WHERE $this->country_field = :code ORDER BY $this->admin_field", array(':code' => $code))->fetchAllKeyed(0,0); + if (!empty($result)) { + foreach ($result as $record) { + $areas[$record] = $all_areas[$record]; + } + } + if (is_array($areas)) { + $admin_areas[$code] = $areas; + } + } + } + $form['value'][$id . '-administrative_area'] = array( + '#type' => 'select', + '#title' => t('State / Province'), + '#options' => $admin_areas, + '#default_value' => $default_admin, + '#attributes' => array('class' => array('addressfield-views-admin-area')), + ); + } + + /** + * Available operators. + */ + function operators() { + return array( + 'in' => array( + 'title' => t('Is one of'), + 'short' => t('in'), + 'short_single' => t('='), + 'method' => 'op_simple', + 'values' => 1, + ), + 'not in' => array( + 'title' => t('Is not one of'), + 'short' => t('not in'), + 'short_single' => t('<>'), + 'method' => 'op_simple', + 'values' => 1, + ), + ); + } + + /** + * Modify the exposed form settings. + */ + function expose_form(&$form, &$form_state) { + parent::expose_form($form, $form_state); + unset($form['expose']['reduce']); + unset($form['expose']['multiple']); + } + + /** + * Sets value based on input. + */ + function accept_exposed_input($input) { + if (empty($this->options['exposed'])) { + return TRUE; + } + + + if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) { + $this->operator = $input[$this->options['expose']['operator_id']]; + } + + if (!empty($this->options['expose']['identifier'])) { + $value = ''; + if (isset($input[$this->options['expose']['identifier'] . '-country'])) { + $value .= $input[$this->options['expose']['identifier'] . '-country']; + } + if (isset($input[$this->options['expose']['identifier'] . '-administrative_area'])) { + $value .= '-' . $input[$this->options['expose']['identifier'] . '-administrative_area']; + } + + if (isset($value)) { + $this->value = $value; + } + else { + return FALSE; + } + } + + return TRUE; + } + + /** + * Perform any necessary changes to the form values prior to storage. + * There is no need for this function to actually store the data. + */ + function value_submit($form, &$form_state) { + $value = $form['value']['location-country']['#value']; + if (isset($form['value']['location-administrative_area']['#value']) && $form['value']['location-administrative_area']['#value'] != '') { + $value .= '-' . $form['value']['location-administrative_area']['#value']; + } + $form_state['values']['options']['value'] = array($value); + } + + /** + * Query the DB based on value. + */ + function query() { + // Since multiple values is not allowed... + $filters = explode('-', $this->value); + + $this->ensure_my_table(); + $this->query->add_where($this->options['group'], "$this->table_alias.$this->country_field", array($filters[0]), $this->operator); + + // @todo find out why this 'Array' hack is necessary. + if (isset($filters[1]) && !empty($filters[1]) && ($filters[1] != 'Array')) { + $this->query->add_where($this->options['group'], "$this->table_alias.$this->admin_field", array($filters[1]), $this->operator); + } + } +}