synonyms_views_handler_filter_term_tid.inc 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. <?php
  2. /**
  3. * @file
  4. * Definition of synonyms_views_handler_filter_term_tid class.
  5. */
  6. /**
  7. * Synonyms friendly taxonomy filter handler.
  8. */
  9. class synonyms_views_handler_filter_term_tid extends views_handler_filter_term_node_tid {
  10. function extra_options_form(&$form, &$form_state) {
  11. parent::extra_options_form($form, $form_state);
  12. $form['type']['#options']['synonyms_autocomplete'] = t('Synonyms friendly autocomplete');
  13. $form['type']['#options']['synonyms_select'] = t('Synonyms friendly select');
  14. }
  15. function value_form(&$form, &$form_state) {
  16. $vocabulary = taxonomy_vocabulary_machine_name_load($this->options['vocabulary']);
  17. if (empty($vocabulary) && $this->options['limit']) {
  18. $form['markup'] = array(
  19. '#markup' => '<div class="form-item">' . t('An invalid vocabulary is selected. Please change it in the options.') . '</div>',
  20. );
  21. return;
  22. }
  23. switch ($this->options['type']) {
  24. case 'synonyms_autocomplete':
  25. $tags = array();
  26. if ($this->value) {
  27. $result = taxonomy_term_load_multiple($this->value);
  28. foreach ($result as $entity_term) {
  29. $tags[] = entity_label('taxonomy_term', $entity_term);
  30. }
  31. }
  32. $tags = drupal_implode_tags($tags);
  33. $info = $this->synonyms_field_instance();
  34. if ($info['instance']['widget']['type'] == 'synonyms_autocomplete_taxonomy_term') {
  35. $widget = $info['instance']['widget']['settings'];
  36. }
  37. else {
  38. $widget = field_info_widget_settings('synonyms_autocomplete_taxonomy_term');
  39. }
  40. $autocomplete_path = $widget['synonyms_autocomplete_path'];
  41. $size = $widget['size'];
  42. $form['value'] = array(
  43. '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'),
  44. '#type' => 'textfield',
  45. '#default_value' => $tags,
  46. '#autocomplete_path' => $autocomplete_path . '/' . $this->definition['field_name'] . '/' . $info['instance']['entity_type'] . '/' . $info['instance']['bundle'],
  47. '#size' => $size,
  48. '#auto_creation' => FALSE,
  49. '#attributes' => array('class' => array('synonyms-autocomplete')),
  50. '#attached' => array('js' => array(
  51. drupal_get_path('module', 'synonyms') . '/js/synonyms-autocomplete.js' => array(),
  52. )),
  53. );
  54. break;
  55. case 'synonyms_select':
  56. $info = $this->synonyms_field_instance();
  57. $options = array();
  58. $widget = $info['instance']['widget']['type'] == 'synonyms_select_taxonomy_term' ? $info['instance']['widget']['settings'] : field_info_widget_settings('synonyms_select_taxonomy_term');
  59. foreach ($info['field']['settings']['allowed_values'] as $tree) {
  60. if ($vocabulary = taxonomy_vocabulary_machine_name_load($tree['vocabulary'])) {
  61. switch ($widget['sort']) {
  62. case 'weight':
  63. if ($terms = taxonomy_get_tree($vocabulary->vid, $tree['parent'], NULL, TRUE)) {
  64. $behavior_implementations = synonyms_behavior_get('select', 'taxonomy_term', field_extract_bundle('taxonomy_term', $vocabulary), TRUE);
  65. foreach ($terms as $term) {
  66. $options[] = synonyms_select_option_entity($term, 'taxonomy_term', NULL, NULL, array('depth'));
  67. foreach ($behavior_implementations as $implementation) {
  68. foreach ($implementation['object']->extractSynonyms($term) as $synonym) {
  69. $options[] = synonyms_select_option_entity($term, 'taxonomy_term', $synonym, $implementation, array('depth'));
  70. }
  71. }
  72. }
  73. }
  74. break;
  75. case 'name':
  76. // TODO: is there any way to leverage DB for the sorting routine?
  77. $options = synonyms_select_taxonomy_term_sort_name_options_recursive($vocabulary, $tree['parent']);
  78. break;
  79. }
  80. }
  81. }
  82. $form['value'] = array(
  83. '#type' => 'select',
  84. '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'),
  85. '#multiple' => TRUE,
  86. '#options' => $options,
  87. '#size' => min(9, count($options)),
  88. '#default_value' => (array) $this->value,
  89. '#element_validate' => array('synonyms_select_validate'),
  90. );
  91. break;
  92. default:
  93. parent::value_form($form, $form_state);
  94. break;
  95. }
  96. }
  97. function value_validate($form, &$form_state) {
  98. switch ($this->options['type']) {
  99. case 'synonyms_autocomplete':
  100. $values = drupal_explode_tags($form_state['values']['options']['value']);
  101. $tids = $this->synonyms_validate_term_strings($form['value'], $values);
  102. if ($tids) {
  103. $form_state['values']['options']['value'] = $tids;
  104. }
  105. break;
  106. case 'synonyms_select':
  107. break;
  108. default:
  109. parent::value_validate($form, $form_state);
  110. break;
  111. }
  112. }
  113. function exposed_validate(&$form, &$form_state) {
  114. switch ($this->options['type']) {
  115. case 'synonyms_autocomplete':
  116. if (empty($this->options['exposed'])) {
  117. return;
  118. }
  119. if (empty($this->options['expose']['identifier'])) {
  120. return;
  121. }
  122. $identifier = $this->options['expose']['identifier'];
  123. $values = drupal_explode_tags($form_state['values'][$identifier]);
  124. $tids = $this->synonyms_validate_term_strings($form[$identifier], $values);
  125. if ($tids) {
  126. $this->validated_exposed_input = $tids;
  127. }
  128. break;
  129. case 'synonyms_select':
  130. if (empty($this->options['exposed'])) {
  131. return;
  132. }
  133. if (empty($this->options['expose']['identifier'])) {
  134. return;
  135. }
  136. $all_key = array_search('All', $form_state['values'][$this->options['expose']['identifier']]);
  137. if ($all_key !== FALSE) {
  138. unset($form_state['values'][$this->options['expose']['identifier']][$all_key]);
  139. }
  140. if (!empty($form_state['values'][$this->options['expose']['identifier']])) {
  141. return;
  142. }
  143. $this->validated_exposed_input = $form_state['values'][$this->options['expose']['identifier']];
  144. break;
  145. default:
  146. parent::exposed_validate($form, $form_state);
  147. break;
  148. }
  149. }
  150. /**
  151. * Validate the user string.
  152. *
  153. * In a great extend it does the same job as parent::validate_term_strings(),
  154. * just that this implementation is synonyms-aware.
  155. *
  156. * @param $element
  157. * The form element which is used, either the views ui or the exposed
  158. * filters.
  159. * @param $values
  160. * The taxonomy names/synonyms which will be converted to tids.
  161. *
  162. * @return array
  163. * The taxonomy ids for all validated terms.
  164. */
  165. protected function synonyms_validate_term_strings($element, $values) {
  166. if (empty($values)) {
  167. return array();
  168. }
  169. $values = array_map('drupal_strtolower', $values);
  170. $missing = array_flip($values);
  171. $tids = array();
  172. $vocabulary = taxonomy_vocabulary_machine_name_load($this->options['vocabulary']);
  173. // Firstly looking up the entered tags as if they were term names. Then,
  174. // the remaining tags are looked up as if they were synonyms of terms.
  175. // Lastly, if any tags are left at this point, we mark form validation
  176. // error.
  177. $query = db_select('taxonomy_term_data', 'td');
  178. $query->fields('td', array('tid', 'name'));
  179. $query->condition('td.vid', $vocabulary->vid);
  180. $query->condition('td.name', $values);
  181. $query->addTag('term_access');
  182. $result = $query->execute();
  183. foreach ($result as $term) {
  184. unset($missing[drupal_strtolower($term->name)]);
  185. $tids[] = $term->tid;
  186. }
  187. $behavior_implementations = synonyms_behavior_get('autocomplete', 'taxonomy_term', $vocabulary->machine_name, TRUE);
  188. foreach ($behavior_implementations as $behavior_implementation) {
  189. if (!empty($missing)) {
  190. $condition = db_or();
  191. foreach ($missing as $tag => $v) {
  192. $condition->condition(AbstractSynonymsBehavior::COLUMN_SYNONYM_PLACEHOLDER, $tag);
  193. }
  194. $synonyms = $behavior_implementation['object']->synonymsFind($condition);
  195. foreach ($synonyms as $synonym) {
  196. $synonym->synonym = drupal_strtolower($synonym->synonym);
  197. unset($missing[$synonym->synonym]);
  198. $tids[] = $synonym->entity_id;
  199. }
  200. }
  201. }
  202. if (!empty($missing) && !empty($this->options['error_message'])) {
  203. form_error($element, format_plural(count($missing), 'Unable to find term: @terms', 'Unable to find terms: @terms', array('@terms' => implode(', ', array_keys($missing)))));
  204. }
  205. return $tids;
  206. }
  207. /**
  208. * Collect info about field and instance that correspond to this filter.
  209. *
  210. * @return array
  211. * Array with the following structure:
  212. * - field: (array) Field definition array that corresponds to this filter
  213. * - instance: (array) Field instance definition array that corresponds to
  214. * this filter
  215. */
  216. protected function synonyms_field_instance() {
  217. $entity_type_base_table = $this->view->base_table;
  218. // TODO: it would be nice to consider the existence of relationships, but
  219. // I just couldn't figure it out at that time.
  220. $entity_info = entity_get_info();
  221. $field_entity_type = FALSE;
  222. $field = field_info_field($this->definition['field_name']);
  223. foreach ($field['bundles'] as $entity_type => $bundles) {
  224. if ($entity_info[$entity_type]['base table'] == $entity_type_base_table) {
  225. $field_entity_type = $entity_type;
  226. break;
  227. }
  228. }
  229. if (!$field_entity_type) {
  230. // Seems like we failed to determine the entity type which is used for
  231. // this field in the view. Well, it's not a fatal fail, we'll just use
  232. // whatever then.
  233. $field_entity_type = array_keys($field['bundles']);
  234. $field_entity_type = $field_entity_type[0];
  235. }
  236. // We just grab the first instance of this field within the determined
  237. // entity type.
  238. $bundle = $field['bundles'][$field_entity_type][0];
  239. $instance = field_info_instance($field_entity_type, $field['field_name'], $bundle);
  240. return array(
  241. 'field' => $field,
  242. 'instance' => $instance,
  243. );
  244. }
  245. }