operator = $this->options['operator']; $this->value = $this->options['value']; // Compatibility: Set use_operator to true if the old way of using // the operator is set and use_operator is NULL (was never set). if (!empty($options['exposed']) && !empty($options['expose']['operator']) && !isset($options['expose']['use_operator'])) { $this->options['expose']['use_operator'] = TRUE; } // If there are relationships in the view, allow empty should be true // so that we can do IS NULL checks on items. Not all filters respect // allow empty, but string and numeric do and that covers enough. if ($this->view->display_handler->get_option('relationships')) { $this->definition['allow empty'] = TRUE; } } function option_definition() { $options = parent::option_definition(); $options['operator'] = array('default' => '='); $options['value'] = array('default' => ''); $options['group'] = array('default' => '0'); $options['exposed'] = array('default' => FALSE); $options['expose'] = array( 'contains' => array( 'operator' => array('default' => FALSE), 'label' => array('default' => '', 'translatable' => TRUE), ), ); return $options; } /** * Display the filter on the administrative summary */ function admin_summary() { return check_plain((string) $this->operator) . ' ' . check_plain((string) $this->value); } /** * Determine if a filter can be exposed. */ function can_expose() { return TRUE; } /** * Provide the basic form which calls through to subforms. * If overridden, it is best to call through to the parent, * or to at least make sure all of the functions in this form * are called. */ function options_form(&$form, &$form_state) { if ($this->can_expose()) { $this->show_expose_button($form, $form_state); } $form['op_val_start'] = array('#value' => '
'); $this->show_operator_form($form, $form_state); $this->show_value_form($form, $form_state); $form['op_val_end'] = array('#value' => '
'); if ($this->can_expose()) { $this->show_expose_form($form, $form_state); } } /** * Simple validate handler */ function options_validate(&$form, &$form_state) { $this->operator_validate($form, $form_state); $this->value_validate($form, $form_state); if (!empty($this->options['exposed'])) { $this->expose_validate($form, $form_state); } } /** * Simple submit handler */ function options_submit(&$form, &$form_state) { unset($form_state['values']['expose_button']); // don't store this. $this->operator_submit($form, $form_state); $this->value_submit($form, $form_state); if (!empty($this->options['exposed'])) { $this->expose_submit($form, $form_state); } } /** * Shortcut to display the operator form. */ function show_operator_form(&$form, &$form_state) { $this->operator_form($form, $form_state); $form['operator']['#prefix'] = '
'; $form['operator']['#suffix'] = '
'; } /** * Provide a form for setting the operator. * * This may be overridden by child classes, and it must * define $form['operator']; */ function operator_form(&$form, &$form_state) { $options = $this->operator_options(); if (!empty($options)) { $form['operator'] = array( '#type' => count($options) < 10 ? 'radios' : 'select', '#title' => t('Operator'), '#default_value' => $this->operator, '#options' => $options, ); } } /** * Provide a list of options for the default operator form. * Should be overridden by classes that don't override operator_form */ function operator_options() { return array(); } /** * Validate the operator form. */ function operator_validate($form, &$form_state) { } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function operator_submit($form, &$form_state) { } /** * Shortcut to display the value form. */ function show_value_form(&$form, &$form_state) { $this->value_form($form, $form_state); if (empty($this->no_operator)) { $form['value']['#prefix'] = '
' . (isset($form['value']['#prefix']) ? $form['value']['#prefix'] : ''); $form['value']['#suffix'] = (isset($form['value']['#suffix']) ? $form['value']['#suffix'] : '') . '
'; } } /** * Provide a form for setting options. * * This should be overridden by all child classes and it must * define $form['value'] */ function value_form(&$form, &$form_state) { $form['value'] = array(); } /** * Validate the options form. */ function value_validate($form, &$form_state) { } /** * Perform any necessary changes to the form values prior to storage. * There is no need for this function to actually store the data. */ function value_submit($form, &$form_state) { } /** * Shortcut to display the expose/hide button. */ function show_expose_button(&$form, &$form_state) { $form['expose_button'] = array( '#prefix' => '
', '#suffix' => '
', ); if (empty($this->options['exposed'])) { $form['expose_button']['button'] = array( '#type' => 'submit', '#value' => t('Expose'), '#submit' => array('views_ui_config_item_form_expose'), ); $form['expose_button']['markup'] = array( '#prefix' => '
', '#value' => t('This item is currently not exposed. If you expose it, users will be able to change the filter as they view it.'), '#suffix' => '
', ); } else { $form['expose_button']['button'] = array( '#type' => 'submit', '#value' => t('Hide'), '#submit' => array('views_ui_config_item_form_expose'), ); $form['expose_button']['markup'] = array( '#prefix' => '
', '#value' => t('This item is currently exposed. If you hide it, users will not be able to change the filter as they view it.'), '#suffix' => '
', ); } } /** * Shortcut to display the exposed options form. */ function show_expose_form(&$form, &$form_state) { if (empty($this->options['exposed'])) { return; } $form['expose'] = array( '#prefix' => '
', '#suffix' => '
', ); $this->expose_form($form, $form_state); // When we click the expose button, we add new gadgets to the form but they // have no data in $_POST so their defaults get wiped out. This prevents // these defaults from getting wiped out. This setting will only be TRUE // during a 2nd pass rerender. if (!empty($form_state['force_expose_options'])) { foreach (element_children($form['expose']) as $id) { if (isset($form['expose'][$id]['#default_value']) && !isset($form['expose'][$id]['#value'])) { $form['expose'][$id]['#value'] = $form['expose'][$id]['#default_value']; } } } } /** * Overridable form for exposed filter options. * * If overridden, it is best to call the parent or re-implement * the stuff here. * * Many filters will need to override this in order to provide options * that are nicely tailored to the given filter. */ function expose_form(&$form, &$form_state) { $form['expose']['start_left'] = array( '#value' => '
', ); $this->expose_form_left($form, $form_state); $form['expose']['end_left'] = array( '#value' => '
', ); $form['expose']['start_checkboxes'] = array( '#value' => '
', ); $this->expose_form_right($form, $form_state); $form['expose']['end_checkboxes'] = array( '#value' => '
', ); } /** * Handle the 'left' side fo the exposed options form. */ function expose_form_left(&$form, &$form_state) { if (!empty($form['operator']['#type'])) { $form['expose']['use_operator'] = array( '#type' => 'checkbox', '#title' => t('Unlock operator'), '#description' => t('When checked, the operator will be exposed to the user'), '#default_value' => !empty($this->options['expose']['use_operator']), ); $form['expose']['operator'] = array( '#type' => 'textfield', '#default_value' => $this->options['expose']['operator'], '#title' => t('Operator identifier'), '#size' => 40, '#description' => t('This will appear in the URL after the ? to identify this operator.'), '#process' => array('views_process_dependency'), '#dependency' => array( 'edit-options-expose-use-operator' => array(1) ), ); } else { $form['expose']['operator'] = array( '#type' => 'value', '#value' => '', ); } $form['expose']['identifier'] = array( '#type' => 'textfield', '#default_value' => $this->options['expose']['identifier'], '#title' => t('Filter identifier'), '#size' => 40, '#description' => t('This will appear in the URL after the ? to identify this filter. Cannot be blank.'), ); $form['expose']['label'] = array( '#type' => 'textfield', '#default_value' => $this->options['expose']['label'], '#title' => t('Label'), '#size' => 40, ); } /** * Handle the 'right' side fo the exposed options form. */ function expose_form_right(&$form, &$form_state) { $form['expose']['optional'] = array( '#type' => 'checkbox', '#title' => t('Optional'), '#description' => t('This exposed filter is optional and will have added options to allow it not to be set.'), '#default_value' => $this->options['expose']['optional'], ); if (empty($this->no_single)) { $form['expose']['single'] = array( '#type' => 'checkbox', '#title' => t('Force single'), '#description' => t('Force this exposed filter to accept only one option.'), '#default_value' => $this->options['expose']['single'], ); } $form['expose']['remember'] = array( '#type' => 'checkbox', '#title' => t('Remember'), '#description' => t('Remember the last setting the user gave this filter.'), '#default_value' => $this->options['expose']['remember'], ); } /** * Validate the options form. */ function expose_validate($form, &$form_state) { if (empty($this->options['expose']['identifier'])) { if (empty($form_state['values']['options']['expose']['identifier'])) { form_error($form['expose']['identifier'], t('The identifier is required if the filter is exposed.')); } } if (!empty($form_state['values']['options']['expose']['identifier']) && $form_state['values']['options']['expose']['identifier'] == 'value') { form_error($form['expose']['identifier'], t('This identifier is not allowed.')); } } /** * Perform any necessary changes to the form exposes prior to storage. * There is no need for this function to actually store the data. */ function expose_submit($form, &$form_state) { } /** * Provide default options for exposed filters. */ function expose_options() { $this->options['expose'] = array( 'use_operator' => FALSE, 'operator' => $this->options['id'] . '_op', 'identifier' => $this->options['id'], 'label' => $this->ui_name(), 'remember' => FALSE, 'single' => TRUE, 'optional' => TRUE, ); } /** * Render our chunk of the exposed filter form when selecting * * You can override this if it doesn't do what you expect. */ function exposed_form(&$form, &$form_state) { if (empty($this->options['exposed'])) { return; } if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator'])) { $operator = $this->options['expose']['operator']; $this->operator_form($form, $form_state); $form[$operator] = $form['operator']; if (isset($form[$operator]['#title'])) { unset($form[$operator]['#title']); } $this->exposed_translate($form[$operator], 'operator'); unset($form['operator']); } if (!empty($this->options['expose']['identifier'])) { $value = $this->options['expose']['identifier']; $this->value_form($form, $form_state); $form[$value] = $form['value']; if (isset($form[$value]['#title']) && !empty($form[$value]['#type']) && $form[$value]['#type'] != 'checkbox') { unset($form[$value]['#title']); } $this->exposed_translate($form[$value], 'value'); if (!empty($form['#type']) && ($form['#type'] == 'checkboxes' || ($form['#type'] == 'select' && !empty($form['#multiple'])))) { unset($form[$value]['#default_value']); } if (!empty($form['#type']) && $form['#type'] == 'select' && empty($form['#multiple'])) { $form[$value]['#default_value'] = 'All'; } if ($value != 'value') { unset($form['value']); } } } /** * Make some translations to a form item to make it more suitable to * exposing. */ function exposed_translate(&$form, $type) { if (!isset($form['#type'])) { return; } if ($form['#type'] == 'radios') { $form['#type'] = 'select'; } // Checkboxes don't work so well in exposed forms due to GET conversions. if ($form['#type'] == 'checkboxes') { if (empty($form['#no_convert']) || !empty($this->options['expose']['single'])) { $form['#type'] = 'select'; } if (empty($this->options['expose']['single'])) { $form['#multiple'] = TRUE; } } if (!empty($this->options['expose']['single']) && isset($form['#multiple'])) { unset($form['#multiple']); $form['#size'] = NULL; } if ($type == 'value' && !empty($this->options['expose']['optional']) && $form['#type'] == 'select' && empty($form['#multiple'])) { $any_label = variable_get('views_exposed_filter_any_label', 'old_any') == 'old_any' ? t('') : t('- Any -'); $form['#options'] = array('All' => $any_label) + $form['#options']; $form['#default_value'] = 'All'; } } /** * Tell the renderer about our exposed form. This only needs to be * overridden for particularly complex forms. And maybe not even then. */ function exposed_info() { if (empty($this->options['exposed'])) { return; } return array( 'operator' => $this->options['expose']['operator'], 'value' => $this->options['expose']['identifier'], 'label' => $this->options['expose']['label'], ); } /** * Check to see if input from the exposed filters should change * the behavior of this filter. */ function accept_exposed_input($input) { if (empty($this->options['exposed'])) { return TRUE; } if (!empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator']) && isset($input[$this->options['expose']['operator']])) { $this->operator = $input[$this->options['expose']['operator']]; } if (!empty($this->options['expose']['identifier'])) { $value = $input[$this->options['expose']['identifier']]; // Various ways to check for the absence of optional input. if (!empty($this->options['expose']['optional'])) { if ($value == 'All' || $value === array()) { return FALSE; } if (!empty($this->no_single) && $value === '') { return FALSE; } } if (isset($value)) { $this->value = $value; if (!empty($this->options['expose']['single'])) { $this->value = array($value); } } else { return FALSE; } } return TRUE; } function store_exposed_input($input, $status) { if (empty($this->options['exposed']) || empty($this->options['expose']['identifier'])) { return TRUE; } if (empty($this->options['expose']['remember'])) { return; } // Figure out which display id is responsible for the filters, so we // know where to look for session stored values. $display_id = ($this->view->display_handler->is_defaulted('filters')) ? 'default' : $this->view->current_display; // shortcut test. $operator = !empty($this->options['expose']['use_operator']) && !empty($this->options['expose']['operator']); // false means that we got a setting that means to recuse ourselves, // so we should erase whatever happened to be there. if (!$status && isset($_SESSION['views'][$this->view->name][$display_id])) { $session = &$_SESSION['views'][$this->view->name][$display_id]; if ($operator && isset($session[$this->options['expose']['operator']])) { unset($session[$this->options['expose']['operator']]); } if (isset($session[$this->options['expose']['identifier']])) { unset($session[$this->options['expose']['identifier']]); } } if ($status) { if (!isset($_SESSION['views'][$this->view->name][$display_id])) { $_SESSION['views'][$this->view->name][$display_id] = array(); } $session = &$_SESSION['views'][$this->view->name][$display_id]; if ($operator && isset($input[$this->options['expose']['operator']])) { $session[$this->options['expose']['operator']] = $input[$this->options['expose']['operator']]; } $session[$this->options['expose']['identifier']] = $input[$this->options['expose']['identifier']]; } } /** * Add this filter to the query. * * Due to the nature of fapi, the value and the operator have an unintended * level of indirection. You will find them in $this->operator * and $this->value respectively. */ function query() { $this->ensure_my_table(); $this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . " '%s'", $this->value); } } /** * A special handler to take the place of missing or broken handlers. */ class views_handler_filter_broken extends views_handler_filter { function ui_name($short = FALSE) { return t('Broken/missing handler'); } function ensure_my_table() { /* No table to ensure! */ } function query() { /* No query to run */ } function options_form(&$form, &$form_state) { $form['markup'] = array( '#prefix' => '
', '#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.'), ); } /** * Determine if the handler is considered 'broken' */ function broken() { return TRUE; } } /** * @} */