From 5fdba26cd963c9d442ac37a274684641edddfad1 Mon Sep 17 00:00:00 2001 From: indigoxela Date: Fri, 13 Mar 2026 13:57:52 +0100 Subject: [PATCH 1/6] Issue #62: Get dynamic form update working for single filter --- geofield.module | 70 ++++++++++++++++++++ js/viewsProximityValue.js | 74 ++++++++++++++++++++-- views/handlers/geofield_handler_filter.inc | 5 ++ 3 files changed, 142 insertions(+), 7 deletions(-) diff --git a/geofield.module b/geofield.module index b12e1f5..fd344a3 100644 --- a/geofield.module +++ b/geofield.module @@ -325,6 +325,76 @@ function geofield_views_api() { ); } +/** + * Implements hook_menu(). + */ +function geofield_menu() { + $items = array(); + $items['admin/structure/geofield_filter_ajax/%views_ui_cache/%'] = array( + 'access callback' => 'user_access', + 'access arguments' => array('administer views'), + // We need function views_ui_config_item_form() from views_ui. + 'file' => 'views_ui.admin.inc', + 'file path' => 'core/modules/views_ui', + 'page callback' => '_geofield_views_ui_change_proximity_widget_js', + 'page arguments' => array(3, 4), + 'delivery callback' => 'ajax_deliver', + 'type' => MENU_CALLBACK, + ); + return $items; +} + +/** + * Ajax callback that returns a form part to replace. + * + * @param view $view + * The views object. + * @param string $display_id + * Name of the current display, for example 'page'. + */ +function _geofield_views_ui_change_proximity_widget_js(view $view, $display_id) { + if (isset($_POST['plugin'])) { + $plugin_name = $_POST['plugin']; + } + else { + backdrop_not_found(); + backdrop_exit(); + } + + $form_state = form_state_defaults(); + // Form builder expects those at least: + $form_state['view'] = $view; + $form_state['display_id'] = $display_id; + $form_state['type'] = 'filter'; + $form_state['id'] = 'field_geofield_distance'; + + // Initially (before submitting), this $item is only stub. + $item = $view->get_item($display_id, 'filter', 'field_geofield_distance'); + $item['source'] = $plugin_name; + + if (isset($_POST['operator'])) { + $item['operator'] = $_POST['operator']; + } + // Hardcode for now. + if ($plugin_name = 'manual') { + $item['value']['origin'] = array('lat' => '', 'lon' => ''); + } + else { + $item['value']['origin'] = ''; + } + $form_state['view']->set_item($form_state['display_id'], $form_state['type'], $form_state['id'], $item); + + $form = backdrop_build_form('views_ui_config_item_form', $form_state); + if (isset($form['options']['value'])) { + // Put it into cache so we don't lose default value settings. + views_ui_cache_set($form_state['view']); + // @see js/viewsProximityValue.js + return $form['options']['value']; + } + + return ''; +} + /** * Geofield Compute Values * diff --git a/js/viewsProximityValue.js b/js/viewsProximityValue.js index 5fe2d74..8924064 100644 --- a/js/viewsProximityValue.js +++ b/js/viewsProximityValue.js @@ -1,13 +1,73 @@ -;(function ($) { +/** + * @file + * Dynamically update Views admin UI form parts by sending POST. + */ +(function ($) { + + "use strict"; + Backdrop.behaviors.viewsProximityValue = { + /** + * @param array data + */ + updateFormItem: function (data) { + let formItemsWrapped = data[1].data; + let formItems = $(formItemsWrapped).find('.geofield-proximity-field-wrapper'); + if (formItems.length) { + $('.geofield-proximity-field-wrapper').replaceWith(formItems); + } + }, + /** + * @return bool + */ + formHasRange: function () { + let checkedValue = $('#edit-options-operator').find(':checked').val(); + if (checkedValue === 'between' || checkedValue === 'not between') { + return true; + } + return false; + }, + /** + * + */ attach: function (context, settings) { - if (!$('body').hasClass('page-admin-structure-views-nojs')) { - $('#edit-options-source-change').hide(); + if ($('#edit-options-group-button-radios :checked').val() === 1) { + // This behavior does not work with grouped exposed filters. + return; } - $('#edit-options-source').change(function() { - $('#edit-options-source-change').mousedown(); - $('#edit-options-source-change').submit(); + const widget = this; + let hasRange = widget.formHasRange(); + $.ajaxSetup({ + type: 'POST', + url: $('#edit-options-source').data('path'), + dataType: 'json' + }); + + $('#edit-options-source').on('change', function() { + let postData = { + plugin: this.value + }; + $.ajax( { data: postData } ) + .done( function (data) { + widget.updateFormItem(data); + }); + }); + + $('#edit-options-operator').on('change', function() { + // Only post if necessary. + if (hasRange === widget.formHasRange()) { + return; + } + let postData = { + plugin: $('#edit-options-source').val(), + operator: $(this).find(':checked').val() + }; + $.ajax( { data: postData } ) + .done( function (data) { + widget.updateFormItem(data); + hasRange = widget.formHasRange(); + }); }); } }; -})(jQuery); \ No newline at end of file +})(jQuery); diff --git a/views/handlers/geofield_handler_filter.inc b/views/handlers/geofield_handler_filter.inc index 3fe0d70..29b0daa 100644 --- a/views/handlers/geofield_handler_filter.inc +++ b/views/handlers/geofield_handler_filter.inc @@ -130,11 +130,16 @@ class geofield_handler_filter extends views_handler_filter_numeric { '#default_value' => $this->options['source'], ); + global $base_path; + $form['source']['#attributes']['data-path'] = $base_path . 'admin/structure/geofield_filter_ajax/' . $this->view->name . '/' . $this->view->current_display; + + // This only works with Javascript disabled. $form['source_change'] = array( '#type' => 'submit', '#value' => 'Change Source Widget', '#submit' => array('geofield_views_ui_change_proximity_widget'), ); + $form['source_change']['#attributes']['class'][] = 'js-hide'; $proximityHandlers = module_invoke_all('proximity_views_handlers'); foreach ($proximityHandlers as $key => $handler) { From 73f8060bdf6230e45a3ae79369cb11e37406a31f Mon Sep 17 00:00:00 2001 From: indigoxela Date: Fri, 13 Mar 2026 14:07:32 +0100 Subject: [PATCH 2/6] Less quirky grammar --- geofield.module | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geofield.module b/geofield.module index fd344a3..b236bf6 100644 --- a/geofield.module +++ b/geofield.module @@ -345,7 +345,7 @@ function geofield_menu() { } /** - * Ajax callback that returns a form part to replace. + * Callback for AJAX to return a form part for replacement. * * @param view $view * The views object. From 140cd46c7b401cafb1ca5756ebc78c8649184432 Mon Sep 17 00:00:00 2001 From: indigoxela Date: Fri, 13 Mar 2026 14:23:16 +0100 Subject: [PATCH 3/6] Make code comment less misleading --- js/viewsProximityValue.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/viewsProximityValue.js b/js/viewsProximityValue.js index 8924064..e423ab4 100644 --- a/js/viewsProximityValue.js +++ b/js/viewsProximityValue.js @@ -1,6 +1,6 @@ /** * @file - * Dynamically update Views admin UI form parts by sending POST. + * Dynamically update Views admin UI form items fetched via POST request. */ (function ($) { From f4ca6d75aade8b9f573bf03ee63e0e447071d360 Mon Sep 17 00:00:00 2001 From: indigoxela Date: Sat, 14 Mar 2026 09:34:52 +0100 Subject: [PATCH 4/6] Also update grouped form items --- geofield.module | 4 ++++ js/viewsProximityValue.js | 27 ++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/geofield.module b/geofield.module index b236bf6..d6006d0 100644 --- a/geofield.module +++ b/geofield.module @@ -391,6 +391,10 @@ function _geofield_views_ui_change_proximity_widget_js(view $view, $display_id) // @see js/viewsProximityValue.js return $form['options']['value']; } + elseif (isset($form['options']['group_info'])) { + views_ui_cache_set($form_state['view']); + return $form['options']['group_info']['group_items']; + } return ''; } diff --git a/js/viewsProximityValue.js b/js/viewsProximityValue.js index e423ab4..9ae2b02 100644 --- a/js/viewsProximityValue.js +++ b/js/viewsProximityValue.js @@ -17,6 +17,18 @@ $('.geofield-proximity-field-wrapper').replaceWith(formItems); } }, + updateMultipleFormItems: function (data) { + let fullHTML = data[1].data; + let nodes = $.parseHTML(fullHTML); + let index = 1; + for (const node of nodes) { + if ($(node).hasClass('geofield-proximity-field-wrapper')) { + $('tr#views-row-' + index + ' .geofield-proximity-field-wrapper').replaceWith(node); + index++;// wow that works, it seems... @todo operator select lists. + // a bit fragile and based on optimism... + } + } + }, /** * @return bool */ @@ -31,11 +43,11 @@ * */ attach: function (context, settings) { - if ($('#edit-options-group-button-radios :checked').val() === 1) { - // This behavior does not work with grouped exposed filters. - return; - } const widget = this; + let isGrouped = false; + if ($('#edit-options-group-button-radios :checked').val() === '1') { + isGrouped = true; + } let hasRange = widget.formHasRange(); $.ajaxSetup({ type: 'POST', @@ -49,7 +61,12 @@ }; $.ajax( { data: postData } ) .done( function (data) { - widget.updateFormItem(data); + if (isGrouped === false) { + widget.updateFormItem(data); + } + else { + widget.updateMultipleFormItems(data); + } }); }); From 48dd04e0c5bbadfe370b863a35f2027ac043a843 Mon Sep 17 00:00:00 2001 From: indigoxela Date: Sat, 14 Mar 2026 09:44:41 +0100 Subject: [PATCH 5/6] Remove obsolete comment --- js/viewsProximityValue.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/viewsProximityValue.js b/js/viewsProximityValue.js index 9ae2b02..ad3a2d8 100644 --- a/js/viewsProximityValue.js +++ b/js/viewsProximityValue.js @@ -24,8 +24,7 @@ for (const node of nodes) { if ($(node).hasClass('geofield-proximity-field-wrapper')) { $('tr#views-row-' + index + ' .geofield-proximity-field-wrapper').replaceWith(node); - index++;// wow that works, it seems... @todo operator select lists. - // a bit fragile and based on optimism... + index++; } } }, From 7fc1b063ef6e1d5f94a07586e3fb7123acaea943 Mon Sep 17 00:00:00 2001 From: indigoxela Date: Sat, 14 Mar 2026 10:03:54 +0100 Subject: [PATCH 6/6] Create wrapper loading function to avoid dependency --- geofield.module | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/geofield.module b/geofield.module index d6006d0..b6139d5 100644 --- a/geofield.module +++ b/geofield.module @@ -330,7 +330,7 @@ function geofield_views_api() { */ function geofield_menu() { $items = array(); - $items['admin/structure/geofield_filter_ajax/%views_ui_cache/%'] = array( + $items['admin/structure/geofield_filter_ajax/%geofield_views_ui_cache/%'] = array( 'access callback' => 'user_access', 'access arguments' => array('administer views'), // We need function views_ui_config_item_form() from views_ui. @@ -344,6 +344,19 @@ function geofield_menu() { return $items; } +/** + * Wrapper for views_ui loader callback. + * + * To avoid dependency on views_ui, which might not be enabled. + */ +function geofield_views_ui_cache_load($name) { + if (module_exists('views_ui')) { + return views_ui_cache_load($name); + } + // Page not found. + return FALSE; +} + /** * Callback for AJAX to return a form part for replacement. *