views_handler_filter_in_operator.inc

  1. 3.x handlers/views_handler_filter_in_operator.inc
  2. 2.x handlers/views_handler_filter_in_operator.inc

Definition of views_handler_filter_in_operator.

File

handlers/views_handler_filter_in_operator.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Definition of views_handler_filter_in_operator.
  5. */
  6. /**
  7. * Simple filter to handle matching of multiple options selectable via checkboxes
  8. *
  9. * Definition items:
  10. * - options callback: The function to call in order to generate the value options. If omitted, the options 'Yes' and 'No' will be used.
  11. * - options arguments: An array of arguments to pass to the options callback.
  12. *
  13. * @ingroup views_filter_handlers
  14. */
  15. class views_handler_filter_in_operator extends views_handler_filter {
  16. var $value_form_type = 'checkboxes';
  17. /**
  18. * @var array
  19. * Stores all operations which are available on the form.
  20. */
  21. var $value_options = NULL;
  22. function construct() {
  23. parent::construct();
  24. $this->value_title = t('Options');
  25. $this->value_options = NULL;
  26. }
  27. /**
  28. * Child classes should be used to override this function and set the
  29. * 'value options', unless 'options callback' is defined as a valid function
  30. * or static public method to generate these values.
  31. *
  32. * This can use a guard to be used to reduce database hits as much as
  33. * possible.
  34. *
  35. * @return
  36. * Return the stored values in $this->value_options if someone expects it.
  37. */
  38. function get_value_options() {
  39. if (isset($this->value_options)) {
  40. return;
  41. }
  42. if (isset($this->definition['options callback']) && is_callable($this->definition['options callback'])) {
  43. if (isset($this->definition['options arguments']) && is_array($this->definition['options arguments'])) {
  44. $this->value_options = call_user_func_array($this->definition['options callback'], $this->definition['options arguments']);
  45. }
  46. else {
  47. $this->value_options = call_user_func($this->definition['options callback']);
  48. }
  49. }
  50. else {
  51. $this->value_options = array(t('Yes'), t('No'));
  52. }
  53. return $this->value_options;
  54. }
  55. function expose_options() {
  56. parent::expose_options();
  57. $this->options['expose']['reduce'] = FALSE;
  58. }
  59. function expose_form(&$form, &$form_state) {
  60. parent::expose_form($form, $form_state);
  61. $form['expose']['reduce'] = array(
  62. '#type' => 'checkbox',
  63. '#title' => t('Limit list to selected items'),
  64. '#description' => t('If checked, the only items presented to the user will be the ones selected here.'),
  65. '#default_value' => !empty($this->options['expose']['reduce']), // safety
  66. );
  67. }
  68. function option_definition() {
  69. $options = parent::option_definition();
  70. $options['operator']['default'] = 'in';
  71. $options['value']['default'] = array();
  72. $options['expose']['contains']['reduce'] = array('default' => FALSE, 'bool' => TRUE);
  73. return $options;
  74. }
  75. /**
  76. * This kind of construct makes it relatively easy for a child class
  77. * to add or remove functionality by overriding this function and
  78. * adding/removing items from this array.
  79. */
  80. function operators() {
  81. $operators = array(
  82. 'in' => array(
  83. 'title' => t('Is one of'),
  84. 'short' => t('in'),
  85. 'short_single' => t('='),
  86. 'method' => 'op_simple',
  87. 'values' => 1,
  88. ),
  89. 'not in' => array(
  90. 'title' => t('Is not one of'),
  91. 'short' => t('not in'),
  92. 'short_single' => t('<>'),
  93. 'method' => 'op_simple',
  94. 'values' => 1,
  95. ),
  96. );
  97. // if the definition allows for the empty operator, add it.
  98. if (!empty($this->definition['allow empty'])) {
  99. $operators += array(
  100. 'empty' => array(
  101. 'title' => t('Is empty (NULL)'),
  102. 'method' => 'op_empty',
  103. 'short' => t('empty'),
  104. 'values' => 0,
  105. ),
  106. 'not empty' => array(
  107. 'title' => t('Is not empty (NOT NULL)'),
  108. 'method' => 'op_empty',
  109. 'short' => t('not empty'),
  110. 'values' => 0,
  111. ),
  112. );
  113. }
  114. return $operators;
  115. }
  116. /**
  117. * Build strings from the operators() for 'select' options
  118. */
  119. function operator_options($which = 'title') {
  120. $options = array();
  121. foreach ($this->operators() as $id => $info) {
  122. $options[$id] = $info[$which];
  123. }
  124. return $options;
  125. }
  126. function operator_values($values = 1) {
  127. $options = array();
  128. foreach ($this->operators() as $id => $info) {
  129. if (isset($info['values']) && $info['values'] == $values) {
  130. $options[] = $id;
  131. }
  132. }
  133. return $options;
  134. }
  135. function value_form(&$form, &$form_state) {
  136. $form['value'] = array();
  137. $options = array();
  138. if (empty($form_state['exposed'])) {
  139. // Add a select all option to the value form.
  140. $options = array('all' => t('Select all'));
  141. }
  142. $this->get_value_options();
  143. $options += $this->value_options;
  144. $default_value = (array) $this->value;
  145. $which = 'all';
  146. if (!empty($form['operator'])) {
  147. $source = ($form['operator']['#type'] == 'radios') ? 'radio:options[operator]' : 'edit-options-operator';
  148. }
  149. if (!empty($form_state['exposed'])) {
  150. $identifier = $this->options['expose']['identifier'];
  151. if (empty($this->options['expose']['use_operator']) || empty($this->options['expose']['operator_id'])) {
  152. // exposed and locked.
  153. $which = in_array($this->operator, $this->operator_values(1)) ? 'value' : 'none';
  154. }
  155. else {
  156. $source = 'edit-' . drupal_html_id($this->options['expose']['operator_id']);
  157. }
  158. if (!empty($this->options['expose']['reduce'])) {
  159. $options = $this->reduce_value_options();
  160. if (!empty($this->options['expose']['multiple']) && empty($this->options['expose']['required'])) {
  161. $default_value = array();
  162. }
  163. }
  164. if (empty($this->options['expose']['multiple'])) {
  165. if (empty($this->options['expose']['required']) && (empty($default_value) || !empty($this->options['expose']['reduce']))) {
  166. $default_value = 'All';
  167. }
  168. elseif (empty($default_value)) {
  169. $keys = array_keys($options);
  170. $default_value = array_shift($keys);
  171. }
  172. else {
  173. $copy = $default_value;
  174. $default_value = array_shift($copy);
  175. }
  176. }
  177. }
  178. if ($which == 'all' || $which == 'value') {
  179. $form['value'] = array(
  180. '#type' => $this->value_form_type,
  181. '#title' => $this->value_title,
  182. '#options' => $options,
  183. '#default_value' => $default_value,
  184. // These are only valid for 'select' type, but do no harm to checkboxes.
  185. '#multiple' => TRUE,
  186. '#size' => count($options) > 8 ? 8 : count($options),
  187. );
  188. if (!empty($form_state['exposed']) && !isset($form_state['input'][$identifier])) {
  189. $form_state['input'][$identifier] = $default_value;
  190. }
  191. if ($which == 'all') {
  192. if (empty($form_state['exposed']) && (in_array($this->value_form_type, array('checkbox', 'checkboxes', 'radios', 'select')))) {
  193. $form['value']['#prefix'] = '<div id="edit-options-value-wrapper">';
  194. $form['value']['#suffix'] = '</div>';
  195. }
  196. $form['value']['#dependency'] = array($source => $this->operator_values(1));
  197. }
  198. }
  199. }
  200. /**
  201. * When using exposed filters, we may be required to reduce the set.
  202. */
  203. function reduce_value_options($input = NULL) {
  204. if (!isset($input)) {
  205. $input = $this->value_options;
  206. }
  207. // Because options may be an array of strings, or an array of mixed arrays
  208. // and strings (optgroups) or an array of objects, we have to
  209. // step through and handle each one individually.
  210. $options = array();
  211. foreach ($input as $id => $option) {
  212. if (is_array($option)) {
  213. $options[$id] = $this->reduce_value_options($option);
  214. continue;
  215. }
  216. elseif (is_object($option)) {
  217. $keys = array_keys($option->option);
  218. $key = array_shift($keys);
  219. if (isset($this->options['value'][$key])) {
  220. $options[$id] = $option;
  221. }
  222. }
  223. elseif (isset($this->options['value'][$id])) {
  224. $options[$id] = $option;
  225. }
  226. }
  227. return $options;
  228. }
  229. function accept_exposed_input($input) {
  230. // A very special override because the All state for this type of
  231. // filter could have a default:
  232. if (empty($this->options['exposed'])) {
  233. return TRUE;
  234. }
  235. // If this is non-multiple and non-required, then this filter will
  236. // participate, but using the default settings, *if* 'limit is true.
  237. if (empty($this->options['expose']['multiple']) && empty($this->options['expose']['required']) && !empty($this->options['expose']['limit'])) {
  238. $identifier = $this->options['expose']['identifier'];
  239. if ($input[$identifier] == 'All') {
  240. return TRUE;
  241. }
  242. }
  243. return parent::accept_exposed_input($input);
  244. }
  245. function value_submit($form, &$form_state) {
  246. // Drupal's FAPI system automatically puts '0' in for any checkbox that
  247. // was not set, and the key to the checkbox if it is set.
  248. // Unfortunately, this means that if the key to that checkbox is 0,
  249. // we are unable to tell if that checkbox was set or not.
  250. // Luckily, the '#value' on the checkboxes form actually contains
  251. // *only* a list of checkboxes that were set, and we can use that
  252. // instead.
  253. $form_state['values']['options']['value'] = $form['value']['#value'];
  254. }
  255. function admin_summary() {
  256. if ($this->is_a_group()) {
  257. return t('grouped');
  258. }
  259. if (!empty($this->options['exposed'])) {
  260. return t('exposed');
  261. }
  262. $info = $this->operators();
  263. $this->get_value_options();
  264. if (!is_array($this->value)) {
  265. return;
  266. }
  267. $operator = check_plain($info[$this->operator]['short']);
  268. $values = '';
  269. if (in_array($this->operator, $this->operator_values(1))) {
  270. // Remove every element which is not known.
  271. foreach ($this->value as $value) {
  272. if (!isset($this->value_options[$value])) {
  273. unset($this->value[$value]);
  274. }
  275. }
  276. // Choose different kind of ouput for 0, a single and multiple values.
  277. if (count($this->value) == 0) {
  278. $values = t('Unknown');
  279. }
  280. else if (count($this->value) == 1) {
  281. // If any, use the 'single' short name of the operator instead.
  282. if (isset($info[$this->operator]['short_single'])) {
  283. $operator = check_plain($info[$this->operator]['short_single']);
  284. }
  285. $keys = $this->value;
  286. $value = array_shift($keys);
  287. if (isset($this->value_options[$value])) {
  288. $values = check_plain($this->value_options[$value]);
  289. }
  290. else {
  291. $values = '';
  292. }
  293. }
  294. else {
  295. foreach ($this->value as $value) {
  296. if ($values !== '') {
  297. $values .= ', ';
  298. }
  299. if (drupal_strlen($values) > 8) {
  300. $values .= '...';
  301. break;
  302. }
  303. if (isset($this->value_options[$value])) {
  304. $values .= check_plain($this->value_options[$value]);
  305. }
  306. }
  307. }
  308. }
  309. return $operator . (($values !== '') ? ' ' . $values : '');
  310. }
  311. function query() {
  312. $info = $this->operators();
  313. if (!empty($info[$this->operator]['method'])) {
  314. $this->{$info[$this->operator]['method']}();
  315. }
  316. }
  317. function op_simple() {
  318. if (empty($this->value)) {
  319. return;
  320. }
  321. $this->ensure_my_table();
  322. // We use array_values() because the checkboxes keep keys and that can cause
  323. // array addition problems.
  324. $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", array_values($this->value), $this->operator);
  325. }
  326. function op_empty() {
  327. $this->ensure_my_table();
  328. if ($this->operator == 'empty') {
  329. $operator = "IS NULL";
  330. }
  331. else {
  332. $operator = "IS NOT NULL";
  333. }
  334. $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field", NULL, $operator);
  335. }
  336. function validate() {
  337. $this->get_value_options();
  338. $errors = array();
  339. // If the operator is an operator which doesn't require a value, there is
  340. // no need for additional validation.
  341. if (in_array($this->operator, $this->operator_values(0))) {
  342. return array();
  343. }
  344. if (!in_array($this->operator, $this->operator_values(1))) {
  345. $errors[] = t('The operator is invalid on filter: @filter.', array('@filter' => $this->ui_name(TRUE)));
  346. }
  347. if (is_array($this->value)) {
  348. if (!isset($this->value_options)) {
  349. // Don't validate if there are none value options provided, for example for special handlers.
  350. return $errors;
  351. }
  352. if ($this->options['exposed'] && !$this->options['expose']['required'] && empty($this->value)) {
  353. // Don't validate if the field is exposed and no default value is provided.
  354. return $errors;
  355. }
  356. // Some filter_in_operator usage uses optgroups forms, so flatten it.
  357. $flat_options = form_options_flatten($this->value_options, TRUE);
  358. // Remove every element which is not known.
  359. foreach ($this->value as $value) {
  360. if (!isset($flat_options[$value])) {
  361. unset($this->value[$value]);
  362. }
  363. }
  364. // Choose different kind of ouput for 0, a single and multiple values.
  365. if (count($this->value) == 0) {
  366. $errors[] = t('No valid values found on filter: @filter.', array('@filter' => $this->ui_name(TRUE)));
  367. }
  368. }
  369. elseif (!empty($this->value) && ($this->operator == 'in' || $this->operator == 'not in')) {
  370. $errors[] = t('The value @value is not an array for @operator on filter: @filter', array('@value' => views_var_export($this->value), '@operator' => $this->operator, '@filter' => $this->ui_name(TRUE)));
  371. }
  372. return $errors;
  373. }
  374. }