You are here

finder.inc in Finder 7.2

Same filename in this branch
  1. 7.2 includes/finder.inc
  2. 7.2 modules/finder_ui/includes/finder.inc
Same filename and directory in other branches
  1. 6 includes/finder.inc

finder.inc

Provides the finder object type and associated methods.

File

includes/finder.inc
View source
<?php

/**
 * @file
 * finder.inc
 *
 * Provides the finder object type and associated methods.
 */

/**
 * An object to contain all of the data to generate a finder, plus the member
 * functions to build the finder, and render the output.
 */
class finder {
  public $id;
  public $name;
  public $views_view;
  public $views_display;
  public $title;
  public $description;
  public $path;
  public $block;
  public $settings;
  public $elements;
  public $status;
  public $export_type;

  /**
   * Finder defaults.
   *
   * Set up defaults on database based finders.
   */
  function defaults() {
    if (!empty($this->id)) {
      if (!isset($this->title)) {
        $this->title = t('Finder') . ' ' . $this->id;
      }
      if (!isset($this->path)) {
        $this->path = 'finder_' . !empty($this->name) ? $this->name : $this->id;
      }
      if (!isset($this->block)) {
        $this->block = TRUE;
      }
      if (!isset($this->status)) {
        $this->status = TRUE;
      }
      $finder_settings = array(
        'block' => TRUE,
        'form_on_page' => TRUE,
        'find_button' => TRUE,
        'find_text' => t('Find'),
        'go_text' => t('Go'),
        'ajax_effect' => 'none',
        'show_results' => 'completed',
        'results_style' => 'views',
        'no_results' => 'default',
        'pager' => 10,
        'redirect' => 'never',
        'element_logic' => 'AND',
        'url' => 'enabled',
        'url_delimiter' => ',',
      );
      foreach ($finder_settings as $key => $default) {
        if (!isset($this->settings[$key])) {
          $this->settings[$key] = $default;
        }
      }
      if (!empty($this->elements)) {
        foreach ($this->elements as &$element) {
          if (!empty($element->id)) {
            if (!$element->title) {
              $element->title = t('Element !id', array(
                '!id' => $element->id,
              ));
            }
            if ($element->element_handler['type'] == 'form') {
              $element_settings = array(
                'field_logic' => 'OR',
                'value_logic' => 'AND',
                'match' => 'e',
              );
            }
            elseif ($element->element_handler['type'] == 'container') {
              $element_settings = array(
                'element_logic' => 'AND',
              );
            }
            if (!isset($element->parent)) {
              $element->parent = NULL;
            }
            foreach ($element_settings as $key => $default) {
              if (!isset($element->settings[$key])) {
                $element->settings[$key] = $default;
              }
            }
          }
        }
      }
      else {
        $this->elements = array();
      }
    }
  }

  /**
   * Finder create element.
   */
  function create_element($id = NULL) {
    if (empty($id)) {
      $id = 'e' . uniqid();
    }
    $element = (object) array(
      'id' => $id,
      'finder' => $this->name,
      'settings' => array(),
      'weight' => 0,
    );
    $this->elements[$element->id] =& $element;
    $this
      ->build_elements();
    $this
      ->defaults();
    return $element;
  }

  /**
   * Finder delete element.
   *
   * Remove the element from the finder.
   */
  function delete_element(&$element) {
    $this
      ->choice_delete($element);
    unset($this->elements[$element->id]);
  }

  /**
   * Finder build elements.
   *
   * Does stuff after elements are loaded or when a new element is created.
   */
  function build_elements() {
    if (!empty($this->elements)) {
      foreach ($this->elements as $key => &$element) {
        if (empty($element->id)) {
          unset($this->elements[$key]);
          continue;
        }
        $element->finder = $this;
      }
    }
    $this
      ->load_element_handlers();
  }

  /**
   * Order elements by weight.
   */
  function element_weights() {
    if (!empty($this->elements)) {

      // First do a quick and dirty sort, at least elements that are siblings
      // with each other will be in the right order.
      uasort($this->elements, array(
        $this,
        '_element_weights_sort',
      ));

      // Now we need to ensure parents come before children.
      $elements = array();
      $root_elements = $this
        ->root_elements();
      foreach ($root_elements as $root) {
        $this
          ->_element_weights($root, $elements);
      }
      $finder_elements = array();
      foreach ($elements as $weight => $element_id) {
        $finder_element = $this->elements[$element_id];
        $finder_element->weight = $weight;
        $finder_elements[$element_id] = $finder_element;
      }
      $this->elements = $finder_elements;
    }
  }

  /**
   * Recursively order elements in respect to parent-child relationships.
   **/
  function _element_weights($parent, &$elements) {
    $elements[] = $parent;
    $parent_element = $this->elements[$parent];
    $child_elements = $this
      ->element_children($parent_element);
    foreach ($child_elements as $child) {
      $this
        ->_element_weights($child, $elements);
    }
  }

  /**
   * Sort callback for element ordering.
   */
  function _element_weights_sort($a, $b) {
    if ($a->weight == $b->weight) {
      return 0;
    }
    return $a->weight < $b->weight ? -1 : 1;
  }

  /**
   * Get an array of root level element ids.
   */
  function root_elements() {
    $roots = array();
    foreach ($this->elements as $finder_element) {
      if (empty($finder_element->parent)) {
        $roots[] = $finder_element->id;
      }
    }
    return $roots;
  }

  /**
   * Get an array of the element's direct child element ids.
   */
  function element_children($element) {
    $children = array();
    foreach ($this->elements as $finder_element) {
      if ($finder_element->parent == $element->id) {
        $children[] = $finder_element->id;
      }
    }
    return $children;
  }

  /**
   * Get an array of the element's ancestors (parent will be the first item).
   */
  function element_parents($element) {
    $parents = array();
    $parent = isset($element->parent) ? $element->parent : NULL;
    while (!empty($parent)) {
      $parents[] = $parent;
      $parent_element =& $this->elements[$parent];
      $parent = $parent_element->parent;
    }
    return $parents;
  }

  /**
   * Get the element's depth.
   */
  function element_depth($element) {
    $parents = $this
      ->element_parents($element);
    return count($parents);
  }

  /**
   * Finder save.
   */
  function save() {
    module_invoke_all('finder_presave', $this);
    $update = array();
    $op = 'finder_insert';
    if (!empty($this->id)) {
      $update[] = 'id';
      $op = 'finder_update';
    }
    if (!empty($this->elements)) {
      foreach ($this->elements as $k => &$element) {
        $element->finder = $this->name;
        if (empty($element->id)) {
          unset($this->elements[$k]);
        }
      }
    }
    $return = drupal_write_record('finder', $this, $update);
    $this
      ->choice_repopulate();
    module_invoke_all($op, $this);
    cache_clear_all();
    menu_rebuild();
    return $return;
  }

  /**
   * Finder delete.
   */
  function delete($clear = TRUE) {
    $this
      ->choice_delete();
    db_delete('finder')
      ->condition('id', $this->id)
      ->execute();
    module_invoke_all('finder_delete', $this);
    if ($clear) {
      cache_clear_all();
      menu_rebuild();
    }
  }

  /**
   * Make sure the finder is valid.
   *
   * @return
   *   TRUE if the finder is valid; an array of error strings if it is not.
   */
  function validate() {
    $errors = array();
    $url_delimiter = preg_replace("/[\\/\\-\\_\\s]/", "", $this
      ->setting('url_delimiter'));
    if (!$url_delimiter) {
      $errors[] = t('Multiple value URL arguments separator must contain at least one character that is not a space ( ), forward-slash (/), hyphen (-), or underscore (_).');
    }
    $empty_symbol = preg_replace("/[\\/\\-\\_\\s]/", "", $this
      ->setting('url_empty'));
    if ($this
      ->setting('url_empty') && !$empty_symbol) {
      $errors[] = t('Empty value URL arguments symbol must contain at least one character that is not a space ( ), forward-slash (/), hyphen (-), or underscore (_).');
    }
    if ($url_delimiter == $empty_symbol) {
      $errors[] = t('The "empty value URL arguments symbol" should not be the same as the "multiple value URL arguments separator".');
    }
    if (empty($this->elements)) {
      $errors[] = t('You must create at least one element.');
    }
    else {
      $element_ids = array();
      foreach ($this->elements as $element) {
        if ($element->element_handler['type'] == 'form' && !$this
          ->esetting($element, 'fields')) {
          $errors[] = t('You must create at least one field for %name.', array(
            '%name' => $element->title,
          ));
        }
        if (in_array($element->id, $element_ids)) {
          $errors[] = t('The machine name %id is used by more than one element.  Please delete the elements and try again.', array(
            '%id' => $element->id,
          ));
        }
        $children = $this
          ->element_children($element);
        if ($element->element_handler['type'] == 'form' && !empty($children)) {
          $errors[] = t('Element %name cannot have child elements.', array(
            '%name' => $element->title,
          ));
        }
        $element_ids[] = $element->id;
      }
    }
    drupal_alter('finder_validate', $errors, $this);
    return !empty($errors) ? $errors : TRUE;
  }

  /**
   * Finder load element handlers.
   *
   * Attach element handler data to the finder.
   */
  function load_element_handlers() {
    if (!empty($this->elements)) {
      $element_handlers = finder_element_handlers();
      foreach ($this->elements as &$element) {
        if (!empty($element_handlers[$element->element])) {
          $element->element_handler = $element_handlers[$element->element];
        }
      }
    }
  }

