/[drupal]/contributions/modules/views/handlers/views_handler_filter.inc
ViewVC logotype

Contents of /contributions/modules/views/handlers/views_handler_filter.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


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

  ViewVC Help
Powered by ViewVC 1.1.2