set_definition($definition);
// let the handler have something like a constructor.
if (isset($definition['arguments'])) {
call_user_func_array(array(&$handler, 'construct'), $definition['arguments']);
}
else {
$handler->construct();
}
return $handler;
}
/**
* Prepare a handler's data by checking defaults and such.
*/
function _views_prepare_handler($definition, $data, $field) {
foreach (array('group', 'title', 'help') as $key) {
// First check the field level
if (!isset($definition[$key]) && !empty($data[$field][$key])) {
$definition[$key] = $data[$field][$key];
}
// Then if that doesn't work, check the table level
if (!isset($definition['table'][$key]) && !empty($data['table'][$key])) {
$definition[$key] = $data['table'][$key];
}
}
return _views_create_handler($definition);
}
/**
* Fetch a handler to join one table to a primary table from the data cache
*/
function views_get_table_join($table, $primary_table) {
$data = views_fetch_data($table);
if (isset($data['table']['join'][$primary_table])) {
$h = $data['table']['join'][$primary_table];
$handler = new $h['handler'];
if (isset($h['arguments'])) {
call_user_func_array(array(&$handler, 'construct'), $h['arguments']);
}
return $handler;
}
// DEBUG -- identify missing handlers
vpr("Missing join: $table $primary_table");
}
/**
* @defgroup views_join_handlers Views' join handlers
* @{
* Handlers to tell Views how to join tables together.
* Here is how you do complex joins:
*
* @code
* class views_join_complex extends views_join {
* // PHP 4 doesn't call constructors of the base class automatically from a
* // constructor of a derived class. It is your responsibility to propagate
* // the call to constructors upstream where appropriate.
* function construct($left_table, $left_field, $field, $extra = array(), $type = 'LEFT') {
* parent::construct($left_table, $left_field, $field, $extra, $type);
* }
*
* function join($table, &$query) {
* $output = parent::join($table, $query);
* }
* $output .= "AND foo.bar = baz.boing";
* return $output;
* }
* @endcode
*/
/**
* A function class to represent a join and create the SQL necessary
* to implement the join.
*
* This is the Delegation pattern. If we had PHP5 exclusively, we would
* declare this an interface.
*
* Extensions of this class can be used to create more interesting joins.
*/
class views_join {
/**
* Construct the views_join object.
*/
function construct($table, $left_table, $left_field, $field, $extra = array(), $type = 'LEFT') {
$this->table = $table;
$this->left_table = $left_table;
$this->left_field = $left_field;
$this->field = $field;
$this->extra = $extra;
$this->type = strtoupper($type);
}
/**
* Build the SQL for the join this object represents.
*/
function join($table, &$query) {
$left = $query->get_table_info($this->left_table);
$output = " $this->type JOIN {" . $this->table . "} $table[alias] ON $left[alias].$this->left_field = $table[alias].$this->field";
// Tack on the extra.
if (isset($extra)) {
foreach ($extra as $field => $value) {
$output .= " AND $table[alias].$this->field";
if (is_array($value) && !empty($value)) {
$output .= " IN ('". implode("','", $value) ."')";
}
else if ($value !== NULL) {
$output .= " = '$value'";
}
}
}
return $output;
}
}
/**
* @}
*/
/**
* Base handler, from which all the other handlers are derived.
* It creates a common interface to create consistency amongst
* handlers and data.
*
* The default handler has no constructor, so there's no need to jank with
* parent::views_handler() here.
*
* This class would be abstract in PHP5, but PHP4 doesn't understand that.
*
*/
class views_handler extends views_object {
/**
* A constructor for the handler base object
*
* This should be overridden to provide for a consistent constructor
* mechanism.
*/
function construct() { }
/**
* init the handler with necessary data.
* @param $view
* The $view object this handler is attached to.
* @param $options
* The item from the database; the actual contents of this will vary
* based upon the type of handler.
*/
function init(&$view, $options) {
$this->view = &$view;
$this->options = &$options;
// This exist on most handlers, but not all. So they are still optional.
if (isset($options['table'])) {
$this->table = $options['table'];
}
if (isset($options['field'])) {
$this->field = $options['field'];
if (!isset($this->real_field)) {
$this->real_field = $options['field'];
}
}
if (isset($options['relationship'])) {
$this->relationship = $options['relationship'];
}
else {
$this->relationship = NULL;
}
if (!empty($view->query)) {
$this->query = &$view->query;
}
}
/**
* Provide defaults for the handler.
*/
function options(&$option) { }
/**
* Provide a form for setting options.
*/
function options_form(&$form, &$form_state) { }
/**
* Validate the options form.
*/
function options_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 options_submit($form, &$form_state) { }
/**
* Set new exposed option defaults when exposed setting is flipped
* on.
*/
function expose_options() { }
/**
* Render our chunk of the exposed filter form when selecting
*/
function exposed_form(&$form, &$form_state) { }
/**
* Validate the exposed filter form
*/
function exposed_validate(&$form, &$form_state) { }
/**
* Submit the exposed filter form
*/
function exposed_submit(&$form, &$form_state) { }
/**
* Get information about the exposed form for the form renderer.
*
* @return
* An array with the following keys:
* - operator: The $form key of the operator. Set to NULL if no operator.
* - value: The $form key of the value. Set to NULL if no value.
* - label: The label to use for this piece.
*/
function exposed_info() { }
/**
* Run before the view is built.
*
* This gives all the handlers some time to set up before any handler has
* been fully run.
*/
function pre_query() { }
/**
* Add this handler into the query.
*
* If we were using PHP5, this would be abstract.
*/
function query() { }
/**
* Ensure the main table for this handler is in the query. This is used
* a lot.
*/
function ensure_my_table() {
if (!isset($this->table_alias)) {
$this->table_alias = $this->query->ensure_table($this->table, $this->relationship);
}
return $this->table_alias;
}
/**
* Provide text for the administrative summary
*/
function admin_summary() { }
/**
* Determine if the argument needs a style plugin.
*
* @return TRUE/FALSE
*/
function needs_style_plugin() { return FALSE; }
/**
* Determine if this item is 'exposed', meaning it provides form elements
* to let users modify the view.
*
* @return TRUE/FALSE
*/
function is_exposed() {
return !empty($this->options['exposed']);
}
/**
* Take input from exposed filters and assign to this handler, if necessary.
*/
function accept_exposed_input($input) { }
}
/**
* @defgroup views_relationship_handlers Views' relationship handlers
* @{
* Handlers to tell Views how to create alternate relationships.
*/
/**
* Simple relationship handler that allows a new version of the primary table
* to be linked in.
*/
class views_handler_relationship extends views_handler {
/**
* Called to implement a relationship in a query.
*/
function query() {
$alias = $this->table . '_' . $this->field . '_' . $this->relationship;
return $this->query->add_relationship($alias, new views_join($this->view->primary_table, $this->table, $this->real_field, $this->primary_field), $this->relationship);
}
}
/**
* @}
*/
/**
* @defgroup views_field_handlers Views' field handlers
* @{
* Handlers to tell Views how to build and display fields.
*
*/
/**
* Base field handler that has no options and renders an unformatted field.
*/
class views_handler_field extends views_handler {
var $field_alias = 'unknown';
/**
* Construct a new field handler.
*/
function construct() {
$this->additional_fields = array();
if (!empty($this->definition['additional fields'])) {
$this->additional_fields = $this->definition['additional fields'];
}
}
/**
* Called to add the field to a query.
*/
function query() {
$this->ensure_my_table();
// Add the field.
$this->field_alias = $this->query->add_field($this->table_alias, $this->real_field);
// Add any additional fields we are given.
if (!empty($this->additional_fields) && is_array($this->additional_fields)) {
foreach ($this->additional_fields as $field) {
$this->aliases[$field] = $this->query->add_field($this->table_alias, $field);
}
}
}
/**
* Called to determine what to tell the clicksorter.
*/
function click_sort($order) {
$this->query->add_orderby($this->table, $this->field, $order, $this->field_alias);
}
/**
* Determine if this field is click sortable.
*/
function click_sortable() {
return !empty($this->definition['click sortable']);
}
/**
* Get this field's label.
*/
function label() {
if (!isset($this->options['label'])) {
return '';
}
return $this->options['label'];
}
/**
* Provide a default label
*/
function options(&$options) {
parent::options($options);
$options['label'] = t('!group: !title', array('!group' => $this->definition['group'], '!title' => $this->definition['title']));
}
/**
* Default options form that provides the label widget that all fields
* should have.
*/
function options_form(&$form, &$form_state) {
$form['label'] = array(
'#type' => 'textfield',
'#title' => t('Label'),
'#default_value' => isset($this->options['label']) ? $this->options['label'] : '',
'#description' => t('The label for this field that will be displayed to end users if the style requires it.'),
);
}
/**
* Run before any fields are rendered.
*
* This gives the handlers some time to set up before any handler has
* been rendered.
*
* @param $values
* An array of all objects returned from the query.
*/
function pre_render($values) { }
/**
* Render the field.
*
* @param $values
* The values retrieved from the database.
*/
function render($values) {
$value = $values->{$this->field_alias};
return check_plain($value);
}
}
/**
* A handler to provide proper displays for dates.
*/
class views_handler_field_date extends views_handler_field {
/**
* Fill in default options.
*/
function options(&$options) {
parent::options($options);
$options['date_format'] = 'small';
$options['custom_date_format'] = '';
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$time = time();
$form['date_format'] = array(
'#type' => 'select',
'#title' => t('Date format'),
'#options' => array(
'small' => format_date($time, 'small'),
'medium' => format_date($time, 'medium'),
'large' => format_date($time, 'large'),
'custom' => t('Custom'),
'time ago' => t('Time ago'),
),
'#default_value' => isset($this->options['date_format']) ? $this->options['date_format'] : 'small',
);
$form['custom_date_format'] = array(
'#type' => 'textfield',
'#title' => t('Custom date format'),
'#description' => t('If "Custom", see the PHP docs for date formats. If "Time ago" this is the the number of different units to display, which defaults to two.'),
'#default_value' => isset($this->options['custom_date_format']) ? $this->options['custom_date_format'] : '',
);
}
function render($values) {
$value = $values->{$this->field_alias};
$format = $this->options['date_format'];
if ($format == 'custom') {
$custom_format = $this->custom_date_format;
}
switch ($format) {
case 'time ago':
return $value ? t('%time ago', array('%time' => format_interval(time() - $value, is_numeric($custom_format) ? $custom_format : 2))) : theme('views_nodate');
case 'custom':
return $value ? format_date($value, $format, $custom_format) : theme('views_nodate');
default:
return $value ? format_date($value, $format) : theme('views_nodate');
}
}
}
/**
* A handler to provide proper displays for dates.
*
* Allows for display of true/false, yes/no, on/off.
*/
class views_handler_field_boolean extends views_handler_field {
function options(&$options) {
parent::options($options);
$options['type'] = 'yes-no';
$options['not'] = FALSE;
}
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['type'] = array(
'#type' => 'select',
'#title' => t('Output format'),
'#options' => array(
'yes-no' => t('Yes/No'),
'true-false' => t('True/False'),
'on-off' => t('On/Off'),
),
'#default_value' => $this->options['type'],
);
$form['not'] = array(
'#type' => 'checkbox',
'#title' => t('Reverse'),
'#description' => t('If checked, true will be displayed as false.'),
'#default_value' => $this->options['not'],
);
}
function render($values) {
$value = $values->{$this->field_alias};
if (!empty($this->options['not'])) {
$value = !$value;
}
switch ($this->options['type']) {
case 'yes-no':
default:
return $value ? t('Yes') : t('No');
case 'true-false':
return $value ? t('True') : t('False');
case 'on-off':
return $value ? t('On') : t('Off');
}
}
}
/**
* A handler to run a field through check_markup, using a companion
* format field.
*/
class views_handler_field_markup extends views_handler_field {
/**
* Constructor; calls to base object constructor.
*/
function construct() {
$this->format = $this->definition['format'];
$this->additional_fields = array();
if (!is_numeric($this->format)) {
$this->additional_fields[] = $this->format;
}
}
function render($values) {
$value = $values->{$this->field_alias};
$format = is_numeric($this->format) ? $this->format : $values->{$this->aliases[$this->format]};
return check_markup($value, $format, FALSE);
}
}
/**
* Field handler to provide simple renderer that turns a URL into a clickable link.
*/
class views_handler_field_url extends views_handler_field {
/**
* Provide link to the page being visited.
*/
function options_form(&$form, &$form_state) {
parent::options_form($form, $form_state);
$form['display_as_link'] = array(
'#title' => t('Display as link'),
'#type' => 'checkbox',
'#default_value' => !empty($this->options['display_as_link']),
);
}
function render($values) {
$value = $values->{$this->field_alias};
if (!empty($this->options['display_as_link'])) {
return l(check_plain($value), $value, array('html' => TRUE));
}
else {
return $value;
}
}
}
/**
* @}
*/
/**
* @defgroup views_sort_handlers Views' sort handlers
* @{
* Handlers to tell Views how to sort queries
*/
/**
* Base sort handler that has no options and performs a simple sort
*/
class views_handler_sort extends views_handler {
/**
* Called to add the sort to a query.
*/
function query() {
$this->ensure_my_table();
// Add the field.
$this->query->add_orderby($this->table_alias, $this->real_field, $this->options['order']);
}
/**
* Set defaults for new handler
*/
function options(&$options) {
parent::options($options);
$options['order'] = 'ASC';
}
/**
* Display whether or not the sort order is ascending or descending
*/
function admin_summary() {
switch ($this->options['order']) {
case 'ASC':
case 'asc':
default:
$type = t('asc');
break;
case 'DESC';
case 'desc';
$type = t('desc');
break;
}
return '' . $type . '';
}
/**
* Basic options for all sort criteria
*/
function options_form(&$form, &$form_state) {
$form['order'] = array(
'#type' => 'radios',
'#title' => t('Sort order'),
'#options' => array('ASC' => t('Ascending'), 'DESC' => t('Descending')),
'#default_value' => $this->options['order'],
);
}
}
/**
* Base sort handler that has no options and performs a simple sort
*/
class views_handler_sort_formula extends views_handler_sort {
/**
* Constructor to take the formula this sorts on.
*/
function construct() {
$this->formula = $this->definition['formula'];
if (is_array($this->formula) && !isset($this->formula['default'])) {
$this->error = t('views_handler_sort_formula missing default: @formula', array('@formula' => var_export($this->formula, TRUE)));
}
parent::construct();
}
/**
* Called to add the sort to a query.
*/
function query() {
if (is_array($this->formula)) {
global $db_type;
if (isset($this->formula[$db_type])) {
$formula = $this->formula[$db_type];
}
else {
$formula = $this->formula['default'];
}
}
else {
$formula = $this->formula;
}
$this->ensure_my_table();
// Add the field.
$this->add_orderby(NULL, $this->formula, $this->options['order'], $this->table_alias . '_' . $this->field);
}
}
/**
* @}
*/
/**
* @defgroup views_filter_handlers Views' filter handlers
* @{
* Handlers to tell Views how to filter queries.
*/
/**
* Base class for filters.
*/
class views_handler_filter extends views_handler {
/**
* Provide some extra help to get the operator/value easier to use.
*
* This likely has to be overridden by filters which are more complex
* than simple operator/value.
*/
function init(&$view, $options) {
parent::init($view, $options);
// @todo -- remove this if once it's safe, and just set it directly.
if (is_array($options['operator']) && !empty($options['operator']['operator'])) {
$this->operator = $options['operator']['operator'];
}
else {
$this->operator = $options['operator'];
}
// @todo -- remove this if once it's safe, and just set it directly.
if (is_array($options['value']) && !empty($options['value']['value'])) {
$this->value = $options['value']['value'];
}
else {
$this->value = $options['value'];
}
}
/**
* Provide a simple default initializer -- should be overridden.
*/
function options(&$options) {
parent::options($options);
$options['operator'] = '=';
$options['value'] = '';
$options['group'] = 0;
$options['exposed'] = FALSE;
$options['expose'] = array(
'operator' => FALSE,
'label' => '',
);
}
/**
* Display the filter on the administrative summary
*/
function admin_summary() {
return check_plain($this->operator) . ' ' . check_plain($this->value);
}
/**
* 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) {
$this->show_expose_button($form, $form_state);
$this->show_operator_form($form, $form_state);
$this->show_value_form($form, $form_state);
$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) {
$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 should be overridden by all child classes, and it must
* define $form['operator'];
*/
function operator_form(&$form, &$form_state) { $form['operator'] = 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);
$form['value']['#prefix'] = '';
$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 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);
}
/**
* 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' => '',
);
if (!empty($form['operator']['#type'])) {
$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. Leave blank to not expose the operator.'),
);
}
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,
);
$form['expose']['end_left'] = array(
'#value' => '
',
);
$form['expose']['start_checkboxes'] = array(
'#value' => '',
);
$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'],
);
$form['expose']['end_checkboxes'] = array(
'#value' => '
',
);
}
/**
* 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.'));
}
}
}
/**
* 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) { }
function expose_options() {
$this->options['expose'] = array(
'operator' => $this->options['id'] . '_oper',
'identifier' => $this->options['id'],
'label' => t('@group: @title', array('@group' => $this->definition['group'], '@title' => $this->definition['title'])),
'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']['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';
}
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';
}
if ($form['#type'] == 'checkboxes' && !empty($this->options['expose']['single'])) {
$form['#type'] = 'select';
}
if (!empty($this->options['expose']['single']) && isset($form['#multiple'])) {
unset($form['#multiple']);
}
if ($type == 'value' && !empty($this->options['expose']['optional']) && $form['#type'] == 'select' && empty($form['#multiple'])) {
$form['#options'] = array('All' => t('')) + $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 if this filter.
*/
function accept_exposed_input($input) {
if (empty($this->options['exposed'])) {
return;
}
if (!empty($this->options['expose']['operator']) && isset($input[$this->options['expose']['operator']])) {
$this->operator = $input[$this->options['expose']['operator']];
if ($this->options['expose']['remember']) {
$_SESSION['views'][$this->view->name][$this->view->current_display][$this->options['expose']['operator']] = $input[$this->options['expose']['operator']];
}
}
if (!empty($this->options['expose']['identifier'])) {
$value = $input[$this->options['expose']['identifier']];
if ($this->options['expose']['remember']) {
$_SESSION['views'][$this->view->name][$this->view->current_display][$this->options['expose']['identifier']] = $value;
}
// 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;
}
else {
return FALSE;
}
}
return TRUE;
}
/**
* 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);
}
}
/**
* Simple filter to handle equal to / not equal to filters
*/
class views_handler_filter_equality extends views_handler_filter {
// exposed filter options
var $no_single = TRUE;
/**
* Provide basic defaults for the equality operator
*/
function options(&$options) {
parent::options($options);
$options['operator'] = '=';
$options['value'] = '';
}
/**
* Provide simple equality operator
*/
function operator_form(&$form, &$form_state) {
$form['operator'] = array(
'#type' => 'radios',
'#title' => t('Operator'),
'#default_value' => $this->operator,
'#options' => array(
'=' => t('Is equal to'),
'!=' => t('Is not equal to'),
),
);
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
$form['value'] = array(
'#type' => 'textfield',
'#title' => t('Value'),
'#size' => 30,
'#default_value' => $this->value,
);
}
}
/**
* Simple filter to handle matching of boolean values
*/
class views_handler_filter_boolean_operator extends views_handler_filter {
// exposed filter options
var $no_single = TRUE;
function construct() {
$this->value_value = t('True');
if (isset($this->definition['label'])) {
$this->value_value = $this->definition['label'];
}
parent::construct();
}
function options(&$options) {
parent::options($options);
$options['value'] = FALSE;
}
function operator_form(&$form, &$form_state) {
$form['operator'] = array();
}
function value_form(&$form, &$form_state) {
if (empty($this->options['exposed'])) {
$form['value'] = array(
'#type' => 'checkbox',
'#title' => $this->value_value,
'#default_value' => $this->value,
);
}
else {
$form['value'] = array(
'#type' => 'select',
'#title' => $this->value_value,
'#options' => array(1 => t('Yes'), 0 => t('No')),
'#default_value' => $this->value,
);
}
}
function admin_summary() {
if (!empty($this->options['exposed'])) {
return t('exposed');
}
return (empty($this->value) ? t("False") : t('True'));
}
function expose_options() {
$this->options['expose'] = array(
'operator' => '',
'identifier' => $this->options['id'],
'label' => $this->value_value,
'remember' => FALSE,
'single' => TRUE,
'optional' => FALSE,
);
}
function query() {
// @todo this should actually reverse the operator so it can compare against 0.
$this->ensure_my_table();
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . (empty($this->value) ? '=' : '<>') . " 0");
}
}
/**
* Simple filter to handle matching of multiple options selectable via checkboxes
*/
class views_handler_filter_in_operator extends views_handler_filter {
function construct() {
parent::construct();
$this->value_title = t('Options');
$this->value_options = array(t('Yes'), t('No'));
}
function options(&$options) {
parent::options($options);
$options['operator'] = 'in';
$options['value'] = 0;
}
/**
* Provide inclusive/exclusive matching
*/
function operator_form(&$form, &$form_state) {
$form['operator'] = array(
'#type' => 'radios',
'#title' => t('Operator'),
'#default_value' => ($this->operator) ? $this->operator : 'in',
'#options' => array(
'in' => t('Is one of'),
'not in' => t('Is not one of'),
),
);
}
function value_form(&$form, &$form_state) {
$form['value'] = array(
'#type' => 'checkboxes',
'#title' => $this->value_title,
'#options' => $this->value_options,
'#default_value' => (array) $this->value,
);
}
function value_submit($form, &$form_state) {
// This is so deeply deeply deeply nested due to the way the form is layered.
$form_state['values']['options']['value'] = array_filter($form_state['values']['options']['value']);
}
function admin_summary() {
if (!empty($this->options['exposed'])) {
return t('exposed');
}
if (count($this->value) == 1) {
// If there is only one, show it as an =.
$keys = array_keys($this->value);
$key = array_shift($keys);
if (!empty($this->value_options[$key])) {
$value = check_plain($this->value_options[$key]);
}
else {
$value = t('Unknown');
}
return ($this->operator == 'in' ? '=' : '<>') . ' ' . $value;
}
$output = '';
foreach ($this->value as $value) {
if ($output) {
$output .= ', ';
}
if (strlen($output) > 8) {
$output .= '...';
break;
}
$output .= check_plain($this->value_options[$value]);
}
return check_plain($this->operator) . ' ' . $output;
}
function query() {
if (empty($this->value)) {
return;
}
$this->ensure_my_table();
$replace = array_fill(0, sizeof($this->value), "'%s'");
$in = ' (' . implode(", ", $replace) . ')';
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . $in, $this->value);
}
}
/**
* Simple filter to handle greater than/less than filters
*/
class views_handler_filter_numeric extends views_handler_filter {
/**
* Provide basic defaults for the filter
*/
function options(&$options) {
parent::options($options);
$options['operator'] = '=';
$options['value']['min'] = '';
$options['value']['max'] = '';
}
/**
* Provide a list of all the numeric operators
*/
function operator_form(&$form, &$form_state) {
$form['operator'] = array(
'#type' => 'select',
'#title' => t('Operator'),
'#default_value' => $this->operator,
'#options' => array(
'<' => t('Is less than'),
'<=' => t('Is less than or equal to'),
'=' => t('Is equal to'),
'!=' => t('Is not equal to'),
'>=' => t('Is greater than or equal to'),
'>' => t('Is greater than'),
'between' => t('Is between'),
'not between' => t('Is not between'),
),
);
}
/**
* Provide a simple textfield for equality
*/
function value_form(&$form, &$form_state) {
$form['value']['#tree'] = TRUE;
$form['value']['min'] = array(
'#type' => 'textfield',
'#title' => t('Min value'),
'#size' => 30,
'#default_value' => $this->value['min'],
'#description' => t('For all non-range operators, this value is used.'),
);
$form['value']['max'] = array(
'#type' => 'textfield',
'#title' => t('Max value'),
'#size' => 30,
'#default_value' => $this->value['max'],
'#description' => t('This value is ignored for all non-range operators.'),
);
}
/**
* We need to override this to avoid doing a string comparison against numeric fields.
*/
function query() {
$this->ensure_my_table();
switch($this->operator) {
case 'between':
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field >= %d", $this->value['min']);
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field <= %d", $this->value['max']);
break;
case 'not between':
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field <= %d", $this->value['min']);
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field >= %d", $this->value['max']);
break;
default:
$this->query->add_where($this->options['group'], "$this->table_alias.$this->real_field " . $this->operator . " %d", $this->value['min']);
break;
}
}
function admin_summary() {
$output = check_plain($this->operator) . ' ' . check_plain($this->value['min']);
if (!empty($this->value['max'])) {
$output .= ' '. t('and') .' '. check_plain($this->value['max']);
}
return $output;
}
}
/**
* @}
*/
/**
* @defgroup views_argument_handlers Handlers for arguments
* @{
*/
/**
* Base class for arguments.
*
* The basic argument works for very simple arguments such as nid and uid
*/
class views_handler_argument extends views_handler {
var $name_field = NULL;
/**
* Constructor
*/
function construct() {
if (!empty($this->definition['name field'])) {
$this->name_field = $this->definition['name field'];
}
}
/**
* Give an argument the opportunity to modify the breadcrumb, if it wants.
* This only gets called on displays where a breadcrumb is actually used.
*
* The breadcrumb will be in the form of an array, with the keys being
* the path and the value being the already sanitized title of the path.
*/
function set_breadcrumb(&$breadcrumb) { }
/**
* Provide defaults for the argument when a new one is created.
*/
function options(&$options) {
parent::options($options);
$options['default_action'] = 'ignore';
$options['style_plugin'] = 'default_summary';
$options['style_options'] = array();
$options['wildcard'] = 'all';
$options['wildcard_substitution'] = t('All');
$options['title'] = '';
}
/**
* Provide a default options form for the argument.
*/
function options_form(&$form, &$form_state) {
$defaults = $this->default_actions();
foreach ($defaults as $id => $info) {
$options[$id] = $info['title'];
}
$form['default_action'] = array(
'#prefix' => '',
'#suffix' => '
',
'#type' => 'radios',
'#title' => t('Action to take if argument is not present'),
'#options' => $options,
'#default_value' => $this->options['default_action'],
);
$form['wildcard'] = array(
'#prefix' => '',
// prefix and no suffix means these two items will be grouped together.
'#type' => 'textfield',
'#title' => t('Wildcard'),
'#size' => 20,
'#default_value' => $this->options['wildcard'],
'#description' => t('If this value is received as an argument, the argument will be ignored; i.e, "all values"'),
);
$form['wildcard_substitution'] = array(
'#suffix' => '
',
'#type' => 'textfield',
'#title' => t('Wildcard title'),
'#size' => 20,
'#default_value' => $this->options['wildcard_substitution'],
'#description' => t('The title to use for the wildcard in substitutions elsewhere.'),
);
$form['title'] = array(
'#prefix' => '',
'#suffix' => '
',
'#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => $this->options['title'],
'#description' => t('The title to use when this argument is present; it will override the title of the view and titles from previous arguments.'),
);
}
/**
* Provide a list of default behaviors for this argument if the argument
* is not present.
*
* Override this method to provide additional (or fewer) default behaviors.
*/
function default_actions($which = NULL) {
$defaults = array(
'ignore' => array(
'title' => t('Display all values'),
'method' => 'default_ignore',
'breadcrumb' => TRUE, // generate a breadcrumb to here
),
'not found' => array(
'title' => t('Display page not found'),
'method' => 'default_not_found',
),
'empty' => array(
'title' => t('Display empty text'),
'method' => 'default_empty',
'breadcrumb' => TRUE, // generate a breadcrumb to here
),
'summary asc' => array(
'title' => t('Summary, sorted ascending'),
'method' => 'default_summary',
'method args' => array('asc'),
'style plugin' => TRUE,
'breadcrumb' => TRUE, // generate a breadcrumb to here
),
'summary desc' => array(
'title' => t('Summary, sorted descending'),
'method' => 'default_summary',
'method args' => array('desc'),
'style plugin' => TRUE,
'breadcrumb' => TRUE, // generate a breadcrumb to here
),
);
if ($which) {
if (!empty($defaults[$which])) {
return $defaults[$which];
}
}
else {
return $defaults;
}
}
/**
* Determine if the can generate a breadcrumb
*
* @return TRUE/FALSE
*/
function uses_breadcrumb() {
$info = $this->default_actions($this->options['default_action']);
return !empty($info['breadcrumb']);
}
/**
* Determine if the argument needs a style plugin.
*
* @return TRUE/FALSE
*/
function needs_style_plugin() {
$info = $this->default_actions($this->options['default_action']);
return !empty($info['style plugin']);
}
/**
* Handle the default action, which means our argument wasn't present.
*
* Override this method only with extreme care.
*
* @return
* A boolean value; if TRUE, continue building this view. If FALSE,
* building the view will be aborted here.
*/
function default_action() {
$info = $this->default_actions($this->options['default_action']);
if (!$info) {
return FALSE;
}
if (!empty($info['method args'])) {
return call_user_func_array(array($this, $info['method']), $info['method args']);
}
else {
return $this->{$info['method']}();
}
}
/**
* Default action: ignore.
*
* If an argument was expected and was not given, in this case, simply
* ignore the argument entirely.
*/
function default_ignore() {
return TRUE;
}
/**
* Default action: not found.
*
* If an argument was expected and was not given, in this case, report
* the view as 'not found' or hide it.
*/
function default_not_found() {
// Set a failure condition and let the display manager handle it.
$this->view->build_info['fail'] = TRUE;
return FALSE;
}
/**
* Default action: empty
*
* If an argument was expected and was not given, in this case, display
* the view's empty text
*/
function default_empty() {
// We return with no query; this will force the empty text.
$this->view->built = TRUE;
$this->view->executed = TRUE;
$this->view->result = array();
return FALSE;
}
/**
* Default action: summary.
*
* If an argument was expected and was not given, in this case, display
* a summary query.
*/
function default_summary($order) {
$this->view->build_info['summary'] = TRUE;
$this->view->build_info['summary_level'] = $this->options['id'];
// Change the display style to the summary style for this
// argument.
$this->view->style_plugin = $this->options['style_plugin'];
$this->view->style_options = $this->options['style_options'];
// Clear out the normal primary field and whatever else may have
// been added and let the summary do the work.
$this->query->clear_fields();
$this->summary_query();
$this->summary_sort($order);
// Summaries have their own sorting and fields, so tell the View not
// to build these.
$this->view->build_sort = $this->view->build_fields = FALSE;
return TRUE;
}
/**
* Build the info for the summary query.
*
* This must:
* - add_groupby: group on this field in order to create summaries.
* - add_field: add a 'num_nodes' field for the count. Usually it will
* be a count on $view->primary_field
* - set_count_field: Reset the count field so we get the right paging.
*
* @return
* The alias used to get the number of records (count) for this entry.
*/
function summary_query() {
$this->ensure_my_table();
// Add the field.
$this->base_alias = $this->query->add_field($this->table_alias, $this->real_field);
// Add the 'name' field. For example, if this is a uid argument, the
// name field would be 'name' (i.e, the username).
if (isset($this->name_field)) {
$this->name_alias = $this->query->add_field($this->table_alias, $this->name_field);
}
else {
$this->name_alias = $this->base_alias;
}
return $this->summary_basics();
}
/**
* Some basic summary behavior that doesn't need to be repeated as much as
* code that goes into summary_query()
*/
function summary_basics($count_field = TRUE) {
// Add the number of nodes counter
$count_alias = $this->query->add_field(NULL, 'COUNT('. $this->query->primary_table .'.'. $this->query->primary_field . ')', 'num_records');
$this->query->add_groupby($this->base_alias);
if ($count_field) {
$this->query->set_count_field($this->table_alias, $this->real_field);
}
$this->count_alias = $count_alias;
}
/**
* Sorts the summary based upon the user's selection. The base variant of
* this is usually adequte.
*
* @param $order
* The order selected in the UI.
*/
function summary_sort($order) {
$this->query->add_orderby(NULL, NULL, $order, $this->base_alias);
}
/**
* Provides a link from the summary to the next level; this will be called
* once per row of a summary.
*
* @param $data
* The query results for the row.
* @param $url
* The base URL to use.
*/
function summary_link($data, $url) {
$value = $data->{$this->base_alias};
return url("$url/$value");
}
/**
* Provides the name to use for the summary. By default this is just
* the name field.
*
* @param $data
* The query results for the row.
*/
function summary_name($data) {
return check_plain($data->{$this->name_alias});
}
/**
* Set up the query for this argument.
*
* The argument sent may be found at $this->argument.
*/
function query() {
$this->ensure_my_table();
$this->query->add_where(0, "$this->table_alias.$this->real_field = '%s'", $this->argument);
}
/**
* Get the title this argument will assign the view, given the argument.
*
* This usually needs to be overridden to provide a proper title.
*/
function title() {
return check_plain($this->argument);
}
/**
* Validate that this argument works. By default, all arguments are valid.
*/
function validate($arg) {
return TRUE;
}
}
/**
* Abstract argument handler for simple formulae.
*
* Child classes of this object should implement summary_link, at least.
*/
class views_handler_argument_formula extends views_handler_argument {
var $formula = NULL;
/**
* Constructor
*/
function construct() {
if (!empty($this->definition['formula'])) {
$this->formula = $this->definition['formula'];
}
}
/**
* Build the summary query based on a formula
*/
function summary_query() {
$this->ensure_my_table();
// Add the field.
$this->base_alias = $this->name_alias = $this->query->add_field(NULL, $this->formula, $this->field);
$this->query->set_count_field(NULL, $this->formula, $this->field);
return $this->summary_basics(FALSE);
}
/**
* Build the query based upon the formula
*/
function query() {
$this->ensure_my_table();
$this->query->add_where(0, "$this->formula = '%s'", $this->argument);
}
}
/**
* @}
*/