  /**
   * Get an option from the element handler.
   *
   * Useful if unsure if element handler info is present as it will load the
   * element info if it is missing.  <-- No it doesn't :/ DERPrecate?
   */
  function element_handler($element, $setting) {
    if (isset($element->element_handler[$setting])) {
      return $element->element_handler[$setting];
    }
  }

  /**
   * Finder page.
   *
   * @return
   *   Render array of a finder page.
   */
  function page() {
    module_invoke_all('finder_page', $this);
    $this->display = 'page';
    return array(
      '#prefix' => '<div id="finder-page-' . $this->name . '" class="finder-page">',
      'finder' => $this
        ->render(),
      '#suffix' => '</div>',
    );
  }

  /**
   * Finder block.
   *
   * @return
   *   Render array of a finder block.
   */
  function block() {
    module_invoke_all('finder_block', $this);
    $this->display = 'block';
    return array(
      'subject' => check_plain($this->title),
      'content' => array(
        '#prefix' => '<div id="finder-block-' . $this->name . '" class="finder-block">',
        'finder' => $this
          ->render(),
        '#suffix' => '</div>',
      ),
    );
  }

  /**
   * Finder build.
   *
   * Prepare a finder for execution and rendering.
   */
  function build() {
    if (empty($this->built)) {
      finder_inc('build');
      $this
        ->build_elements();
    }
    $this->built = TRUE;
  }

  /**
   * Finder render.
   *
   * Generate output of the finder.
   */
  function render() {
    finder_inc('form');
    module_invoke_all('finder_render', $this);
    $this
      ->build();
    $this->build_id = 'finder-' . $this->display . '-' . $this->name . '-wrapper';

    // Always get the form in order to populate the form_state in case there are results we need to present.
    // The form building function will not spend resources building elements if it doesn't need to.
    // to do: this non form_on_page get_form may only be needed when hiding url args?
    // @todo: this comment may no longer be relevant, and this call may not be needed anymore.
    $form = drupal_get_form('finder_form_' . $this->name, $this);
    if ($this->display != 'page' || $this->display == 'page' && $this
      ->setting('form_on_page')) {
      $output['form'] = $form;
    }
    $output['results'] = $this
      ->results();
    drupal_add_css(drupal_get_path('module', 'finder') . '/finder.css');
    $this->render = $output;
    $this->render['#prefix'] = '<div id="' . $this->build_id . '" class="finder-wrapper">';
    $this->render['#suffix'] = '</div>';
    drupal_alter('finder_render', $this);
    return $this->render;
  }

  /**
   * Finder find.
   *
   * Get a list of choices for form or results.
   *
   * @return
   *   An array of choices/results.
   */
  function find() {
    static $finder_find_cache = array();
    $this
      ->build();
    $keywords =& $this->find['keywords'];
    $mode =& $this->find['mode'];
    $matches =& $this->find['matches'];
    $pager =& $this->find['pager'];
    $element =& $this->find['element'];
    $reset =& $this->find['reset'];

    //    $field_logic = &$this->find['field_logic'];
    //    $value_logic = &$this->find['value_logic'];
    //    $nesting_order = &$this->find['nesting_order'];
    // Some defaults.
    $mode = $mode ? $mode : 'choices';
    $pager = $pager ? $pager : 0;
    $element = $element ? $element : NULL;
    $reset = $reset ? $reset : FALSE;

    // Handle reduce setting.
    if ($mode == 'choices' && $this
      ->esetting($element, 'choices_style', 'used_values') == 'used_values' && $this
      ->esetting($element, 'reduce')) {
      $reduce = $this
        ->esetting($element, 'reduce');
      foreach ($reduce as $reduce_element_id) {
        if (isset($this->form_state['values'][$reduce_element_id]) && !isset($keywords[$reduce_element_id])) {
          $reduce_element_keys = is_array($this->form_state['values'][$reduce_element_id]) ? $this->form_state['values'][$reduce_element_id] : array(
            $this->form_state['values'][$reduce_element_id],
          );
          foreach ($reduce_element_keys as $reduce_element_key) {
            $keywords[$reduce_element_id][] = $reduce_element_key;
          }
        }
      }
    }

    // Build $find_data for cache ids, and populate the $matches variable.
    $find_data = $keywords;
    foreach (array_keys($keywords) as $eid) {
      if (empty($matches[$eid])) {
        $matches[$eid]['match'] = $this
          ->esetting($this->elements[$eid], 'match', 'e');
      }
      if (empty($matches[$eid]['match_x'])) {
        $matches[$eid]['match_x'] = array(
          'operator' => $this
            ->esetting($this->elements[$eid], 'match_custom_operator', '='),
          'value_prefix' => $this
            ->esetting($this->elements[$eid], 'match_custom_prefix'),
          'value_suffix' => $this
            ->esetting($this->elements[$eid], 'match_custom_suffix'),
        );
      }
      if ($matches[$eid]['match'] != 'e') {
        if ($matches[$eid]['match'] == 'x') {
          $find_data[$eid] = implode(',', $matches[$eid]['match_x']) . '=' . implode(';', $find_data[$eid]);
        }
        else {
          $find_data[$eid] = $matches[$eid]['match'] . '=' . implode(';', $find_data[$eid]);
        }
      }
    }

    // Create an ID using the function params so we can cache the return value.
    $id = ($mode == 'choices' ? 'e' . $element->id : 'f' . $this->id) . '|';
    $id_length = strlen($id);
    $cache_id = ($pager ? $pager . (isset($_GET['page']) ? '.' . $_GET['page'] : '') : '') . json_encode($find_data);
    $cache_id_length = strlen($cache_id);
    if ($cache_id_length + $id_length > 255) {

      // For ID's that (will) exceed 255 we will try to represent them a unique way and pray for the best :/
      $cache_id = md5($cache_id) . $cache_id_length . substr($cache_id, 0, 223 - strlen($cache_id_length) - $id_length);

      // 223 = 255 - 32
    }
    $cache_id = $id . $cache_id;
    if (isset($finder_find_cache[$cache_id]) && !$reset) {

      // Use the static data.
      $this->find = $finder_find_cache[$cache_id];
    }
    elseif ($this
      ->setting('cache') && !$reset && ($cache = cache_get($cache_id, 'cache_finder_find')) && !empty($cache->data)) {

      // Use the cached data.
      $this->find = $cache->data;
    }
    else {

      // Allow other modules to react to and alter the finder.
      module_invoke_all('finder_find', $this);
      if ($mode == 'choices' && $this
        ->esetting($element, 'choices_style', 'used_values') != 'used_values') {

        // Calculate the values from the finder_choice table.
        $this
          ->choice_find();
      }
      else {

        // Calculate the values from the original tables..
        $this
          ->views_find();
      }
      if ($mode == 'choices' && $this
        ->esetting($element, 'sort')) {
        natcasesort($this->find['results']);
      }

      // Allow other modules to react to and alter the finder.
      module_invoke_all('finder_find_alter', $this);

      // Add the resulting $finder_find to the drupal cache.
      if ($this
        ->setting('cache')) {
        cache_set($cache_id, $this->find, 'cache_finder_find', REQUEST_TIME + 60 * $this
          ->setting('cache'));
      }

      // Add the resulting $finder_find to the static internal load cache.
      $finder_find_cache[$cache_id] = $this->find;
    }
  }

  /**
   * Finder choice find.
   *
   * A subfunction of find() which deals specifically with the finder_choice table.
   */
  function choice_find() {
    $keywords =& $this->find['keywords'];
    $match =& $this->find['match'];
    $matches =& $this->find['matches'];
    $pager =& $this->find['pager'];
    $element =& $this->find['element'];

    // Populate the $matches variable.
    foreach (array_keys($keywords) as $eid) {
      if ($eid == $element->id && !empty($match)) {
        $matches[$eid]['match'] = $match;
      }
      elseif (empty($matches[$eid])) {
        $matches[$eid]['match'] = $this
          ->esetting($this->elements[$eid], 'match', 'e');
      }
      if (empty($matches[$eid]['match_x'])) {
        $matches[$eid]['match_x'] = array(
          'operator' => $this
            ->esetting($this->elements[$eid], 'match_custom_operator', '='),
          'value_prefix' => $this
            ->esetting($this->elements[$eid], 'match_custom_prefix'),
          'value_suffix' => $this
            ->esetting($this->elements[$eid], 'match_custom_suffix'),
        );
      }
    }
    $query = db_select('finder_choice', 'fc')
      ->fields('fc', array(
      'choice_key',
      'choice_value',
    ))
      ->condition('fc.finder', $this->id)
      ->condition('fc.element', $element->id)
      ->distinct();
    if (!empty($keywords[$element->id])) {
      $placeholder_count = 0;
      foreach (array_values($keywords[$element->id]) as $keyword_position => $keyword) {
        if (!empty($keyword)) {
          $key_placeholder = ':finder_keyword_' . $placeholder_count++;
          list($key_field, $key_value, $key_op) = array_values((array) $this
            ->match_args('fc.choice_key', $keyword, $matches[$element->id]['match'], $matches[$element->id]['match_x']));
          $val_placeholder = ':finder_keyword_' . $placeholder_count++;
          list($val_field, $val_value, $val_op) = array_values((array) $this
            ->match_args('fc.choice_value', $keyword, $matches[$element->id]['match'], $matches[$element->id]['match_x']));
          $expression = '(' . $key_field . ' ' . $key_op . ' ' . $key_placeholder . ' OR ' . $val_field . ' ' . $val_op . ' ' . $val_placeholder . ')';
          $query
            ->where($expression, array(
            $key_placeholder => $key_value,
            $val_placeholder => $val_value,
          ));
        }
      }
    }
    if ($pager) {
      $query
        ->range(0, $pager);
    }
    $this->find['results'] = $query
      ->execute()
      ->fetchAllKeyed(0, 1);
  }

  /**
   * Finder choice repopulate.
   *
   * Repopulate the finder_choice table for this finder, or a particular element
   * if an element object is passed in.
   */
  function choice_repopulate($element = NULL) {
    if (!empty($element)) {
      $elements = array(
        $element,
      );
    }
    else {
      $elements =& $this->elements;
    }
    foreach ((array) $elements as $finder_element) {
      $this
        ->choice_delete($finder_element);
      $choices_style = $this
        ->esetting($finder_element, 'choices_style');
      $list = array();
      if ($choices_style == 'available_options' || $choices_style == 'available_options_php') {
        if ($choices_style == 'available_options_php') {
          finder_inc('build');
          $available_options = finder_eval($this
            ->esetting($finder_element, 'available_options_php'), array(
            'finder_element' => $finder_element,
          ));
        }
        else {
          $available_options = $this
            ->esetting($finder_element, 'available_options');
        }
        $available_options = explode("\n", $available_options);
        foreach ($available_options as $available_option) {
          if (strpos($available_option, '|') !== FALSE) {
            list($key, $value) = explode('|', $available_option);
            $list[$key] = isset($value) && $value !== '' ? $value : $key;
          }
          else {
            $list[$available_option] = $available_option;
          }
        }
      }
      foreach ($list as $choice_key => $choice_value) {

        // insert list items into table.
        db_insert('finder_choice')
          ->fields(array(
          'finder' => $this->id,
          'element' => $finder_element->id,
          'choice_key' => $choice_key,
          'choice_value' => $choice_value,
        ))
          ->execute();
      }
    }
  }

  /**
   * Finder choice delete.
   *
   * Delete the finder_choice table choices for this finder, or a particular element
   * if an element object is passed in.
   */
  function choice_delete($element = NULL) {
    $query = db_delete('finder_choice')
      ->condition('finder', $this->id);
    if (!empty($element)) {
      $query
        ->condition('element', $element->id);
    }
    $query
      ->execute();
  }

  /**
   * Finder views find.
   *
   * A subfunction of find() which deals specifically with the Views module.
   */
  function views_find() {
    $this->find['field_info'] = array();
    $this->find['groups'] = array();
    $mode =& $this->find['mode'];
    $pager =& $this->find['pager'];
    $element =& $this->find['element'];
    $reset =& $this->find['reset'];

    // Some defaults.
    $mode = $mode ? $mode : 'choices';
    $pager = $pager ? $pager : 0;
    $element = $element ? $element : NULL;
    $reset = $reset ? $reset : FALSE;
    if ($view = views_get_view($this->views_view)) {
      $this->view = $view;
      $view_args = array();
      $display = null;
      if ($mode == 'choices' || $mode == 'results' && $this
        ->setting('results_style') == 'custom' && !$this
        ->setting('custom_results_style_render')) {
        $display = $view
          ->add_display('finder');

        // Change the display options based on 'query display' setting.
        $custom_display = null;
        if ($mode == 'results') {
          $custom_display = $this->views_display;
        }
        elseif ($mode == 'choices') {
          $custom_display = $this
            ->esetting($element, 'display');
          if (!$custom_display) {
            $custom_display = $this->views_display;
          }
        }
        $view->display[$display]->display_options = $view->display[$custom_display]->display_options;
      }
      elseif ($mode == 'results') {
        $display = $view
          ->add_display($this->views_display);
        $view->finder_results_display = TRUE;
      }

      // Activate the display
      $view
        ->set_display($display);
      if ($mode != 'results' || !$this
        ->setting('results_style_views_format')) {
        $view->display_handler
          ->set_option('style_plugin', 'finder');
      }
      if ($mode == 'choices' || $mode == 'results' && $this
        ->setting('results_style') == 'custom' && !$this
        ->setting('custom_results_style_render')) {
        $view->display_handler
          ->set_option('row_plugin', 'fields');
      }
      $view->display_handler
        ->set_option('finder', $this);

      // Limit result set size.
      if (!empty($pager)) {
        $view
          ->set_items_per_page($pager);
        $view->display_handler
          ->set_option('use_pager', TRUE);
        $pager_element = $mode == 'results' ? 0 : 'finder_choices_' . $element->id;
        $view->display_handler
          ->set_option('pager_element', $pager_element);
        if (!empty($_GET['page'])) {
          $view
            ->set_offset($_GET['page'] * $pager);
        }
        $view->get_total_rows = true;

        // Added because of #1648984.
        $pager_option = $view->display_handler
          ->get_option('pager');
        if ($pager_option['type'] == 'none') {
          $pager_option['type'] = 'some';
          $view->display_handler
            ->set_option('pager', $pager_option);
        }
      }
      if ($mode == 'results') {
        $view_args = $this
          ->arguments();

        // Limit result for redirect.
        if (isset($this->go) && $this->go) {
          $view->display_handler
            ->set_option('use_pager', TRUE);
          $view->display_handler
            ->set_option('items_per_page', 1);
          $view->display_handler
            ->set_option('pager_element', 0);

          // Added because of #1648984.
          $pager_option = $view->display_handler
            ->get_option('pager');
          if ($pager_option['type'] == 'none' && $pager) {
            $pager_option['type'] = 'some';
            $view->display_handler
              ->set_option('pager', $pager_option);
          }
        }
      }
      elseif ($mode == 'choices') {
        $view_args = $this
          ->element_arguments($element);
        $view->display_handler
          ->set_option('distinct', TRUE);
      }

      // Make sure the query is not cached
      // @todo: what happens is we do let it get cached?
      $view->is_cacheable = FALSE;

      // Removed because of issue #1345930.

      //$view->build($display);
      $view->display[$display]->handler = $view->display_handler;
      $this->find['results'] = $view
        ->execute_display($display, $view_args);
      if ($mode == 'results' && ($this
        ->setting('results_style') == 'views' || $this
        ->setting('results_style') == 'custom' && $this
        ->setting('custom_results_style_render'))) {
        if (is_array($this->find['results']) && isset($this->find['results']['content'])) {
          $this->find['results'] = $this->find['results']['content'];
        }
        $wrapper = explode('[%FINDER_RESULTS%]', $this->find['results']);
        $this->find['results_prefix'] = isset($wrapper[0]) ? $wrapper[0] : '';
        $this->find['results_suffix'] = isset($wrapper[1]) ? $wrapper[1] : '';
        $this->find['results'] = $view->result;
      }
    }
  }

