views_handler_filter_term_node_tid.inc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. <?php
  2. /**
  3. * @file
  4. * Definition of views_handler_filter_term_node_tid.
  5. */
  6. /**
  7. * Filter by term id.
  8. *
  9. * @ingroup views_filter_handlers
  10. */
  11. class views_handler_filter_term_node_tid extends views_handler_filter_many_to_one {
  12. // Stores the exposed input for this filter.
  13. var $validated_exposed_input = NULL;
  14. function init(&$view, &$options) {
  15. parent::init($view, $options);
  16. if (!empty($this->definition['vocabulary'])) {
  17. $this->options['vocabulary'] = $this->definition['vocabulary'];
  18. }
  19. // Convert legacy vid option to machine name vocabulary.
  20. if (isset($this->options['vid']) && !empty($this->options['vid']) & empty($this->options['vocabulary'])) {
  21. $vocabularies = taxonomy_get_vocabularies();
  22. $vid = $this->options['vid'];
  23. if (isset($vocabularies[$vid], $vocabularies[$vid]->machine_name)) {
  24. $this->options['vocabulary'] = $vocabularies[$vid]->machine_name;
  25. }
  26. }
  27. }
  28. function has_extra_options() { return TRUE; }
  29. function get_value_options() { /* don't overwrite the value options */ }
  30. function option_definition() {
  31. $options = parent::option_definition();
  32. $options['type'] = array('default' => 'textfield');
  33. $options['limit'] = array('default' => TRUE, 'bool' => TRUE);
  34. $options['vocabulary'] = array('default' => 0);
  35. $options['hierarchy'] = array('default' => 0);
  36. $options['error_message'] = array('default' => TRUE, 'bool' => TRUE);
  37. return $options;
  38. }
  39. function extra_options_form(&$form, &$form_state) {
  40. $vocabularies = taxonomy_get_vocabularies();
  41. $options = array();
  42. foreach ($vocabularies as $voc) {
  43. $options[$voc->machine_name] = check_plain($voc->name);
  44. }
  45. if ($this->options['limit']) {
  46. // We only do this when the form is displayed.
  47. if (empty($this->options['vocabulary'])) {
  48. $first_vocabulary = reset($vocabularies);
  49. $this->options['vocabulary'] = $first_vocabulary->machine_name;
  50. }
  51. if (empty($this->definition['vocabulary'])) {
  52. $form['vocabulary'] = array(
  53. '#type' => 'radios',
  54. '#title' => t('Vocabulary'),
  55. '#options' => $options,
  56. '#description' => t('Select which vocabulary to show terms for in the regular options.'),
  57. '#default_value' => $this->options['vocabulary'],
  58. );
  59. }
  60. }
  61. $form['type'] = array(
  62. '#type' => 'radios',
  63. '#title' => t('Selection type'),
  64. '#options' => array('select' => t('Dropdown'), 'textfield' => t('Autocomplete')),
  65. '#default_value' => $this->options['type'],
  66. );
  67. $form['hierarchy'] = array(
  68. '#type' => 'checkbox',
  69. '#title' => t('Show hierarchy in dropdown'),
  70. '#default_value' => !empty($this->options['hierarchy']),
  71. '#dependency' => array('radio:options[type]' => array('select')),
  72. );
  73. }
  74. function value_form(&$form, &$form_state) {
  75. $vocabulary = taxonomy_vocabulary_machine_name_load($this->options['vocabulary']);
  76. if (empty($vocabulary) && $this->options['limit']) {
  77. $form['markup'] = array(
  78. '#markup' => '<div class="form-item">' . t('An invalid vocabulary is selected. Please change it in the options.') . '</div>',
  79. );
  80. return;
  81. }
  82. if ($this->options['type'] == 'textfield') {
  83. $default = '';
  84. if ($this->value) {
  85. $result = taxonomy_term_load_multiple($this->value);
  86. foreach ($result as $entity_term) {
  87. if ($default) {
  88. $default .= ', ';
  89. }
  90. $default .= entity_label('taxonomy_term', $entity_term);
  91. }
  92. }
  93. $form['value'] = array(
  94. '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'),
  95. '#type' => 'textfield',
  96. '#default_value' => $default,
  97. );
  98. if ($this->options['limit']) {
  99. $form['value']['#autocomplete_path'] = 'admin/views/ajax/autocomplete/taxonomy/' . $vocabulary->vid;
  100. }
  101. }
  102. else {
  103. if (!empty($this->options['hierarchy']) && $this->options['limit']) {
  104. $tree = taxonomy_get_tree($vocabulary->vid, 0, NULL, TRUE);
  105. $options = array();
  106. if ($tree) {
  107. // Translation system needs full entity objects, so we have access to label.
  108. foreach ($tree as $term) {
  109. $choice = new stdClass();
  110. $choice->option = array($term->tid => str_repeat('-', $term->depth) . entity_label('taxonomy_term', $term));
  111. $options[] = $choice;
  112. }
  113. }
  114. }
  115. else {
  116. $options = array();
  117. $query = db_select('taxonomy_term_data', 'td');
  118. $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid');
  119. $query->fields('td');
  120. $query->orderby('tv.weight');
  121. $query->orderby('tv.name');
  122. $query->orderby('td.weight');
  123. $query->orderby('td.name');
  124. $query->addTag('term_access');
  125. if ($this->options['limit']) {
  126. $query->condition('tv.machine_name', $vocabulary->machine_name);
  127. }
  128. $result = $query->execute();
  129. $tids = array();
  130. foreach ($result as $term) {
  131. $tids[] = $term->tid;
  132. }
  133. $entities = taxonomy_term_load_multiple($tids);
  134. foreach ($entities as $entity_term) {
  135. $options[$entity_term->tid] = entity_label('taxonomy_term', $entity_term);
  136. }
  137. }
  138. $default_value = (array) $this->value;
  139. if (!empty($form_state['exposed'])) {
  140. $identifier = $this->options['expose']['identifier'];
  141. if (!empty($this->options['expose']['reduce'])) {
  142. $options = $this->reduce_value_options($options);
  143. if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
  144. $default_value = array();
  145. }
  146. }
  147. if (empty($this->options['expose']['multiple'])) {
  148. if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
  149. $default_value = 'All';
  150. }
  151. elseif (empty($default_value)) {
  152. $keys = array_keys($options);
  153. $default_value = array_shift($keys);
  154. }
  155. // Due to #1464174 there is a chance that array('') was saved in the admin ui.
  156. // Let's choose a safe default value.
  157. elseif ($default_value == array('')) {
  158. $default_value = 'All';
  159. }
  160. else {
  161. $copy = $default_value;
  162. $default_value = array_shift($copy);
  163. }
  164. }
  165. }
  166. $form['value'] = array(
  167. '#type' => 'select',
  168. '#title' => $this->options['limit'] ? t('Select terms from vocabulary @voc', array('@voc' => $vocabulary->name)) : t('Select terms'),
  169. '#multiple' => TRUE,
  170. '#options' => $options,
  171. '#size' => min(9, count($options)),
  172. '#default_value' => $default_value,
  173. );
  174. if (!empty($form_state['exposed']) && isset($identifier) && !isset($form_state['input'][$identifier])) {
  175. $form_state['input'][$identifier] = $default_value;
  176. }
  177. }
  178. if (empty($form_state['exposed'])) {
  179. // Retain the helper option
  180. $this->helper->options_form($form, $form_state);
  181. // Show help text if not exposed to end users.
  182. $form['value']['#description'] = t('Leave blank for all. Otherwise, the first selected term will be the default instead of "Any".');
  183. }
  184. }
  185. function value_validate($form, &$form_state) {
  186. // We only validate if they've chosen the text field style.
  187. if ($this->options['type'] != 'textfield') {
  188. return;
  189. }
  190. $values = drupal_explode_tags($form_state['values']['options']['value']);
  191. $tids = $this->validate_term_strings($form['value'], $values);
  192. if ($tids) {
  193. $form_state['values']['options']['value'] = $tids;
  194. }
  195. }
  196. function accept_exposed_input($input) {
  197. if (empty($this->options['exposed'])) {
  198. return TRUE;
  199. }
  200. // We need to know the operator, which is normally set in
  201. // views_handler_filter::accept_exposed_input(), before we actually call
  202. // the parent version of ourselves.
  203. if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator_id']) && isset($input[$this->options['expose']['operator_id']])) {
  204. $this->operator = $input[$this->options['expose']['operator_id']];
  205. }
  206. // If view is an attachment and is inheriting exposed filters, then assume
  207. // exposed input has already been validated
  208. if (!empty($this->view->is_attachment) && $this->view->display_handler->uses_exposed()) {
  209. $this->validated_exposed_input = (array) $this->view->exposed_raw_input[$this->options['expose']['identifier']];
  210. }
  211. // If we're checking for EMPTY or NOT, we don't need any input, and we can
  212. // say that our input conditions are met by just having the right operator.
  213. if ($this->operator == 'empty' || $this->operator == 'not empty') {
  214. return TRUE;
  215. }
  216. // If it's non-required and there's no value don't bother filtering.
  217. if (!$this->options['expose']['required'] && empty($this->validated_exposed_input)) {
  218. return FALSE;
  219. }
  220. $rc = parent::accept_exposed_input($input);
  221. if ($rc) {
  222. // If we have previously validated input, override.
  223. if (!$this->is_a_group() && isset($this->validated_exposed_input)) {
  224. $this->value = $this->validated_exposed_input;
  225. }
  226. }
  227. return $rc;
  228. }
  229. function exposed_validate(&$form, &$form_state) {
  230. if (empty($this->options['exposed'])) {
  231. return;
  232. }
  233. $identifier = $this->options['expose']['identifier'];
  234. // We only validate if they've chosen the text field style.
  235. if ($this->options['type'] != 'textfield') {
  236. if ($form_state['values'][$identifier] != 'All') {
  237. $this->validated_exposed_input = (array) $form_state['values'][$identifier];
  238. }
  239. return;
  240. }
  241. if (empty($this->options['expose']['identifier'])) {
  242. return;
  243. }
  244. $values = drupal_explode_tags($form_state['values'][$identifier]);
  245. $tids = $this->validate_term_strings($form[$identifier], $values);
  246. if ($tids) {
  247. $this->validated_exposed_input = $tids;
  248. }
  249. }
  250. /**
  251. * Validate the user string. Since this can come from either the form
  252. * or the exposed filter, this is abstracted out a bit so it can
  253. * handle the multiple input sources.
  254. *
  255. * @param $form
  256. * The form which is used, either the views ui or the exposed filters.
  257. * @param $values
  258. * The taxonomy names which will be converted to tids.
  259. *
  260. * @return array
  261. * The taxonomy ids fo all validated terms.
  262. */
  263. function validate_term_strings(&$form, $values) {
  264. if (empty($values)) {
  265. return array();
  266. }
  267. $tids = array();
  268. $names = array();
  269. $missing = array();
  270. foreach ($values as $value) {
  271. $missing[strtolower($value)] = TRUE;
  272. $names[] = $value;
  273. }
  274. if (!$names) {
  275. return FALSE;
  276. }
  277. $query = db_select('taxonomy_term_data', 'td');
  278. $query->innerJoin('taxonomy_vocabulary', 'tv', 'td.vid = tv.vid');
  279. $query->fields('td');
  280. $query->condition('td.name', $names);
  281. $query->condition('tv.machine_name', $this->options['vocabulary']);
  282. $query->addTag('term_access');
  283. $result = $query->execute();
  284. foreach ($result as $term) {
  285. unset($missing[strtolower($term->name)]);
  286. $tids[] = $term->tid;
  287. }
  288. if ($missing && !empty($this->options['error_message'])) {
  289. form_error($form, format_plural(count($missing), 'Unable to find term: @terms', 'Unable to find terms: @terms', array('@terms' => implode(', ', array_keys($missing)))));
  290. }
  291. elseif ($missing && empty($this->options['error_message'])) {
  292. $tids = array(0);
  293. }
  294. return $tids;
  295. }
  296. function value_submit($form, &$form_state) {
  297. // prevent array_filter from messing up our arrays in parent submit.
  298. }
  299. function expose_form(&$form, &$form_state) {
  300. parent::expose_form($form, $form_state);
  301. if ($this->options['type'] != 'select') {
  302. unset($form['expose']['reduce']);
  303. }
  304. $form['error_message'] = array(
  305. '#type' => 'checkbox',
  306. '#title' => t('Display error message'),
  307. '#default_value' => !empty($this->options['error_message']),
  308. );
  309. }
  310. function admin_summary() {
  311. // set up $this->value_options for the parent summary
  312. $this->value_options = array();
  313. if ($this->value) {
  314. $this->value = array_filter($this->value);
  315. $result = taxonomy_term_load_multiple($this->value);
  316. foreach ($result as $entity_term) {
  317. $this->value_options[$entity_term->tid] = entity_label('taxonomy_term', $entity_term);
  318. }
  319. }
  320. return parent::admin_summary();
  321. }
  322. }