Removing translation directories
[project/views.git] / handlers / views_handler_filter.inc
1 <?php
2 /**
3 * @defgroup views_filter_handlers Views' filter handlers
4 * @{
5 * Handlers to tell Views how to filter queries.
6 *
7 * Definition items:
8 * - allow empty: If true, the 'IS NULL' and 'IS NOT NULL' operators become
9 * available as standard operators.
10 * -
11 */
12
13 /**
14 * Base class for filters.
15 */
16 class views_handler_filter extends views_handler {
17 /**
18 * Provide some extra help to get the operator/value easier to use.
19 *
20 * This likely has to be overridden by filters which are more complex
21 * than simple operator/value.
22 */
23 function init(&$view, $options) {
24 parent::init($view, $options);
25
26 $this->operator = $this->options['operator'];
27 $this->value = $this->options['value'];
28
29 // Compatibility: Set use_operator to true if the old way of using
30 // the operator is set and use_operator is NULL (was never set).
31 if (!empty($options['exposed']) && !empty($options['expose']['operator']) && !isset($options['expose']['use_operator'])) {
32 $this->options['expose']['use_operator'] = TRUE;
33 }
34
35 // If there are relationships in the view, allow empty should be true
36 // so that we can do IS NULL checks on items. Not all filters respect
37 // allow empty, but string and numeric do and that covers enough.
38 if ($this->view->display_handler->get_option('relationships')) {
39 $this->definition['allow empty'] = TRUE;
40 }
41 }
42
43 function option_definition() {
44 $options = parent::option_definition();
45
46 $options['operator'] = array('default' => '=');
47 $options['value'] = array('default' => '');
48 $options['group'] = array('default' => '0');
49 $options['exposed'] = array('default' => FALSE);
50 $options['expose'] = array(
51 'contains' => array(
52 'operator' => array('default' => FALSE),
53 'label' => array('default' => '', 'translatable' => TRUE),
54 ),
55 );
56
57 return $options;
58 }
59
60 /**
61 * Display the filter on the administrative summary
62 */
63 function admin_summary() {
64 return check_plain((string) $this->operator) . ' ' . check_plain((string) $this->value);
65 }
66
67 /**
68 * Determine if a filter can be exposed.
69 */
70 function can_expose() { return TRUE; }
71
72 /**
73 * Provide the basic form which calls through to subforms.
74 * If overridden, it is best to call through to the parent,
75 * or to at least make sure all of the functions in this form
76 * are called.
77 */
78 function options_form(&$form, &$form_state) {
79 if ($this->can_expose()) {
80 $this->show_expose_button($form, $form_state);
81 }
82 $form['op_val_start'] = array('#value' => '<div class="clear-block">');
83 $this->show_operator_form($form, $form_state);
84 $this->show_value_form($form, $form_state);
85 $form['op_val_end'] = array('#value' => '</div>');
86 if ($this->can_expose()) {
87 $this->show_expose_form($form, $form_state);
88 }
89 }
90
91 /**
92 * Simple validate handler
93 */
94 function options_validate(&$form, &$form_state) {
95 $this->operator_validate($form, $form_state);
96 $this->value_validate($form, $form_state);
97 if (!empty($this->options['exposed'])) {
98 $this->expose_validate($form, $form_state);
99 }
100
101 }
102
103 /**
104 * Simple submit handler
105 */
106 function options_submit(&$form, &$form_state) {
107 unset($form_state['values']['expose_button']); // don't store this.
108 $this->operator_submit($form, $form_state);
109 $this->value_submit($form, $form_state);
110 if (!empty($this->options['exposed'])) {
111 $this->expose_submit($form, $form_state);
112 }
113 }
114
115 /**
116 * Shortcut to display the operator form.
117 */
118 function show_operator_form(&$form, &$form_state) {
119 $this->operator_form($form, $form_state);
120 $form['operator']['#prefix'] = '<div class="views-left-30">';
121 $form['operator']['#suffix'] = '</div>';
122 }
123
124 /**
125 * Provide a form for setting the operator.
126 *
127 * This may be overridden by child classes, and it must
128 * define $form['operator'];
129 */
130 function operator_form(&$form, &$form_state) {
131 $options = $this->operator_options();
132 if (!empty($options)) {
133 $form['operator'] = array(
134 '#type' => count($options) < 10 ? 'radios' : 'select',
135 '#title' => t('Operator'),
136 '#default_value' => $this->operator,
137 '#options' => $options,
138 );
139 }
140 }
141
142 /**
143 * Provide a list of options for the default operator form.
144 * Should be overridden by classes that don't override operator_form
145 */
146 function operator_options() { return array(); }
147
148 /**
149 * Validate the operator form.
150 */
151 function operator_validate($form, &$form_state) { }
152
153 /**
154 * Perform any necessary changes to the form values prior to storage.
155 * There is no need for this function to actually store the data.
156 */
157 function operator_submit($form, &$form_state) { }
158
159 /**
160 * Shortcut to display the value form.
161 */
162 function show_value_form(&$form, &$form_state) {
163 $this->value_form($form, $form_state);
164 if (empty($this->no_operator)) {
165 $form['value']['#prefix'] = '<div class="views-right-70">' . (isset($form['value']['#prefix']) ? $form['value']['#prefix'] : '');
166 $form['value']['#suffix'] = (isset($form['value']['#suffix']) ? $form['value']['#suffix'] : '') . '</div>';
167 }
168 }
169
170 /**
171 * Provide a form for setting options.
172 *
173 * This should be overridden by all child classes and it must
174 * define $form['value']
175 */
176 function value_form(&$form, &$form_state) { $form['value'] = array(); }
177
178 /**
179 * Validate the options form.
180 */
181 function value_validate($form, &$form_state) { }
182
183 /**
184 * Perform any necessary changes to the form values prior to storage.
185 * There is no need for this function to actually store the data.
186 */
187 function value_submit($form, &$form_state) { }
188
189 /**
190 * Shortcut to display the expose/hide button.
191 */
192 function show_expose_button(&$form, &$form_state) {
193 $form['expose_button'] = array(
194 '#prefix' => '<div class="views-expose clear-block">',
195 '#suffix' => '</div>',
196 );
197 if (empty($this->options['exposed'])) {
198 $form['expose_button']['button'] = array(
199 '#type' => 'submit',
200 '#value' => t('Expose'),
201 '#submit' => array('views_ui_config_item_form_expose'),
202 );
203 $form['expose_button']['markup'] = array(
204 '#prefix' => '<div class="description">',
205 '#value' => t('This item is currently not exposed. If you <strong>expose</strong> it, users will be able to change the filter as they view it.'),
206 '#suffix' => '</div>',
207 );
208 }
209 else {
210 $form['expose_button']['button'] = array(
211 '#type' => 'submit',
212 '#value' => t('Hide'),
213 '#submit' => array('views_ui_config_item_form_expose'),
214 );
215 $form['expose_button']['markup'] = array(
216 '#prefix' => '<div class="description">',
217 '#value' => t('This item is currently exposed. If you <strong>hide</strong> it, users will not be able to change the filter as they view it.'),
218 '#suffix' => '</div>',
219 );
220 }
221 }
222
223 /**
224 * Shortcut to display the exposed options form.
225 */
226 function show_expose_form(&$form, &$form_state) {
227 if (empty($this->options['exposed'])) {
228 return;
229 }
230
231 $form['expose'] = array(
232 '#prefix' => '<div class="views-expose-options clear-block">',
233 '#suffix' => '</div>',
234 );
235 $this->expose_form($form, $form_state);
236
237 // When we click the expose button, we add new gadgets to the form but they
238 // have no data in $_POST so their defaults get wiped out. This prevents
239 // these defaults from getting wiped out. This setting will only be TRUE
240 // during a 2nd pass rerender.
241 if (!empty($form_state['force_expose_options'])) {
242 foreach (element_children($form['expose']) as $id) {
243 if (isset($form['expose'][$id]['#default_value']) && !isset($form['expose'][$id]['#value'])) {
244 $form['expose'][$id]['#value'] = $form['expose'][$id]['#default_value'];
245 }
246 }
247 }
248 }
249
250 /**
251 * Overridable form for exposed filter options.
252 *
253 * If overridden, it is best to call the parent or re-implement
254 * the stuff here.
255 *
256 * Many filters will need to override this in order to provide options
257 * that are nicely tailored to the given filter.
258 */
259 function expose_form(&$form, &$form_state) {
260 $form['expose']['start_left'] = array(
261 '#value' => '<div class="views-left-50">',
262 );
263
264 $this->expose_form_left($form, $form_state);
265
266 $form['expose']['end_left'] = array(
267 '#value' => '</div>',
268 );
269
270 $form['expose']['start_checkboxes'] = array(
271 '#value' => '<div class="form-checkboxes views-left-40 clear-block">',
272 );
273
274 $this->expose_form_right($form, $form_state);
275
276 $form['expose']['end_checkboxes'] = array(
277 '#value' => '</div>',
278 );
279 }
280
281 /**
282 * Handle the 'left' side fo the exposed options form.
283 */
284 function expose_form_left(&$form, &$form_state) {
285 if (!empty($form['operator']['#type'])) {
286 $form['expose']['use_operator'] = array(
287 '#type' => 'checkbox',
288 '#title' => t('Unlock operator'),
289 '#description' => t('When checked, the operator will be exposed to the user'),
290 '#default_value' => !empty($this->options['expose']['use_operator']),
291 );
292 $form['expose']['operator'] = array(
293 '#type' => 'textfield',
294 '#default_value' => $this->options['expose']['operator'],
295 '#title' => t('Operator identifier'),
296 '#size' => 40,
297 '#description' => t('This will appear in the URL after the ? to identify this operator.'),
298 '#process' => array('views_process_dependency'),
299 '#dependency' => array(
300 'edit-options-expose-use-operator' => array(1)
301 ),
302 );
303 }
304 else {
305 $form['expose']['operator'] = array(
306 '#type' => 'value',
307 '#value' => '',
308 );
309 }
310
311 $form['expose']['identifier'] = array(
312 '#type' => 'textfield',
313 '#default_value' => $this->options['expose']['identifier'],
314 '#title' => t('Filter identifier'),
315 '#size' => 40,
316 '#description' => t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'),
317 );
318 $form['expose']['label'] = array(
319 '#type' => 'textfield',
320 '#default_value' => $this->options['expose']['label'],
321 '#title' => t('Label'),
322 '#size' => 40,
323 );
324 }
325
326 /**
327 * Handle the 'right' side fo the exposed options form.
328 */
329 function expose_form_right(&$form, &$form_state) {
330 $form['expose']['optional'] = array(
331 '#type' => 'checkbox',
332 '#title' => t('Optional'),
333 '#description' => t('This exposed filter is optional and will have added options to allow it not to be set.'),
334 '#default_value' => $this->options['expose']['optional'],
335 );
336 if (empty($this->no_single)) {
337 $form['expose']['single'] = array(
338 '#type' => 'checkbox',
339 '#title' => t('Force single'),
340 '#description' => t('Force this exposed filter to accept only one option.'),
341 '#default_value' => $this->options['expose']['single'],
342 );
343 }
344 $form['expose']['remember'] = array(
345 '#type' => 'checkbox',
346 '#title' => t('Remember'),
347 '#description' => t('Remember the last setting the user gave this filter.'),
348 '#default_value' => $this->options['expose']['remember'],
349 );
350 }
351
352 /**
353 * Validate the options form.
354 */
355 function expose_validate($form, &$form_state) {
356 if (empty($this->options['expose']['identifier'])) {
357 if (empty($form_state['values']['options']['expose']['identifier'])) {
358 form_error($form['expose']['identifier'], t('The identifier is required if the filter is exposed.'));
359 }
360 }
361
362 if (!empty($form_state['values']['options']['expose']['identifier']) && $form_state['values']['options']['expose']['identifier'] == 'value') {
363 form_error($form['expose']['identifier'], t('This identifier is not allowed.'));
364 }
365 }
366
367 /**
368 * Perform any necessary changes to the form exposes prior to storage.
369 * There is no need for this function to actually store the data.
370 */
371 function expose_submit($form, &$form_state) { }
372
373 /**
374 * Provide default options for exposed filters.
375 */
376 function expose_options() {
377 $this->options['expose'] = array(
378 'use_operator' => FALSE,
379 'operator' => $this->options['id'] . '_op',
380 'identifier' => $this->options['id'],
381 'label' => $this->ui_name(),
382 'remember' => FALSE,
383 'single' => TRUE,
384 'optional' => TRUE,
385 );
386 }
387
388 /**
389 * Render our chunk of the exposed filter form when selecting
390 *
391 * You can override this if it doesn't do what you expect.
392 */
393 function exposed_form(&$form, &$form_state) {
394 if (empty($this->options['exposed'])) {
395 return;
396 }
397
398 if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator'])) {
399 $operator = $this->options['expose']['operator'];
400 $this->operator_form($form, $form_state);
401 $form[$operator] = $form['operator'];
402
403 if (isset($form[$operator]['#title'])) {
404 unset($form[$operator]['#title']);
405 }
406
407 $this->exposed_translate($form[$operator], 'operator');
408
409 unset($form['operator']);
410 }
411
412 if (!empty($this->options['expose']['identifier'])) {
413 $value = $this->options['expose']['identifier'];
414 $this->value_form($form, $form_state);
415 $form[$value] = $form['value'];
416
417 if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') {
418 unset($form[$value]['#title']);
419 }
420
421 $this->exposed_translate($form[$value], 'value');
422
423 if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) {
424 unset($form[$value]['#default_value']);
425 }
426
427 if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
428 $form[$value]['#default_value'] = 'All';
429 }
430
431 if ($value != 'value') {
432 unset($form['value']);
433 }
434 }
435 }
436
437 /**
438 * Make some translations to a form item to make it more suitable to
439 * exposing.
440 */
441 function exposed_translate(&$form, $type) {
442 if (!isset($form['#type'])) {
443 return;
444 }
445
446 if ($form['#type'] == 'radios') {
447 $form['#type'] = 'select';
448 }
449 // Checkboxes don't work so well in exposed forms due to GET conversions.
450 if ($form['#type'] == 'checkboxes') {
451 if (empty($form['#no_convert']) || !empty($this->options['expose']['single'])) {
452 $form['#type'] = 'select';
453 }
454 if (empty($this->options['expose']['single'])) {
455 $form['#multiple'] = TRUE;
456 }
457 }
458 if (!empty($this->options['expose']['single']) && isset($form['#multiple'])) {
459 unset($form['#multiple']);
460 $form['#size'] = NULL;
461 }
462
463 if ($type == 'value' && !empty($this->options['expose']['optional']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
464 $any_label = variable_get('views_exposed_filter_any_label', 'old_any') == 'old_any' ? t('<Any>') : t('- Any -');
465 $form['#options'] = array('All' => $any_label) + $form['#options'];
466 $form['#default_value'] = 'All';
467 }
468 }
469
470 /**
471 * Tell the renderer about our exposed form. This only needs to be
472 * overridden for particularly complex forms. And maybe not even then.
473 */
474 function exposed_info() {
475 if (empty($this->options['exposed'])) {
476 return;
477 }
478
479 return array(
480 'operator' => $this->options['expose']['operator'],
481 'value' => $this->options['expose']['identifier'],
482 'label' => $this->options['expose']['label'],
483 );
484 }
485
486 /**
487 * Check to see if input from the exposed filters should change
488 * the behavior of this filter.
489 */
490 function accept_exposed_input($input) {
491 if (empty($this->options['exposed'])) {
492 return TRUE;
493 }
494
495
496 if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator']) && isset($input[$this->options['expose']['operator']])) {
497 $this->operator = $input[$this->options['expose']['operator']];
498 }
499
500 if (!empty($this->options['expose']['identifier'])) {
501 $value = $input[$this->options['expose']['identifier']];
502
503 // Various ways to check for the absence of optional input.
504 if (!empty($this->options['expose']['optional'])) {
505 if ($value == 'All' || $value === array()) {
506 return FALSE;
507 }
508
509 if (!empty($this->no_single) && $value === '') {
510 return FALSE;
511 }
512 }
513
514
515 if (isset($value)) {
516 $this->value = $value;
517 if (!empty($this->options['expose']['single'])) {
518 $this->value = array($value);
519 }
520 }
521 else {
522 return FALSE;
523 }
524 }
525
526 return TRUE;
527 }
528
529 function store_exposed_input($input, $status) {
530 if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) {
531 return TRUE;
532 }
533
534 if (empty($this->options['expose']['remember'])) {
535 return;
536 }
537
538 // Figure out which display id is responsible for the filters, so we
539 // know where to look for session stored values.
540 $display_id = ($this->view->display_handler->is_defaulted('filters')) ? 'default' : $this->view->current_display;
541
542 // shortcut test.
543 $operator = !empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator']);
544
545 // false means that we got a setting that means to recuse ourselves,
546 // so we should erase whatever happened to be there.
547 if (!$status && isset($_SESSION['views'][$this->view->name][$display_id])) {
548 $session = &$_SESSION['views'][$this->view->name][$display_id];
549 if ($operator && isset($session[$this->options['expose']['operator']])) {
550 unset($session[$this->options['expose']['operator']]);
551 }
552
553 if (isset($session[$this->options['expose']['identifier']])) {
554 unset($session[$this->options['expose']['identifier']]);
555 }
556 }
557
558 if ($status) {
559 if (!isset($_SESSION['views'][$this->view->name][$display_id])) {
560 $_SESSION['views'][$this->view->name][$display_id] = array();
561 }
562
563 $session = &$_SESSION['views'][$this->view->name][$display_id];
564
565 if ($operator && isset($input[$this->options['expose']['operator']])) {
566 $session[$this->options['expose']['operator']] = $input[$this->options['expose']['operator']];
567 }
568
569 $session[$this->options['expose']['identifier']] = $input[$this->options['expose']['identifier']];
570 }
571 }
572
573 /**
574 * Add this filter to the query.
575 *
576 * Due to the nature of fapi, the value and the operator have an unintended
577 * level of indirection. You will find them in $this->operator
578 * and $this->value respectively.
579 */
580 function query() {
581 $this->ensure_my_table();
582 $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . " '%s'", $this->value);
583 }
584 }
585
586
587 /**
588 * A special handler to take the place of missing or broken handlers.
589 */
590 class views_handler_filter_broken extends views_handler_filter {
591 function ui_name($short = FALSE) {
592 return t('Broken/missing handler');
593 }
594
595 function ensure_my_table() { /* No table to ensure! */ }
596 function query() { /* No query to run */ }
597 function options_form(&$form, &$form_state) {
598 $form['markup'] = array(
599 '#prefix' => '<div class="form-item description">',
600 '#value' => t('The handler for this item is broken or missing and cannot be used. If a module provided the handler and was disabled, re-enabling the module may restore it. Otherwise, you should probably delete this item.'),
601 );
602 }
603
604 /**
605 * Determine if the handler is considered 'broken'
606 */
607 function broken() { return TRUE; }
608 }
609
610
611 /**
612 * @}
613 */