updated synonyms to 1.3

This commit is contained in:
Bachir Soussi Chiadmi
2016-11-05 17:52:37 +01:00
parent 86c993f638
commit 8d24211bf5
37 changed files with 4562 additions and 1923 deletions

View File

@@ -7,11 +7,27 @@
/**
* Implements hook_views_plugins_alter().
*
* @see hook_views_plugins_alter()
*/
function synonyms_views_plugins_alter(&$plugins) {
// Replace default taxonomy term argument validator with our extended version,
// which can also handle a term synonym as an argument.
$plugins['argument validator']['taxonomy_term']['handler'] = 'synonyms_views_plugin_argument_validate_taxonomy_term';
}
/**
* Implements hook_field_views_data_alter().
*/
function synonyms_field_views_data_alter(&$result, $field, $module) {
if ($field['type'] == 'taxonomy_term_reference') {
// Add synonyms friendly autocomplete filter.
foreach ($field['storage']['details']['sql'] as $table) {
$tid_column = reset($table);
$tid_column = $tid_column['tid'];
$table = array_keys($table);
$table = $table[0];
if (isset($result[$table][$tid_column]['filter'])) {
$result[$table][$tid_column]['filter']['handler'] = 'synonyms_views_handler_filter_term_tid';
}
}
}
}

View File

@@ -0,0 +1,174 @@
<?php
/**
* @file
* Definition of synonyms_views_handler_filter_term_tid class.
*/
/**
* Synonyms friendly taxonomy filter handler.
*/
class synonyms_views_handler_filter_term_tid extends views_handler_filter_term_node_tid {
function extra_options_form(&$form, &$form_state) {
parent::extra_options_form($form, $form_state);
$form['type']['#options']['synonyms_autocomplete'] = t('Synonyms friendly autocomplete');
}
function value_form(&$form, &$form_state) {
$restore_value = $this->options['type'] == 'synonyms_autocomplete';
if ($restore_value) {
$this->options['type'] = 'textfield';
}
parent::value_form($form, $form_state);
if ($restore_value) {
// We need to determine the entity type onto which this field is attached
// that is used in this view.
$entity_type_base_table = $this->view->base_table;
// TODO: it would be nice to consider the existence of relationships, but
// I just couldn't figure it out at that time.
$entity_info = entity_get_info();
$field_entity_type = FALSE;
$field = field_info_field($this->definition['field_name']);
foreach ($field['bundles'] as $entity_type => $bundles) {
if ($entity_info[$entity_type]['base table'] == $entity_type_base_table) {
$field_entity_type = $entity_type;
break;
}
}
if (!$field_entity_type) {
// Seems like we failed to determine the entity type which is used for
// this field in the view. Well, it's not a fatal fail, we'll just use
// whatever then.
$field_entity_type = array_keys($field['bundles']);
$field_entity_type = $field_entity_type[0];
}
// We just grab the first instance of this field within the determined
// entity type.
$bundle = $field['bundles'][$field_entity_type][0];
$instance = field_info_instance($field_entity_type, $field['field_name'], $bundle);
if ($instance['widget']['type'] == 'synonyms_autocomplete') {
$widget = $instance['widget']['settings'];
}
else {
$widget = field_info_widget_settings('synonyms_autocomplete');
}
$autocomplete_path = $widget['synonyms_autocomplete_path'];
$size = $widget['size'];
$form['value']['#autocomplete_path'] = $autocomplete_path . '/' . $this->definition['field_name'] . '/' . $field_entity_type . '/' . $bundle;
$form['value']['#size'] = $size;
$form['value']['#auto_creation'] = FALSE;
$form['value']['#attributes']['class'][] = 'synonyms-autocomplete';
$form['value']['#attached']['js'][drupal_get_path('module', 'synonyms') . '/js/synonyms-autocomplete.js'] = array();
$this->options['type'] = 'synonyms_autocomplete';
}
}
function value_validate($form, &$form_state) {
if ($this->options['type'] == 'synonyms_autocomplete') {
$values = drupal_explode_tags($form_state['values']['options']['value']);
$tids = $this->synonyms_validate_term_strings($form['value'], $values);
if ($tids) {
$form_state['values']['options']['value'] = $tids;
}
}
else {
parent::value_validate($form, $form_state);
}
}
function exposed_validate(&$form, &$form_state) {
if ($this->options['type'] == 'synonyms_autocomplete') {
if (empty($this->options['exposed'])) {
return;
}
if (empty($this->options['expose']['identifier'])) {
return;
}
$identifier = $this->options['expose']['identifier'];
$values = drupal_explode_tags($form_state['values'][$identifier]);
$tids = $this->synonyms_validate_term_strings($form[$identifier], $values);
if ($tids) {
$this->validated_exposed_input = $tids;
}
}
else {
parent::exposed_validate($form, $form_state);
}
}
/**
* Validate the user string.
*
* In a great extend it does the same job as parent::validate_term_strings(),
* just that this implementation is synonyms-aware.
*
* @param $element
* The form element which is used, either the views ui or the exposed
* filters.
* @param $values
* The taxonomy names/synonyms which will be converted to tids.
*
* @return array
* The taxonomy ids for all validated terms.
*/
protected function synonyms_validate_term_strings($element, $values) {
if (empty($values)) {
return array();
}
$values = array_map('drupal_strtolower', $values);
$missing = array_flip($values);
$tids = array();
$vocabulary = taxonomy_vocabulary_machine_name_load($this->options['vocabulary']);
// Firstly looking up the entered tags as if they were term names. Then,
// the remaining tags are looked up as if they were synonyms of terms.
// Lastly, if any tags are left at this point, we mark form validation
// error.
$query = db_select('taxonomy_term_data', 'td');
$query->fields('td', array('tid', 'name'));
$query->condition('td.vid', $vocabulary->vid);
$query->condition('td.name', $values);
$query->addTag('term_access');
$result = $query->execute();
foreach ($result as $term) {
unset($missing[drupal_strtolower($term->name)]);
$tids[] = $term->tid;
}
$behavior_implementations = synonyms_behavior_get('autocomplete', 'taxonomy_term', $vocabulary->machine_name, TRUE);
foreach ($behavior_implementations as $behavior_implementation) {
if (!empty($missing)) {
$condition = db_or();
foreach ($missing as $tag => $v) {
$condition->condition(AbstractSynonymsSynonymsBehavior::COLUMN_PLACEHOLDER, $tag);
}
$synonyms = synonyms_synonyms_find_behavior($condition, $behavior_implementation);
foreach ($synonyms as $synonym) {
$synonym->synonym = drupal_strtolower($synonym->synonym);
unset($missing[$synonym->synonym]);
$tids[] = $synonym->entity_id;
}
}
}
if (!empty($missing) && !empty($this->options['error_message'])) {
form_error($element, format_plural(count($missing), 'Unable to find term: @terms', 'Unable to find terms: @terms', array('@terms' => implode(', ', array_keys($missing)))));
}
return $tids;
}
}

