You are here

class views_oai_pmh_plugin_style in Views OAI-PMH 7.3

Same name and namespace in other branches
  1. 6.2 plugins/views_oai_pmh_plugin_style.inc \views_oai_pmh_plugin_style
  2. 6 plugins/views_oai_pmh_plugin_style.inc \views_oai_pmh_plugin_style
  3. 7 plugins/views_oai_pmh_plugin_style.inc \views_oai_pmh_plugin_style
  4. 7.2 plugins/views_oai_pmh_plugin_style.inc \views_oai_pmh_plugin_style

Views OAI-PMH_plugin style.

Hierarchy

Expanded class hierarchy of views_oai_pmh_plugin_style

1 string reference to 'views_oai_pmh_plugin_style'
views_oai_pmh_views_plugins in ./views_oai_pmh.views.inc
Implements hook_views_plugins().

File

plugins/views_oai_pmh_plugin_style.inc, line 11
Contains the base OAI-PMH style plugin.

View source
class views_oai_pmh_plugin_style extends views_plugin_style {

  /**
   * The OAI-PMH request, represented by a views_oai_pmh_request object.
   *
   * @var object
   */
  protected $request;

  /**
   * The XML response document, a DOMDocument object.
   *
   * @var object
   */
  protected $xml;

  /**
   * The root element of the XML response, a DOMElement object.
   *
   * @var object
   */
  protected $xml_root;

  /**
   * The container element for the response.
   *
   * @var object
   */
  protected $xml_verb;

  /**
   * Views plugin interface.
   */

  /**
   * Option definition.
   */
  public function option_definition() {
    $options = parent::option_definition();
    $options['enabled_formats'] = array(
      'default' => array(
        'oai_dc' => 'oai_dc',
      ),
    );
    $options['field_mappings'] = array(
      'default' => array(),
    );
    return $options;
  }

