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 a267da3..4bbecd4 100644 --- a/src/Controller/RowAutocompleteController.php +++ b/src/Controller/RowAutocompleteController.php @@ -67,23 +67,30 @@ public static function create(ContainerInterface $container) { * Filters against Labels * */ - public function handleAutocomplete(Request $request, ContentEntityInterface $node, $label_header, $url_header) { + 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) { + 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)) { $files = $node->get('field_file_drop')->getValue(); - foreach($files as $offset => $fileinfo) { - /** @var \Drupal\file\FileInterface $file|null */ + foreach ($files as $offset => $fileinfo) { + /** @var \Drupal\file\FileInterface $file |null */ $file = $this->entityTypeManager ->getStorage('file') ->load($fileinfo['target_id']); @@ -97,18 +104,37 @@ 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 ) { + if ($label_original_index !== FALSE && $url_original_index !== FALSE) { foreach ($file_data_all['data'] as $id => &$row) { - if (isset($row[$label_original_index]) && stripos($row[$label_original_index], $input) === 0) { - $i++; - - $results[] = [ - 'value' => $row[$url_original_index], - 'label' => $row[$label_original_index], - ]; - if ($i == 10) { - break; + if (isset($row[$label_original_index])) { + 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].' '.$desc_string, + 'desc' => $desc_string + ]; + if ($i == $limit) { + break; + } } } } diff --git a/src/Plugin/WebformElement/WebformEuropeana.php b/src/Plugin/WebformElement/WebformEuropeana.php index 3ebaee5..8810bb5 100644 --- a/src/Plugin/WebformElement/WebformEuropeana.php +++ b/src/Plugin/WebformElement/WebformEuropeana.php @@ -84,6 +84,15 @@ protected function prepareMultipleWrapper(array &$element) { 'count' => 10 ]; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + $element['#webform_composite_elements']['label']["#autocomplete_route_parameters"] = + [ + 'auth_type' => 'europeana', + 'vocab' => $vocab, + 'rdftype' => $rdftype, + 'count' => 10 + ]; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) { diff --git a/src/Plugin/WebformElement/WebformGetty.php b/src/Plugin/WebformElement/WebformGetty.php index 55dcc5f..8c8c856 100644 --- a/src/Plugin/WebformElement/WebformGetty.php +++ b/src/Plugin/WebformElement/WebformGetty.php @@ -78,6 +78,16 @@ protected function prepareMultipleWrapper(array &$element) { 'count' => 10 ]; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + $element['#webform_composite_elements']['label']["#autocomplete_route_parameters"] = + [ + 'auth_type' => 'getty', + 'vocab' => $vocab, + 'rdftype' => $matchtype, + 'count' => 10 + ]; + } + // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) { diff --git a/src/Plugin/WebformElement/WebformLoC.php b/src/Plugin/WebformElement/WebformLoC.php index b18664d..d5f49d2 100644 --- a/src/Plugin/WebformElement/WebformLoC.php +++ b/src/Plugin/WebformElement/WebformLoC.php @@ -90,6 +90,15 @@ protected function prepareMultipleWrapper(array &$element) { 'count' => 10 ]; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + $element['#webform_composite_elements']['label']["#autocomplete_route_parameters"] = + [ + 'auth_type' => 'loc', + 'vocab' => $vocab, + 'rdftype' => $rdftype, + 'count' => 10 + ]; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) { diff --git a/src/Plugin/WebformElement/WebformLoDfromCSV.php b/src/Plugin/WebformElement/WebformLoDfromCSV.php index d4af84a..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,10 +63,24 @@ 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'], 'url_header' => $element['#autocomplete_url_header'] ?? $properties['autocomplete_url_header'], + '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, ]; } } @@ -85,6 +100,11 @@ protected function prepareMultipleWrapper(array &$element) { $element['#element']['#webform_composite_elements']['label']['#autocomplete_route_name'] = $autocomplete_route; $element['#element']['#webform_composite_elements']['label']['#autocomplete_route_parameters'] = $autocomplete_route_params; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + // Not a multiple one. So assign the Autocomplete route directly to the composite children. + $element['#webform_composite_elements']['label']['#autocomplete_route_name'] = $autocomplete_route; + $element['#webform_composite_elements']['label']['#autocomplete_route_parameters'] = $autocomplete_route_params; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... @@ -153,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/src/Plugin/WebformElement/WebformLoDfromOptions.php b/src/Plugin/WebformElement/WebformLoDfromOptions.php index 24d4b0d..9a876e8 100644 --- a/src/Plugin/WebformElement/WebformLoDfromOptions.php +++ b/src/Plugin/WebformElement/WebformLoDfromOptions.php @@ -80,6 +80,11 @@ protected function prepareMultipleWrapper(array &$element) { $element['#element']['#webform_composite_elements']['label']['#autocomplete_route_name'] = $autocomplete_route; $element['#element']['#webform_composite_elements']['label']['#autocomplete_route_parameters'] = $autocomplete_route_params; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + // Not a multiple one. So assign the Autocomplete route directly to the composite children. + $element['#webform_composite_elements']['label']['#autocomplete_route_name'] = $autocomplete_route; + $element['#webform_composite_elements']['label']['#autocomplete_route_parameters'] = $autocomplete_route_params; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... diff --git a/src/Plugin/WebformElement/WebformMesh.php b/src/Plugin/WebformElement/WebformMesh.php index fedf6d0..3abcd9c 100644 --- a/src/Plugin/WebformElement/WebformMesh.php +++ b/src/Plugin/WebformElement/WebformMesh.php @@ -84,6 +84,15 @@ protected function prepareMultipleWrapper(array &$element) { 'count' => 10 ]; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + $element['#webform_composite_elements']['label']["#autocomplete_route_parameters"] = + [ + 'auth_type' => 'mesh', + 'vocab' => $vocab, + 'rdftype' => $matchtype, + 'count' => 10 + ]; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) { diff --git a/src/Plugin/WebformElement/WebformSnac.php b/src/Plugin/WebformElement/WebformSnac.php index dffe00d..e10d60e 100644 --- a/src/Plugin/WebformElement/WebformSnac.php +++ b/src/Plugin/WebformElement/WebformSnac.php @@ -90,6 +90,15 @@ protected function prepareMultipleWrapper(array &$element) { 'count' => 10 ]; } + elseif (isset($element['#webform_multiple']) && $element['#webform_multiple'] == FALSE && isset($element['#webform_composite_elements']['label'])) { + $element['#webform_composite_elements']['label']["#autocomplete_route_parameters"] = + [ + 'auth_type' => 'snac', + 'vocab' => $vocab, + 'rdftype' => $rdftype, + 'count' => 10 + ]; + } // For some reason i can not understand, when multiples are using // Tables, the #webform_composite_elements -> 'label' is not used... if (isset($element["#multiple__header"]) && $element["#multiple__header"] == true) { diff --git a/webform_strawberryfield.routing.yml b/webform_strawberryfield.routing.yml index dcfdb75..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}' + path: '/webform_strawberry/csv_autocomplete/{node}/{label_header}/{url_header}/{match}/{limit}/{min}/{desc_headers}' options: parameters: node: @@ -70,6 +70,10 @@ webform_strawberryfield.rowsbylabel.autocomplete: defaults: label_header: 'label' url_header: 'url' + match: 'STARTS_WITH' + limit: 10 + min: 2 + desc_headers: '' _controller: '\Drupal\webform_strawberryfield\Controller\RowAutocompleteController::handleAutocomplete' _format: json requirements: