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 $data * 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) { } /** * 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->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; } } /** * @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($click_sortable = FALSE, $additional_fields = array()) { function construct() { $this->click_sortable = !empty($this->definition['click sortable']); $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() { return "$this->field_alias"; } /** * 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) { $form['date_format'] = array( '#type' => 'select', '#title' => t('Date format'), '#options' => array( 'small' => t('Small'), 'medium' => t('Medium'), 'large' => t('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) { $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); } } /** * @} */ /** * @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. * * @param $formula * The formula used to sort. If an array, may be keyed by database type. If * used, 'default' MUST be defined. */ // function construct($formula) { 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. */ function init(&$view, $options) { parent::init($view, $options); if (!empty($options['operator']['operator'])) { $this->operator = $options['operator']['operator']; } else { $this->operator = $options['operator']; } if (!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; } /** * 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. */ function options_form(&$form, &$form_state) { $this->operator_form($form['operator'], $form_state); $form['operator']['#prefix'] = '
'; $form['operator']['#suffix'] = '
'; $this->value_form($form['value'], $form_state); $form['value']['#prefix'] = '
'; $form['value']['#suffix'] = '
'; } /** * Simple validate handler */ function options_validate(&$form, &$form_state) { $this->operator_validate($form['operator'], $form_state); $this->value_validate($form['value'], $form_state); } /** * Simple submit handler */ function options_submit(&$form, &$form_state) { $this->operator_submit($form['operator'], $form_state); $this->value_submit($form['value'], $form_state); } /** * 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) { } /** * 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) { } /** * 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 { /** * 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 { 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['operator'] = '='; } function operator_form(&$form, &$form_state) { $form['operator'] = array( '#type' => 'radios', '#default_value' => $this->operator, '#options' => array( '=' => t('Is'), '<>' => t('Is not'), ), ); } function value_form(&$form, &$form_state) { $form['value'] = array( '#type' => 'textfield', '#disabled' => TRUE, '#value' => $this->value_value, ); } function admin_summary() { return check_plain($this->operator) . ' ' . check_plain($this->value_value); } 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 " . $this->operator . " 1"); } } /** * 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', '#required' => TRUE, '#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 (count($this->value) == 1) { // If there is only one, show it as an =. $keys = array_keys($this->value); $value = check_plain($this->value_options[array_shift($keys)]); 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() { $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); } } /** * @} */ /** * @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; 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); } } /** * @} */