  /**
   * Finder results.
   *
   * Create finder results output.
   *
   * @return
   *   Themed output of finder results.
   */
  function results() {
    $results = array(
      '#prefix' => '<div class="finder-results-' . $this->name . '">',
      '#suffix' => '</div>',
    );
    if (($this->display != 'block' || $this->display == 'block' && !empty($this->ajax)) && (!$this
      ->setting('ajax_results_load') || !empty($this->ajax))) {
      $this->form_state = finder_form_state($this);
      module_invoke_all('finder_results', $this);
      if (!empty($this->form_state['finished']) || $this
        ->setting('show_results') == 'filter') {
        $keywords = array();
        $pager = $this
          ->setting('pager');
        foreach ($this->elements as $element) {
          if ($element->element_handler['type'] == 'form') {
            $keyword = array(
              NULL,
            );
            if (isset($this->form_state['values'][$element->id]) && !is_null($this->form_state['values'][$element->id])) {
              $keyword = (array) $this->form_state['values'][$element->id];
              if ($this
                ->esetting($element, 'delimit')) {

                // This enables escaped characters to work in delimiters.
                $delimiter = stripcslashes($this
                  ->esetting($element, 'delimit'));
                foreach ($keyword as $k => $v) {
                  unset($keyword[$k]);
                  $exploded = explode($delimiter, $v);
                  foreach ($exploded as $e) {
                    $keyword[] = trim($e);
                  }
                }
              }
            }
            $keywords[$element->id] = $keyword;
          }
        }
        $redirect = $this
          ->setting('redirect');
        if (!empty($_GET['go']) || isset($this->form_state['clicked_button']) && $this->form_state['clicked_button']['#name'] == 'go' && $this->form_state['finished'] || $redirect == 'always') {
          $this->go = TRUE;
        }
        $this->find = array(
          'mode' => 'results',
          'keywords' => $keywords,
          'pager' => $pager,
        );
        $this
          ->find();
        if ($pager) {
          pager_default_initialize($this->view->query->pager->total_items, $pager);
        }
        if ($this
          ->setting('url') == 'disabled') {
          if ($pager && !isset($this->form_state['pager_token'])) {
            $token = drupal_get_token();
            $this->form_state['pager_token'] = $token;
            $_SESSION['finder'][$token] = $this->form_state;
          }
        }
        if (empty($this->ajax)) {
          if (!empty($this->go) && !empty($this->find['results']) || $redirect == 'best' && count($this->find['results']) === 1 && empty($_GET['page'])) {
            $this
              ->go();
          }
        }

        // Search module integration.
        if ($this
          ->setting('search') && empty($this->find['results']) && module_exists('search') && !empty($keywords)) {
          $keys = array();

          // @todo ability to exclude certain fields/elements from being included in the search keywords.
          foreach ($keywords as $finder_element_id => $finder_element_keywords) {
            foreach ((array) $finder_element_keywords as $keyword) {
              $keys[] = $keyword;
            }
          }
          $search_module_output = search_data(implode(' OR ', $keys), $this
            ->setting('search_tab'));
          if (!empty($search_module_output['#results'])) {
            $results['results'] = $search_module_output;
          }
        }

        // Results handling.
        if (empty($results['results']) && (!empty($this->find['results']) || $this
          ->setting('no_results') == 'default')) {
          if ($this
            ->setting('results_style') == 'custom') {

            // Custom themed results.
            $results['results'] = array(
              '#theme' => 'finder_results',
              '#finder' => $this,
            );
          }
          elseif ($this
            ->setting('results_style') == 'views') {

            // @todo: see if we can use a views_handler_area here to put in footer/header/empty.
            // Results from views module.
            $results['results']['#prefix'] = $this->find['results_prefix'];
            if (!$this
              ->setting('results_style_views_format')) {
              foreach ($this->find['results'] as $result) {
                $results['results'][]['#markup'] = $result->{$result->display_key};
              }
            }
            $results['results']['#suffix'] = $this->find['results_suffix'];
          }
          $results['pager'] = array(
            '#theme' => 'pager',
          );
        }
        elseif (empty($results['results']) && $this
          ->setting('no_results') == 'configured') {

          // Configured output.
          $variables = array(
            'finder' => $this,
            'keywords' => $keywords,
            'form_state' => $this->form_state,
          );
          $results['results']['#markup'] = finder_eval($this
            ->setting('no_results_configured'), $variables);
        }
      }

      // custom changes - undo separator encoding
      $sep = $this
        ->setting('url_delimiter');
      $url_empty_sep = $this
        ->setting('url_empty');
      if (isset($results['results'])) {
        $results['results']['#suffix'] = str_replace("%20", $url_empty_sep, str_replace(urlencode($url_empty_sep), $url_empty_sep, str_replace(urlencode($sep), $sep, $results['results']['#suffix'])));
      }
      $this->find['results_render'] = $results;
    }
    return $results;
  }

