diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php new file mode 100644 index 0000000..96e124c --- /dev/null +++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TermAutocompleteController.php @@ -0,0 +1,161 @@ +termEntityQuery = $termEntityQuery; + $this->fieldInfo = $fieldInfo; + $this->termStorageController = $termStorageController; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('entity.query')->get('taxonomy_term'), + $container->get('field.info'), + $container->get('plugin.manager.entity')->getStorageController('taxonomy_term') + ); + } + + /** + * Retrieves suggestions for taxonomy term autocompletion. + * + * This function outputs term name suggestions in response to Ajax requests + * made by the taxonomy autocomplete widget for taxonomy term reference + * fields. The output is a JSON object of plain-text term suggestions, keyed + * by the user-entered value with the completed term name appended. + * Term names containing commas are wrapped in quotes. + * + * For example, suppose the user has entered the string 'red fish, blue' in + * the field, and there are two taxonomy terms, 'blue fish' and 'blue moon'. + * The JSON output would have the following structure: + * @code + * { + * "red fish, blue fish": "blue fish", + * "red fish, blue moon": "blue moon", + * }; + * @endcode + * + * @param \Symfony\Component\HttpFoundation\Request $request + * The request object. + * + * @param string $field_name + * The name of the term reference field. + * + * @return \Symfony\Component\HttpFoundation\JsonResponse | \Symfony\Component\HttpFoundation\Response + * When valid field name is specified, a JSON response containing the + * autocomplete suggestions for taxonomy terms. Otherwie a normal response + * containing an error message. + */ + public function autocomplete(Request $request, $field_name) { + // A comma-separated list of term names entered in the autocomplete form + // element. Only the last term is used for autocompletion. + $tags_typed = $request->query->get('q'); + + // Make sure the field exists and is a taxonomy field. + if (!($field = $this->fieldInfo->getField($field_name)) || $field['type'] !== 'taxonomy_term_reference') { + // Error string. The JavaScript handler will realize this is not JSON and + // will display it as debugging information. + return new Response(t('Taxonomy field @field_name not found.', array('@field_name' => $field_name)), 403); + } + + // The user enters a comma-separated list of tags. We only autocomplete the + // last tag. + $tags_typed = Tags::explode($tags_typed); + $tag_last = Unicode::strtolower(array_pop($tags_typed)); + + $matches = array(); + if ($tag_last != '') { + + // Part of the criteria for the query come from the field's own settings. + $vids = array(); + foreach ($field['settings']['allowed_values'] as $tree) { + $vids[] = $tree['vocabulary']; + } + + $this->termEntityQuery->addTag('term_access'); + + // Do not select already entered terms. + if (!empty($tags_typed)) { + $this->termEntityQuery->condition('name', $tags_typed, 'NOT IN'); + } + // Select rows that match by term name. + $tids = $this->termEntityQuery + ->condition('vid', $vids, 'IN') + ->condition('name', $tag_last, 'CONTAINS') + ->range(0, 10) + ->execute(); + + $prefix = count($tags_typed) ? Tags::implode($tags_typed) . ', ' : ''; + if (!empty($tids)) { + $terms = $this->termStorageController->loadMultiple(array_keys($tids)); + foreach ($terms as $term) { + $name = $term->label(); + // Term names containing commas or quotes must be wrapped in quotes. + if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) { + $name = '"' . str_replace('"', '""', $name) . '"'; + } + $matches[$prefix . $name] = String::checkPlain($term->label()); + } + } + } + + return new JsonResponse($matches); + } + +} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 17bbb8c..2bd7a70 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -292,14 +292,6 @@ function taxonomy_menu() { 'type' => MENU_CALLBACK, 'file' => 'taxonomy.pages.inc', ); - $items['taxonomy/autocomplete/%'] = array( - 'title' => 'Autocomplete taxonomy', - 'page callback' => 'taxonomy_autocomplete', - 'page arguments' => array(2), - 'access arguments' => array('access content'), - 'type' => MENU_CALLBACK, - 'file' => 'taxonomy.pages.inc', - ); $items['admin/structure/taxonomy/manage/%taxonomy_vocabulary'] = array( 'title callback' => 'entity_page_label', diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc index 4baf150..491ce50 100644 --- a/core/modules/taxonomy/taxonomy.pages.inc +++ b/core/modules/taxonomy/taxonomy.pages.inc @@ -6,8 +6,6 @@ */ use Drupal\taxonomy\Plugin\Core\Entity\Term; -use Drupal\taxonomy\Plugin\Core\Entity\Vocabulary; -use Symfony\Component\HttpFoundation\JsonResponse; /** * Menu callback; displays all nodes associated with a term. @@ -78,86 +76,3 @@ function taxonomy_term_feed(Term $term) { return node_feed($nids, $channel); } - -/** - * Page callback: Outputs JSON for taxonomy autocomplete suggestions. - * - * This callback outputs term name suggestions in response to Ajax requests - * made by the taxonomy autocomplete widget for taxonomy term reference - * fields. The output is a JSON object of plain-text term suggestions, keyed by - * the user-entered value with the completed term name appended. Term names - * containing commas are wrapped in quotes. - * - * For example, suppose the user has entered the string 'red fish, blue' in the - * field, and there are two taxonomy terms, 'blue fish' and 'blue moon'. The - * JSON output would have the following structure: - * @code - * { - * "red fish, blue fish": "blue fish", - * "red fish, blue moon": "blue moon", - * }; - * @endcode - * - * @param $field_name - * The name of the term reference field. - * - * @see taxonomy_menu() - * @see taxonomy_field_widget_info() - */ -function taxonomy_autocomplete($field_name) { - // A comma-separated list of term names entered in the autocomplete form - // element. Only the last term is used for autocompletion. - $tags_typed = Drupal::request()->query->get('q'); - - // Make sure the field exists and is a taxonomy field. - if (!($field = field_info_field($field_name)) || $field['type'] !== 'taxonomy_term_reference') { - // Error string. The JavaScript handler will realize this is not JSON and - // will display it as debugging information. - print t('Taxonomy field @field_name not found.', array('@field_name' => $field_name)); - exit; - } - - // The user enters a comma-separated list of tags. We only autocomplete the last tag. - $tags_typed = drupal_explode_tags($tags_typed); - $tag_last = drupal_strtolower(array_pop($tags_typed)); - - $matches = array(); - if ($tag_last != '') { - - // Part of the criteria for the query come from the field's own settings. - $vids = array(); - foreach ($field['settings']['allowed_values'] as $tree) { - $vids[] = $tree['vocabulary']; - } - - $query = db_select('taxonomy_term_data', 't'); - $query->addTag('term_access'); - - // Do not select already entered terms. - if (!empty($tags_typed)) { - $query->condition('t.name', $tags_typed, 'NOT IN'); - } - // Select rows that match by term name. - $tags_return = $query - ->fields('t', array('tid', 'name')) - ->condition('t.vid', $vids) - ->condition('t.name', '%' . db_like($tag_last) . '%', 'LIKE') - ->range(0, 10) - ->execute() - ->fetchAllKeyed(); - - $prefix = count($tags_typed) ? drupal_implode_tags($tags_typed) . ', ' : ''; - - $term_matches = array(); - foreach ($tags_return as $tid => $name) { - $n = $name; - // Term names containing commas or quotes must be wrapped in quotes. - if (strpos($name, ',') !== FALSE || strpos($name, '"') !== FALSE) { - $n = '"' . str_replace('"', '""', $name) . '"'; - } - $term_matches[$prefix . $n] = check_plain($name); - } - } - - return new JsonResponse($term_matches); -} diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml index 09dc6d0..d89019e 100644 --- a/core/modules/taxonomy/taxonomy.routing.yml +++ b/core/modules/taxonomy/taxonomy.routing.yml @@ -32,3 +32,10 @@ taxonomy_vocabulary_reset: _entity_form: 'taxonomy_vocabulary.reset' requirements: _permission: 'administer taxonomy' + +taxonomy_autocomplete: + pattern: '/taxonomy/autocomplete/{field_name}' + defaults: + _controller: '\Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete' + requirements: + _permission: 'access content'