View File

@@ -27,18 +27,10 @@ class synonyms_views_plugin_argument_validate_taxonomy_term extends views_plugin
switch ($type) {
case 'synonym':
case 'synonym_tid':
// We are requested to do synonyms friendly validation. We will have to
// go through each of allowed vocabularies, collecting terms whose
// synonyms might look like the argument we have. However, it is not
// that easy to achieve due to the fact that synonyms may be kept in
// whatever format in whatever column and only corresponding Synonyms
// extractor class has the knowledge how it is organized. Firstly we are
// going to query database trying to find a term with our argument's
// We are requested to do synonyms friendly validation. Firstly we are
// going to query the database trying to find a term with our argument's
// name. If we find one, it is great and we stop right there. Otherwise,
// the nightmare starts: we are going to use
// AbstractSynonymsExtractor::processEntityFieldQuery() method to find
// synonyms that are similar to our argument. Then we will load those
// terms and manually in PHP will determine whether it is a match.
// we look up by synonyms.
$query = db_select('taxonomy_term_data', 't')
->fields('t', array('tid', 'name'));
if (!empty($vocabularies)) {
@@ -46,7 +38,7 @@ class synonyms_views_plugin_argument_validate_taxonomy_term extends views_plugin
$query->condition('v.machine_name', $vocabularies);
}
if ($transform) {
$query->where("replace(t.name, ' ', '-') = :name", array(':name' => $argument));
$query->where("REPLACE(t.name, ' ', '-') = :name", array(':name' => $argument));
}
else {
$query->condition('t.name', $argument, '=');
@@ -61,76 +53,32 @@ class synonyms_views_plugin_argument_validate_taxonomy_term extends views_plugin
return TRUE;
}
$bundles = $vocabularies;
if (empty($vocabularies)) {
// At this point we want to convert an empty $vocabulary (implicitly
// meaning "all") into actually a list of all fully loaded
// vocabularies.
$vocabularies = taxonomy_vocabulary_load_multiple(FALSE);
// At this point we want to convert an empty $vocabularies (implicitly
// meaning "all") into actually a list of all vocabularies.
$bundles = array_keys(taxonomy_vocabulary_get_names());
}
else {
// We need to load the filtered vocabularies based on their machine
// names.
foreach ($vocabularies as $v) {
$vocabularies[$v] = taxonomy_vocabulary_machine_name_load($v);
foreach ($bundles as $bundle) {
$condition = db_and();
if ($transform) {
$condition->where("REPLACE(" . AbstractSynonymsSynonymsBehavior::COLUMN_PLACEHOLDER . ", ' ', '-') = :argument", array(
':argument' => $argument,
));
}
else {
$condition->condition(AbstractSynonymsSynonymsBehavior::COLUMN_PLACEHOLDER, $argument);
}
$synonyms = synonyms_synonyms_find($condition, 'taxonomy_term', $bundle);
if (!empty($synonyms)) {
$term = taxonomy_term_load($synonyms[0]->entity_id);
$this->argument->argument = $this->synonyms_get_term_property($term);
$this->argument->validated_title = check_plain(entity_label('taxonomy_term', $term));
return TRUE;
}
}
// We haven't had much luck, seems like we have to do the hard part. We
// will pull up terms that are similar to our argument using the same
// functionality as for synonyms friendly autocomplete widget and then
// will find those who actually match among this narrowed set of terms.
// Since $match will be use as value for LIKE SQL operator, we are not
// sure whether we need to substitute dash (-) with spacebar, or keep
// the dash, to match both we will use underscore (_) since this symbol
// matches one symbol in LIKE operator.
$match = $transform ? str_replace('-', '_', $argument) : $argument;
$tids = array();
// Unfortunately we have to query multiple times the database for each
// vocabulary for each synonyms-source field.
foreach ($vocabularies as $vocabulary) {
$bundle = field_extract_bundle('taxonomy_term', $vocabulary);
foreach (synonyms_synonyms_fields($vocabulary) as $field) {
$field = field_info_field($field);
$instance = field_info_instance('taxonomy_term', $field['field_name'], $bundle);
$query = new EntityFieldQuery();
$query->entityCondition('entity_type', 'taxonomy_term')
->entityCondition('bundle', $vocabulary->machine_name);
// We let the class that defines this field type as a source of
// synonyms filter out and provide its suggestions based on this
// field.
$class = synonyms_extractor_info($field['type']);
call_user_func(array($class, 'processEntityFieldQuery'), $match, $query, $field, $instance);
if (!empty($tids)) {
$query->entityCondition('entity_id', $tids, 'NOT IN');
}
$result = $query->execute();
if (isset($result['taxonomy_term'])) {
$tids = array_merge($tids, array_keys($result['taxonomy_term']));
}
}
}
$terms = taxonomy_term_load_multiple($tids);
// We have an array of terms whose synonyms look like they could be
// equal to our argument, let us iterate over each term's synonyms to
// actually see if they match.
$argument = mb_strtoupper($argument, 'UTF-8');
foreach ($terms as $term) {
foreach (synonyms_get_term_synonyms($term) as $synonym) {
$synonym = mb_strtoupper($synonym['safe_value'], 'UTF-8');
if ($transform) {
$synonym = str_replace(' ', '-', $synonym);
}
if ($synonym == $argument) {
// Finally we found a synonym that matches our argument. We are
// going to use the corresponding term and there is no point to
// continue searching.
$this->argument->argument = $this->synonyms_get_term_property($term);
$this->argument->validated_title = check_plain(entity_label('taxonomy_term', $term));
return TRUE;
}
}
}
// We haven't found any synonym that matched our argument, so there is
// no term to return.
return FALSE;