3 * @defgroup views_filter_handlers Views' filter handlers
5 * Handlers to tell Views how to filter queries.
8 * - allow empty: If true, the 'IS NULL' and 'IS NOT NULL' operators become
9 * available as standard operators.
14 * Base class for filters.
16 class views_handler_filter
extends views_handler
{
18 * Provide some extra help to get the operator/value easier to use.
20 * This likely has to be overridden by filters which are more complex
21 * than simple operator/value.
23 function init(&$view, $options) {
24 parent
::init($view, $options);
26 $this->operator
= $this->options
['operator'];
27 $this->value
= $this->options
['value'];
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
;
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
;
43 function option_definition() {
44 $options = parent
::option_definition();
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(
52 'operator' => array('default' => FALSE
),
53 'label' => array('default' => '', 'translatable' => TRUE
),
61 * Display the filter on the administrative summary
63 function admin_summary() {
64 return check_plain((string) $this->operator
) .
' ' .
check_plain((string) $this->value
);
68 * Determine if a filter can be exposed.
70 function can_expose() { return TRUE
; }
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
78 function options_form(&$form, &$form_state) {
79 if ($this->can_expose()) {
80 $this->show_expose_button($form, $form_state);
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);
92 * Simple validate handler
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);
104 * Simple submit handler
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);
116 * Shortcut to display the operator form.
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>';
125 * Provide a form for setting the operator.
127 * This may be overridden by child classes, and it must
128 * define $form['operator'];
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,
143 * Provide a list of options for the default operator form.
144 * Should be overridden by classes that don't override operator_form
146 function operator_options() { return array(); }
149 * Validate the operator form.
151 function operator_validate($form, &$form_state) { }
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.
157 function operator_submit($form, &$form_state) { }
160 * Shortcut to display the value form.
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>';
171 * Provide a form for setting options.
173 * This should be overridden by all child classes and it must
174 * define $form['value']
176 function value_form(&$form, &$form_state) { $form['value'] = array(); }
179 * Validate the options form.
181 function value_validate($form, &$form_state) { }
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.
187 function value_submit($form, &$form_state) { }
190 * Shortcut to display the expose/hide button.
192 function show_expose_button(&$form, &$form_state) {
193 $form['expose_button'] = array(
194 '#prefix' => '<div class="views-expose clear-block">',
195 '#suffix' => '</div>',
197 if (empty($this->options
['exposed'])) {
198 $form['expose_button']['button'] = array(
200 '#value' => t('Expose'),
201 '#submit' => array('views_ui_config_item_form_expose'),
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>',
210 $form['expose_button']['button'] = array(
212 '#value' => t('Hide'),
213 '#submit' => array('views_ui_config_item_form_expose'),
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>',
224 * Shortcut to display the exposed options form.
226 function show_expose_form(&$form, &$form_state) {
227 if (empty($this->options
['exposed'])) {
231 $form['expose'] = array(
232 '#prefix' => '<div class="views-expose-options clear-block">',
233 '#suffix' => '</div>',
235 $this->expose_form($form, $form_state);
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'];
251 * Overridable form for exposed filter options.
253 * If overridden, it is best to call the parent or re-implement
256 * Many filters will need to override this in order to provide options
257 * that are nicely tailored to the given filter.
259 function expose_form(&$form, &$form_state) {
260 $form['expose']['start_left'] = array(
261 '#value' => '<div class="views-left-50">',
264 $this->expose_form_left($form, $form_state);
266 $form['expose']['end_left'] = array(
267 '#value' => '</div>',
270 $form['expose']['start_checkboxes'] = array(
271 '#value' => '<div class="form-checkboxes views-left-40 clear-block">',
274 $this->expose_form_right($form, $form_state);
276 $form['expose']['end_checkboxes'] = array(
277 '#value' => '</div>',
282 * Handle the 'left' side fo the exposed options form.
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']),
292 $form['expose']['operator'] = array(
293 '#type' => 'textfield',
294 '#default_value' => $this->options
['expose']['operator'],
295 '#title' => t('Operator identifier'),
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)
305 $form['expose']['operator'] = array(
311 $form['expose']['identifier'] = array(
312 '#type' => 'textfield',
313 '#default_value' => $this->options
['expose']['identifier'],
314 '#title' => t('Filter identifier'),
316 '#description' => t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'),
318 $form['expose']['label'] = array(
319 '#type' => 'textfield',
320 '#default_value' => $this->options
['expose']['label'],
321 '#title' => t('Label'),
327 * Handle the 'right' side fo the exposed options form.
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'],
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'],
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'],
353 * Validate the options form.
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.'));
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.'));
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.
371 function expose_submit($form, &$form_state) { }
374 * Provide default options for exposed filters.
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(),
389 * Render our chunk of the exposed filter form when selecting
391 * You can override this if it doesn't do what you expect.
393 function exposed_form(&$form, &$form_state) {
394 if (empty($this->options
['exposed'])) {
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'];
403 if (isset($form[$operator]['#title'])) {
404 unset($form[$operator]['#title']);
407 $this->exposed_translate($form[$operator], 'operator');
409 unset($form['operator']);
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'];
417 if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') {
418 unset($form[$value]['#title']);
421 $this->exposed_translate($form[$value], 'value');
423 if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) {
424 unset($form[$value]['#default_value']);
427 if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
428 $form[$value]['#default_value'] = 'All';
431 if ($value != 'value') {
432 unset($form['value']);
438 * Make some translations to a form item to make it more suitable to
441 function exposed_translate(&$form, $type) {
442 if (!isset($form['#type'])) {
446 if ($form['#type'] == 'radios') {
447 $form['#type'] = 'select';
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';
454 if (empty($this->options
['expose']['single'])) {
455 $form['#multiple'] = TRUE
;
458 if (!empty($this->options
['expose']['single']) && isset($form['#multiple'])) {
459 unset($form['#multiple']);
460 $form['#size'] = NULL
;
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';
471 * Tell the renderer about our exposed form. This only needs to be
472 * overridden for particularly complex forms. And maybe not even then.
474 function exposed_info() {
475 if (empty($this->options
['exposed'])) {
480 'operator' => $this->options
['expose']['operator'],
481 'value' => $this->options
['expose']['identifier'],
482 'label' => $this->options
['expose']['label'],
487 * Check to see if input from the exposed filters should change
488 * the behavior of this filter.
490 function accept_exposed_input($input) {
491 if (empty($this->options
['exposed'])) {
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']];
500 if (!empty($this->options
['expose']['identifier'])) {
501 $value = $input[$this->options
['expose']['identifier']];
503 // Various ways to check for the absence of optional input.
504 if (!empty($this->options
['expose']['optional'])) {
505 if ($value == 'All' || $value === array()) {
509 if (!empty($this->no_single
) && $value === '') {
516 $this->value
= $value;
517 if (!empty($this->options
['expose']['single'])) {
518 $this->value
= array($value);
529 function store_exposed_input($input, $status) {
530 if (empty($this->options
['exposed']) || empty($this->options
['expose']['identifier'])) {
534 if (empty($this->options
['expose']['remember'])) {
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
;
543 $operator = !empty($this->options
['expose']['use_operator']) && !empty($this->options
['expose']['operator']);
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']]);
553 if (isset($session[$this->options
['expose']['identifier']])) {
554 unset($session[$this->options
['expose']['identifier']]);
559 if (!isset($_SESSION['views'][$this->view
->name
][$display_id])) {
560 $_SESSION['views'][$this->view
->name
][$display_id] = array();
563 $session = &$_SESSION['views'][$this->view
->name
][$display_id];
565 if ($operator && isset($input[$this->options
['expose']['operator']])) {
566 $session[$this->options
['expose']['operator']] = $input[$this->options
['expose']['operator']];
569 $session[$this->options
['expose']['identifier']] = $input[$this->options
['expose']['identifier']];
574 * Add this filter to the query.
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.
581 $this->ensure_my_table();
582 $this->query
->add_where($this->options
['group'], "$this->table_alias.$this->real_field " .
$this->operator .
" '%s'", $this->value
);
588 * A special handler to take the place of missing or broken handlers.
590 class views_handler_filter_broken
extends views_handler_filter
{
591 function ui_name($short = FALSE
) {
592 return t('Broken/missing handler');
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.'),
605 * Determine if the handler is considered 'broken'
607 function broken() { return TRUE
; }