<?php
// $Id: views_plugin_style.inc,v 1.5 2009/03/24 22:22:11 merlinofchaos Exp $
/**
 * Base class to define a style plugin handler.
 */
class views_plugin_style extends views_plugin {
  /**
   * Initialize a style plugin.
   *
   * @param $view
   * @param $display
   * @param $options
   *   The style options might come externally as the style can be sourced
   *   from at least two locations. If it's not included, look on the display.
   */
  function init(&$view, &$display, $options = NULL) {
    $this->view = &$view;
    $this->display = &$display;

    // Overlay incoming options on top of defaults
    $this->unpack_options($this->options, isset($options) ? $options : $display->handler->get_option('style_options'));

    if ($this->uses_row_plugin() && $display->handler->get_option('row_plugin')) {
      $this->row_plugin = $display->handler->get_plugin('row');
    }

    $this->options += array(
      'grouping' => '',
    );

    $this->definition += array(
      'uses grouping' => TRUE,
    );
  }

  function destroy() {
    parent::destroy();

    if (isset($this->row_plugin)) {
      $this->row_plugin->destroy();
    }
  }

  /**
   * Return TRUE if this style also uses a row plugin.
   */
  function uses_row_plugin() {
    return !empty($this->definition['uses row plugin']);
  }

  /**
   * Return TRUE if this style also uses fields.
   */
  function uses_fields() {
    // If we use a row plugin, ask the row plugin. Chances are, we don't
    // care, it does.
    if ($this->uses_row_plugin() && !empty($this->row_plugin)) {
      return $this->row_plugin->uses_fields();
    }
    // Otherwise, maybe we do.
    return !empty($this->definition['uses fields']);
  }

  function option_definition() {
    $options = parent::option_definition();
    $options['grouping'] = array('default' => '');
    return $options;
  }

  function options_form(&$form, &$form_state) {
    // Only fields-based views can handle grouping.  Style plugins can also exclude
    // themselves from being groupable by setting their "use grouping" definiton
    // key to FALSE.
    // @TODO: Document "uses grouping" in docs.php when docs.php is written.
    if ($this->uses_fields() && $this->definition['uses grouping']) {
      $options = array('' => t('<None>'));
      foreach ($this->display->handler->get_handlers('field') as $field => $handler) {

        if ($label = $handler->label()) {
          $options[$field] = $label;
        }
        else {
          $options[$field] = $handler->ui_name();
        }
      }

      // If there are no fields, we can't group on them.
      if (count($options) > 1) {
        $form['grouping'] = array(
          '#type' => 'select',
          '#title' => t('Grouping field'),
          '#options' => $options,
          '#default_value' => $this->options['grouping'],
          '#description' => t('You may optionally specify a field by which to group the records. Leave blank to not group.'),
        );
      }
    }
  }

  /**
   * Called by the view builder to see if this style handler wants to
   * interfere with the sorts. If so it should build; if it returns
   * any non-TRUE value, normal sorting will NOT be added to the query.
   */
  function build_sort() { return TRUE; }

  /**
   * Allow the style to do stuff before each row is rendered.
   *
   * @param $result
   *   The full array of results from the query.
   */
  function pre_render($result) {
    if (!empty($this->row_plugin)) {
      $this->row_plugin->pre_render($result);
    }
  }

  /**
   * Render the display in this style.
   */
  function render() {
    if ($this->uses_row_plugin() && empty($this->row_plugin)) {
      vpr('views_plugin_style_default: Missing row plugin');
      return;
    }

    // Group the rows according to the grouping field, if specified.
    $sets = $this->render_grouping($this->view->result, $this->options['grouping']);

    // Render each group separately and concatenate.  Plugins may override this
    // method if they wish some other way of handling grouping.
    $output = '';
    foreach ($sets as $title => $records) {
      if ($this->uses_row_plugin()) {
        $rows = array();
        foreach ($records as $label => $row) {
          $rows[] = $this->row_plugin->render($row);
        }
      }
      else {
        $rows = $records;
      }

      $output .= theme($this->theme_functions(), $this->view, $this->options, $rows, $title);
    }
    return $output;
  }

  /**
   * Group records as needed for rendering.
   *
   * @param $records
   *   An array of records from the view to group.
   * @param $grouping_field
   *   The field id on which to group.  If empty, the result set will be given
   *   a single group with an empty string as a label.
   * @return
   *   The grouped record set.
   */
  function render_grouping($records, $grouping_field = '') {
    $sets = array();
    if ($grouping_field) {
      foreach ($records as $row) {
        $grouping = '';
        // Group on the rendered version of the field, not the raw.  That way,
        // we can control any special formatting of the grouping field through
        // the admin or theme layer or anywhere else we'd like.
        if (isset($this->view->field[$grouping_field])) {
          $grouping = $this->view->field[$grouping_field]->theme($row);
          if ($this->view->field[$grouping_field]->options['label']) {
            $grouping = $this->view->field[$grouping_field]->options['label'] . ': ' . $grouping;
          }
        }
        $sets[$grouping][] = $row;
      }
    }
    else {
      // Create a single group with an empty grouping field.
      $sets[''] = $records;
    }
    return $sets;
  }

  function validate() {
    $errors = parent::validate();

    if ($this->uses_row_plugin()) {
      $plugin = $this->display->handler->get_plugin('row');
      if (empty($plugin)) {
        $errors[] = t('Style @style requires a row style but the row plugin is invalid.', array('@style' => $this->definition['title']));
      }
    }
    return $errors;
  }

  function query() {
    parent::query();
    if (isset($this->row_plugin)) {
      $this->row_plugin->query();
    }
  }
}