  /**
   * Finder fields.
   *
   * Gets the list of possible fields that can be used with this finder.
   */
  function fields($groups = TRUE, $title_only = TRUE) {
    if ($view = views_get_view($this->views_view)) {
      views_include('admin');
      $display = $view
        ->add_display('finder_views');
      $view
        ->set_display($display);
      $base_tables = $view
        ->get_base_tables();
      $views_fields = views_fetch_fields(array_keys($base_tables), 'filter');
      if (!empty($views_fields['search_index.keys'])) {

        // Special case, allow search indexes to work. #1410862
        $views_fields['search_index.word'] = $views_fields['search_index.keys'];
        unset($views_fields['search_index.keys']);
      }
      $options = array();
      foreach ($views_fields as $k => $v) {
        if ($groups) {
          $options[$v['group']][$k] = $title_only ? $v['title'] : $v;
        }
        else {
          $options[$k] = $title_only ? $v['title'] : $v;
        }
      }
      return $options;
    }
    return FALSE;
  }

  /**
   * Finder relationships.
   *
   * Gets the list of possible relationships that can be used with this finder.
   */
  function relationships() {
    if ($view = views_get_view($this->views_view)) {
      $options = array();
      $display = $this->views_display;
      if (isset($view->display[$display]->display_options['relationships'])) {
        foreach ($view->display[$display]->display_options['relationships'] as $rel_key => $rel) {
          $options[$rel_key] = $rel['label'];
        }
      }
      return $options;
    }
    return FALSE;
  }

  /**
   * Finder go.
   */
  function go() {
    module_invoke_all('finder_go', $this);
    $result = reset($this->find['results']);
    $id =& $result->{$this->view->base_field};
    $path = finder_path($this->view->base_table, $id);
    if ($path) {
      if ($this->view->base_table == 'files') {
        file_download($path);
      }
      drupal_goto($path);
    }
  }

  /**
   * Finder element arguments.
   *
   * Get the contextual filter arguments for an element.
   *
   * @param $element
   *   The finder element object.
   * @return
   *   The array of views arguments.
   */
  function element_arguments($element) {
    finder_inc('build');
    $variables = array(
      'finder_element' => $element,
    );
    $string = $this
      ->esetting($element, 'contextual_filter');
    return finder_get_args($string, $variables);
  }

  /**
   * Finder arguments.
   *
   * @param $element
   *   The finder element object.
   * @return
   *   The array of views arguments.
   */
  function arguments() {
    $variables = array(
      'finder' => $this,
    );
    $string = $this
      ->setting('contextual_filter');
    return finder_get_args($string, $variables);
  }

  /**
   * Finder setting.
   *
   * @param $setting
   *   The name of the setting.
   * @param $default
   *   The value to return if there is no setting.
   * @return
   *   The setting or default value.
   */
  function setting($setting, $default = NULL) {
    if (isset($this->settings[$setting])) {
      return $this->settings[$setting];
    }
    return $default;
  }

  /**
   * Finder element setting.
   *
   * @param $element
   *   The element object.
   * @param $setting
   *   The name of the setting.
   * @param $default
   *   The value to return if there is no setting.
   * @return
   *   The setting or default value.
   */
  function esetting($element, $setting, $default = NULL) {
    if (isset($element->settings[$setting])) {
      return $element->settings[$setting];
    }
    return $default;
  }

  /**
   * Finder matches.
   *
   * Get preconfigured condition match methods.
   */
  function matches() {
    $matches = array(
      'c' => array(
        'name' => t('Contains'),
        'description' => t('!matches <em>contain</em> the !keywords.'),
        'operator' => 'LIKE',
        'value_prefix' => '%',
        'value_suffix' => '%',
      ),
      'cw' => array(
        'name' => t('Contains word'),
        'description' => t('!matches <em>contain</em> the !keywords as whole words.'),
        'operator' => 'REGEXP',
        'value_prefix' => '[[:<:]]',
        'value_suffix' => '[[:>:]]',
      ),
      'e' => array(
        'name' => t('Equals'),
        'description' => t('!matches must match the !keywords <em>exactly</em>.'),
        'operator' => '=',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
      'sw' => array(
        'name' => t('Starts with'),
        'description' => t('!matches must <em>start with</em> the !keywords.'),
        'operator' => 'LIKE',
        'value_prefix' => '',
        'value_suffix' => '%',
      ),
      'ew' => array(
        'name' => t('Ends with'),
        'description' => t('!matches must <em>end with</em> the !keywords.'),
        'operator' => 'LIKE',
        'value_prefix' => '%',
        'value_suffix' => '',
      ),
      'lt' => array(
        'name' => t('Less than'),
        'description' => t('!matches must be <em>less than</em> the !keywords.'),
        'operator' => '<',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
      'lte' => array(
        'name' => t('Less than or equals'),
        'description' => t('!matches must be <em>less than or equal to</em> the !keywords.'),
        'operator' => '<=',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
      'gt' => array(
        'name' => t('Greater than'),
        'description' => t('!matches must be <em>greater than</em> the !keywords.'),
        'operator' => '>',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
      'gte' => array(
        'name' => t('Greater than or equals'),
        'description' => t('!matches must be <em>greater than or equal to</em> the !keywords.'),
        'operator' => '>=',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
      'nc' => array(
        'name' => t("Doesn't contain"),
        'description' => t("!matches <em>don't contain</em> the !keywords."),
        'operator' => 'NOT LIKE',
        'value_prefix' => '%',
        'value_suffix' => '%',
      ),
      'ncw' => array(
        'name' => t("Doesn't contain word"),
        'description' => t("!matches <em>don't contain</em> the !keywords as whole words."),
        'operator' => 'NOT REGEXP',
        'value_prefix' => '[[:<:]]',
        'value_suffix' => '[[:>:]]',
      ),
      'ne' => array(
        'name' => t('Not equals'),
        'description' => t('!matches must <em>not</em> match the !keywords.'),
        'operator' => '<>',
        'value_prefix' => '',
        'value_suffix' => '',
      ),
    );
    return $matches;
  }

  /**
   * Get data about finder match methods.
   *
   * @param $field
   *   The field to match.
   * @param $value
   *   The value to match.
   * @param $match
   *   The match method according to finder's settings.
   * @param $match_x
   *   The custom match method, if available.
   * @return
   *   An object cast from an array with keys field/value/match if $match is set.
   */
  function match_args($field, $value, $match, $match_x = NULL) {
    $matches = $this
      ->matches();
    if ($match_x) {
      $matches['x'] = $match_x;
    }
    if ($matches[$match]['value_prefix']) {
      if (strpos($matches[$match]['operator'], 'LIKE') !== FALSE) {
        $value = db_like($value);
      }
      $value = $matches[$match]['value_prefix'] . $value;
    }
    if ($matches[$match]['value_suffix']) {
      $value = $value . $matches[$match]['value_suffix'];
    }
    return (object) array(
      'field' => $field,
      'value' => $value,
      'match' => $matches[$match]['operator'],
    );
  }

}

Classes

Namesort descending Description
finder An object to contain all of the data to generate a finder, plus the member functions to build the finder, and render the output.