options['vocabulary']); if (empty($vocabulary) && $this->options['limit']) { $form['markup'] = array( '#markup' => '
' . t('An invalid vocabulary is selected. Please change it in the options.') . '
', ); return; } switch ($this->options['type']) { case 'synonyms_autocomplete': $tags = array(); if ($this->value) { $result = taxonomy_term_load_multiple($this->value); foreach ($result as $entity_term) { $tags[] = entity_label('taxonomy_term', $entity_term); } } $tags = drupal_implode_tags($tags); $info = $this->synonyms_field_instance(); if ($info['instance']['widget']['type'] == 'synonyms_autocomplete_taxonomy_term') { $widget = $info['instance']['widget']['settings']; } else { $widget = field_info_widget_settings('synonyms_autocomplete_taxonomy_term'); } $autocomplete_path = $widget['synonyms_autocomplete_path']; $size = $widget['size']; $form['value'] = array( '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'), '#type' => 'textfield', '#default_value' => $tags, '#autocomplete_path' => $autocomplete_path . '/' . $this->definition['field_name'] . '/' . $info['instance']['entity_type'] . '/' . $info['instance']['bundle'], '#size' => $size, '#auto_creation' => FALSE, '#attributes' => array('class' => array('synonyms-autocomplete')), '#attached' => array('js' => array( drupal_get_path('module', 'synonyms') . '/js/synonyms-autocomplete.js' => array(), )), ); break; case 'synonyms_select': $info = $this->synonyms_field_instance(); $options = array(); $widget = $info['instance']['widget']['type'] == 'synonyms_select_taxonomy_term' ? $info['instance']['widget']['settings'] : field_info_widget_settings('synonyms_select_taxonomy_term'); foreach ($info['field']['settings']['allowed_values'] as $tree) { if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) { switch ($widget['sort']) { case 'weight': if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'], NULL, TRUE)) { $behavior_implementations = synonyms_behavior_get('select', 'taxonomy_term', field_extract_bundle('taxonomy_term', $vocabulary), TRUE); foreach ($terms as $term) { $options[] = synonyms_select_option_entity($term, 'taxonomy_term', NULL, NULL, array('depth')); foreach ($behavior_implementations as $implementation) { foreach ($implementation['object']->extractSynonyms($term) as $synonym) { $options[] = synonyms_select_option_entity($term, 'taxonomy_term', $synonym, $implementation, array('depth')); } } } } break; case 'name': // TODO: is there any way to leverage DB for the sorting routine? $options = synonyms_select_taxonomy_term_sort_name_options_recursive($vocabulary, $tree['parent']); break; } } } $form['value'] = array( '#type' => 'select', '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'), '#multiple' => TRUE, '#options' => $options, '#size' => min(9, count($options)), '#default_value' => (array) $this->value, '#element_validate' => array('synonyms_select_validate'), ); break; default: parent::value_form($form, $form_state); break; } } function value_validate($form, &$form_state) { switch ($this->options['type']) { case '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; } break; case 'synonyms_select': break; default: parent::value_validate($form, $form_state); break; } } function exposed_validate(&$form, &$form_state) { switch ($this->options['type']) { case '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; } break; case 'synonyms_select': if (empty($this->options['exposed'])) { return; } if (empty($this->options['expose']['identifier'])) { return; } $all_key = array_search('All', $form_state['values'][$this->options['expose']['identifier']]); if ($all_key !== FALSE) { unset($form_state['values'][$this->options['expose']['identifier']][$all_key]); } if (!empty($form_state['values'][$this->options['expose']['identifier']])) { return; } $this->validated_exposed_input = $form_state['values'][$this->options['expose']['identifier']]; break; default: parent::exposed_validate($form, $form_state); break; } } /** * 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(AbstractSynonymsBehavior::COLUMN_SYNONYM_PLACEHOLDER, $tag); } $synonyms = $behavior_implementation['object']->synonymsFind($condition); 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; } /** * Collect info about field and instance that correspond to this filter. * * @return array * Array with the following structure: * - field: (array) Field definition array that corresponds to this filter * - instance: (array) Field instance definition array that corresponds to * this filter */ protected function synonyms_field_instance() { $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); return array( 'field' => $field, 'instance' => $instance, ); } }