[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/modules/views/includes/ -> handlers.inc (source)

   1  <?php
   2  // $Id: handlers.inc,v 1.119.2.4 2009/12/24 01:08:31 merlinofchaos Exp $
   3  /**
   4   * @file handlers.inc
   5   * Defines the various handler objects to help build and display views.
   6   */
   7  
   8  /**
   9   * Instantiate and construct a new handler
  10   */
  11  function _views_create_handler($definition, $type = 'handler') {
  12  //  vpr('Instantiating handler ' . $definition['handler']);
  13    if (empty($definition['handler'])) {
  14      return;
  15    }
  16  
  17    if (!class_exists($definition['handler']) && !views_include_handler($definition, $type)) {
  18      return;
  19    }
  20  
  21    $handler = new $definition['handler'];
  22    $handler->set_definition($definition);
  23    // let the handler have something like a constructor.
  24    $handler->construct();
  25  
  26    return $handler;
  27  }
  28  
  29  /**
  30   * Attempt to find the include file for a given handler from its definition.
  31   *
  32   * This will also attempt to include all parents, though we're maxing the
  33   * parent chain to 10 to prevent infinite loops.
  34   */
  35  function views_include_handler($definition, $type, $count = 0) {
  36    // Do not proceed if the class already exists.
  37    if (isset($definition['handler']) && class_exists($definition['handler'])) {
  38      return TRUE;
  39    }
  40  
  41    // simple infinite loop prevention.
  42    if ($count > 10) {
  43      vpr(t('Handler @handler include tried to loop infinitely!', array('@handler' => $definition['handler'])));
  44      return FALSE;
  45    }
  46  
  47    if (!isset($definition['path'])) {
  48      if ($type == 'handler') {
  49        $definition += views_fetch_handler_data($definition['handler']);
  50      }
  51      else {
  52        $definition += views_fetch_plugin_data($type, $definition['handler']);
  53      }
  54    }
  55  
  56    if (!empty($definition['parent'])) {
  57      if ($type == 'handler') {
  58        $parent = views_fetch_handler_data($definition['parent']);
  59      }
  60      else {
  61        $parent = views_fetch_plugin_data($type, $definition['parent']);
  62      }
  63  
  64      if ($parent) {
  65        $rc = views_include_handler($parent, $type, $count + 1);
  66        // If the parent chain cannot be included, don't try; this will
  67        // help alleviate problems with modules with cross dependencies.
  68        if (!$rc) {
  69          return FALSE;
  70        }
  71      }
  72    }
  73  
  74    if (isset($definition['path']) && $definition['file']) {
  75      $filename = './' . $definition['path'] . '/' . $definition['file'];
  76      if (file_exists($filename)) {
  77        require_once $filename;
  78      }
  79    }
  80  
  81    return class_exists($definition['handler']);
  82  }
  83  
  84  /**
  85   * Prepare a handler's data by checking defaults and such.
  86   */
  87  function _views_prepare_handler($definition, $data, $field) {
  88    foreach (array('group', 'title', 'title short', 'help', 'real field') as $key) {
  89      if (!isset($definition[$key])) {
  90        // First check the field level
  91        if (!empty($data[$field][$key])) {
  92          $definition[$key] = $data[$field][$key];
  93        }
  94        // Then if that doesn't work, check the table level
  95        else if (!empty($data['table'][$key])) {
  96          $definition[$key] = $data['table'][$key];
  97        }
  98      }
  99    }
 100  
 101    return _views_create_handler($definition);
 102  }
 103  
 104  /**
 105   * Fetch the handler data from cache.
 106   */
 107  function views_fetch_handler_data($handler = NULL) {
 108    static $cache = NULL;
 109    if (!isset($cache)) {
 110      $start = views_microtime();
 111  
 112      $cache = views_discover_handlers();
 113  
 114      vpr('Views handlers build time: ' . (views_microtime() - $start) * 1000 . ' ms');
 115    }
 116  
 117    if (!$handler) {
 118      return $cache;
 119    }
 120    else if (isset($cache[$handler])) {
 121      return $cache[$handler];
 122    }
 123  
 124    // Return an empty array if there is no match.
 125    return array();
 126  }
 127  
 128  /**
 129   * Builds and return a list of all handlers available in the system.
 130   *
 131   * @return Nested array of handlers
 132   */
 133  function views_discover_handlers() {
 134    $cache = array();
 135    // Get handlers from all modules.
 136    foreach (module_implements('views_handlers') as $module) {
 137      $function = $module . '_views_handlers';
 138      $result = $function();
 139      if (!is_array($result)) {
 140        continue;
 141      }
 142  
 143      $module_dir = isset($result['info']['module']) ? $result['info']['module'] : $module;
 144      $path = isset($result['info']['path']) ? $result['info']['path'] : drupal_get_path('module', $module_dir);
 145  
 146      foreach ($result['handlers'] as $handler => $def) {
 147        if (!isset($def['module'])) {
 148          $def['module'] = $module_dir;
 149        }
 150        if (!isset($def['path'])) {
 151          $def['path'] = $path;
 152        }
 153        if (!isset($def['file'])) {
 154          $def['file'] = "$handler.inc";
 155        }
 156        if (!isset($def['handler'])) {
 157          $def['handler'] = $handler;
 158        }
 159        // merge the new data in
 160        $cache[$handler] = $def;
 161      }
 162    }
 163    return $cache;
 164  }
 165  
 166  /**
 167   * Fetch a handler to join one table to a primary table from the data cache
 168   */
 169  function views_get_table_join($table, $base_table) {
 170    $data = views_fetch_data($table);
 171    if (isset($data['table']['join'][$base_table])) {
 172      $h = $data['table']['join'][$base_table];
 173      if (!empty($h['handler']) && class_exists($h['handler'])) {
 174        $handler = new $h['handler'];
 175      }
 176      else {
 177        $handler = new views_join();
 178      }
 179  
 180      // Fill in some easy defaults
 181      $handler->definition = $h;
 182      if (empty($handler->definition['table'])) {
 183        $handler->definition['table'] = $table;
 184      }
 185      // If this is empty, it's a direct link.
 186      if (empty($handler->definition['left_table'])) {
 187        $handler->definition['left_table'] = $base_table;
 188      }
 189  
 190      if (isset($h['arguments'])) {
 191        call_user_func_array(array(&$handler, 'construct'), $h['arguments']);
 192      }
 193      else {
 194        $handler->construct();
 195      }
 196  
 197      return $handler;
 198    }
 199    // DEBUG -- identify missing handlers
 200    vpr("Missing join: $table $base_table");
 201  }
 202  
 203  /**
 204   * Base handler, from which all the other handlers are derived.
 205   * It creates a common interface to create consistency amongst
 206   * handlers and data.
 207   *
 208   * This class would be abstract in PHP5, but PHP4 doesn't understand that.
 209   *
 210   * Definition terms:
 211   * - table: The actual table this uses; only specify if different from
 212   *          the table this is attached to.
 213   * - real field: The actual field this uses; only specify if different from
 214   *               the field this item is attached to.
 215   * - group: A text string representing the 'group' this item is attached to,
 216   *          for display in the UI. Examples: "Node", "Taxonomy", "Comment",
 217   *          "User", etc. This may be inherited from the parent definition or
 218   *          the 'table' definition.
 219   * - title: The title for this handler in the UI. This may be inherited from
 220   *          the parent definition or the 'table' definition.
 221   * - help: A more informative string to give to the user to explain what this
 222   *         field/handler is or does.
 223   * - access callback: If this field should have access control, this could
 224   *                    be a function to use. 'user_access' is a common
 225   *                    function to use here. If not specified, no access
 226   *                    control is provided.
 227   * - access arguments: An array of arguments for the access callback.
 228   */
 229  class views_handler extends views_object {
 230    /**
 231     * init the handler with necessary data.
 232     * @param $view
 233     *   The $view object this handler is attached to.
 234     * @param $options
 235     *   The item from the database; the actual contents of this will vary
 236     *   based upon the type of handler.
 237     */
 238    function init(&$view, $options) {
 239      $this->view = &$view;
 240      $this->unpack_options($this->options, $options);
 241  
 242      // This exist on most handlers, but not all. So they are still optional.
 243      if (isset($options['table'])) {
 244        $this->table = $options['table'];
 245      }
 246  
 247      if (isset($this->definition['real field'])) {
 248        $this->real_field = $this->definition['real field'];
 249      }
 250  
 251      if (isset($this->definition['field'])) {
 252        $this->real_field = $this->definition['field'];
 253      }
 254  
 255      if (isset($options['field'])) {
 256        $this->field = $options['field'];
 257        if (!isset($this->real_field)) {
 258          $this->real_field = $options['field'];
 259        }
 260      }
 261  
 262      $this->query = &$view->query;
 263    }
 264  
 265    /**
 266     * Return a string representing this handler's name in the UI.
 267     */
 268    function ui_name($short = FALSE) {
 269      $title = ($short && isset($this->definition['title short'])) ? $this->definition['title short'] : $this->definition['title'];
 270      return t('!group: !title', array('!group' => $this->definition['group'], '!title' => $title));
 271    }
 272  
 273    /**
 274     * Provide a form for setting options.
 275     */
 276    function options_form(&$form, &$form_state) { }
 277  
 278    /**
 279     * Validate the options form.
 280     */
 281    function options_validate($form, &$form_state) { }
 282  
 283    /**
 284     * Perform any necessary changes to the form values prior to storage.
 285     * There is no need for this function to actually store the data.
 286     */
 287    function options_submit($form, &$form_state) { }
 288  
 289    /**
 290     * If a handler has 'extra options' it will get a little settings widget and
 291     * another form called extra_options.
 292     */
 293    function has_extra_options() { return FALSE; }
 294  
 295    /**
 296     * Provide defaults for the handler.
 297     */
 298    function extra_options(&$option) { }
 299  
 300    /**
 301     * Provide a form for setting options.
 302     */
 303    function extra_options_form(&$form, &$form_state) { }
 304  
 305    /**
 306     * Validate the options form.
 307     */
 308    function extra_options_validate($form, &$form_state) { }
 309  
 310    /**
 311     * Perform any necessary changes to the form values prior to storage.
 312     * There is no need for this function to actually store the data.
 313     */
 314    function extra_options_submit($form, &$form_state) { }
 315  
 316    /**
 317     * Set new exposed option defaults when exposed setting is flipped
 318     * on.
 319     */
 320    function expose_options() { }
 321    /**
 322     * Render our chunk of the exposed filter form when selecting
 323     */
 324    function exposed_form(&$form, &$form_state) { }
 325  
 326    /**
 327     * Validate the exposed filter form
 328     */
 329    function exposed_validate(&$form, &$form_state) { }
 330  
 331    /**
 332     * Submit the exposed filter form
 333     */
 334    function exposed_submit(&$form, &$form_state) { }
 335  
 336    /**
 337     * Get information about the exposed form for the form renderer.
 338     *
 339     * @return
 340     *   An array with the following keys:
 341     *   - operator: The $form key of the operator. Set to NULL if no operator.
 342     *   - value: The $form key of the value. Set to NULL if no value.
 343     *   - label: The label to use for this piece.
 344     */
 345    function exposed_info() { }
 346  
 347    /**
 348     * Determine if a handler can be exposed.
 349     */
 350    function can_expose() { return FALSE; }
 351  
 352   /**
 353    * Check whether current user has access to this handler.
 354    *
 355    * @return boolean
 356    */
 357    function access() {
 358      if (isset($this->definition['access callback']) && function_exists($this->definition['access callback'])) {
 359        if (isset($this->definition['access arguments']) && is_array($this->definition['access arguments'])) {
 360          return call_user_func_array($this->definition['access callback'], $this->definition['access arguments']);
 361        }
 362        return $this->definition['access callback']();
 363      }
 364  
 365      return TRUE;
 366    }
 367  
 368    /**
 369     * Run before the view is built.
 370     *
 371     * This gives all the handlers some time to set up before any handler has
 372     * been fully run.
 373     */
 374    function pre_query() { }
 375  
 376    /**
 377     * Called just prior to query(), this lets a handler set up any relationship
 378     * it needs.
 379     */
 380    function set_relationship() {
 381      // Ensure this gets set to something.
 382      $this->relationship = NULL;
 383  
 384      // Don't process non-existant relationships.
 385      if (empty($this->options['relationship']) || $this->options['relationship'] == 'none') {
 386        return;
 387      }
 388  
 389      $relationship = $this->options['relationship'];
 390  
 391      // Ignore missing/broken relationships.
 392      if (empty($this->view->relationship[$relationship])) {
 393        return;
 394      }
 395  
 396      // Check to see if the relationship has already processed. If not, then we
 397      // cannot process it.
 398      if (empty($this->view->relationship[$relationship]->alias)) {
 399        return;
 400      }
 401  
 402      // Finally!
 403      $this->relationship = $this->view->relationship[$relationship]->alias;
 404    }
 405  
 406    /**
 407     * Add this handler into the query.
 408     *
 409     * If we were using PHP5, this would be abstract.
 410     */
 411    function query() { }
 412  
 413    /**
 414     * Ensure the main table for this handler is in the query. This is used
 415     * a lot.
 416     */
 417    function ensure_my_table() {
 418      if (!isset($this->table_alias)) {
 419        $this->table_alias = $this->query->ensure_table($this->table, $this->relationship);
 420      }
 421      return $this->table_alias;
 422    }
 423  
 424    /**
 425     * Provide text for the administrative summary
 426     */
 427    function admin_summary() { }
 428  
 429    /**
 430     * Determine if the argument needs a style plugin.
 431     *
 432     * @return TRUE/FALSE
 433     */
 434    function needs_style_plugin() { return FALSE; }
 435  
 436    /**
 437     * Determine if this item is 'exposed', meaning it provides form elements
 438     * to let users modify the view.
 439     *
 440     * @return TRUE/FALSE
 441     */
 442    function is_exposed() {
 443      return !empty($this->options['exposed']);
 444    }
 445  
 446    /**
 447     * Take input from exposed filters and assign to this handler, if necessary.
 448     */
 449    function accept_exposed_input($input) { return TRUE; }
 450  
 451    /**
 452     * If set to remember exposed input in the session, store it there.
 453     */
 454    function store_exposed_input($input, $status) { return TRUE; }
 455  
 456    /**
 457     * Get the join object that should be used for this handler.
 458     *
 459     * This method isn't used a great deal, but it's very handy for easily
 460     * getting the join if it is necessary to make some changes to it, such
 461     * as adding an 'extra'.
 462     */
 463    function get_join() {
 464      // get the join from this table that links back to the base table.
 465      // Determine the primary table to seek
 466      if (empty($this->query->relationships[$this->relationship])) {
 467        $base_table = $this->query->base_table;
 468      }
 469      else {
 470        $base_table = $this->query->relationships[$this->relationship]['base'];
 471      }
 472  
 473      $join = views_get_table_join($this->table, $base_table);
 474      if ($join) {
 475        return drupal_clone($join);
 476      }
 477    }
 478  
 479    /**
 480     * Validates the handler against the complete View.
 481     *
 482     * This is called when the complete View is being validated. For validating
 483     * the handler options form use options_validate().
 484     *
 485     * @see views_handler::options_validate()
 486     *
 487     * @return
 488     *   Empty array if the handler is valid; an array of error strings if it is not.
 489     */
 490    function validate() { return array(); }
 491  
 492    /**
 493     * Determine if the handler is considered 'broken', meaning it's a
 494     * a placeholder used when a handler can't be found.
 495     */
 496    function broken() { }
 497  }
 498  
 499  /**
 500   * This many to one helper object is used on both arguments and filters.
 501   *
 502   * @todo This requires extensive documentation on how this class is to
 503   * be used. For now, look at the arguments and filters that use it. Lots
 504   * of stuff is just pass-through but there are definitely some interesting
 505   * areas where they interact.
 506   *
 507   * Any handler that uses this can have the following possibly additional
 508   * definition terms:
 509   * - numeric: If true, treat this field as numeric, using %d instead of %s in
 510   *            queries.
 511   *
 512   */
 513  class views_many_to_one_helper {
 514    function views_many_to_one_helper(&$handler) {
 515      $this->handler = &$handler;
 516    }
 517  
 518    function option_definition(&$options) {
 519      $options['reduce_duplicates'] = array('default' => FALSE);
 520    }
 521  
 522    function options_form(&$form, &$form_state) {
 523      $form['reduce_duplicates'] = array(
 524        '#type' => 'checkbox',
 525        '#title' => t('Reduce duplicates'),
 526        '#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.'),
 527        '#default_value' => !empty($this->handler->options['reduce_duplicates']),
 528      );
 529    }
 530  
 531    /**
 532     * Sometimes the handler might want us to use some kind of formula, so give
 533     * it that option. If it wants us to do this, it must set $helper->formula = TRUE
 534     * and implement handler->get_formula();
 535     */
 536    function get_field() {
 537      if (!empty($this->formula)) {
 538        return $this->handler->get_formula();
 539      }
 540      else {
 541        return $this->handler->table_alias . '.' . $this->handler->real_field;
 542      }
 543    }
 544  
 545    /**
 546     * Add a table to the query.
 547     *
 548     * This is an advanced concept; not only does it add a new instance of the table,
 549     * but it follows the relationship path all the way down to the relationship
 550     * link point and adds *that* as a new relationship and then adds the table to
 551     * the relationship, if necessary.
 552     */
 553    function add_table($join = NULL, $alias = NULL) {
 554      // This is used for lookups in the many_to_one table.
 555      $field = $this->handler->table . '.' . $this->handler->field;
 556  
 557      if (empty($join)) {
 558        $join = $this->get_join();
 559      }
 560  
 561      // See if there's a chain between us and the base relationship. If so, we need
 562      // to create a new relationship to use.
 563      $relationship = $this->handler->relationship;
 564  
 565      // Determine the primary table to seek
 566      if (empty($this->handler->query->relationships[$relationship])) {
 567        $base_table = $this->handler->query->base_table;
 568      }
 569      else {
 570        $base_table = $this->handler->query->relationships[$relationship]['base'];
 571      }
 572  
 573      // Cycle through the joins. This isn't as error-safe as the normal
 574      // ensure_path logic. Perhaps it should be.
 575      $r_join = drupal_clone($join);
 576      while ($r_join->left_table != $base_table) {
 577        $r_join = views_get_table_join($r_join->left_table, $base_table);
 578      }
 579      // If we found that there are tables in between, add the relationship.
 580      if ($r_join->table != $join->table) {
 581        $relationship = $this->handler->query->add_relationship($this->handler->table . '_' . $r_join->table, $r_join, $r_join->table, $this->handler->relationship);
 582      }
 583  
 584      // And now add our table, using the new relationship if one was used.
 585      $alias = $this->handler->query->add_table($this->handler->table, $relationship, $join, $alias);
 586  
 587      // Store what values are used by this table chain so that other chains can
 588      // automatically discard those values.
 589      if (empty($this->handler->view->many_to_one_tables[$field])) {
 590        $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
 591      }
 592      else {
 593        $this->handler->view->many_to_one_tables[$field] = array_merge($this->handler->view->many_to_one_tables[$field], $this->handler->value);
 594      }
 595  
 596      return $alias;
 597    }
 598  
 599    function get_join() {
 600      return $this->handler->get_join();
 601    }
 602  
 603    /**
 604     * Provide the proper join for summary queries. This is important in part because
 605     * it will cooperate with other arguments if possible.
 606     */
 607    function summary_join() {
 608      $field = $this->handler->table . '.' . $this->handler->field;
 609      $join = $this->get_join();
 610  
 611      // shortcuts
 612      $options = $this->handler->options;
 613      $view = &$this->handler->view;
 614      $query = &$this->handler->query;
 615  
 616      if (!empty($options['require_value'])) {
 617        $join->type = 'INNER';
 618      }
 619  
 620      if (empty($options['add_table']) || empty($view->many_to_one_tables[$field])) {
 621        return $query->ensure_table($this->handler->table, $this->handler->relationship, $join);
 622      }
 623      else {
 624        if (!empty($view->many_to_one_tables[$field])) {
 625          foreach ($view->many_to_one_tables[$field] as $value) {
 626            $join->extra = array(
 627              array(
 628                'field' => $this->handler->real_field,
 629                'operator' => '!=',
 630                'value' => $value,
 631                'numeric' => !empty($this->definition['numeric']),
 632              ),
 633            );
 634          }
 635        }
 636        return $this->add_table($join);
 637      }
 638    }
 639  
 640    /**
 641     * Override ensure_my_table so we can control how this joins in.
 642     * The operator actually has influence over joining.
 643     */
 644    function ensure_my_table() {
 645      if (!isset($this->handler->table_alias)) {
 646        // For 'or' if we're not reducing duplicates, we get the absolute simplest:
 647        $field = $this->handler->table . '.' . $this->handler->field;
 648        if ($this->handler->operator == 'or' && empty($this->handler->options['reduce_duplicates'])) {
 649          if (empty($this->handler->options['add_table']) && empty($this->handler->view->many_to_one_tables[$field])) {
 650            // query optimization, INNER joins are slightly faster, so use them
 651            // when we know we can.
 652            $join = $this->get_join();
 653            $join->type = 'INNER';
 654            $this->handler->table_alias = $this->handler->query->ensure_table($this->handler->table, $this->handler->relationship, $join);
 655            $this->handler->view->many_to_one_tables[$field] = $this->handler->value;
 656          }
 657          else {
 658            $join = $this->get_join();
 659            $join->type = 'LEFT';
 660            if (!empty($this->handler->view->many_to_one_tables[$field])) {
 661              foreach ($this->handler->view->many_to_one_tables[$field] as $value) {
 662                $join->extra = array(
 663                  array(
 664                    'field' => $this->handler->real_field,
 665                    'operator' => '!=',
 666                    'value' => $value,
 667                    'numeric' => !empty($this->handler->definition['numeric']),
 668                  ),
 669                );
 670              }
 671            }
 672  
 673            $this->handler->table_alias = $this->add_table($join);
 674          }
 675  
 676          return $this->handler->table_alias;
 677        }
 678  
 679        if ($this->handler->operator != 'not') {
 680          // If it's an and or an or, we do one join per selected value.
 681          // Clone the join for each table:
 682          $this->handler->table_aliases = array();
 683          foreach ($this->handler->value as $value) {
 684            $join = $this->get_join();
 685            if ($this->handler->operator == 'and') {
 686              $join->type = 'INNER';
 687            }
 688            $join->extra = array(
 689              array(
 690                'field' => $this->handler->real_field,
 691                'value' => $value,
 692                'numeric' => !empty($this->handler->definition['numeric']),
 693              ),
 694            );
 695  
 696            // The table alias needs to be unique to this value across the
 697            // multiple times the filter or argument is called by the view.
 698            if (!isset($this->handler->view->many_to_one_aliases[$field][$value])) {
 699              if (!isset($this->handler->view->many_to_one_count[$this->handler->table])) {
 700                $this->handler->view->many_to_one_count[$this->handler->table] = 0;
 701              }
 702              $this->handler->view->many_to_one_aliases[$field][$value] = $this->handler->table . '_value_' . ($this->handler->view->many_to_one_count[$this->handler->table]++);
 703            }
 704            $alias = $this->handler->table_aliases[$value] = $this->add_table($join, $this->handler->view->many_to_one_aliases[$field][$value]);
 705  
 706            // and set table_alias to the first of these.
 707            if (empty($this->handler->table_alias)) {
 708              $this->handler->table_alias = $alias;
 709            }
 710          }
 711        }
 712        else {
 713          // For not, we just do one join. We'll add a where clause during
 714          // the query phase to ensure that $table.$field IS NULL.
 715          $join = $this->get_join();
 716          $join->type = 'LEFT';
 717          $join->extra = array();
 718          $join->extra_type = 'OR';
 719          foreach ($this->handler->value as $value) {
 720            $join->extra[] = array(
 721              'field' => $this->handler->real_field,
 722              'value' => $value,
 723              'numeric' => !empty($this->handler->definition['numeric']),
 724            );
 725          }
 726  
 727          $this->handler->table_alias = $this->add_table($join);
 728        }
 729      }
 730      return $this->handler->table_alias;
 731    }
 732  
 733    function add_filter() {
 734      if (empty($this->handler->value)) {
 735        return;
 736      }
 737      $this->handler->ensure_my_table();
 738  
 739      // Shorten some variables:
 740      $field = $this->get_field();
 741      $options = $this->handler->options;
 742      $operator = $this->handler->operator;
 743      if (empty($options['group'])) {
 744        $options['group'] = 0;
 745      }
 746  
 747      $placeholder = !empty($this->handler->definition['numeric']) ? '%d' : "'%s'";
 748  
 749      if ($operator == 'not') {
 750        $this->handler->query->add_where($options['group'], "$field IS NULL");
 751      }
 752      else if ($operator == 'or' && empty($options['reduce_duplicates'])) {
 753        if (count($this->handler->value) > 1) {
 754          $replace = array_fill(0, sizeof($this->handler->value), $placeholder);
 755          $in = '(' . implode(", ", $replace) . ')';
 756          $this->handler->query->add_where($options['group'], "$field IN $in", $this->handler->value);
 757        }
 758        else {
 759          $this->handler->query->add_where($options['group'], "$field = $placeholder", $this->handler->value);
 760        }
 761      }
 762      else {
 763        $field = $this->handler->real_field;
 764        $clauses = array();
 765        foreach ($this->handler->table_aliases as $value => $alias) {
 766          $clauses[] = "$alias.$field = $placeholder";
 767        }
 768  
 769        $group = empty($options['group']) ? 0 : $options['group'];
 770  
 771        // implode on either AND or OR.
 772        $this->handler->query->add_where($group, implode(' ' . strtoupper($operator) . ' ', $clauses), $this->handler->value);
 773      }
 774    }
 775  }
 776  
 777  /*
 778   * Break x,y,z and x+y+z into an array. Numeric only.
 779   *
 780   * @param $str
 781   *   The string to parse.
 782   * @param $filter
 783   *   The filter object to use as a base.
 784   *
 785   * @return $filter
 786   *   The new filter object.
 787   */
 788  function views_break_phrase($str, &$filter) {
 789    if (!$filter) {
 790      $filter = new stdClass();
 791    }
 792  
 793    // Set up defaults:
 794    if (!isset($filter->value)) {
 795      $filter->value = array();
 796    }
 797  
 798    if (!isset($filter->operator)) {
 799      $filter->operator = 'or';
 800    }
 801  
 802    if ($str == '') {
 803      return $filter;
 804    }
 805  
 806    if (preg_match('/^([0-9]+[+ ])+[0-9]+$/', $str)) {
 807      // The '+' character in a query string may be parsed as ' '.
 808      $filter->operator = 'or';
 809      $filter->value = preg_split('/[+ ]/', $str);
 810    }
 811    else if (preg_match('/^([0-9]+,)*[0-9]+$/', $str)) {
 812      $filter->operator = 'and';
 813      $filter->value = explode(',', $str);
 814    }
 815  
 816    // Keep an 'error' value if invalid strings were given.
 817    if (!empty($str) && (empty($filter->value) || !is_array($filter->value))) {
 818      $filter->value = array(-1);
 819      return $filter;
 820    }
 821  
 822    // Doubly ensure that all values are numeric only.
 823    foreach ($filter->value as $id => $value) {
 824      $filter->value[$id] = intval($value);
 825    }
 826  
 827    return $filter;
 828  }
 829  
 830  // --------------------------------------------------------------------------
 831  // Date helper functions
 832  
 833  /**
 834   * Figure out what timezone we're in; needed for some date manipulations.
 835   */
 836  function views_get_timezone() {
 837    global $user;
 838    if (variable_get('configurable_timezones', 1) && $user->uid && strlen($user->timezone)) {
 839      $timezone = $user->timezone;
 840    }
 841    else {
 842      $timezone = variable_get('date_default_timezone', 0);
 843    }
 844  
 845    // set up the database timezone
 846    if (in_array($GLOBALS['db_type'], array('mysql', 'mysqli', 'pgsql'))) {
 847      $offset = '+00:00';
 848      static $already_set = false;
 849      if (!$already_set) {
 850        if ($GLOBALS['db_type'] == 'pgsql') {
 851          db_query("SET TIME ZONE INTERVAL '$offset' HOUR TO MINUTE");
 852        }
 853        elseif ($GLOBALS['db_type'] == 'mysqli' || version_compare(mysql_get_server_info(), '4.1.3', '>=')) {
 854          db_query("SET @@session.time_zone = '$offset'");
 855        }
 856  
 857        $already_set = true;
 858      }
 859    }
 860  
 861    return $timezone;
 862  }
 863  
 864  /**
 865   * Helper function to create cross-database SQL dates.
 866   *
 867   * @param $field
 868   *   The real table and field name, like 'tablename.fieldname'.
 869   * @param $field_type
 870   *  The type of date field, 'int' or 'datetime'.
 871   * @param $set_offset
 872   *   The name of a field that holds the timezone offset or a fixed timezone
 873   *   offset value. If not provided, the normal Drupal timezone handling
 874   *   will be used, i.e. $set_offset = 0 will make no timezone adjustment.
 875   * @return
 876   *   An appropriate SQL string for the db type and field type.
 877   */
 878  function views_date_sql_field($field, $field_type = 'int', $set_offset = NULL) {
 879    $db_type = $GLOBALS['db_type'];
 880    $offset = $set_offset !== NULL ? $set_offset : views_get_timezone();
 881    switch ($db_type) {
 882      case 'mysql':
 883      case 'mysqli':
 884        switch ($field_type) {
 885          case 'int':
 886            $field = "FROM_UNIXTIME($field)";
 887            break;
 888          case 'datetime':
 889            break;
 890        }
 891        if (!empty($offset)) {
 892          $field = "($field + INTERVAL $offset SECOND)";
 893        }
 894        return $field;
 895      case 'pgsql':
 896        switch ($field_type) {
 897          case 'int':
 898            $field = "$field::ABSTIME";
 899            break;
 900          case 'datetime':
 901            break;
 902        }
 903        if (!empty($offset)) {
 904          $field = "($field + INTERVAL '$offset SECONDS')";
 905        }
 906        return $field;
 907    }
 908  }
 909  
 910  /**
 911   * Helper function to create cross-database SQL date formatting.
 912   *
 913   * @param $format
 914   *   A format string for the result, like 'Y-m-d H:i:s'.
 915   * @param $field
 916   *   The real table and field name, like 'tablename.fieldname'.
 917   * @param $field_type
 918   *   The type of date field, 'int' or 'datetime'.
 919   * @param $set_offset
 920   *   The name of a field that holds the timezone offset or a fixed timezone
 921   *   offset value. If not provided, the normal Drupal timezone handling
 922   *   will be used, i.e. $set_offset = 0 will make no timezone adjustment.
 923   * @return
 924   *   An appropriate SQL string for the db type and field type.
 925   */
 926  function views_date_sql_format($format, $field, $field_type = 'int', $set_offset = NULL) {
 927    $db_type = $GLOBALS['db_type'];
 928    $field = views_date_sql_field($field, $field_type, $set_offset);
 929    switch ($db_type) {
 930      case 'mysql':
 931      case 'mysqli':
 932        $replace = array(
 933          'Y' => '%Y',
 934          'm' => '%m',
 935          'd' => '%%d',
 936          'H' => '%H',
 937          'i' => '%i',
 938          's' => '%s',
 939          );
 940        $format = strtr($format, $replace);
 941        return "DATE_FORMAT($field, '$format')";
 942      case 'pgsql':
 943        $replace = array(
 944          'Y' => 'YYYY',
 945          'm' => 'MM',
 946          'd' => 'DD',
 947          'H' => 'HH24',
 948          'i' => 'MI',
 949          's' => 'SS',
 950          );
 951        $format = strtr($format, $replace);
 952        return "TO_CHAR($field, '$format')";
 953    }
 954  }
 955  
 956  /**
 957   * Helper function to create cross-database SQL date extraction.
 958   *
 959   * @param $extract_type
 960   *   The type of value to extract from the date, like 'MONTH'.
 961   * @param $field
 962   *   The real table and field name, like 'tablename.fieldname'.
 963   * @param $field_type
 964   *   The type of date field, 'int' or 'datetime'.
 965   * @param $set_offset
 966   *   The name of a field that holds the timezone offset or a fixed timezone
 967   *   offset value. If not provided, the normal Drupal timezone handling
 968   *   will be used, i.e. $set_offset = 0 will make no timezone adjustment.
 969   * @return
 970   *   An appropriate SQL string for the db type and field type.
 971   */
 972  function views_date_sql_extract($extract_type, $field, $field_type = 'int', $set_offset = NULL) {
 973    $db_type = $GLOBALS['db_type'];
 974    $field = views_date_sql_field($field, $field_type, $set_offset);
 975  
 976    // Note there is no space after FROM to avoid db_rewrite problems
 977    // see http://drupal.org/node/79904.
 978    switch ($extract_type) {
 979    case('DATE'):
 980      return $field;
 981    case('YEAR'):
 982      return "EXTRACT(YEAR FROM($field))";
 983    case('MONTH'):
 984      return "EXTRACT(MONTH FROM($field))";
 985    case('DAY'):
 986      return "EXTRACT(DAY FROM($field))";
 987    case('HOUR'):
 988      return "EXTRACT(HOUR FROM($field))";
 989    case('MINUTE'):
 990      return "EXTRACT(MINUTE FROM($field))";
 991    case('SECOND'):
 992      return "EXTRACT(SECOND FROM($field))";
 993    case('WEEK'):  // ISO week number for date
 994      switch ($db_type) {
 995        case('mysql'):
 996        case('mysqli'):
 997          // WEEK using arg 3 in mysql should return the same value as postgres EXTRACT
 998          return "WEEK($field, 3)";
 999        case('pgsql'):
1000          return "EXTRACT(WEEK FROM($field))";
1001      }
1002    case('DOW'):
1003      switch ($db_type) {
1004        case('mysql'):
1005        case('mysqli'):
1006          // mysql returns 1 for Sunday through 7 for Saturday
1007          // php date functions and postgres use 0 for Sunday and 6 for Saturday
1008          return "INTEGER(DAYOFWEEK($field) - 1)";
1009        case('pgsql'):
1010          return "EXTRACT(DOW FROM($field))";
1011      }
1012    case('DOY'):
1013      switch ($db_type) {
1014        case('mysql'):
1015        case('mysqli'):
1016          return "DAYOFYEAR($field)";
1017        case('pgsql'):
1018          return "EXTRACT(DOY FROM($field))";
1019      }
1020    }
1021  }
1022  
1023  /**
1024   * Implementation of hook_views_handlers() to register all of the basic handlers
1025   * views uses.
1026   */
1027  function views_views_handlers() {
1028    return array(
1029      'info' => array(
1030        'path' => drupal_get_path('module', 'views') . '/handlers',
1031      ),
1032      'handlers' => array(
1033        // argument handlers
1034        'views_handler_argument' => array(
1035          'parent' => 'views_handler',
1036        ),
1037        'views_handler_argument_numeric' => array(
1038          'parent' => 'views_handler_argument',
1039        ),
1040        'views_handler_argument_formula' => array(
1041          'parent' => 'views_handler_argument',
1042        ),
1043        'views_handler_argument_date' => array(
1044          'parent' => 'views_handler_argument_formula',
1045        ),
1046        'views_handler_argument_string' => array(
1047          'parent' => 'views_handler_argument',
1048        ),
1049        'views_handler_argument_many_to_one' => array(
1050          'parent' => 'views_handler_argument',
1051        ),
1052        'views_handler_argument_null' => array(
1053          'parent' => 'views_handler_argument',
1054        ),
1055  
1056        // field handlers
1057        'views_handler_field' => array(
1058          'parent' => 'views_handler',
1059        ),
1060        'views_handler_field_date' => array(
1061          'parent' => 'views_handler_field',
1062        ),
1063        'views_handler_field_boolean' => array(
1064          'parent' => 'views_handler_field',
1065        ),
1066        'views_handler_field_markup' => array(
1067          'parent' => 'views_handler_field',
1068        ),
1069        'views_handler_field_xss' => array(
1070          'parent' => 'views_handler_field',
1071          'file' => 'views_handler_field.inc',
1072        ),
1073        'views_handler_field_url' => array(
1074          'parent' => 'views_handler_field',
1075        ),
1076        'views_handler_field_file_size' => array(
1077          'parent' => 'views_handler_field',
1078          'file' => 'views_handler_field.inc',
1079        ),
1080        'views_handler_field_prerender_list' => array(
1081          'parent' => 'views_handler_field',
1082        ),
1083        'views_handler_field_numeric' => array(
1084          'parent' => 'views_handler_field',
1085        ),
1086        'views_handler_field_custom' => array(
1087          'parent' => 'views_handler_field',
1088        ),
1089        'views_handler_field_counter' => array(
1090          'parent' => 'views_handler_field',
1091        ),
1092  
1093        // filter handlers
1094        'views_handler_filter' => array(
1095          'parent' => 'views_handler',
1096        ),
1097        'views_handler_filter_equality' => array(
1098          'parent' => 'views_handler_filter',
1099        ),
1100        'views_handler_filter_string' => array(
1101          'parent' => 'views_handler_filter',
1102        ),
1103        'views_handler_filter_boolean_operator' => array(
1104          'parent' => 'views_handler_filter',
1105        ),
1106        'views_handler_filter_boolean_operator_string' => array(
1107          'parent' => 'views_handler_filter_boolean_operator',
1108        ),
1109        'views_handler_filter_in_operator' => array(
1110          'parent' => 'views_handler_filter',
1111        ),
1112        'views_handler_filter_numeric' => array(
1113          'parent' => 'views_handler_filter',
1114        ),
1115        'views_handler_filter_float' => array(
1116          'parent' => 'views_handler_filter_numeric',
1117        ),
1118        'views_handler_filter_date' => array(
1119          'parent' => 'views_handler_filter_numeric',
1120        ),
1121        'views_handler_filter_many_to_one' => array(
1122          'parent' => 'views_handler_filter_in_operator',
1123        ),
1124  
1125        // relationship handlers
1126        'views_handler_relationship' => array(
1127          'parent' => 'views_handler',
1128        ),
1129  
1130  
1131        // sort handlers
1132        'views_handler_sort' => array(
1133          'parent' => 'views_handler',
1134        ),
1135        'views_handler_sort_formula' => array(
1136          'parent' => 'views_handler_sort',
1137        ),
1138        'views_handler_sort_date' => array(
1139          'parent' => 'views_handler_sort',
1140        ),
1141        'views_handler_sort_menu_hierarchy' => array(
1142          'parent' => 'views_handler_sort',
1143        ),
1144        'views_handler_sort_random' => array(
1145          'parent' => 'views_handler_sort',
1146        ),
1147      ),
1148    );
1149  }
1150  
1151  
1152  /**
1153   * @}
1154   */
1155  
1156  /**
1157   * @defgroup views_join_handlers Views' join handlers
1158   * @{
1159   * Handlers to tell Views how to join tables together.
1160  
1161   * Here is how you do complex joins:
1162   *
1163   * @code
1164   * class views_join_complex extends views_join {
1165   *   // PHP 4 doesn't call constructors of the base class automatically from a
1166   *   // constructor of a derived class. It is your responsibility to propagate
1167   *   // the call to constructors upstream where appropriate.
1168   *   function construct($table, $left_table, $left_field, $field, $extra = array(), $type = 'LEFT') {
1169   *     parent::construct($table, $left_table, $left_field, $field, $extra, $type);
1170   *   }
1171   *
1172   *   function join($table, &$query) {
1173   *     $output = parent::join($table, $query);
1174   *   }
1175   *   $output .= "AND foo.bar = baz.boing";
1176   *   return $output;
1177   * }
1178   * @endcode
1179   */
1180  /**
1181   * A function class to represent a join and create the SQL necessary
1182   * to implement the join.
1183   *
1184   * This is the Delegation pattern. If we had PHP5 exclusively, we would
1185   * declare this an interface.
1186   *
1187   * Extensions of this class can be used to create more interesting joins.
1188   *
1189   * join definition
1190   *   - table: table to join (right table)
1191   *   - field: field to join on (right field)
1192   *   - left_table: The table we join to
1193   *   - left_field: The field we join to
1194   *   - type: either LEFT (default) or INNER
1195   *   - extra: Either a string that's directly added, or an array of items:
1196   *   - - table: if not set, current table; if NULL, no table. This field can't
1197   *       be set in the cached definition because it can't know aliases; this field
1198   *       can only be used by realtime joins.
1199   *   - - field: Field or formula
1200   *   - - operator: defaults to =
1201   *   - - value: Must be set. If an array, operator will be defaulted to IN.
1202   *   - - numeric: If true, the value will not be surrounded in quotes.
1203   *   - extra type: How all the extras will be combined. Either AND or OR. Defaults to AND.
1204   */
1205  class views_join {
1206    /**
1207     * Construct the views_join object.
1208     */
1209    function construct($table = NULL, $left_table = NULL, $left_field = NULL, $field = NULL, $extra = array(), $type = 'LEFT') {
1210      $this->extra_type = 'AND';
1211      if (!empty($table)) {
1212        $this->table = $table;
1213        $this->left_table = $left_table;
1214        $this->left_field = $left_field;
1215        $this->field = $field;
1216        $this->extra = $extra;
1217        $this->type = strtoupper($type);
1218      }
1219      else if (!empty($this->definition)) {
1220        // if no arguments, construct from definition.
1221        // These four must exist or it will throw notices.
1222        $this->table = $this->definition['table'];
1223        $this->left_table = $this->definition['left_table'];
1224        $this->left_field = $this->definition['left_field'];
1225        $this->field = $this->definition['field'];
1226        if (!empty($this->definition['extra'])) {
1227          $this->extra = $this->definition['extra'];
1228        }
1229        if (!empty($this->definition['extra type'])) {
1230          $this->extra_type = strtoupper($this->definition['extra type']);
1231        }
1232  
1233        $this->type = !empty($this->definition['type']) ? strtoupper($this->definition['type']) : 'LEFT';
1234      }
1235    }
1236  
1237    /**
1238     * Build the SQL for the join this object represents.
1239     */
1240    function join($table, &$query) {
1241      if (empty($this->definition['table formula'])) {
1242        $right_table = "{" . $this->table . "}";
1243      }
1244      else {
1245        $right_table = $this->definition['table formula'];
1246      }
1247  
1248      if ($this->left_table) {
1249        $left = $query->get_table_info($this->left_table);
1250        $left_field = "$left[alias].$this->left_field";
1251      }
1252      else {
1253        // This can be used if left_field is a formula or something. It should be used only *very* rarely.
1254        $left_field = $this->left_field;
1255      }
1256  
1257      $output = " $this->type JOIN $right_table $table[alias] ON $left_field = $table[alias].$this->field";
1258      // Tack on the extra.
1259      if (isset($this->extra)) {
1260        if (is_array($this->extra)) {
1261          $extras = array();
1262          foreach ($this->extra as $info) {
1263            $extra = '';
1264            // Figure out the table name. Remember, only use aliases provided
1265            // if at all possible.
1266            $join_table = '';
1267            if (!array_key_exists('table', $info)) {
1268              $join_table = $table['alias'] . '.';
1269            }
1270            elseif (isset($info['table'])) {
1271              $join_table = $info['table'] . '.';
1272            }
1273  
1274            // And now deal with the value and the operator.  Set $q to
1275            // a single-quote for non-numeric values and the
1276            // empty-string for numeric values, then wrap all values in $q.
1277            $raw_value = $this->db_safe($info['value']);
1278            $q = (empty($info['numeric']) ? "'" : '');
1279  
1280            if (is_array($raw_value)) {
1281              $operator = !empty($info['operator']) ? $info['operator'] : 'IN';
1282              // Transform from IN() notation to = notation if just one value.
1283              if (count($raw_value) == 1) {
1284                $value = $q . array_shift($raw_value) . $q;
1285                $operator = $operator == 'NOT IN' ? '!=' : '=';
1286              }
1287              else {
1288                $value = "($q" . implode("$q, $q", $raw_value) . "$q)";
1289              }
1290            }
1291            else {
1292              $operator = !empty($info['operator']) ? $info['operator'] : '=';
1293              $value = "$q$raw_value$q";
1294            }
1295            $extras[] = "$join_table$info[field] $operator $value";
1296          }
1297  
1298          if ($extras) {
1299            if (count($extras) == 1) {
1300              $output .= ' AND ' . array_shift($extras);
1301            }
1302            else {
1303              $output .= ' AND (' . implode(' ' . $this->extra_type . ' ', $extras) . ')';
1304            }
1305          }
1306        }
1307        else if ($this->extra && is_string($this->extra)) {
1308          $output .= " AND ($this->extra)";
1309        }
1310      }
1311      return $output;
1312    }
1313  
1314    /**
1315     * Ensure that input is db safe. We only check strings and ints tho
1316     * so something that needs floats in their joins needs to do their
1317     * own type checking.
1318     */
1319    function db_safe($input) {
1320      if (is_array($input)) {
1321        $output = array();
1322        foreach ($input as $value) {
1323          if (empty($info['numeric'])) {
1324            $output[] = db_escape_string($value);
1325          }
1326          else {
1327            $output[] = intval($value);
1328          }
1329        }
1330      }
1331      else if (empty($info['numeric'])) {
1332        $output = db_escape_string($input);
1333      }
1334      else {
1335        $output = intval($input);
1336      }
1337  
1338      return $output;
1339    }
1340  }
1341  
1342  /**
1343   * @}
1344   */
1345  
1346  // Declare API compatibility on behalf of core modules:
1347  
1348  /**
1349   * Implementation of hook_views_api().
1350   *
1351   * This one is used as the base to reduce errors when updating.
1352   */
1353  function views_views_api() {
1354    return array(
1355      'api' => 2,
1356      'path' => drupal_get_path('module', 'views') . '/modules',
1357    );
1358  }
1359  
1360  function aggregator_views_api() { return views_views_api(); }
1361  
1362  function book_views_api() { return views_views_api(); }
1363  
1364  function comment_views_api() { return views_views_api(); }
1365  
1366  function locale_views_api() { return views_views_api(); }
1367  
1368  function filter_views_api() { return views_views_api(); }
1369  
1370  function node_views_api() { return views_views_api(); }
1371  
1372  function poll_views_api() { return views_views_api(); }
1373  
1374  function profile_views_api() { return views_views_api(); }
1375  
1376  function search_views_api() { return views_views_api(); }
1377  
1378  function statistics_views_api() { return views_views_api(); }
1379  
1380  function system_views_api() { return views_views_api(); }
1381  
1382  function taxonomy_views_api() { return views_views_api(); }
1383  
1384  function translation_views_api() { return views_views_api(); }
1385  
1386  function upload_views_api() { return views_views_api(); }
1387  
1388  function user_views_api() { return views_views_api(); }
1389  
1390  function contact_views_api() { return views_views_api(); }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7