  /**
   * Provide settings for this plugin.
   */
  public function options_form(&$form, &$form_state) {

    // It is very important to call the parent function here:
    parent::options_form($form, $form_state);
    $handlers = $this->display->handler
      ->get_handlers('field');
    if (empty($handlers)) {
      $form['error_markup'] = array(
        '#markup' => '<div class="error messages">' . t('You need at least one field before you can configure your table settings') . '</div>',
      );
      return;
    }
    $formats = views_oai_pmh_list_metadata_formats();

    // Add metadata format checkboxes.
    foreach ($formats as $id) {
      $format_options[$id] = $id . ' - ' . views_oai_pmh_get_metadata_format($id)->label;
    }
    $form['enabled_formats'] = array(
      '#type' => 'checkboxes',
      '#title' => t('OAI-PMH metadata formats'),
      '#description' => t('Select the metadata format(s) that you wish to publish. Note that the Dublin Core format must remain enabled as it is required by the OAI-PMH standard.'),
      '#default_value' => $this->options['enabled_formats'],
      '#options' => $format_options,
    );

    // 'oai_dc' is read-only.
    $form['enabled_formats']['oai_dc']['#disabled'] = TRUE;
    $form['metadata_prefix'] = array(
      '#type' => 'fieldset',
      '#title' => t('Metadata prefixes'),
    );

    // Add mapping selectors.
    $fields = $this->display->handler
      ->get_option('fields');
    $field_labels = $this->display->handler
      ->get_field_labels();
    foreach ($formats as $id) {
      $format = views_oai_pmh_get_metadata_format($id);
      $form['metadata_prefix'][$id] = array(
        '#type' => 'textfield',
        '#title' => $format->label,
        '#default_value' => $this->options['metadata_prefix'][$id] ? $this->options['metadata_prefix'][$id] : $id,
        '#required' => TRUE,
        '#size' => 16,
        '#maxlength' => 32,
      );
      $form['field_mappings'][$id] = array(
        '#type' => 'fieldset',
        '#title' => t('Field mappings for <em>@format</em>', array(
          '@format' => $format->label,
        )),
        '#theme' => 'views_oai_pmh_field_mappings_form',
        '#states' => array(
          'visible' => array(
            ':input[name="style_options[enabled_formats][' . $id . ']"]' => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
      foreach ($fields as $field_name => $info) {
        $form['field_mappings'][$id][$field_name] = array(
          '#type' => 'select',
          '#options' => $format->elements,
          '#default_value' => !empty($this->options['field_mappings'][$id][$field_name]) ? $this->options['field_mappings'][$id][$field_name] : '',
          '#title' => $field_labels[$field_name],
        );
      }
    }

    // Metadata prefix for 'oai_dc' is read-only.
    $form['metadata_prefix']['oai_dc']['#disabled'] = TRUE;
  }

  /**
   * Option validate.
   */
  public function options_validate(&$form, &$form_state) {

    // 'oai_dc' is always on.
    $form_state['values']['style_options']['enabled_formats']['oai_dc'] = 'oai_dc';

    // 'oai_dc' is read-only.
    $form_state['values']['style_options']['metadata_prefix']['oai_dc'] = 'oai_dc';
    foreach ($form_state['values']['style_options']['metadata_prefix'] as $id1 => $metadata_prefix1) {

      // Check for invalid metadata prefixes.
      // Regex based on http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd.
      if (!preg_match("/^[A-Za-z0-9\\-_\\.!~\\*'\\(\\)]+\$/", $metadata_prefix1)) {
        form_error($form['metadata_prefix'][$id1], t("The metadata prefix '@prefix' contains characters that are not allowed in metadata prefixes.", array(
          '@prefix' => $metadata_prefix1,
        )));
      }

      // Check for duplicate metadata prefixes.
      foreach ($form_state['values']['style_options']['metadata_prefix'] as $id2 => $metadata_prefix2) {
        if ($metadata_prefix1 == $metadata_prefix2 && $id1 != $id2) {
          form_error($form['metadata_prefix'][$id2], t("Metadata prefix '@prefix' is not unique.", array(
            '@prefix' => $metadata_prefix1,
          )));
        }
      }
    }
    parent::options_validate($form, $form_state);
  }

  /**
   * Query.
   */
  public function query() {
    global $base_url;
    if (!isset($this->request)) {

      // Initialize the request from URL arguments.
      $this->request = new views_oai_pmh_request($base_url . base_path() . $this->display->handler
        ->get_path(), $this
        ->get_metadata_prefixes(), isset($this->view->live_preview) && $this->view->live_preview);
    }

    // Perform the query action specified by the requested OAI-PMH verb.
    if ($this->request->verb && !$this->request->errors) {
      $callback = $this->request->callback . '_query';
      if (method_exists($this, $callback)) {
        call_user_func(array(
          $this,
          $callback,
        ));
      }
    }
    if ($this->request->errors) {

      // Avoid execution of the view's query.
      $this->view->executed = TRUE;
    }
  }

  /**
   * Render.
   */
  public function render() {
    $this
      ->xml_init();
    $this
      ->xml_append_request();

    // Perform the render action specified by the requested OAI-PMH verb.
    if ($this->request->verb && !$this->request->errors) {
      $callback = $this->request->callback . '_render';
      if (method_exists($this, $callback)) {
        call_user_func(array(
          $this,
          $callback,
        ));
      }
    }

    // Append all errors, if any, to the XML document.
    $this
      ->xml_append_errors();
    $variables = array(
      'view' => $this->view,
      'options' => $this->options,
      'request' => $this->request,
      'xml' => $this->xml,
      'xml_root' => $this->xml_root,
      'xml_verb' => $this->xml_verb,
    );
    return theme('views_oai_pmh_response', $variables);
  }

  /**
   * Internal query methods.
   */

  /**
   * Prepares the query for an 'Identify' OAI-PMH request.
   */
  protected function identify_query() {

    // We do not need the view's results to provide a response.
    $this->view->executed = TRUE;
  }

  /**
   * Prepares the query for a 'ListMetadataFormats' OAI-PMH request.
   */
  protected function list_metadata_formats_query() {

    // We do not need the view's results to provide a response.
    $this->view->executed = TRUE;
  }

  /**
   * Prepares the query for a 'ListIdentifiers' OAI-PMH request.
   */
  protected function list_identifiers_query() {
    if ($this->request->resumption_token) {
      $this
        ->load_resumption_token_and_resume_query();
    }
    else {
      $this
        ->add_record_query_fields();
      $this
        ->add_record_query_dates();
    }
  }

  /**
   * Prepares the query for a 'GetRecord' OAI-PMH request.
   */
  protected function get_record_query() {
    $this
      ->add_record_query_fields();
    $this->view->query
      ->add_where(0, 'node.nid', $this->request->nid, '=');
  }

  /**
   * Prepares the query for a 'ListRecords' OAI-PMH request.
   */
  protected function list_records_query() {
    if ($this->request->resumption_token) {
      $this
        ->load_resumption_token_and_resume_query();
    }
    else {
      $this
        ->add_record_query_fields();
      $this
        ->add_record_query_dates();
    }
  }

  /**
   * Adds the fields needed for record retrieval to the view's query.
   */
  protected function add_record_query_fields() {
    $this->view->query
      ->add_field('node', 'nid');
    $this->view->query
      ->add_field('node', 'changed');
    $this->view->query
      ->add_orderby('node', 'nid', 'ASC');
  }

  /**
   * Add the requested date range to the view's query.
   */
  protected function add_record_query_dates() {
    if ($this->request->from) {
      $this->view->query
        ->add_where(0, 'node.changed', strtotime($this->request->from), '>=');
    }
    if ($this->request->until) {
      $this->view->query
        ->add_where(0, 'node.changed', strtotime($this->request->until), '<=');
    }
  }

  /**
   * Load the resumption token and resume query.
   */
  protected function load_resumption_token_and_resume_query() {
    $cache = db_select('cache_views_oai_pmh', 'c')
      ->fields('c')
      ->condition('cid', $this->request->resumption_token, '=')
      ->condition('expire', time() - VIEWS_OAI_PMH_TOKEN_LIFETIME, '>')
      ->execute()
      ->fetchObject();
    if (isset($cache->data)) {
      $cache->data = unserialize($cache->data);
      if (is_array($cache->data) && $cache->data['verb'] == $this->request->verb) {
        $this->view->query = $cache->data['query'];

        // Offset the query.
        $this->view->query->pager
          ->set_current_page($cache->data['current_page'] + 1);

        // Restore some of the request data.
        $this->request->metadata_format = views_oai_pmh_get_metadata_format($cache->data['metadata_format']);
        return;
      }
    }
    $this->request->errors[] = new views_oai_pmh_error_bad_resumption_token($this->request->resumption_token);
  }

  /**
   * Internal render methods.
   */

  /**
   * Renders a response to the 'identify' OAI-PMH request.
   */
  protected function identify_render() {
    $this
      ->xml_append_verb();

    // TODO: Should use the view's query to get a more accurate value.
    $datestamp = db_query('SELECT MIN(changed) FROM {node}')
      ->fetchField();
    $elements = array(
      'repositoryName' => $this->display->handler->options['title'],
      'baseURL' => $this->request->base_url,
      'protocolVersion' => '2.0',
      'adminEmail' => variable_get('site_mail', ini_get('sendmail_from')),
      'earliestDatestamp' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $datestamp),
      'deletedRecord' => 'no',
      'granularity' => 'YYYY-MM-DDThh:mm:ssZ',
    );
    $this
      ->xml_append_children($this->xml_verb, $elements);
    $this
      ->append_oai_identifier_description($this->xml_verb);
  }

  /**
   * Renders a response to the 'ListMetadataFormats' OAI-PMH request.
   *
   * The request's 'identifier' argument, if present, is just ignored here
   * since the same formats are supported regardless of the requested node.
   */
  protected function list_metadata_formats_render() {
    $this
      ->xml_append_verb();
    $enabled_formats = $this
      ->get_metadata_prefixes();
    foreach ($enabled_formats as $metadata_prefix => $format_id) {
      if ($format = views_oai_pmh_get_metadata_format($format_id)) {
        $format_element = $this->xml
          ->createElement('metadataFormat');
        $this->xml_verb
          ->appendChild($format_element);
        $elements = array(
          'metadataPrefix' => $metadata_prefix,
          'schema' => $format->schema,
          'metadataNamespace' => $format
            ->get_metadata_namespace(),
        );
        $this
          ->xml_append_children($format_element, $elements);
      }
    }
  }

  /**
   * Renders a response to the 'ListIdentifiers' OAI-PMH request.
   */
  protected function list_identifiers_render() {
    if ($this->view->result) {
      $this
        ->xml_append_verb();
      foreach ($this->view->result as $row_index => $row) {
        $this
          ->append_record_header($this->xml_verb, $row_index, $row);
      }
      $this
        ->save_resumption_token();
    }
    else {
      $this->request->errors[] = new views_oai_pmh_error_no_records_match();
    }
  }

  /**
   * Renders a response to the 'GetRecord' OAI-PMH request.
   */
  protected function get_record_render() {
    if ($this->view->result && ($row = $this->view->result[0])) {
      $this
        ->xml_append_verb();
      $record = $this->xml
        ->createElement('record');
      $this->xml_verb
        ->appendChild($record);
      $this
        ->append_record_header($record, 0, $row);
      $this
        ->append_record_metadata($record, 0, $row);
    }
    else {
      $this->request->errors[] = new views_oai_pmh_error_unknown_id($this->request->original_args['identifier']);
    }
  }

  /**
   * Renders a response to the 'ListRecords' OAI-PMH request.
   */
  protected function list_records_render() {
    if ($this->view->result) {
      $this
        ->xml_append_verb();
      foreach ($this->view->result as $row_index => $row) {
        $record = $this->xml
          ->createElement('record');
        $this->xml_verb
          ->appendChild($record);
        $this
          ->append_record_header($record, $row_index, $row);
        $this
          ->append_record_metadata($record, $row_index, $row);
      }
      $this
        ->save_resumption_token();
    }
    else {
      $this->request->errors[] = new views_oai_pmh_error_no_records_match();
    }
  }

  /**
   *
   */
  protected function append_oai_identifier_description($parent) {
    $description_element = $this->xml
      ->createElement('description');
    $parent
      ->appendChild($description_element);
    $oai_identifier_element = $this->xml
      ->createElementNS('http://www.openarchives.org/OAI/2.0/oai-identifier', 'oai-identifier');
    $oai_identifier_attributes = array(
      'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
      'xsi:schemaLocation' => 'http://www.openarchives.org/OAI/2.0/oai-identifier  http://www.openarchives.org/OAI/2.0/oai-identifier.xsd',
    );
    $this
      ->xml_set_attributes($oai_identifier_element, $oai_identifier_attributes);
    $description_element
      ->appendChild($oai_identifier_element);
    $elements = array(
      'scheme' => 'oai',
      'repositoryIdentifier' => $this->request
        ->get_host(),
      'delimiter' => ':',
      'sampleIdentifier' => $this
        ->make_record_identifier(VIEWS_OAI_PMH_SAMPLE_IDENTIFIER),
    );
    $this
      ->xml_append_children($oai_identifier_element, $elements);
  }

  /**
   * Append record header.
   */
  protected function append_record_header($parent, $row_index, $row) {
    $header_element = $this->xml
      ->createElement('header');
    $parent
      ->appendChild($header_element);
    $elements = array(
      'identifier' => $this
        ->make_record_identifier($row->nid),
      'datestamp' => gmstrftime('%Y-%m-%dT%H:%M:%SZ', $row->node_changed),
    );
    $this
      ->xml_append_children($header_element, $elements);
  }

  /**
   * Builds the core of a record's XML.
   *
   * Builds the core of a record's XML by using the mappings configured in
   * the view.
   */
  protected function append_record_metadata($parent, $row_index, $row) {
    $metadata_element = $this->xml
      ->createElement('metadata');
    $parent
      ->appendChild($metadata_element);
    $metadata_root = $this->xml
      ->createElement($this->request->metadata_format->root_element);
    $metadata_element
      ->appendChild($metadata_root);

    // Getting the configurable root elements.
    $configurable_root_attributes = $this
      ->get_root_attribute_elements($row_index, $this->view->field);

    // Merging all the root attributes.
    $root_attributes = array_merge($this->request->metadata_format->root_attributes, $configurable_root_attributes);

    // Setting the root attributes.
    $this
      ->xml_set_attributes($metadata_root, $root_attributes);

    // Prepare XPath object for later use.
    $xpath = new DOMXPath($this->xml);
    foreach ($this->request->metadata_format->namespaces as $namespace_prefix => $namespace_uri) {
      if (!$xpath
        ->registerNamespace($namespace_prefix, $namespace_uri)) {
        watchdog('views_oai_pmh', 'Error registering namespace @uri', array(
          '@uri' => $namespace_uri,
        ), WATCHDOG_ERROR);
      }
    }

    // Available attributes, as an array of field ids keyed by attribute name.
    $available_attributes = array();
    foreach ($this->view->field as $field_name => $field) {
      list($mapping, $is_attribute) = $this
        ->get_mapping($field_name);
      if ($is_attribute) {

        // Field is mapped to an attribute. Mark it is available for an upcoming
        // element.
        $available_attributes[$mapping] = $field_name;
      }
      else {

        // Field is mapped to an element. Build the element in the XML tree.
        $used_attributes = array();

        // $field_value is the output value from the view exactly as it should
        // be shown if the view mode was a page.
        $field_value = $this
          ->get_field($row_index, $field_name);
        if ($field->options['exclude'] || empty($mapping) || empty($field_value) && $field->options['hide_empty']) {
          if (!empty($mapping)) {

            // Eat attributes that would have been consumed by the field's
            // mapping, had the field not been empty. This prevents attributes
            // from spilling to subsequent fields they were not intended for.
            $this
              ->eat_attributes(explode('/', $mapping), $available_attributes);
          }

          // Skip this field.
          continue;
        }

        // Convert field value to an array.
        if (isset($field->multiple) && $field->multiple) {
          $field_values = explode($field->options['separator'], $field_value);
        }
        else {
          $field_values = array(
            $field_value,
          );
        }

        // Build XML elements for each value.
        // Track position of current field values.
        $delta = 0;
        foreach ($field_values as $field_value) {

          // Path of the current element in the mapping string.
          $mapping_path = array();

          // Path of the current element, free of mapping codes.
          $element_path = array();
          $nodes_in_element_path = array();
          $parent = $metadata_root;
          foreach (explode('/', $mapping) as $element) {
            $mapping_path[] = $element;
            if (implode('/', $mapping_path) != $mapping) {

              // We're not on a tree leaf. If the element has a '[]' suffix,
              // we'll try to work under an existing node whose position
              // matches $delta. If the element has a '[n]' suffix, we'll try
              // to work under an existing node at position 'n'. If neither
              // case applies, a new node will get created.
              $element_xpath_pattern = $this
                ->prepare_tag($element, $delta);

              // Check if child exists.
              $existing_nodes = $xpath
                ->query($element_xpath_pattern, $parent);
              if ($existing_nodes !== FALSE && $existing_nodes->length) {

                // Found an existing node matching the path. No need to create a
                // new one.
                $element_node = $existing_nodes
                  ->item(0);
              }
              else {

                // Create a new node.
                $element_node = $this->xml
                  ->createElement($element);
                $parent
                  ->appendChild($element_node);
              }
            }
            else {

              // We're on a tree leaf. Create a new node no matter what, with
              // the field value.
              $this
                ->prepare_tag($element, $delta);
              $element_node = $this->xml
                ->createElement($element, check_plain($field_value));
              $parent
                ->appendChild($element_node);
            }
            $element_path[] = $element;
            $nodes_in_element_path[implode('/', $element_path)] = $element_node;

            // Prepare next iteration.
            $parent = $element_node;
          }
          $this
            ->apply_attributes($row_index, $field, $delta, $nodes_in_element_path, $available_attributes, $used_attributes);

          // Moving on to next field value.
          $delta++;
        }

        // Remove attributes that have been consumed by this field.
        $available_attributes = array_diff_key($available_attributes, $used_attributes);
      }
    }
  }

  /**
   * Applies the available attributes to any of the given nodes.
   *
   * Applies the available attributes to any of the given nodes, whenever a
   * node's path matches an attribute's path. Unused attributes may remain.
   * Matching is starts with the leaf node, to avoid overwriting attributes
   * that might have already been applied to parent nodes.
   *
   * @param int $row_index
   *   The row index.
   * @param string $field
   *   The field name.
   * @param int $delta
   *   The delta.
   * @param array $nodes
   *   A list of nodes belonging to a single path, sorted parents first, and
   *   keyed by their paths.
   * @param array $available_attributes
   *   A list of available attributes.
   * @param array $used_attributes
   *   A list of used attributes.
   */
  protected function apply_attributes($row_index, $field, $delta, array $nodes, array $available_attributes, array &$used_attributes) {
    foreach (array_reverse($nodes) as $element_path => $element_node) {

      // Check if the metadata format supports any attributes on the current
      // path.
      if (isset($this->request->metadata_format->attributes[$element_path]) && !empty($this->request->metadata_format->attributes[$element_path])) {

        // Check if any of our available attributes are among those supported by
        // the metadata format.
        foreach ($available_attributes as $attribute_name => $attribute_field_name) {
          if (in_array($attribute_name, $this->request->metadata_format->attributes[$element_path])) {

            // Attribute applies. Let's add it!
            $attribute_field = $this->view->field[$attribute_field_name];
            $attribute_value = $this
              ->get_field($row_index, $attribute_field_name);
            if (isset($attribute_field->multiple) && $attribute_field->multiple && isset($field->multiple) && $field->multiple) {

              // Find attribute delta matching the current field delta.
              $attribute_values = explode($attribute_field->options['separator'], $attribute_value);
              if (isset($attribute_values[$delta])) {
                $attribute_value = $attribute_values[$delta];
              }
            }
            $attribute_value = trim($attribute_value);
            if (!$attribute_field->options['hide_empty'] || !empty($attribute_value)) {
              $element_node
                ->setAttribute($attribute_name, check_plain($attribute_value));
            }
            $used_attributes[$attribute_name] = TRUE;

            // Remove this attribute from the available ones before proceeding
            // with the remainder of the path.
            unset($available_attributes[$attribute_name]);
          }
        }
      }
    }
  }

  /**
   * Check whether any of the available attributes applies to elements.
   *
   * Check whether any of the available attributes applies to elements. in the
   * given path. Attributes that apply get removed from $available_attributes.
   */
  protected function eat_attributes($path, &$available_attributes) {

    // Clean path to match format used by metadata_format->attributes keys.
    foreach (array_keys($path) as $index) {
      $this
        ->prepare_tag($path[$index]);
    }

    // Loop through all subpaths, starting with the full path.
    while (!empty($path)) {
      $subpath = implode('/', $path);

      // Check if the metadata format supports any attributes on the current
      // path.
      if (isset($this->request->metadata_format->attributes[$subpath]) && !empty($this->request->metadata_format->attributes[$subpath])) {

        // Check if any of our available attributes are among those supported by
        // the metadata format.
        foreach ($available_attributes as $attribute_name => $attribute_field_name) {
          if (in_array($attribute_name, $this->request->metadata_format->attributes[$subpath])) {

            // Attribute applies. Let's remove it from the available ones!
            unset($available_attributes[$attribute_name]);
          }
        }
      }
      array_pop($path);
    }
  }

  /**
   * Prepare a tag.
   *
   * Given a tag originating from a mapping string, cleans it from mapping
   * codes (so it becomes proper for addition to the XML document), and returns
   * an xpath pattern useful to check if the element already exists.
   */
  protected static function prepare_tag(&$tag, $delta = 0) {
    $matches = array();
    if (preg_match('/^(.+)\\[([0-9]+)\\]$/', $tag, $matches)) {

      // Mapping element has a '[0-9]' suffix. Use that directly as the xpath.
      $xpath_pattern = $tag;

      // Replace with string without suffix.
      $tag = $matches[1];
    }
    elseif (preg_match('/^(.+)\\[\\]$/', $tag, $matches)) {

      // Mapping element has a '[]' suffix. Fill brackets with current delta.
      // Replace with string without suffix.
      $tag = $matches[1];
      $xpath_pattern = $tag . '[' . ($delta + 1) . ']';
    }
    else {
      $xpath_pattern = $tag;
    }
    return $xpath_pattern;
  }

  /**
   * Saves a resumption token to the database and appends it to the XML.
   */
  protected function save_resumption_token() {
    if (get_class($this->view->query->pager) != 'views_plugin_pager_full') {
      watchdog('views_oai_pmh', 'Unexpected pager type. A full pager is required for harvesters to be able to retrieve all the data.', NULL, WATCHDOG_ERROR);
      return;
    }
    $items_per_page = $this->view->query->pager
      ->get_items_per_page();
    $total_items = $this->view->query->pager
      ->get_total_items();

    // Only generate the token if the list is incomplete.
    if ($total_items > $items_per_page) {
      $current_page = $this->view->query->pager
        ->get_current_page();
      $total_pages = $this->view->query->pager
        ->get_pager_total();
      $cursor = $items_per_page * $current_page;

      // When the list is complete, standard requires that an empty token is
      // output.
      $token = $current_page == $total_pages - 1 ? '' : md5(uniqid());

      // Add token output.
      $token_element = $this->xml
        ->createElement('resumptionToken', $token);
      $this->xml_verb
        ->appendChild($token_element);
      $token_element
        ->setAttribute('completeListSize', $total_items);
      $token_element
        ->setAttribute('cursor', $cursor);
      if ($token) {
        $expire = time() + VIEWS_OAI_PMH_TOKEN_LIFETIME;
        $token_element
          ->setAttribute('expirationDate', gmstrftime('%Y-%m-%dT%H:%M:%SZ', $expire));

        // Save token to database.
        $data = array(
          'verb' => $this->request->verb,
          'metadata_format' => $this->request->metadata_format->id,
          'query' => $this->view->query,
          'current_page' => $current_page,
        );
        $fields = array(
          'serialized' => 1,
          'created' => $this->request->response_timestamp,
          'expire' => $expire,
          'data' => serialize($data),
          'cid' => $token,
        );

        // Doing our own insert here because cache_set() is silently eating
        // max_allowed_packet errors and dropping cache inserts. Changing the
        // database configuration fixes the error, so let's make users aware of
        // it. Reference: http://drupal.org/node/542874.
        //
        // cache_set($token, $cache, 'cache_views_oai_pmh', time() +
        // VIEWS_OAI_PMH_TOKEN_LIFETIME);.
        db_insert('cache_views_oai_pmh')
          ->fields($fields)
          ->execute();
      }
    }
  }

  /**
   * Internal XML building methods.
   */

  /**
   * Creates the DOMDocument for the OAI-PMH response.
   *
   * Creates the DOMDocument for the OAI-PMH response. with elements that are
   * common to all responses.
   */
  protected function xml_init() {
    $this->xml = new DOMDocument('1.0', 'UTF-8');
    $this->xml->preserveWhitespace = FALSE;
    $this->xml->formatOutput = TRUE;
    $this->xml_root = $this->xml
      ->createElement('OAI-PMH');
    $this->xml_root = $this->xml
      ->appendChild($this->xml_root);
    $this->xml_root
      ->setAttribute('xmlns', 'http://www.openarchives.org/OAI/2.0/');
    $this->xml_root
      ->setAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance');
    $this->xml_root
      ->setAttribute('xsi:schemaLocation', 'http://www.openarchives.org/OAI/2.0/   http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd');
    $this
      ->xml_append_children($this->xml_root, array(
      'responseDate' => $this->request->response_date,
    ));
  }

  /**
   * Adds a node for the <request> element to the XML document.
   */
  protected function xml_append_request() {
    $request_element = $this->xml
      ->createElement('request', $this->request->base_url);
    $this->xml_root
      ->appendChild($request_element);

    // Provide attributes, unless a badVerb or badArgument error has occurred.
    if (!$this->request
      ->is_bad()) {
      if ($this->request->verb) {
        $request_element
          ->setAttribute('verb', $this->request->verb);
      }
      foreach ($this->request->original_args as $arg => $value) {
        $request_element
          ->setAttribute($arg, $value);
      }
    }
  }

  /**
   * Adds a node for the OAI-PMH verb to the XML document.
   *
   * Successful responses append their content to such nodes.
   */
  protected function xml_append_verb() {

    // Add verb element, e.g. <Identify>, <GetRecord>, etc.
    if (!$this->request->errors) {
      $this->xml_verb = $this->xml
        ->createElement($this->request->verb);
      $this->xml_root
        ->appendChild($this->xml_verb);
    }
  }

  /**
   * Adds all errors to the XML document.
   */
  protected function xml_append_errors() {
    foreach ($this->request->errors as $error) {
      $error_element = $this->xml
        ->createElement('error', $error
        ->get_message());
      $error_element
        ->setAttribute('code', $error->code);
      $this->xml_root
        ->appendChild($error_element);
    }
  }

  /**
   * Creates multiple children elements under a common parent in the XML.
   *
   * Creates multiple children elements under a common parent in the XML.
   * document. Key-value pairs from the $children array correspond to tag-value
   * pairs to create.
   */
  protected function xml_append_children($parent, $children) {
    foreach ($children as $tag => $value) {
      $element = $this->xml
        ->createElement($tag, check_plain($value));
      $parent
        ->appendChild($element);
    }
  }

  /**
   * Adds multiple attributes to a given element.
   *
   * Key-value pairs from the $attributes array correspond to attribute-value
   * pairs to set.
   */
  protected static function xml_set_attributes($element, $attributes) {
    foreach ($attributes as $name => $value) {
      $element
        ->setAttribute($name, $value);
    }
  }

  /**
   * Internal utility methods.
   */

  /**
   * Make the record identifier.
   */
  protected function make_record_identifier($entity_id) {
    return $this->request
      ->make_record_identifier_prefix() . $entity_id;
  }

  /**
   * Get the values for the root attribute elements.
   *
   * @return array
   *   An array with root attribute elements keyed by his name.
   */
  protected function get_root_attribute_elements($row_index, $fields) {
    $root_attributes = array();
    foreach ($fields as $field_name => $field) {
      $attribute_field = $this->view->field[$field_name];
      $attribute_value = $this
        ->get_field($row_index, $field_name);
      if (isset($this->options['field_mappings'][$this->request->metadata_format->id][$field_name]) && (!$attribute_field->options['hide_empty'] || !empty($attribute_value))) {
        $mapping = $this->options['field_mappings'][$this->request->metadata_format->id][$field_name];
        if ($mapping != 'none' && $mapping[0] == '_') {
          $root_attributes[substr($mapping, 1)] = $this
            ->get_field($row_index, $field_name);
        }
      }
    }
    return $root_attributes;
  }

  /**
   * Get the mapping for a field.
   *
   * @return array
   *   An array where the first value is either a element path, or an attribute
   *   name the field is mapped to, and the second value a boolean that is TRUE
   *   if the first value is an attribute (not root attribute), FALSE otherwise.
   */
  protected function get_mapping($field_name) {
    if (isset($this->options['field_mappings'][$this->request->metadata_format->id][$field_name])) {
      $mapping = $this->options['field_mappings'][$this->request->metadata_format->id][$field_name];
      if ($mapping != 'none' && $mapping[0] != '_') {
        if ($mapping[0] == '@') {

          // Mapping is an attribute.
          return array(
            substr($mapping, 1),
            TRUE,
          );
        }

        // Mapping is an element.
        return array(
          $mapping,
          FALSE,
        );
      }
    }
    return array(
      '',
      FALSE,
    );
  }

  /**
   * Returns an array with all the enabled metadata formats.
   *
   * The array is keyed by metadata prefix.
   */
  protected function get_metadata_prefixes() {
    $enabled = array();
    foreach ($this->options['enabled_formats'] as $id) {

      // If format $id is enabled.
      if ($id) {

        // Add its metadata prefix.
        $enabled[$this
          ->get_metadata_prefix($id)] = $id;
      }
    }
    return $enabled;
  }

  /**
   * Get the metadata prefix corresponding to the given metadata format id.
   *
   * Get the metadata prefix corresponding to the given metadata format id,
   * based on the plugin's settings.
   */
  protected function get_metadata_prefix($format_id) {
    if (isset($this->options['metadata_prefix'])) {
      return $this->options['metadata_prefix'][$format_id];
    }
    else {

      // Prefix settings are missing. We're probably dealing with a view that
      // was created before the setting existed.
      return $format_id;
    }
  }

}

Members

Namesort descending Modifiers Type Description Overrides
views_oai_pmh_plugin_style::$request protected property The OAI-PMH request, represented by a views_oai_pmh_request object.
views_oai_pmh_plugin_style::$xml protected property The XML response document, a DOMDocument object.
views_oai_pmh_plugin_style::$xml_root protected property The root element of the XML response, a DOMElement object.
views_oai_pmh_plugin_style::$xml_verb protected property The container element for the response.
views_oai_pmh_plugin_style::add_record_query_dates protected function Add the requested date range to the view's query.
views_oai_pmh_plugin_style::add_record_query_fields protected function Adds the fields needed for record retrieval to the view's query.
views_oai_pmh_plugin_style::append_oai_identifier_description protected function
views_oai_pmh_plugin_style::append_record_header protected function Append record header.
views_oai_pmh_plugin_style::append_record_metadata protected function Builds the core of a record's XML.
views_oai_pmh_plugin_style::apply_attributes protected function Applies the available attributes to any of the given nodes.
views_oai_pmh_plugin_style::eat_attributes protected function Check whether any of the available attributes applies to elements.
views_oai_pmh_plugin_style::get_mapping protected function Get the mapping for a field.
views_oai_pmh_plugin_style::get_metadata_prefix protected function Get the metadata prefix corresponding to the given metadata format id.
views_oai_pmh_plugin_style::get_metadata_prefixes protected function Returns an array with all the enabled metadata formats.
views_oai_pmh_plugin_style::get_record_query protected function Prepares the query for a 'GetRecord' OAI-PMH request.
views_oai_pmh_plugin_style::get_record_render protected function Renders a response to the 'GetRecord' OAI-PMH request.
views_oai_pmh_plugin_style::get_root_attribute_elements protected function Get the values for the root attribute elements.
views_oai_pmh_plugin_style::identify_query protected function Prepares the query for an 'Identify' OAI-PMH request.
views_oai_pmh_plugin_style::identify_render protected function Renders a response to the 'identify' OAI-PMH request.
views_oai_pmh_plugin_style::list_identifiers_query protected function Prepares the query for a 'ListIdentifiers' OAI-PMH request.
views_oai_pmh_plugin_style::list_identifiers_render protected function Renders a response to the 'ListIdentifiers' OAI-PMH request.
views_oai_pmh_plugin_style::list_metadata_formats_query protected function Prepares the query for a 'ListMetadataFormats' OAI-PMH request.
views_oai_pmh_plugin_style::list_metadata_formats_render protected function Renders a response to the 'ListMetadataFormats' OAI-PMH request.
views_oai_pmh_plugin_style::list_records_query protected function Prepares the query for a 'ListRecords' OAI-PMH request.
views_oai_pmh_plugin_style::list_records_render protected function Renders a response to the 'ListRecords' OAI-PMH request.
views_oai_pmh_plugin_style::load_resumption_token_and_resume_query protected function Load the resumption token and resume query.
views_oai_pmh_plugin_style::make_record_identifier protected function Make the record identifier.
views_oai_pmh_plugin_style::options_form public function Provide settings for this plugin. Overrides views_plugin_style::options_form
views_oai_pmh_plugin_style::options_validate public function Option validate. Overrides views_plugin_style::options_validate
views_oai_pmh_plugin_style::option_definition public function Option definition. Overrides views_plugin_style::option_definition
views_oai_pmh_plugin_style::prepare_tag protected static function Prepare a tag.
views_oai_pmh_plugin_style::query public function Query. Overrides views_plugin_style::query
views_oai_pmh_plugin_style::render public function Render. Overrides views_plugin_style::render
views_oai_pmh_plugin_style::save_resumption_token protected function Saves a resumption token to the database and appends it to the XML.
views_oai_pmh_plugin_style::xml_append_children protected function Creates multiple children elements under a common parent in the XML.
views_oai_pmh_plugin_style::xml_append_errors protected function Adds all errors to the XML document.
views_oai_pmh_plugin_style::xml_append_request protected function Adds a node for the <request> element to the XML document.
views_oai_pmh_plugin_style::xml_append_verb protected function Adds a node for the OAI-PMH verb to the XML document.
views_oai_pmh_plugin_style::xml_init protected function Creates the DOMDocument for the OAI-PMH response.
views_oai_pmh_plugin_style::xml_set_attributes protected static function Adds multiple attributes to a given element.
views_object::$definition public property Handler's definition.
views_object::$options public property Except for displays, options for the object will be held here. 1
views_object::altered_option_definition function Collect this handler's option definition and alter them, ready for use.
views_object::construct public function Views handlers use a special construct function. 4
views_object::export_option public function 1
views_object::export_options public function
views_object::export_option_always public function Always exports the option, regardless of the default value.
views_object::options Deprecated public function Set default options on this object. 1
views_object::set_default_options public function Set default options.
views_object::set_definition public function Let the handler know what its full definition is.
views_object::unpack_options public function Unpack options over our existing defaults, drilling down into arrays so that defaults don't get totally blown away.
views_object::unpack_translatable public function Unpack a single option definition.
views_object::unpack_translatables public function Unpacks each handler to store translatable texts.
views_object::_set_option_defaults public function
views_plugin::$display public property The current used views display.
views_plugin::$plugin_name public property The plugin name of this plugin, for example table or full.
views_plugin::$plugin_type public property The plugin type of this plugin, for example style or query.
views_plugin::$view public property The top object of a view. Overrides views_object::$view 1
views_plugin::additional_theme_functions public function Provide a list of additional theme functions for the theme info page.
views_plugin::options_submit public function Handle any special handling on the validate form. 9
views_plugin::plugin_title public function Return the human readable name of the display.
views_plugin::summary_title public function Returns the summary of the settings in the display. 8
views_plugin::theme_functions public function Provide a full list of possible theme templates used by this style.
views_plugin_style::$row_plugin public property The row plugin, if it's initialized and the style itself supports it.
views_plugin_style::$row_tokens public property Store all available tokens row rows.
views_plugin_style::build_sort public function 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. 1
views_plugin_style::build_sort_post public function Called by the view builder to let the style build a second set of sorts that will come after any other sorts in the view. 1
views_plugin_style::destroy public function Destructor. Overrides views_object::destroy
views_plugin_style::even_empty public function Should the output of the style plugin be rendered even if it's empty. 1
views_plugin_style::get_field public function Get a rendered field.
views_plugin_style::get_field_value public function Get the raw field value.
views_plugin_style::get_row_class public function Return the token replaced row class for the specified row.
views_plugin_style::init public function Initialize a style plugin.
views_plugin_style::pre_render public function Allow the style to do stuff before each row is rendered.
views_plugin_style::render_fields public function Render all of the fields for a given style and store them on the object.
views_plugin_style::render_grouping public function Group records as needed for rendering.
views_plugin_style::render_grouping_sets public function Render the grouping sets.
views_plugin_style::tokenize_value public function Take a value and apply token replacement logic to it.
views_plugin_style::uses_fields public function Return TRUE if this style also uses fields.
views_plugin_style::uses_row_class public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_row_plugin public function Return TRUE if this style also uses a row plugin.
views_plugin_style::uses_tokens public function Return TRUE if this style uses tokens.
views_plugin_style::validate public function Validate that the plugin is correct and can be saved. Overrides views_plugin::validate