• Main Page
  • Related Pages
  • Modules
  • Classes
  • Files
  • File List
  • File Members

includes/handlers.inc

Go to the documentation of this file.
00001 <?php
00010 function _views_create_handler($definition, $type = 'handler', $handler_type = NULL) {
00011 //  debug('Instantiating handler ' . $definition['handler']);
00012   if (empty($definition['handler'])) {
00013     vpr('_views_create_handler - type: @type - failed: handler has not been provided.',
00014       array('@type' => isset($handler_type) ? ( $type . '(handler type: ' . $handler_type . ')' ) : $type)
00015     );
00016     return;
00017   }
00018 
00019   // class_exists will automatically load the code file.
00020   if (!empty($definition['override handler']) &&
00021       !class_exists($definition['override handler'])) {
00022     vpr(
00023       '_views_create_handler - loading override handler @type failed: class @override_handler could not be loaded. ' .
00024       'Verify the class file has been registered in the corresponding .info-file (files[]).',
00025       array(
00026         '@type' => isset($handler_type) ? ( $type . '(handler type: ' . $handler_type . ')' ) : $type,
00027         '@override_handler' => $definition['override handler']
00028       )
00029     );
00030     return;
00031   }
00032 
00033   if (!class_exists($definition['handler'])) {
00034     vpr(
00035       '_views_create_handler - loading handler @type failed: class @handler could not be loaded. ' .
00036       'Verify the class file has been registered in the corresponding .info-file (files[]).',
00037       array(
00038         '@type' => isset($handler_type) ? ( $type . '(handler type: ' . $handler_type . ')' ) : $type,
00039         '@handler' => $definition['handler']
00040       )
00041     );
00042     return;
00043   }
00044 
00045    if (!empty($definition['override handler'])) {
00046      $handler = new $definition['override handler'];
00047    }
00048    else {
00049      $handler = new $definition['handler'];
00050    }
00051 
00052   $handler->set_definition($definition);
00053   if ($type == 'handler') {
00054     $handler->is_handler = TRUE;
00055     $handler->handler_type = $handler_type;
00056   }
00057   else {
00058     $handler->is_plugin = TRUE;
00059     $handler->plugin_type = $type;
00060   }
00061 
00062   // let the handler have something like a constructor.
00063   $handler->construct();
00064 
00065   return $handler;
00066 }
00067 
00071 function _views_prepare_handler($definition, $data, $field, $type) {
00072   foreach (array('group', 'title', 'title short', 'help', 'real field') as $key) {
00073     if (!isset($definition[$key])) {
00074       // First check the field level
00075       if (!empty($data[$field][$key])) {
00076         $definition[$key] = $data[$field][$key];
00077       }
00078       // Then if that doesn't work, check the table level
00079       elseif (!empty($data['table'][$key])) {
00080         $definition[$key] = $data['table'][$key];
00081       }
00082     }
00083   }
00084 
00085   return _views_create_handler($definition, 'handler', $type);
00086 }
00087 
00091 function views_get_table_join($table, $base_table) {
00092   $data = views_fetch_data($table);
00093   if (isset($data['table']['join'][$base_table])) {
00094     $h = $data['table']['join'][$base_table];
00095     if (!empty($h['handler']) && class_exists($h['handler'])) {
00096       $handler = new $h['handler'];
00097     }
00098     else {
00099       $handler = new views_join();
00100     }
00101 
00102     // Fill in some easy defaults
00103     $handler->definition = $h;
00104     if (empty($handler->definition['table'])) {
00105       $handler->definition['table'] = $table;
00106     }
00107     // If this is empty, it's a direct link.
00108     if (empty($handler->definition['left_table'])) {
00109       $handler->definition['left_table'] = $base_table;
00110     }
00111 
00112     if (isset($h['arguments'])) {
00113       call_user_func_array(array(&$handler, 'construct'), $h['arguments']);
00114     }
00115     else {
00116       $handler->construct();
00117     }
00118 
00119     return $handler;
00120   }
00121 
00122   // DEBUG -- identify missing handlers
00123   vpr("Missing join: @table @base_table", array('@table' => $table, '@base_table' => $base_table));
00124 }
00125 
00152 class views_handler extends views_object {
00158   var $view = NULL;
00159 
00165   var $query = NULL;
00166 
00170   var $handler_type = NULL;
00171 
00175   public $table_alias;
00176 
00181   var $real_field;
00182 
00186   var $relationship = NULL;
00187 
00196   function init(&$view, &$options) {
00197     $this->view = &$view;
00198     $display_id = $this->view->current_display;
00199     // Check to see if this handler type is defaulted. Note that
00200     // we have to do a lookup because the type is singular but the
00201     // option is stored as the plural.
00202 
00203     // If the 'moved to' keyword moved our handler, let's fix that now.
00204     if (isset($this->actual_table)) {
00205       $options['table'] = $this->actual_table;
00206     }
00207 
00208     if (isset($this->actual_field)) {
00209       $options['field'] = $this->actual_field;
00210     }
00211 
00212     $types = views_object_types();
00213     $plural = $this->handler_type;
00214     if (isset($types[$this->handler_type]['plural'])) {
00215       $plural = $types[$this->handler_type]['plural'];
00216     }
00217     if ($this->view->display_handler->is_defaulted($plural)) {
00218       $display_id = 'default';
00219     }
00220 
00221     $this->localization_keys = array(
00222       $display_id,
00223       $this->handler_type,
00224       $options['table'],
00225       $options['id']
00226     );
00227 
00228     $this->unpack_options($this->options, $options);
00229 
00230     // This exist on most handlers, but not all. So they are still optional.
00231     if (isset($options['table'])) {
00232       $this->table = $options['table'];
00233     }
00234 
00235     if (isset($this->definition['real field'])) {
00236       $this->real_field = $this->definition['real field'];
00237     }
00238 
00239     if (isset($this->definition['field'])) {
00240       $this->real_field = $this->definition['field'];
00241     }
00242 
00243     if (isset($options['field'])) {
00244       $this->field = $options['field'];
00245       if (!isset($this->real_field)) {
00246         $this->real_field = $options['field'];
00247       }
00248     }
00249 
00250     $this->query = &$view->query;
00251   }
00252 
00253   function option_definition() {
00254     $options = parent::option_definition();
00255 
00256     $options['id'] = array('default' => '');
00257     $options['table'] = array('default' => '');
00258     $options['field'] = array('default' => '');
00259     $options['relationship'] = array('default' => 'none');
00260     $options['group_type'] = array('default' => 'group');
00261     $options['ui_name'] = array('default' => '');
00262 
00263     return $options;
00264   }
00265 
00269   function ui_name($short = FALSE) {
00270     if (!empty($this->options['ui_name'])) {
00271       $title = check_plain($this->options['ui_name']);
00272       return $title;
00273     }
00274     $title = ($short && isset($this->definition['title short'])) ? $this->definition['title short'] : $this->definition['title'];
00275     return t('!group: !title', array('!group' => $this->definition['group'], '!title' => $title));
00276   }
00277 
00285   function get_field($field = NULL) {
00286     if (!isset($field)) {
00287       if (!empty($this->formula)) {
00288         $field = $this->get_formula();
00289       }
00290       else {
00291         $field = $this->table_alias . '.' . $this->real_field;
00292       }
00293     }
00294 
00295     // If grouping, check to see if the aggregation method needs to modify the field.
00296     if ($this->view->display_handler->use_group_by()) {
00297       $this->view->init_query();
00298       if ($this->query) {
00299         $info = $this->query->get_aggregation_info();
00300         if (!empty($info[$this->options['group_type']]['method']) && function_exists($info[$this->options['group_type']]['method'])) {
00301           return $info[$this->options['group_type']]['method']($this->options['group_type'], $field);
00302         }
00303       }
00304     }
00305 
00306     return $field;
00307   }
00308 
00317   function sanitize_value($value, $type = NULL) {
00318     switch ($type) {
00319       case 'xss':
00320         $value = filter_xss($value);
00321         break;
00322       case 'url':
00323         $value = check_url($value);
00324         break;
00325       default:
00326         $value = check_plain($value);
00327         break;
00328     }
00329     return $value;
00330   }
00331 
00347   function case_transform($string, $option) {
00348     global $multibyte;
00349 
00350     switch ($option) {
00351       default:
00352         return $string;
00353       case 'upper':
00354         return drupal_strtoupper($string);
00355       case 'lower':
00356         return drupal_strtolower($string);
00357       case 'ucfirst':
00358         return drupal_strtoupper(drupal_substr($string, 0, 1)) . drupal_substr($string, 1);
00359       case 'ucwords':
00360         if ($multibyte == UNICODE_MULTIBYTE) {
00361           return mb_convert_case($string, MB_CASE_TITLE);
00362         }
00363         else {
00364           return ucwords($string);
00365         }
00366     }
00367   }
00368 
00372   function options_validate(&$form, &$form_state) { }
00373 
00377   function options_form(&$form, &$form_state) {
00378     // Some form elements belong in a fieldset for presentation, but can't
00379     // be moved into one because of the form_state['values'] hierarchy. Those
00380     // elements can add a #fieldset => 'fieldset_name' property, and they'll
00381     // be moved to their fieldset during pre_render.
00382     $form['#pre_render'][] = 'views_ui_pre_render_add_fieldset_markup';
00383 
00384     $form['ui_name'] = array(
00385       '#type' => 'textfield',
00386       '#title' => t('Administrative title'),
00387       '#description' => t('This title will be displayed on the views edit page instead of the default one. This might be useful if you have the same item twice.'),
00388       '#default_value' => $this->options['ui_name'],
00389       '#fieldset' => 'more',
00390     );
00391 
00392     // This form is long and messy enough that the "Administrative title" option
00393     // belongs in a "more options" fieldset at the bottom of the form.
00394     $form['more'] = array(
00395       '#type' => 'fieldset',
00396       '#title' => t('More'),
00397       '#collapsible' => TRUE,
00398       '#collapsed' => TRUE,
00399       '#weight' => 150,
00400     );
00401     // Allow to alter the default values brought into the form.
00402     drupal_alter('views_handler_options', $this->options, $view);
00403   }
00404 
00409   function options_submit(&$form, &$form_state) { }
00410 
00414   function use_group_by() {
00415     return TRUE;
00416   }
00420   function groupby_form(&$form, &$form_state) {
00421     $view = &$form_state['view'];
00422     $display_id = $form_state['display_id'];
00423     $types = views_object_types();
00424     $type = $form_state['type'];
00425     $id = $form_state['id'];
00426 
00427     $form['#title'] = check_plain($view->display[$display_id]->display_title) . ': ';
00428     $form['#title'] .= t('Configure aggregation settings for @type %item', array('@type' => $types[$type]['lstitle'], '%item' => $this->ui_name()));
00429 
00430     $form['#section'] = $display_id . '-' . $type . '-' . $id;
00431 
00432     $view->init_query();
00433     $info = $view->query->get_aggregation_info();
00434     foreach ($info as $id => $aggregate) {
00435       $group_types[$id] = $aggregate['title'];
00436     }
00437 
00438     $form['group_type'] = array(
00439       '#type' => 'select',
00440       '#title' => t('Aggregation type'),
00441       '#default_value' => $this->options['group_type'],
00442       '#description' => t('Select the aggregation function to use on this field.'),
00443       '#options' => $group_types,
00444     );
00445   }
00446 
00451   function groupby_form_submit(&$form, &$form_state) {
00452     $item =& $form_state['handler']->options;
00453 
00454     $item['group_type'] = $form_state['values']['options']['group_type'];
00455   }
00456 
00461   function has_extra_options() { return FALSE; }
00462 
00466   function extra_options(&$option) { }
00467 
00471   function extra_options_form(&$form, &$form_state) { }
00472 
00476   function extra_options_validate($form, &$form_state) { }
00477 
00482   function extra_options_submit($form, &$form_state) { }
00483 
00487   function can_expose() { return FALSE; }
00488 
00493   function expose_options() { }
00494 
00498   function exposed_info() { }
00499 
00503   function exposed_form(&$form, &$form_state) { }
00504 
00508   function exposed_validate(&$form, &$form_state) { }
00509 
00513   function exposed_submit(&$form, &$form_state) { }
00514 
00518   function expose_form(&$form, &$form_state) { }
00519 
00523   function expose_validate($form, &$form_state) { }
00524 
00529   function expose_submit($form, &$form_state) { }
00530 
00534   function show_expose_button(&$form, &$form_state) { }
00535 
00539   function show_expose_form(&$form, &$form_state) {
00540     if (empty($this->options['exposed'])) {
00541       return;
00542     }
00543 
00544     $this->expose_form($form, $form_state);
00545 
00546     // When we click the expose button, we add new gadgets to the form but they
00547     // have no data in $_POST so their defaults get wiped out. This prevents
00548     // these defaults from getting wiped out. This setting will only be TRUE
00549     // during a 2nd pass rerender.
00550     if (!empty($form_state['force_expose_options'])) {
00551       foreach (element_children($form['expose']) as $id) {
00552         if (isset($form['expose'][$id]['#default_value']) && !isset($form['expose'][$id]['#value'])) {
00553           $form['expose'][$id]['#value'] = $form['expose'][$id]['#default_value'];
00554         }
00555       }
00556     }
00557   }
00558 
00564   function access() {
00565     if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
00566       if (isset($this->definition['access arguments']) && is_array($this->definition['access arguments'])) {
00567         return call_user_func_array($this->definition['access callback'], $this->definition['access arguments']);
00568       }
00569       return $this->definition['access callback']();
00570     }
00571 
00572     return TRUE;
00573   }
00574 
00581   function pre_query() { }
00582 
00590   function post_execute(&$values) { }
00591 
00595   function placeholder() {
00596     return $this->query->placeholder($this->options['table'] . '_' . $this->options['field']);
00597   }
00598 
00603   function set_relationship() {
00604     // Ensure this gets set to something.
00605     $this->relationship = NULL;
00606 
00607     // Don't process non-existant relationships.
00608     if (empty($this->options['relationship']) || $this->options['relationship'] == 'none') {
00609       return;
00610     }
00611 
00612     $relationship = $this->options['relationship'];
00613 
00614     // Ignore missing/broken relationships.
00615     if (empty($this->view->relationship[$relationship])) {
00616       return;
00617     }
00618 
00619     // Check to see if the relationship has already processed. If not, then we
00620     // cannot process it.
00621     if (empty($this->view->relationship[$relationship]->alias)) {
00622       return;
00623     }
00624 
00625     // Finally!
00626     $this->relationship = $this->view->relationship[$relationship]->alias;
00627   }
00628 
00633   function ensure_my_table() {
00634     if (!isset($this->table_alias)) {
00635       if (!method_exists($this->query, 'ensure_table')) {
00636         vpr(t('Ensure my table called but query has no ensure_table method.'));
00637         return;
00638       }
00639       $this->table_alias = $this->query->ensure_table($this->table, $this->relationship);
00640     }
00641     return $this->table_alias;
00642   }
00643 
00647   function admin_summary() { }
00648 
00654   function needs_style_plugin() { return FALSE; }
00655 
00662   function is_exposed() {
00663     return !empty($this->options['exposed']);
00664   }
00665 
00669   function accept_exposed_input($input) { return TRUE; }
00670 
00674   function store_exposed_input($input, $status) { return TRUE; }
00675 
00683   function get_join() {
00684     // get the join from this table that links back to the base table.
00685     // Determine the primary table to seek
00686     if (empty($this->query->relationships[$this->relationship])) {
00687       $base_table = $this->query->base_table;
00688     }
00689     else {
00690       $base_table = $this->query->relationships[$this->relationship]['base'];
00691     }
00692 
00693     $join = views_get_table_join($this->table, $base_table);
00694     if ($join) {
00695       return clone $join;
00696     }
00697   }
00698 
00710   function validate() { return array(); }
00711 
00716   function broken() { }
00717 }
00718 
00733 class views_many_to_one_helper {
00734   function views_many_to_one_helper(&$handler) {
00735     $this->handler = &$handler;
00736   }
00737 
00738   static function option_definition(&$options) {
00739     $options['reduce_duplicates'] = array('default' => FALSE);
00740   }
00741 
00742   function options_form(&$form, &$form_state) {
00743     $form['reduce_duplicates'] = array(
00744       '#type' => 'checkbox',
00745       '#title' => t('Reduce duplicates'),
00746       '#description' => t('This filter can cause items that have more than one of the selected options to appear as duplicate results. If this filter causes duplicate results to occur, this checkbox can reduce those duplicates; however, the more terms it has to search for, the less performant the query will be, so use this with caution. Shouldn\'t be set on single-value fields, as it may cause values to disappear from display, if used on an incompatible field.'),
00747       '#default_value' => !empty($this->handler->options['reduce_duplicates']),
00748       '#weight' => 4,
00749     );
00750   }
00751 
00757   function get_field() {
00758     if (!empty($this->formula)) {
00759       return $this->handler->get_formula();
00760     }
00761     else {
00762       return $this->handler->table_alias . '.' . $this->handler->real_field;
00763     }
00764   }
00765 
00774   function add_table($join = NULL, $alias = NULL) {
00775     // This is used for lookups in the many_to_one table.
00776     $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
00777 
00778     if (empty($join)) {
00779       $join = $this->get_join();
00780     }
00781 
00782     // See if there's a chain between us and the base relationship. If so, we need
00783     // to create a new relationship to use.
00784     $relationship = $this->handler->relationship;
00785 
00786     // Determine the primary table to seek
00787     if (empty($this->handler->query->relationships[$relationship])) {
00788       $base_table = $this->handler->query->base_table;
00789     }
00790     else {
00791       $base_table = $this->handler->query->relationships[$relationship]['base'];
00792     }
00793 
00794     // Cycle through the joins. This isn't as error-safe as the normal
00795     // ensure_path logic. Perhaps it should be.
00796     $r_join = clone $join;
00797     while ($r_join->left_table != $base_table) {
00798       $r_join = views_get_table_join($r_join->left_table, $base_table);
00799     }
00800     // If we found that there are tables in between, add the relationship.
00801     if ($r_join->table != $join->table) {
00802       $relationship = $this->handler->query->add_relationship($this->handler->table . '_' . $r_join->table, $r_join, $r_join->table, $this->handler->relationship);
00803     }
00804 
00805     // And now add our table, using the new relationship if one was used.
00806     $alias = $this->handler->query->add_table($this->handler->table, $relationship, $join, $alias);
00807 
00808     // Store what values are used by this table chain so that other chains can
00809     // automatically discard those values.
00810     if (empty($this->handler->view->many_to_one_tables[$field])) {
00811       $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
00812     }
00813     else {
00814       $this->handler->view->many_to_one_tables[$field] = array_merge($this->handler->view->many_to_one_tables[$field], $this->handler->value);
00815     }
00816 
00817     return $alias;
00818   }
00819 
00820   function get_join() {
00821     return $this->handler->get_join();
00822   }
00823 
00828   function summary_join() {
00829     $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
00830     $join = $this->get_join();
00831 
00832     // shortcuts
00833     $options = $this->handler->options;
00834     $view = &$this->handler->view;
00835     $query = &$this->handler->query;
00836 
00837     if (!empty($options['require_value'])) {
00838       $join->type = 'INNER';
00839     }
00840 
00841     if (empty($options['add_table']) || empty($view->many_to_one_tables[$field])) {
00842       return $query->ensure_table($this->handler->table, $this->handler->relationship, $join);
00843     }
00844     else {
00845       if (!empty($view->many_to_one_tables[$field])) {
00846         foreach ($view->many_to_one_tables[$field] as $value) {
00847           $join->extra = array(
00848             array(
00849               'field' => $this->handler->real_field,
00850               'operator' => '!=',
00851               'value' => $value,
00852               'numeric' => !empty($this->definition['numeric']),
00853             ),
00854           );
00855         }
00856       }
00857       return $this->add_table($join);
00858     }
00859   }
00860 
00865   function ensure_my_table() {
00866     if (!isset($this->handler->table_alias)) {
00867       // Case 1: Operator is an 'or' and we're not reducing duplicates.
00868       // We hence get the absolute simplest:
00869       $field = $this->handler->relationship . '_' . $this->handler->table . '.' . $this->handler->field;
00870       if ($this->handler->operator == 'or' && empty($this->handler->options['reduce_duplicates'])) {
00871         if (empty($this->handler->options['add_table']) && empty($this->handler->view->many_to_one_tables[$field])) {
00872           // query optimization, INNER joins are slightly faster, so use them
00873           // when we know we can.
00874           $join = $this->get_join();
00875           if (isset($join)) {
00876             $join->type = 'INNER';
00877           }
00878           $this->handler->table_alias = $this->handler->query->ensure_table($this->handler->table, $this->handler->relationship, $join);
00879           $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
00880         }
00881         else {
00882           $join = $this->get_join();
00883           $join->type = 'LEFT';
00884           if (!empty($this->handler->view->many_to_one_tables[$field])) {
00885             foreach ($this->handler->view->many_to_one_tables[$field] as $value) {
00886               $join->extra = array(
00887                 array(
00888                   'field' => $this->handler->real_field,
00889                   'operator' => '!=',
00890                   'value' => $value,
00891                   'numeric' => !empty($this->handler->definition['numeric']),
00892                 ),
00893               );
00894             }
00895           }
00896 
00897           $this->handler->table_alias = $this->add_table($join);
00898         }
00899 
00900         return $this->handler->table_alias;
00901       }
00902 
00903       // Case 2: it's an 'and' or an 'or'.
00904       // We do one join per selected value.
00905       if ($this->handler->operator != 'not') {
00906         // Clone the join for each table:
00907         $this->handler->table_aliases = array();
00908         foreach ($this->handler->value as $value) {
00909           $join = $this->get_join();
00910           if ($this->handler->operator == 'and') {
00911             $join->type = 'INNER';
00912           }
00913           $join->extra = array(
00914             array(
00915               'field' => $this->handler->real_field,
00916               'value' => $value,
00917               'numeric' => !empty($this->handler->definition['numeric']),
00918             ),
00919           );
00920 
00921           // The table alias needs to be unique to this value across the
00922           // multiple times the filter or argument is called by the view.
00923           if (!isset($this->handler->view->many_to_one_aliases[$field][$value])) {
00924             if (!isset($this->handler->view->many_to_one_count[$this->handler->table])) {
00925               $this->handler->view->many_to_one_count[$this->handler->table] = 0;
00926             }
00927             $this->handler->view->many_to_one_aliases[$field][$value] = $this->handler->table . '_value_' . ($this->handler->view->many_to_one_count[$this->handler->table]++);
00928           }
00929           $alias = $this->handler->table_aliases[$value] = $this->add_table($join, $this->handler->view->many_to_one_aliases[$field][$value]);
00930 
00931           // and set table_alias to the first of these.
00932           if (empty($this->handler->table_alias)) {
00933             $this->handler->table_alias = $alias;
00934           }
00935         }
00936       }
00937       // Case 3: it's a 'not'.
00938       // We just do one join. We'll add a where clause during
00939       // the query phase to ensure that $table.$field IS NULL.
00940       else {
00941         $join = $this->get_join();
00942         $join->type = 'LEFT';
00943         $join->extra = array();
00944         $join->extra_type = 'OR';
00945         foreach ($this->handler->value as $value) {
00946           $join->extra[] = array(
00947             'field' => $this->handler->real_field,
00948             'value' => $value,
00949             'numeric' => !empty($this->handler->definition['numeric']),
00950           );
00951         }
00952 
00953         $this->handler->table_alias = $this->add_table($join);
00954       }
00955     }
00956     return $this->handler->table_alias;
00957   }
00958 
00962   function placeholder() {
00963     return $this->handler->query->placeholder($this->handler->options['table'] . '_' . $this->handler->options['field']);
00964   }
00965 
00966   function add_filter() {
00967     if (empty($this->handler->value)) {
00968       return;
00969     }
00970     $this->handler->ensure_my_table();
00971 
00972     // Shorten some variables:
00973     $field = $this->get_field();
00974     $options = $this->handler->options;
00975     $operator = $this->handler->operator;
00976     $formula = !empty($this->formula);
00977     $value = $this->handler->value;
00978     if (empty($options['group'])) {
00979       $options['group'] = 0;
00980     }
00981 
00982     // add_condition determines whether a single expression is enough(FALSE) or the
00983     // conditions should be added via an db_or()/db_and() (TRUE).
00984     $add_condition = TRUE;
00985     if ($operator == 'not') {
00986       $value = NULL;
00987       $operator = 'IS NULL';
00988       $add_condition = FALSE;
00989     }
00990     elseif ($operator == 'or' && empty($options['reduce_duplicates'])) {
00991       if (count($value) > 1) {
00992         $operator = 'IN';
00993       }
00994       else {
00995         $value = is_array($value) ? array_pop($value) : $value;
00996         $operator = '=';
00997       }
00998       $add_condition = FALSE;
00999     }
01000 
01001     if (!$add_condition) {
01002       if ($formula) {
01003         $placeholder = $this->placeholder();
01004         if ($operator == 'IN') {
01005           $operator = "$operator IN($placeholder)";
01006         }
01007         else {
01008           $operator = "$operator $placeholder";
01009         }
01010         $placeholders = array(
01011           $placeholder => $value,
01012         ) + $this->placeholders;
01013         $this->handler->query->add_where_expression($options['group'], "$field $operator", $placeholders);
01014       }
01015       else {
01016         $this->handler->query->add_where($options['group'], $field, $value, $operator);
01017       }
01018     }
01019 
01020     if ($add_condition) {
01021       $field = $this->handler->real_field;
01022       $clause = $operator == 'or' ? db_or() : db_and();
01023       foreach ($this->handler->table_aliases as $value => $alias) {
01024         $clause->condition("$alias.$field", $value);
01025       }
01026 
01027       // implode on either AND or OR.
01028       $this->handler->query->add_where($options['group'], $clause);
01029     }
01030   }
01031 }
01032 
01033 /*
01034  * Break x,y,z and x+y+z into an array. Works for strings.
01035  *
01036  * @param $str
01037  *   The string to parse.
01038  * @param $object
01039  *   The object to use as a base. If not specified one will
01040  *   be created.
01041  *
01042  * @return $object
01043  *   An object containing
01044  *   - operator: Either 'and' or 'or'
01045  *   - value: An array of numeric values.
01046  */
01047 function views_break_phrase_string($str, &$handler = NULL) {
01048   if (!$handler) {
01049     $handler = new stdClass();
01050   }
01051 
01052   // Set up defaults:
01053   if (!isset($handler->value)) {
01054     $handler->value = array();
01055   }
01056 
01057   if (!isset($handler->operator)) {
01058     $handler->operator = 'or';
01059   }
01060 
01061   if ($str == '') {
01062     return $handler;
01063   }
01064 
01065   if (preg_match('/^(\w+[+ ])+\w+$/', $str)) {
01066     // The '+' character in a query string may be parsed as ' '.
01067     $handler->operator = 'or';
01068     $handler->value = preg_split('/[+ ]/', $str);
01069   }
01070   else if (preg_match('/^((\w|\s)+,)*(\w|\s)+$/', $str)) {
01071     $handler->operator = 'and';
01072     $handler->value = explode(',', $str);
01073   }
01074 
01075   // Keep an 'error' value if invalid strings were given.
01076   if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
01077     $handler->value = array(-1);
01078     return $handler;
01079   }
01080 
01081   // Doubly ensure that all values are strings only.
01082   foreach ($handler->value as $id => $value) {
01083     $handler->value[$id] = (string) $value;
01084   }
01085 
01086   return $handler;
01087 }
01088 /*
01089  * Break x,y,z and x+y+z into an array. Numeric only.
01090  *
01091  * @param $str
01092  *   The string to parse.
01093  * @param $handler
01094  *   The handler object to use as a base. If not specified one will
01095  *   be created.
01096  *
01097  * @return $handler
01098  *   The new handler object.
01099  */
01100 function views_break_phrase($str, &$handler = NULL) {
01101   if (!$handler) {
01102     $handler = new stdClass();
01103   }
01104 
01105   // Set up defaults:
01106 
01107   if (!isset($handler->value)) {
01108     $handler->value = array();
01109   }
01110 
01111   if (!isset($handler->operator)) {
01112     $handler->operator = 'or';
01113   }
01114 
01115   if (empty($str)) {
01116     return $handler;
01117   }
01118 
01119   if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
01120     // The '+' character in a query string may be parsed as ' '.
01121     $handler->operator = 'or';
01122     $handler->value = preg_split('/[+ ]/', $str);
01123   }
01124   elseif (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
01125     $handler->operator = 'and';
01126     $handler->value = explode(',', $str);
01127   }
01128 
01129   // Keep an 'error' value if invalid strings were given.
01130   if (!empty($str) && (empty($handler->value) || !is_array($handler->value))) {
01131     $handler->value = array(-1);
01132     return $handler;
01133   }
01134 
01135   // Doubly ensure that all values are numeric only.
01136   foreach ($handler->value as $id => $value) {
01137     $handler->value[$id] = intval($value);
01138   }
01139 
01140   return $handler;
01141 }
01142 
01143 // --------------------------------------------------------------------------
01144 // Date helper functions
01145 
01149 function views_get_timezone() {
01150   global $user;
01151   if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
01152     $timezone = $user->timezone;
01153   }
01154   else {
01155     $timezone = variable_get('date_default_timezone', 0);
01156   }
01157 
01158   // set up the database timezone
01159   $db_type = Database::getConnection()->databaseType();
01160   if (in_array($db_type, array('mysql', 'pgsql'))) {
01161     $offset = '+00:00';
01162     static $already_set = FALSE;
01163     if (!$already_set) {
01164       if ($db_type == 'pgsql') {
01165         db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
01166       }
01167       elseif ($db_type == 'mysql') {
01168         db_query("SET @@session.time_zone = '$offset'");
01169       }
01170 
01171       $already_set = true;
01172     }
01173   }
01174 
01175   return $timezone;
01176 }
01177 
01192 function views_date_sql_field($field, $field_type = 'int', $set_offset = NULL) {
01193   $db_type = Database::getConnection()->databaseType();
01194   $offset = $set_offset !== NULL ? $set_offset : views_get_timezone();
01195   if (isset($offset) && !is_numeric($offset)) {
01196     $dtz = new DateTimeZone($offset);
01197     $dt = new DateTime("now", $dtz);
01198     $offset_seconds = $dtz->getOffset($dt);
01199   }
01200 
01201   switch ($db_type) {
01202     case 'mysql':
01203       switch ($field_type) {
01204         case 'int':
01205           $field = "DATE_ADD('19700101', INTERVAL $field SECOND)";
01206           break;
01207         case 'datetime':
01208           break;
01209       }
01210       if (!empty($offset)) {
01211         $field = "($field + INTERVAL $offset_seconds SECOND)";
01212       }
01213       return $field;
01214     case 'pgsql':
01215       switch ($field_type) {
01216         case 'int':
01217           $field = "TO_TIMESTAMP($field)";
01218           break;
01219         case 'datetime':
01220           break;
01221       }
01222       if (!empty($offset)) {
01223         $field = "($field + INTERVAL '$offset_seconds SECONDS')";
01224       }
01225       return $field;
01226     case 'sqlite':
01227       if (!empty($offset)) {
01228         $field = "($field + '$offset_seconds')";
01229       }
01230       return $field;
01231   }
01232 }
01233 
01250 function views_date_sql_format($format, $field, $field_type = 'int', $set_offset = NULL) {
01251   $db_type = Database::getConnection()->databaseType();
01252   $field = views_date_sql_field($field, $field_type, $set_offset);
01253   switch ($db_type) {
01254     case 'mysql':
01255       $replace = array(
01256         'Y' => '%Y',
01257         'y' => '%y',
01258         'M' => '%b',
01259         'm' => '%m',
01260         'n' => '%c',
01261         'F' => '%M',
01262         'D' => '%a',
01263         'd' => '%d',
01264         'l' => '%W',
01265         'j' => '%e',
01266         'W' => '%v',
01267         'H' => '%H',
01268         'h' => '%h',
01269         'i' => '%i',
01270         's' => '%s',
01271         'A' => '%p',
01272         );
01273       $format = strtr($format, $replace);
01274       return "DATE_FORMAT($field, '$format')";
01275     case 'pgsql':
01276       $replace = array(
01277         'Y' => 'YYYY',
01278         'y' => 'YY',
01279         'M' => 'Mon',
01280         'm' => 'MM',
01281         'n' => 'MM', // no format for Numeric representation of a month, without leading zeros
01282         'F' => 'Month',
01283         'D' => 'Dy',
01284         'd' => 'DD',
01285         'l' => 'Day',
01286         'j' => 'DD', // no format for Day of the month without leading zeros
01287         'W' => 'WW',
01288         'H' => 'HH24',
01289         'h' => 'HH12',
01290         'i' => 'MI',
01291         's' => 'SS',
01292         'A' => 'AM',
01293         );
01294       $format = strtr($format, $replace);
01295       return "TO_CHAR($field, '$format')";
01296     case 'sqlite':
01297       $replace = array(
01298         'Y' => '%Y', // 4 digit year number
01299         'y' => '%Y', // no format for 2 digit year number
01300         'M' => '%m', // no format for 3 letter month name
01301         'm' => '%m', // month number with leading zeros
01302         'n' => '%m', // no format for month number without leading zeros
01303         'F' => '%m', // no format for full month name
01304         'D' => '%d', // no format for 3 letter day name
01305         'd' => '%d', // day of month number with leading zeros
01306         'l' => '%d', // no format for full day name
01307         'j' => '%d', // no format for day of month number without leading zeros
01308         'W' => '%W', // ISO week number
01309         'H' => '%H', // 24 hour hour with leading zeros
01310         'h' => '%H', // no format for 12 hour hour with leading zeros
01311         'i' => '%M', // minutes with leading zeros
01312         's' => '%S', // seconds with leading zeros
01313         'A' => '', // no format for  AM/PM
01314       );
01315       $format = strtr($format, $replace);
01316       return "strftime('$format', $field, 'unixepoch')";
01317   }
01318 }
01319 
01336 function views_date_sql_extract($extract_type, $field, $field_type = 'int', $set_offset = NULL) {
01337   $db_type = Database::getConnection()->databaseType();
01338   $field = views_date_sql_field($field, $field_type, $set_offset);
01339 
01340   // Note there is no space after FROM to avoid db_rewrite problems
01341   // see http://drupal.org/node/79904.
01342   switch ($extract_type) {
01343   case('DATE'):
01344     return $field;
01345   case('YEAR'):
01346     return "EXTRACT(YEAR FROM($field))";
01347   case('MONTH'):
01348     return "EXTRACT(MONTH FROM($field))";
01349   case('DAY'):
01350     return "EXTRACT(DAY FROM($field))";
01351   case('HOUR'):
01352     return "EXTRACT(HOUR FROM($field))";
01353   case('MINUTE'):
01354     return "EXTRACT(MINUTE FROM($field))";
01355   case('SECOND'):
01356     return "EXTRACT(SECOND FROM($field))";
01357   case('WEEK'):  // ISO week number for date
01358     switch ($db_type) {
01359       case('mysql'):
01360         // WEEK using arg 3 in mysql should return the same value as postgres EXTRACT
01361         return "WEEK($field, 3)";
01362       case('pgsql'):
01363         return "EXTRACT(WEEK FROM($field))";
01364     }
01365   case('DOW'):
01366     switch ($db_type) {
01367       case('mysql'):
01368         // mysql returns 1 for Sunday through 7 for Saturday
01369         // php date functions and postgres use 0 for Sunday and 6 for Saturday
01370         return "INTEGER(DAYOFWEEK($field) - 1)";
01371       case('pgsql'):
01372         return "EXTRACT(DOW FROM($field))";
01373     }
01374   case('DOY'):
01375     switch ($db_type) {
01376       case('mysql'):
01377         return "DAYOFYEAR($field)";
01378       case('pgsql'):
01379         return "EXTRACT(DOY FROM($field))";
01380     }
01381   }
01382 }
01383 
01439 class views_join {
01440   var $table = NULL;
01441   var $left_table = NULL;
01442   var $left_field = NULL;
01443   var $field = NULL;
01444   var $extra = NULL;
01445   var $type = NULL;
01446   var $definition = array();
01447 
01451   function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT') {
01452     $this->extra_type = 'AND';
01453     if (!empty($table)) {
01454       $this->table = $table;
01455       $this->left_table = $left_table;
01456       $this->left_field = $left_field;
01457       $this->field = $field;
01458       $this->extra = $extra;
01459       $this->type = strtoupper($type);
01460     }
01461     elseif (!empty($this->definition)) {
01462       // if no arguments, construct from definition.
01463       // These four must exist or it will throw notices.
01464       $this->table = $this->definition['table'];
01465       $this->left_table = $this->definition['left_table'];
01466       $this->left_field = $this->definition['left_field'];
01467       $this->field = $this->definition['field'];
01468       if (!empty($this->definition['extra'])) {
01469         $this->extra = $this->definition['extra'];
01470       }
01471       if (!empty($this->definition['extra type'])) {
01472         $this->extra_type = strtoupper($this->definition['extra type']);
01473       }
01474 
01475       $this->type = !empty($this->definition['type']) ? strtoupper($this->definition['type']) : 'LEFT';
01476     }
01477   }
01478 
01491   function build_join($select_query, $table, $view_query) {
01492     if (empty($this->definition['table formula'])) {
01493       $right_table = $this->table;
01494     }
01495     else {
01496       $right_table = $this->definition['table formula'];
01497     }
01498 
01499     if ($this->left_table) {
01500       $left = $view_query->get_table_info($this->left_table);
01501       $left_field = "$left[alias].$this->left_field";
01502     }
01503     else {
01504       // This can be used if left_field is a formula or something. It should be used only *very* rarely.
01505       $left_field = $this->left_field;
01506     }
01507 
01508     $condition = "$left_field = $table[alias].$this->field";
01509     $arguments = array();
01510 
01511     // Tack on the extra.
01512     if (isset($this->extra)) {
01513       if (is_array($this->extra)) {
01514         $extras = array();
01515         foreach ($this->extra as $info) {
01516           $extra = '';
01517           // Figure out the table name. Remember, only use aliases provided
01518           // if at all possible.
01519           $join_table = '';
01520           if (!array_key_exists('table', $info)) {
01521             $join_table = $table['alias'] . '.';
01522           }
01523           elseif (isset($info['table'])) {
01524             $join_table = $info['table'] . '.';
01525           }
01526 
01527           // Convert a single-valued array of values to the single-value case,
01528           // and transform from IN() notation to = notation
01529           if (is_array($info['value']) && count($info['value']) == 1) {
01530             if (empty($info['operator'])) {
01531               $operator = '=';
01532             }
01533             else {
01534               $operator = $info['operator'] == 'NOT IN' ? '!=' : '=';
01535             }
01536             $info['value'] = array_shift($info['value']);
01537           }
01538 
01539           if (is_array($info['value'])) {
01540             // With an array of values, we need multiple placeholders and the
01541             // 'IN' operator is implicit.
01542             foreach ($info['value'] as $value) {
01543               $placeholder_i = ':views_join_condition_' . $select_query->nextPlaceholder();
01544               $arguments[$placeholder_i] = $value;
01545             }
01546 
01547             $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
01548             $placeholder = '( ' . implode(', ', array_keys($arguments)) . ' )';
01549           }
01550           else {
01551             // With a single value, the '=' operator is implicit.
01552             $operator = !empty($info['operator']) ? $info['operator'] : '=';
01553             $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
01554             $arguments[$placeholder] = $info['value'];
01555           }
01556 
01557           $extras[] = "$join_table$info[field] $operator $placeholder";
01558         }
01559 
01560         if ($extras) {
01561           if (count($extras) == 1) {
01562             $condition .= ' AND ' . array_shift($extras);
01563           }
01564           else {
01565             $condition .= ' AND (' . implode(' ' . $this->extra_type . ' ', $extras) . ')';
01566           }
01567         }
01568       }
01569       elseif ($this->extra && is_string($this->extra)) {
01570         $condition .= " AND ($this->extra)";
01571       }
01572     }
01573 
01574     $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
01575   }
01576 }
01577 
01587 class views_join_subquery extends views_join {
01588   function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT') {
01589     parent::construct($table, $left_table, $left_field, $field, $extra, $type);
01590     $this->left_query = $this->definition['left_query'];
01591   }
01592 
01605   function build_join($select_query, $table, $view_query) {
01606     if (empty($this->definition['table formula'])) {
01607       $right_table = "{" . $this->table . "}";
01608     }
01609     else {
01610       $right_table = $this->definition['table formula'];
01611     }
01612 
01613     // Add our join condition, using a subquery on the left instead of a field.
01614     $condition = "($this->left_query) = $table[alias].$this->field";
01615     $arguments = array();
01616 
01617     // Tack on the extra.
01618     // This is just copied verbatim from the parent class, which itself has a bug: http://drupal.org/node/1118100
01619     if (isset($this->extra)) {
01620       if (is_array($this->extra)) {
01621         $extras = array();
01622         foreach ($this->extra as $info) {
01623           $extra = '';
01624           // Figure out the table name. Remember, only use aliases provided
01625           // if at all possible.
01626           $join_table = '';
01627           if (!array_key_exists('table', $info)) {
01628             $join_table = $table['alias'] . '.';
01629           }
01630           elseif (isset($info['table'])) {
01631             $join_table = $info['table'] . '.';
01632           }
01633 
01634           $placeholder = ':views_join_condition_' . $select_query->nextPlaceholder();
01635 
01636           if (is_array($info['value'])) {
01637             $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
01638             // Transform from IN() notation to = notation if just one value.
01639             if (count($info['value']) == 1) {
01640               $info['value'] = array_shift($info['value']);
01641               $operator = $operator == 'NOT IN' ? '!=' : '=';
01642             }
01643           }
01644           else {
01645             $operator = !empty($info['operator']) ? $info['operator'] : '=';
01646           }
01647 
01648           $extras[] = "$join_table$info[field] $operator $placeholder";
01649           $arguments[$placeholder] = $info['value'];
01650         }
01651 
01652         if ($extras) {
01653           if (count($extras) == 1) {
01654             $condition .= ' AND ' . array_shift($extras);
01655           }
01656           else {
01657             $condition .= ' AND (' . implode(' ' . $this->extra_type . ' ', $extras) . ')';
01658           }
01659         }
01660       }
01661       elseif ($this->extra && is_string($this->extra)) {
01662         $condition .= " AND ($this->extra)";
01663       }
01664     }
01665 
01666     $select_query->addJoin($this->type, $right_table, $table['alias'], $condition, $arguments);
01667   }
01668 }
01669 

Generated on Sun Feb 26 2012 12:52:51 for Views by  doxygen 1.7.1