diff --git a/js/metadataauth-webform_strawberryfield.js b/js/metadataauth-webform_strawberryfield.js index 951c76f..2f4ccfa 100644 --- a/js/metadataauth-webform_strawberryfield.js +++ b/js/metadataauth-webform_strawberryfield.js @@ -64,7 +64,69 @@ return settings; }; + /** + * Overrides Drupal.autocomplete.options.source so we can avoid the opinionated cache + */ + + Drupal.autocomplete.options.source = function sourceData(request, response) { + const elementId = this.element.attr('id'); + var options = Drupal.autocomplete.options; + const is_sbf = this.element.data('strawberry-autocomplete'); + + if (!(elementId in Drupal.autocomplete.cache)) { + Drupal.autocomplete.cache[elementId] = {}; + } + + /** + * Filter through the suggestions removing all terms already tagged and + * display the available terms to the user. + * + * @param {object} suggestions + * Suggestions returned by the server. + */ + function showSuggestions(suggestions) { + const tagged = Drupal.autocomplete.splitValues(request.term); + const il = tagged.length; + for (let i = 0; i < il; i++) { + const index = suggestions.indexOf(tagged[i]); + if (index >= 0) { + suggestions.splice(index, 1); + } + } + response(suggestions); + } + + // Get the desired term and construct the autocomplete URL for it. + const term = Drupal.autocomplete.extractLastTerm(request.term); + /** + * Transforms the data object into an array and update autocomplete results. + * + * @param {object} data + * The data sent back from the server. + */ + function sourceCallbackHandler(data) { + if (!is_sbf) { + Drupal.autocomplete.cache[elementId][term] = data; + } + + // Send the new string array of terms to the jQuery UI list. + showSuggestions(data); + } + + // Check if the term is already cached. + if (Drupal.autocomplete.cache[elementId].hasOwnProperty(term) && !is_sbf) { + showSuggestions(Drupal.autocomplete.cache[elementId][term]); + } else { + const options = $.extend( + { success: sourceCallbackHandler, data: { q: term } }, + Drupal.autocomplete.ajax, + ); + $.ajax(this.element.attr('data-autocomplete-path'), options); + } + } + + /** * Overrides Drupal.autocomplete.options.search for no splitting of terms * * This is the only function where even is present, means we can decide per instance diff --git a/src/Controller/RowAutocompleteController.php b/src/Controller/RowAutocompleteController.php index c861419..4bbecd4 100644 --- a/src/Controller/RowAutocompleteController.php +++ b/src/Controller/RowAutocompleteController.php @@ -67,18 +67,24 @@ public static function create(ContainerInterface $container) { * Filters against Labels * */ - public function handleAutocomplete(Request $request, ContentEntityInterface $node, $label_header, $url_header, $match = 'STARTS_WITH', $limit = 10, $min = 2) { + public function handleAutocomplete(Request $request, ContentEntityInterface $node, $label_header, $url_header, $match = 'STARTS_WITH', $limit = 10, $min = 2, $desc_headers = NULL) { $results = []; $input = $request->query->get('q'); $input = Xss::filter($input); $label_header = strtolower($label_header); $url_header = strtolower($url_header); - + $desc_headers = strtolower($desc_headers); + $desc_headers_exploded = []; + $desc_headers_indexes = []; // Find a CSV file in this ADO. // Get the typed string from the URL, if it exists. if (!$input && strlen(trim($input)) < $min) { return new JsonResponse($results); } + if (is_string($desc_headers)) { + $desc_headers_exploded = explode(',', $desc_headers); + $desc_headers_exploded = array_slice($desc_headers_exploded, 0, 2); + } $file = null; if ($sbf_fields = \Drupal::service('strawberryfield.utility')->bearsStrawberryfield($node)) { @@ -98,32 +104,38 @@ public function handleAutocomplete(Request $request, ContentEntityInterface $nod $column_keys = $file_data_all['headers'] ?? []; $label_original_index = array_search($label_header, $column_keys); $url_original_index = array_search($url_header, $column_keys); + foreach ($desc_headers_exploded as $desc_header) { + $index = array_search($desc_header, $column_keys); + if ($index!== FALSE) { + $desc_headers_indexes[] = $index; + } + } + $i = 0; if ($label_original_index !== FALSE && $url_original_index !== FALSE) { foreach ($file_data_all['data'] as $id => &$row) { if (isset($row[$label_original_index])) { - - if ($match == 'STARTS_WITH' && stripos($row[$label_original_index], $input) === 0) { + if (($match == 'STARTS_WITH' && stripos($row[$label_original_index], $input) === 0) || ($match == 'CONTAINS' && stripos($row[$label_original_index], $input) !== FALSE)) { $i++; - + $desc = []; + $desc_string = ''; + foreach ($desc_headers_indexes as $desc_header_index) { + $desc[] = $row[$desc_header_index]; + } + $desc = array_filter($desc); + if (count($desc)) { + $desc_string = implode('|', $desc); + } + $desc_string = ($desc_string !== '') ? '(' . $desc_string . ')' : NULL; $results[] = [ 'value' => $row[$url_original_index], - 'label' => $row[$label_original_index], + 'label' => $row[$label_original_index].' '.$desc_string, + 'desc' => $desc_string ]; if ($i == $limit) { break; } - } else - if ($match == 'CONTAINS' && stripos($row[$label_original_index], $input) !== FALSE) { - $i++; - $results[] = [ - 'value' => $row[$url_original_index], - 'label' => $row[$label_original_index], - ]; - if ($i == $limit) { - break; - } - } + } } } } diff --git a/src/Plugin/WebformElement/WebformLoDfromCSV.php b/src/Plugin/WebformElement/WebformLoDfromCSV.php index fbf31e6..b57d32a 100644 --- a/src/Plugin/WebformElement/WebformLoDfromCSV.php +++ b/src/Plugin/WebformElement/WebformLoDfromCSV.php @@ -41,6 +41,7 @@ protected function defineDefaultProperties() { 'autocomplete_match' => 3, 'autocomplete_label_header' => 'label', 'autocomplete_url_header' => 'url', + 'autocomplete_desc_headers' => '', 'autocomplete_match_operator' => 'CONTAINS', ] + parent::defineDefaultProperties() + $this->defineDefaultMultipleProperties(); @@ -62,6 +63,16 @@ public function prepare( if (isset($element['#webform_key'])) { $element['#autocomplete_route_name'] = 'webform_strawberryfield.rowsbylabel.autocomplete'; + $desc = $element['#autocomplete_desc_headers'] ?? $properties['autocomplete_desc_headers']; + if (is_string($desc) && strlen(trim($desc)) > 0 ) { + $desc = explode(',', $desc); + $desc = array_slice($desc, 0, 2); + $desc = array_map('trim', $desc); + $desc = implode(',', $desc); + } + else { + $desc = ''; + } $element['#autocomplete_route_parameters'] = [ 'node' => $element['#autocomplete_items'], 'label_header' => $element['#autocomplete_label_header'] ?? $properties['autocomplete_label_header'], @@ -69,6 +80,7 @@ public function prepare( 'match' => $element['#autocomplete_match_operator'] ?? $properties['autocomplete_match_operator'], 'limit' => $element['#autocomplete_limit'] ?? $properties['autocomplete_limit'], 'min' => $element['#autocomplete_match'] ?? $properties['autocomplete_match'], + 'desc_headers' => $desc, ]; } } @@ -161,6 +173,11 @@ public function form(array $form, FormStateInterface $form_state) { '#title' => $this->t('The CSV column(header name) that will be used for the URL value'), '#required' => TRUE, ]; + $form['autocomplete']['autocomplete_desc_headers'] = [ + '#type' => 'textfield', + '#title' => $this->t('The CSV columns(header names), separated by a comma, that will be used for additional context/description. Leave empty if not used. It has a limited of 2 headers. Any extra ones will be ignored.'), + '#required' => FALSE, + ]; $form['autocomplete']['autocomplete_limit'] = [ '#type' => 'number', '#title' => $this->t('Autocomplete limit'), diff --git a/webform_strawberryfield.routing.yml b/webform_strawberryfield.routing.yml index 8930f6e..b2ee8ff 100644 --- a/webform_strawberryfield.routing.yml +++ b/webform_strawberryfield.routing.yml @@ -60,7 +60,7 @@ webform_strawberryfield.element.autocomplete: _entity_access: 'webform.submission_create' webform_strawberryfield.rowsbylabel.autocomplete: - path: '/webform_strawberry/csv_autocomplete/{node}/{label_header}/{url_header}/{match}/{limit}/{min}' + path: '/webform_strawberry/csv_autocomplete/{node}/{label_header}/{url_header}/{match}/{limit}/{min}/{desc_headers}' options: parameters: node: @@ -73,6 +73,7 @@ webform_strawberryfield.rowsbylabel.autocomplete: match: 'STARTS_WITH' limit: 10 min: 2 + desc_headers: '' _controller: '\Drupal\webform_strawberryfield\Controller\RowAutocompleteController::handleAutocomplete' _format: json requirements: