You are here

biblio.module in Bibliography Module 6.2

Main file for Drupal module biblio.

// @todo: Explain briefly what the biblio module does...

Copyright (C) 2006-2012 Ron Jerome

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

File

biblio.module
View source
<?php

/**
 * @file
 * Main file for Drupal module biblio.
 *
 * // @todo: Explain briefly what the biblio module does...
 *
 * Copyright (C) 2006-2012  Ron Jerome
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
define('BIBLIO_VERSION', '6.x-2.x-dev');

/**
 * Retrieves author types based upon author category and biblio type.
 *
 * @param ?? $auth_category
 *   The category of author.
 * @param string $biblio_type
 *   A string representing the type of biblio item.
 *
 * @return array|null
 *   An associative array keyed by author category and biblio type.  NULL is
 *   returned if no matches could be found.
 */
function _biblio_get_auth_types($auth_category, $biblio_type) {
  static $auth_types = array();
  if (empty($auth_types)) {
    $db_res = db_query("SELECT * FROM {biblio_contributor_type}");
    while ($row = db_fetch_object($db_res)) {
      $auth_types[$row->auth_category][$row->biblio_type][] = $row->auth_type;
    }
  }
  $result = isset($auth_types[$auth_category][$biblio_type]) ? $auth_types[$auth_category][$biblio_type] : NULL;

  // Fall back to defaults if no author types are defined for this biblio_type.
  if (empty($result)) {
    $result = $auth_types[$auth_category][0];
  }
  return $result;
}

/**
 * Retrieves primary author type based on author category and biblio type.
 *
 * @param string $auth_category
 *   A string representing the category of author.
 * @param string $biblio_type
 *   A string representing the type of biblio item.
 *
 * @return string|null
 *   A string with the primary author type. NULL is returned if no matches could
 *   be found.
 */
function _biblio_get_auth_type($auth_category, $biblio_type) {
  $result = (array) _biblio_get_auth_types($auth_category, $biblio_type);

  // Return first element of the author types array.
  return empty($result) ? NULL : current($result);
}

/**
 * Retrieves field information based upon biblio type.
 *
 * @param string $biblio_type
 *   The type of biblio to retrieve field information about.
 * @param bool $only_visible
 *   (optional) A logical flag indicating whether to only return visible fields.
 *   The default is FALSE which indicates information on all fields for this
 *   biblio_type should be returned.
 *
 * @return array
 *   An associative array keyed by integer of field ID.
 */
function _biblio_get_field_information($biblio_type, $only_visible = FALSE) {
  $fields = array();
  $visible = $only_visible ? ' AND (bt.common = 1 OR bt.visible=1) ' : '';
  $result = db_query("SELECT b.*, btd.*, btt.name AS type_name\n                      FROM {biblio_fields} AS b\n                      INNER JOIN {biblio_field_type} AS bt ON bt.fid = b.fid\n                      INNER JOIN {biblio_field_type_data} AS btd ON btd.ftdid = bt.ftdid\n                      INNER JOIN {biblio_types} as btt ON btt.tid = bt.tid\n                      WHERE bt.tid = %d {$visible}\n                      ORDER BY bt.weight ASC", $biblio_type);
  while ($row = db_fetch_array($result)) {
    $fields[$row['fid']] = $row;
  }
  return $fields;
}

/**
 * Translates interface field titles and hints.
 *
 * If the i18nstrings module is enabled, this function translates field titles
 * and hints through the interface translation system.
 *
 * @param array $fields
 *   An associative array with field information (passed by reference) keyed by
 *   field ID number with two elements:
 *   - title: A string with the title of the field.
 *   - hint: A string with the hint text for the field.
 */
function _biblio_localize_fields(&$fields) {
  if (module_exists('i18nstrings')) {
    foreach ($fields as $key => $row) {
      if (isset($row['ftdid'])) {
        $fields[$key]['title'] = tt("biblio:field:{$row['ftdid']}:title", $fields[$key]['title']);
        $fields[$key]['hint'] = tt("biblio:field:{$row['ftdid']}:hint", $fields[$key]['hint']);
      }
    }
  }
}

/**
 * Translates a publication type for the interface.
 *
 * If the i18nstrings module is enabled, this function translates a publication
 * type through the interface translation system.
 *
 * @param integer $tid
 *   The biblio publication type identifier.
 * @param string $value
 *   The string to translate.
 * @param string $field
 *   (optional) The publication type field to translate (either 'name' or
 *   'description'). The default value is 'name'.
 *
 * @return string
 *   The translated text for publication type.
 */
function _biblio_localize_type($tid, $value, $field = 'name') {
  if (module_exists('i18nstrings')) {
    return tt("biblio:type:{$tid}:{$field}", $value);
  }
  return $value;
}

/**
 * Implements hook_locale().
 */
function biblio_locale($op = 'groups', $group = NULL) {
  switch ($op) {
    case 'groups':
      return array(
        'biblio' => t('Biblio'),
      );
    case 'refresh':
      if ($group == 'biblio') {
        biblio_locale_refresh_fields();
        biblio_locale_refresh_types();
      }
      break;
  }
}

/**
 * Refreshes all translatable field strings.
 *
 * @param integer $tid
 *   (optional) Biblio publication type ID whose field strings are to be
 *   refreshed. If not specified, strings for all fields will be refreshed. The
 *   default value is NULL.
 */
function biblio_locale_refresh_fields($tid = NULL) {
  if (module_exists('i18nstrings')) {
    if (isset($tid)) {
      $result = db_query('SELECT d.* FROM {biblio_field_type} b INNER JOIN {biblio_field_type_data} d ON b.ftdid = d.ftdid WHERE tid = %d', $tid);
    }
    else {
      $result = db_query('SELECT * FROM {biblio_field_type_data}');
    }
    while ($row = db_fetch_array($result)) {
      tt("biblio:field:{$row['ftdid']}:title", $row['title'], NULL, TRUE);
      tt("biblio:field:{$row['ftdid']}:hint", $row['hint'], NULL, TRUE);
    }
  }
}

/**
 * Refreshes all publication type strings.
 *
 * @param integer $tid
 *   (optional) Biblio publication type ID whose field strings are to be
 *   refreshed. If not specified, strings for all fields will be refreshed. The
 *   default value is NULL.
 */
function biblio_locale_refresh_types($tid = NULL) {
  if (module_exists('i18nstrings')) {
    if (isset($tid)) {
      $result = db_query('SELECT * FROM {biblio_types} WHERE tid = %d', $tid);
    }
    else {
      $result = db_query('SELECT * FROM {biblio_types} WHERE tid > 0');
    }
    while ($row = db_fetch_array($result)) {
      tt("biblio:type:{$row['tid']}:name", $row['name'], NULL, TRUE);
      tt("biblio:type:{$row['tid']}:description", $row['description'], NULL, TRUE);
    }
  }
}

/**
 * Implements hook_init().
 */
function biblio_init() {
  global $user, $conf;
  drupal_add_css(drupal_get_path('module', 'biblio') . '/biblio.css');

  // Prevent caching of biblio pages for anonymous users so session variables
  // work and hence filering also works.
  if ($user->uid === 0) {
    $base = variable_get('biblio_base', 'biblio');
    if (drupal_match_path($_GET['q'], "{$base}\n{$base}/*")) {
      $conf['cache'] = FALSE;
    }
  }
}

/**
 * Implements hook_cron().
 */
function biblio_cron() {
  require_once drupal_get_path('module', 'biblio') . '/includes/biblio.contributors.inc';
  require_once drupal_get_path('module', 'biblio') . '/includes/biblio.keywords.inc';

  // Defaults to a value of daily (24*60*60 or 86,400 seconds).
  $interval = variable_get('biblio_orphan_clean_interval', 86400);
  if (time() >= variable_get('biblio_orphan_clean_next_execution', 0)) {
    biblio_delete_orphan_authors();
    biblio_delete_orphan_keywords();
    variable_set('biblio_orphan_clean_next_execution', time() + $interval);
  }
}

/**
 * Implements hook_theme().
 */
function biblio_theme() {
  $path = drupal_get_path('module', 'biblio');
  return array(
    //   'views_view_unformatted__biblio_year' => array(
    //      'template' => 'views-view-unformatted--biblio-year',
    //      'path'  => $path,
    //    ),
    'biblio_alpha_line' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'type',
      ),
    ),
    'biblio_admin_author_types_form' => array(
      'file' => 'includes/biblio.admin.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_io_mapper_form' => array(
      'file' => 'includes/biblio.admin.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_type_mapper_form' => array(
      'file' => 'includes/biblio.admin.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_field_mapper_form' => array(
      'file' => 'includes/biblio.admin.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_types_edit_form' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_author_edit_form' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_orphans_form' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_admin_keyword_orphans_form' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_coin_button' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'element',
      ),
    ),
    'biblio_openurl' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'openURL',
      ),
    ),
    'biblio_style' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
        'base' => 'biblio',
        'style_name' => 'classic',
        'inline' => FALSE,
      ),
    ),
    'biblio_long' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
        'base' => 'biblio',
        'style_name' => 'classic',
      ),
    ),
    'biblio_tabular' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
        'base' => 'biblio',
        'teaser' => FALSE,
      ),
    ),
    'biblio_entry' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
        'base' => 'biblio',
        'style_name' => 'classic',
        'inline' => FALSE,
      ),
    ),
    'biblio_authors' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'contributors',
        'style' => 'classic',
        'inline' => FALSE,
        'glue' => ', ',
      ),
    ),
    'biblio_format_authors' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'contributors',
        'options' => array(),
        'inline' => FALSE,
      ),
    ),
    'biblio_author_link' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'author',
        'authorID',
        'base' => 'biblio',
        'inline' => FALSE,
      ),
    ),
    'biblio_filters' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'form_filter' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
    'biblio_export_links' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
      ),
    ),
    'biblio_download_links' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
      ),
    ),
    'google_scholar_link' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'node',
      ),
    ),
    'biblio_contributors' => array(
      'file' => 'includes/biblio_theme.inc',
      'arguments' => array(
        'form',
      ),
    ),
  );
}

/**
 *
 *
 * @param $field
 *
 * @param string $string
 *   (optional)
 */
function biblio_autocomplete($field, $string = '') {
  $matches = array();
  if ($field == 'contributor') {
    $sql = "SELECT * FROM {biblio_contributor_data} " . "WHERE LOWER(lastname) LIKE LOWER('%s%%') OR LOWER(firstname) LIKE LOWER('%s%%') " . "ORDER BY lastname ASC ";
    $result = db_query_range($sql, array(
      $string,
      $string,
    ), 0, 10);
    while ($data = db_fetch_object($result)) {
      $matches[$data->name] = check_plain($data->name);
    }
  }
  elseif ($field == 'biblio_keywords') {
    $sep = check_plain(variable_get('biblio_keyword_sep', ','));

    // Locate the position of last separator in the string.
    $sep_pos = strrpos($string, $sep);

    // The beginning of the string until the last separator.
    $start = trim(drupal_substr($string, 0, $sep_pos));
    $end_sep = $sep_pos ? $sep_pos + 1 : $sep_pos;

    // The balance of the string after the last separator.
    $end = trim(drupal_substr($string, $end_sep));
    $sql = "SELECT * FROM {biblio_keyword_data} WHERE LOWER(word) LIKE LOWER('%s%%') ORDER BY word ASC ";
    $result = db_query_range($sql, array(
      $end,
    ), 0, 10);
    while ($data = db_fetch_object($result)) {

      // Glue the word found onto the end of the original string.
      $keywords = $sep_pos ? $start . ', ' . check_plain($data->word) : check_plain($data->word);
      $matches[$keywords] = $keywords;
    }
  }
  else {
    $sql = "SELECT %s FROM {biblio} WHERE LOWER(%s) LIKE LOWER('%s%%') ORDER BY %s ASC";
    $result = db_query_range($sql, array(
      $field,
      $field,
      $string,
      $field,
    ), 0, 10);
    while ($data = db_fetch_object($result)) {
      $matches[$data->{$field}] = check_plain($data->{$field});
    }
  }
  print drupal_to_js($matches);
  exit;
}

/**
 * Generates a page of translated help text applicable to the biblio module.
 *
 * @return string
 *   An HTML formatted string with possibly translated content for this module.
 */
function biblio_help_page() {
  $base = variable_get('biblio_base', 'biblio');
  $text = "<h3>" . t('General:') . "</h3>";
  $text .= "<p>" . t('By default, the !url page will list all of the entries in the database sorted by Year in descending order. If you wish to sort by "Title" or "Type",  you may do so by clicking on the appropriate links at the top of the page.  To reverse the sort order, simply click the link a second time.', array(
    '!url' => l('', $base),
  )) . "</p>";
  $text .= "<h3>" . t('Filtering Search Results:') . "</h3>";
  $text .= "<p>" . t('If you wish to filter the results, click on the "Filter" tab at the top of the page.  To add a filter, click the radio button to the left of the filter type you wish to apply, then select the filter criteria from the drop down list on the right, then click the filter button.') . "</p>";
  $text .= "<p>" . t('It is possible to create complex filters by returning to the <i>Filter</i> tab and adding additional filters.  Simply follow the steps outlined above and press the "Refine" button.') . "</p>";
  $text .= "<p>" . t('All filters can be removed by clicking the <i>Clear All Filters</i> link at the top of the result page, or on the <i>Filter</i> tab they can be removed one at a time using the <i>Undo</i> button, or you can remove them all using the <i>Clear All</i> button.') . "</p>";
  $text .= "<p>" . t('You may also construct URLs which filter.  For example, /biblio/year/2005 will show all of the entries for 2005.  /biblio/year/2005/author/smith will show all of entries from 2005 for smith.') . "</p>";
  $text .= "<h3>" . t('Exporting Search Results:') . "</h3>";
  $text .= "<p>" . t('Assuming this option has been enabled by the administrator, you can export search results directly into EndNote.  The link at the top of the result page will export all of the search results, and the links on individual entries will export the information related to that single entry.') . "</p>";
  $text .= "<p>" . t('The information is exported in EndNote "Tagged" format similar to this...') . "<pre>" . t('
                  %0  Book
                  %A  John Smith
                  %D  1959
                  %T  The Works of John Smith
                  ...') . '</pre></p>';
  $text .= "<p>" . t('Clicking on one of the export links should cause your browser to ask you whether you want to Open, or Save To Disk, the file endnote.enw.  If you choose to open it, Endnote should start and ask you which library you would like store the results in.  Alternatively, you can save the file to disk and manually import it into EndNote.') . "</p>";
  return $text;
}

/**
 * Implements hook_help().
 *
 * Throughout Drupal, hook_help() is used to display help text at the top of
 * pages. Some other parts of Drupal pages get explanatory text from these hooks
 * as well. We use it here to provide a description of the module on the module
 * administration page.
 */
function biblio_help($path, $arg) {
  switch ($path) {
    case 'admin/help#biblio':
      return biblio_help_page();
    case 'admin/modules#description':

      // This description is shown in the listing at admin/modules.
      return t('Manages a list of scholarly papers on your site');
    case 'node/add#biblio':

      // This description appears when users click "create content."
      return t('This allows you to add a bibliographic entry to the database');
  }
}

/**
 * Implements hook_node_info().
 */
function biblio_node_info() {
  return array(
    'biblio' => array(
      'name' => t('Biblio'),
      'module' => 'biblio',
      'description' => t('Manages bibliographies'),
    ),
  );
}

/**
 * Implements hook_access().
 *
 * Node modules may implement node_access() to determine the operations users
 * may perform on nodes. This example uses a very common access pattern.
 */
function biblio_access($op, $node = '', $user = '') {
  switch ($op) {
    case 'create':
      return user_access('create biblio');
    case 'delete':
    case 'update':
      if (user_access('edit all biblio entries')) {
        return TRUE;
      }
      if (!isset($user->uid)) {
        return;
      }
      if (user_access('edit own biblio entries') && $user->uid == $node->uid) {
        return TRUE;
      }
      break;
    case 'view':
      if (variable_get('biblio_view_only_own', 0) && $user->uid != $node->uid) {
        return FALSE;
      }
      break;
    case 'admin':
      return user_access('administer biblio');
    case 'import':
      return user_access('import from file');
    case 'export':
      return user_access('show export links');
    case 'edit_author':
      if (user_access('administer biblio') || user_access('edit biblio authors')) {
        return TRUE;
      }
      break;
    case 'download':
      if (user_access('show download links')) {
        return TRUE;
      }
      if (!isset($user->uid)) {
        return;
      }
      if (user_access('show own download links') && $user->uid == $node->uid) {
        return TRUE;
      }
      break;
    case 'rss':
      return variable_get('biblio_rss', 0);
    default:
      break;
  }
  return;
}

/**
 * Implements of hook_perm().
 *
 * Since we are restricting users in various ways when they wish to create,
 * view, update and/or delete biblio content, we need to define what those
 * permissions are here.
 */
function biblio_perm() {
  return array(
    'administer biblio',
    'access biblio content',
    'create biblio',
    'edit all biblio entries',
    'edit own biblio entries',
    'edit biblio authors',
    'import from file',
    'show export links',
    'show download links',
    'show own download links',
    'show filter tab',
    'show sort links',
    'view full text',
  );
}

/**
 * Implements hook_link().
 *
 * This is implemented so that an edit link is displayed for users who have the
 * correct permission(s) to edit a node.
 */
function biblio_link($type, $node = NULL, $teaser = FALSE) {
  $links = array();
  $base = variable_get('biblio_base', 'biblio');
  if ($type == 'node' && $node->type == 'biblio') {

    // Don't display a redundant edit link if they are node administrators.
    if (biblio_access('update', $node) && !user_access('administer nodes')) {
      $links['biblio_edit'] = array(
        'title' => t('edit this entry'),
        'href' => "node/{$node->nid}/edit",
      );
    }
    if (biblio_access('export', $node)) {
      $show_link = variable_get('biblio_lookup_links', array(
        'google' => TRUE,
      ));
      if ($show_link['google']) {
        $links['biblio_google_scholar'] = theme('google_scholar_link', $node);
      }
    }
  }
  return $links;
}

/**
 * Implements hook_link_alter to modifiy taxonomy links.
 *
 * @param $links
 * @param $node
 * @return none
 */

//function biblio_link_alter(&$links, $node) {

//  foreach ($links AS $module => $link) {
//    if (strstr($module, 'taxonomy_term')) {
//      // Link back to the forum and not the taxonomy term page
//      $links[$module]['href'] = str_replace('taxonomy/term', 'biblio/term_id', $link['href']);
//    }
//  }

//}

/**
 * Implements hook_user().
 */
function biblio_user($type, &$edit, &$account, $category = NULL) {
  global $user;
  if ($type == 'form' && $category == 'account') {
    $form = array();
    module_load_include('inc', 'biblio', 'includes/biblio.admin');
    $show_form = variable_get('biblio_show_user_profile_form', '1') || variable_get('biblio_show_crossref_profile_form', '1') || variable_get('biblio_show_openurl_profile_form', '1');
    $admin_show_form = $user->uid == 1 || user_access('administer users') && user_access('administer biblio') ? TRUE : FALSE;
    if ($admin_show_form || $show_form) {
      $form['biblio_fieldset'] = array(
        '#type' => 'fieldset',
        '#title' => t('Biblio settings'),
        '#weight' => 5,
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
      );
      if ($admin_show_form || variable_get('biblio_show_user_profile_form', '1')) {
        _biblio_get_user_profile_form($form['biblio_fieldset'], $account);
      }
      if ($admin_show_form || variable_get('biblio_show_openurl_profile_form', '1')) {
        _biblio_get_user_openurl_form($form['biblio_fieldset'], $account);
      }
      if ($admin_show_form || variable_get('biblio_show_crossref_profile_form', '1')) {
        _biblio_get_user_doi_form($form['biblio_fieldset'], $account);
      }
    }
    return $form;
  }
  if ($type == 'validate' && $category == 'account') {

    // TODO: this is not reached at all
    if (($edit['biblio_my_pubs_menu'] || $edit['biblio_show_profile']) && $edit['biblio_contributor_id'] == 0) {
      $message = t('You did not supply an associated biblio author.');
      drupal_set_message($message, 'warning');
    }
  }
  if ($type == 'after_update' && $category == 'account') {

    //TODO: detect if a biblio setting was changed before invoking menu_rebuild

    // menu_rebuild();
  }
  if ($type == 'update' && $category == 'account') {
    if (isset($edit['biblio_contributor_id'])) {
      db_query("UPDATE {biblio_contributor_data} SET drupal_uid = 0 WHERE drupal_uid = %d", $account->uid);
      db_query('UPDATE {biblio_contributor_data} SET drupal_uid = %d WHERE cid = %d ', $account->uid, $edit['biblio_contributor_id']);
    }
  }
  if ($type == 'categories') {

    //  return array(array('name' => 'account', 'title' => t('Account settings'), 'weight' => 1));
  }
}

/**
 *
 */
function biblio_forms() {
  $forms['biblio_admin_author_types_form_new'] = array(
    'callback' => 'biblio_admin_author_types_form',
  );
  $forms['biblio_admin_author_types_form_edit'] = array(
    'callback' => 'biblio_admin_author_types_form',
  );
  return $forms;
}

/**
 * Determines actual argument for %biblio_user placeholders in menu paths.
 *
 * This function returns the current user ID when called from a module like
 * tracker (aka with an empty arg).  It also is used to get the current user ID
 * when called from a menu item with a % for the current user account link.
 *
 * @param mixed $arg
 *   A variable representing the user identifier.
 *
 * @return mixed
 *   If $arg is empty or equal to '%', the user ID is returned; otherwise, the
 *   value of the input $arg variable is returned.
 */
function biblio_user_to_arg($arg) {
  return empty($arg) || $arg == '%' ? $GLOBALS['user']->uid : $arg;
}

/**
 * Loads a user object based upon user ID.
 *
 * This is used with %biblio_user placeholders in resolving user menu paths.
 *
 * @param integer $uid
 *   An integer identifier of a user object to retrieve from data table(s).
 *
 * @return object
 *   The user object with identifier $uid.
 */
function biblio_user_load($uid) {
  return user_load($uid);
}

/**
 * Implements hook_menu().
 *
 * This function defines all of the menu items that are handled by the biblio
 * module.  The variables 'biblio_base' and 'biblio_base_title" allow what is
 * presented to the user to vary from biblio.  Hence, what is by default
 * 'biblio/authors' could be changed to 'journal/authors' by setting the
 * variable 'biblio_base' to 'journal'.
 */
function biblio_menu() {
  global $user;
  $items = array();
  $base = variable_get('biblio_base', 'biblio');
  $base_title = check_plain(variable_get('biblio_base_title', 'Biblio'));
  $items["{$base}"] = array(
    'title' => $base_title,
    'page callback' => 'biblio_db_search',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
  );
  $items["{$base}/authors"] = array(
    'title' => 'Authors',
    'page callback' => 'biblio_author_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'weight' => 1,
  );
  $items["{$base}/keywords"] = array(
    'title' => 'Keywords',
    'page callback' => 'biblio_keyword_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    //    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
  $items["{$base}/import"] = array(
    'title' => 'Import',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_import_form',
    ),
    'file' => 'includes/biblio.import.export.inc',
    'access callback' => 'user_access',
    'access arguments' => array(
      'import from file',
    ),
    //   'type'              => MENU_LOCAL_TASK,
    'weight' => 10,
  );
  $items["{$base}/user/%biblio_user"] = array(
    'title' => 'My publications',
    'page callback' => 'biblio_get_user_pubs',
    'page arguments' => array(
      2,
    ),
    'access callback' => '_biblio_profile_access',
    'access arguments' => array(
      2,
      'menu',
    ),
    'parent' => '',
    'file' => 'includes/biblio.pages.inc',
  );

  /*
  $items["$base/backup"] = array(
  'title' => '',
  'page callback' => 'biblio_backup',
  'access callback' => 'user_access',
  'access arguments' => array('access content'),
  'file' => 'biblio.import.export.inc',
  'type' => MENU_CALLBACK
  );
  */
  $items["{$base}/pot"] = array(
    'title' => '',
    'page callback' => 'biblio_dump_db_data_for_pot',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'type' => MENU_CALLBACK,
  );
  $wildcard = 2 + (count(explode("/", $base)) - 1);
  $items["{$base}/authors/%/edit"] = array(
    'title' => 'Edit author information',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_edit_form',
      $wildcard,
    ),
    'access callback' => 'biblio_access',
    'access arguments' => array(
      'edit_author',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/keywords/%/edit"] = array(
    'title' => '',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_keyword_edit_form',
      $wildcard,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/keyword/%/delete"] = array(
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_keyword_delete_confirm',
      $wildcard,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'weight' => 1,
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/view/%"] = array(
    'page callback' => 'biblio_view_node',
    'page arguments' => array(
      $wildcard,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_CALLBACK,
  );
  $items["user/%user/{$base}"] = array(
    'title' => 'Publications',
    'page callback' => 'biblio_get_user_pubs',
    'page arguments' => array(
      1,
      'profile',
      'no_filters',
    ),
    'access callback' => '_biblio_profile_access',
    'access arguments' => array(
      1,
      'profile',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_LOCAL_TASK,
  );

  // The next two "LOCAL TASKS" are for the admin/settings/biblio page
  $items['admin/settings/biblio'] = array(
    'title' => 'Biblio settings',
    'description' => 'Configure default behavior of the biblio module.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_settings',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
  );
  $items['admin/settings/biblio/ahah'] = array(
    'title' => '',
    'page callback' => 'biblio_admin_ahah',
    'file' => 'includes/biblio.admin.inc',
    'access arguments' => array(
      'administer biblio',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/basic'] = array(
    'title' => 'Preferences',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items['admin/settings/biblio/import'] = array(
    'title' => 'Import',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_import_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.import.export.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 1,
  );
  $items['admin/settings/biblio/export'] = array(
    'title' => 'Export',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_export_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.import.export.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 2,
  );
  $items['admin/settings/biblio/fields'] = array(
    'title' => 'Fields',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_edit_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -9,
  );
  $items['admin/settings/biblio/fields/common'] = array(
    'title' => 'Common',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_edit_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -9,
  );
  $items['admin/settings/biblio/iomap'] = array(
    'title' => 'Import/Export Mapping',
    'page callback' => 'biblio_admin_io_mapper_page',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => '/includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -1,
  );
  $items['admin/settings/biblio/iomap/formats'] = array(
    'title' => 'Import/Export Mapping',
    'page callback' => 'biblio_admin_io_mapper_page',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => '/includes/biblio.admin.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -100,
  );
  $formats = module_invoke_all('biblio_mapper_options');
  foreach ($formats as $key => $format) {
    $items['admin/settings/biblio/iomap/edit/' . $key] = array(
      'title' => $format['title'],
      'page callback' => 'drupal_get_form',
      'page arguments' => array(
        'biblio_admin_io_mapper_form',
        5,
      ),
      'access arguments' => array(
        'administer biblio',
      ),
      'file' => '/includes/biblio.admin.inc',
      'tab_parent' => 'admin/settings/biblio/iomap',
      'type' => MENU_LOCAL_TASK,
      'weight' => -1,
    );
  }
  $items['admin/settings/biblio/iomap/%/%/add'] = array(
    'title' => '',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_io_mapper_add_form',
      4,
      5,
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'tab_parent' => 'admin/settings/biblio/iomap',
    'file' => '/includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
    'weight' => -1,
  );
  $items['admin/settings/biblio/fields/type'] = array(
    'title' => 'Publication Types',
    'page callback' => 'biblio_admin_types_form',
    //  'page arguments'    => array('biblio_admin_types_form'),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -8,
  );
  $items['admin/settings/biblio/fields/type/edit'] = array(
    'title' => '',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_edit_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/fields/type/delete'] = array(
    'title' => '',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_delete_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/fields/type/new'] = array(
    'title' => 'Add New Type',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_add_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -9,
  );
  $items['admin/settings/biblio/fields/type/reset'] = array(
    'title' => 'Reset all types to defaults',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_types_reset_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
  );
  $items['admin/settings/biblio/fields/type/hide'] = array(
    'title' => '',
    'page callback' => 'biblio_admin_types_hide',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/fields/type/show'] = array(
    'title' => '',
    'page callback' => 'biblio_admin_types_show',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/author'] = array(
    'title' => 'Authors',
    'page callback' => 'biblio_author_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -7,
  );
  $items['admin/settings/biblio/author/list'] = array(
    'title' => 'List',
    'page callback' => 'biblio_author_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -7,
  );
  $items['admin/settings/biblio/author/%/edit'] = array(
    'title' => 'Edit author information',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_edit_form',
      4,
    ),
    'access callback' => 'biblio_access',
    'access arguments' => array(
      'edit_author',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
    'weight' => -6,
  );
  $items['admin/settings/biblio/author/orphans'] = array(
    'title' => 'Orphaned Authors',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_orphans_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'description' => 'Delete orphaned biblio authors.',
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -6,
  );
  $items['admin/settings/biblio/author/type'] = array(
    'title' => 'Author Types',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_types_form',
      6,
      5,
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -5,
  );
  $items['admin/settings/biblio/author/type/new'] = array(
    'title' => 'Add New Author Type',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_types_form_new',
      'new',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -9,
  );
  $items['admin/settings/biblio/author/type/%/edit'] = array(
    'title' => 'Add New Author Type',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_types_form_edit',
      'edit',
      5,
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
    'weight' => -9,
  );
  $items['admin/settings/biblio/author/type/%/delete'] = array(
    'title' => 'Delete',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_author_type_delete_confirm',
      5,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'weight' => 1,
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/biblio/keywords'] = array(
    'title' => 'Keywords',
    'page callback' => 'biblio_keyword_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -7,
  );
  $items['admin/settings/biblio/keywords/list'] = array(
    'title' => 'List',
    'page callback' => 'biblio_keyword_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -7,
  );
  $items['admin/settings/biblio/keywords/%/edit'] = array(
    'title' => 'Edit keyword information',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_keyword_edit_form',
      4,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer biblio',
    ),
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_CALLBACK,
    'weight' => -6,
  );
  $items['admin/settings/biblio/keywords/orphans'] = array(
    'title' => 'Orphaned Keywords',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_admin_keyword_orphans_form',
    ),
    'access arguments' => array(
      'administer biblio',
    ),
    'description' => 'Delete orphaned biblio keywords.',
    'file' => 'includes/biblio.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => -6,
  );

  /*    $items['admin/settings/biblio/authors/reset'] = array(
     'title' => t('Reset all Author types to defaults'),
     'page callback' => 'drupal_get_form',
     'page arguments' => array('biblio_admin_author_type_reset_form'),
     'access arguments' => array('administer biblio'),
     'file' => 'biblio.admin.inc',
     'type' => MENU_LOCAL_TASK
     );
     */
  $items['biblio/autocomplete'] = array(
    'title' => 'Autocomplete ',
    'page callback' => 'biblio_autocomplete',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items['biblio/js'] = array(
    'title' => 'Javascript Contributors Form',
    'page callback' => 'biblio_contributors_js',
    'access arguments' => array(
      'access biblio content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/list"] = array(
    'title' => 'List',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  $items["{$base}/filter"] = array(
    'title' => 'Filter',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'biblio_form_filter',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'show filter tab',
    ),
    'type' => MENU_LOCAL_TASK,
    'file' => 'includes/biblio.pages.inc',
    'weight' => -9,
  );
  $items["{$base}/filter/clear"] = array(
    'title' => '',
    'page callback' => 'biblio_filter_clear',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/help"] = array(
    'title' => 'Help',
    'page callback' => 'biblio_help_page',
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/export"] = array(
    'title' => '',
    'page callback' => 'biblio_export',
    'access callback' => 'user_access',
    'access arguments' => array(
      'show export links',
    ),
    'file' => 'includes/biblio.import.export.inc',
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/citekey"] = array(
    'title' => '',
    'page callback' => 'biblio_citekey_view',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/viewinline/%node"] = array(
    'title' => '',
    'page callback' => 'biblio_view_inline',
    'page arguments' => array(
      2,
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'access biblio content',
    ),
    'file' => 'includes/biblio.pages.inc',
    'type' => MENU_CALLBACK,
  );
  $items["{$base}/recent/rss.xml"] = array(
    'title' => 'RSS feed',
    'page callback' => 'biblio_recent_feed',
    'access callback' => 'biblio_access',
    'access arguments' => array(
      'rss',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_filter_clear()
 */
function biblio_filter_clear() {
  $options = '';
  $_SESSION['biblio_filter'] = array();
  $base = variable_get('biblio_base', 'biblio');
  if (isset($_GET['sort'])) {
    $options .= "sort=" . $_GET['sort'];
  }
  if (isset($_GET['order'])) {
    $options .= $options['query'] ? "&" : "";
    $options .= "order=" . $_GET['order'];
  }
  drupal_goto($base, $options);
}

/**
 * Removes curly braces from a string.
 *
 * @param string $title_string
 *   The text string to remove curly braces from.
 *
 * @return string
 *   The resulting string with curly braces removed.
 */
function biblio_remove_brace($title_string) {

  //$title_string = utf8_encode($title_string);
  $matchpattern = '/\\{\\$(?:(?!\\$\\}).)*\\$\\}|(\\{[^}]*\\})/';
  $output = preg_replace_callback($matchpattern, 'biblio_remove_brace_callback', $title_string);
  return $output;
}

/**
 * Assists in the removal of curly braces with preg_replace_callback().
 *
 * @param array $match
 *
 *
 * @return string
 *
 */
function biblio_remove_brace_callback($match) {
  if (isset($match[1])) {
    $braceless = str_replace('{', '', $match[1]);
    $braceless = str_replace('}', '', $braceless);
    return $braceless;
  }
  return $match[0];
}

/**
 * Implements hook_nodeapi().
 */
function biblio_nodeapi(&$node, $op, $a3, $a4) {
  if ($node->type == 'biblio') {
    switch ($op) {
      case 'delete revision':
        db_query('DELETE FROM {biblio} WHERE vid = %d', $node->vid);
        db_query('DELETE FROM {biblio_contributor} WHERE nid = %d AND vid = %d', array(
          $node->nid,
          $node->vid,
        ));
        db_query('DELETE FROM {biblio_keyword} WHERE nid = %d AND vid = %d', array(
          $node->nid,
          $node->vid,
        ));
        break;

      /*   case 'presave':
           if ($node->type == 'biblio')
           {
           //  $node->body = '';
           //  $node_clone = clone($node); // we need a clone since objects get passed by reference regardless and we don't want to change the whole node object
           //  $node->body = theme('biblio_tabular', $node_clone, $base, $teaser);
           $style = biblio_get_style();
           $node->teaser = theme('biblio_style', $node_clone, $base, $style);
           }
           break;
           */
      case 'insert':
        if (variable_get('biblio_index', 0)) {
          _node_index_node($node);
          search_update_totals();
        }
        break;
      case 'update':
        if (variable_get('biblio_index', 0)) {

          // _node_index_node performs a node_load without resetting the node_load cache,
          // so it would index the old version. We reset the cache here.
          // Don't assign node_load to $node because node_load resets e.g. the menus mlid etc.
          $mynode = node_load($node->nid, NULL, TRUE);
          _node_index_node($mynode);
          search_update_totals();
        }
        break;
      case 'view':
        if ($node->type == 'biblio' && variable_get('biblio_hide_bibtex_braces', 0) && !empty($a4)) {
          drupal_set_title(filter_xss($node->title, biblio_get_allowed_tags()));
        }
        break;
    }
  }
}

/**
 * Implements hook_form_alter().
 *
 * @param string $form_id
 *   The machine name of the form on which alterations are to be performed.
 */
function biblio_form_alter(&$form, $form_state, $form_id) {
  if ($form_id == "biblio_node_form") {

    // For the first display of the biblio node form, this section removes all
    // the form elements execpt the publication type select box.
    if (!isset($form_state['values']['biblio_type']) && empty($form_state['post']['biblio_type']) && empty($form_state['submitted']) && empty($form['vid']['#value'])) {
      foreach (element_children($form) as $form_element) {
        if (strstr($form_element, 'biblio_')) {
          continue;
        }
        if ($form[$form_element]['#type'] == 'value' || $form[$form_element]['#type'] == 'hidden' || $form[$form_element]['#type'] == 'token') {
          continue;
        }
        $form[$form_element]['#access'] = FALSE;
      }
    }
    else {
      if (isset($form['menu']['#weight']) && $form['menu']['#weight'] < 20) {
        $form['menu']['#weight'] = 20;
      }
      if (isset($form['book']['#weight']) && $form['book']['#weight'] < 20) {
        $form['book']['#weight'] = 20;
      }
      if (isset($form['taxonomy'])) {
        if (!isset($form['taxonomy']['#title'])) {
          $form['taxonomy'] += array(
            '#type' => 'fieldset',
            '#title' => t('Vocabularies'),
            '#collapsible' => TRUE,
            '#collapsed' => FALSE,
          );
        }
        $form['taxonomy']['#description'] = t('Select taxonomy terms which will be related to this %biblio_base_title entry.', array(
          '%biblio_base_title' => variable_get('biblio_base_title', 'Biblio'),
        ));
        $form['taxonomy']['copy_to_biblio'] = array(
          '#type' => 'checkbox',
          '#title' => t('Copy these terms to the biblio keyword database'),
          '#return_value' => 1,
          '#default_value' => variable_get('biblio_copy_taxo_terms_to_keywords', 0),
          '#description' => t('If this option is selected, the selected taxonomy terms will be copied to the %biblio_base_title keyword database and be displayed as keywords (as well as taxonomy terms) for this entry.', array(
            '%biblio_base_title' => variable_get('biblio_base_title', 'Biblio'),
          )),
        );
      }
      $kw_vocab = variable_get('biblio_keyword_vocabulary', 0);
      $freetagging = variable_get('biblio_keyword_freetagging', 0);
      if ($freetagging && $kw_vocab && isset($form['taxonomy']['tags'][$kw_vocab])) {
        unset($form['taxonomy']['tags'][$kw_vocab]);
      }
    }
  }
  return $form;
}

/**
 * Implements hook_form().
 *
 * This function creates the form for collecting the information specific to the
 * biblio node type. This hook requires us to return a $form array that later
 * will be incorporated into a complete form.
 */
function biblio_form($node, $form_state) {
  global $user;
  $fields = array();
  $tid = isset($form_state['storage']['biblio_type']) ? $form_state['storage']['biblio_type'] : (isset($node->biblio_type) ? $node->biblio_type : '');
  $show_fields = !empty($tid);
  $form['#validate'][] = 'biblio_form_validate';
  $form['#cache'] = TRUE;

  // Create a select box for the publication type of this biblio node
  $param['options'] = array(
    "enctype" => "multipart/form-data",
  );
  $result = db_query('SELECT t.* FROM {biblio_types} as t WHERE tid > -2 AND visible = 1');
  while ($option = db_fetch_object($result)) {
    $options[$option->tid] = _biblio_localize_type($option->tid, $option->name);
  }
  asort($options);
  $type_options[-1] = t('Select Type...');
  $type_options += $options;
  $form['biblio_type'] = array(
    '#type' => 'select',
    '#title' => t('Publication Type'),
    '#default_value' => !empty($tid) ? $tid : -1,
    '#options' => $type_options,
    '#description' => NULL,
    '#weight' => -15,
    '#attributes' => array(
      'onchange' => 'document.getElementById(\'node-form\').submit()',
    ),
    '#multiple' => FALSE,
    '#required' => TRUE,
  );

  // If the biblio type is defined, add various fields to the form
  if ($show_fields) {
    $form['title'] = array(
      '#type' => 'textfield',
      '#title' => t('Title'),
      '#required' => TRUE,
      '#default_value' => trim($form_state['values']['title'] ? $form_state['values']['title'] : $node->title),
      '#size' => 60,
      '#maxlength' => 255,
      '#weight' => -4,
    );

    // Build the field array used to make the form
    $result = db_query("SELECT * FROM {biblio_fields} b\n              INNER JOIN {biblio_field_type} bt ON b.fid = bt.fid\n              INNER JOIN {biblio_field_type_data} btd ON btd.ftdid=bt.ftdid\n              WHERE bt.tid=%d ORDER BY bt.weight ASC", $tid);
    while ($row = db_fetch_array($result)) {
      $fields[$row['name']] = $row;
    }
    _biblio_localize_fields($fields);
    if (!variable_get('biblio_hide_other_fields', 0)) {
      $form['other_fields'] = array(
        '#type' => 'fieldset',
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#title' => t('Other Biblio Fields'),
        '#description' => '',
        '#weight' => 0,
      );
    }
    $max_visible_weight = -5;
    foreach ($fields as $key => $fld) {
      $options = '';
      $main_area = $fld['common'] || $fld['visible'];
      if ($fld['type'] == 'contrib_widget') {
        $auth_category = $fld['fid'];
        if ($main_area) {
          if (isset($form_state['values'])) {
            $form += _biblio_contributor_widget($form_state['values'], $fld, $auth_category, $tid);
          }
          else {
            $form += _biblio_contributor_widget($node, $fld, $auth_category, $tid);
          }
        }
        else {
          if (isset($form_state['values']) && !variable_get('biblio_hide_other_fields', 0)) {
            $form['other_fields'] += _biblio_contributor_widget($form_state['values'], $fld, $auth_category, $tid, TRUE);
          }
          elseif (!variable_get('biblio_hide_other_fields', 0)) {
            $form['other_fields'] += _biblio_contributor_widget($node, $fld, $auth_category, $tid, TRUE);
          }
        }
      }
      else {
        if ($key == 'biblio_keywords') {
          module_load_include('inc', 'biblio', 'includes/biblio.keywords');
          $sep = check_plain(variable_get('biblio_keyword_sep', ','));

          // If the kewords are in array form, then implode them into a string.
          if (isset($form_state['values']['biblio_keywords']) && is_array($form_state['values']['biblio_keywords'])) {
            $form_state['values']['biblio_keywords'] = biblio_implode_keywords($form_state['values']['biblio_keywords']);
          }
          if (isset($node->{$key}) && is_array($node->{$key})) {
            $node->{$key} = biblio_implode_keywords($node->{$key});
          }
          if (empty($fld['hint'])) {
            $fld['hint'] = t('Separate keywords using the " @sep " character', array(
              '@sep' => $sep,
            ));
          }
        }
        $field_widget = array(
          '#default_value' => $form_state['values'][$key] ? $form_state['values'][$key] : $node->{$key},
          '#type' => $fld['type'],
          '#title' => check_plain($fld['title']),
          '#size' => $fld['size'],
          '#required' => $fld['required'],
          '#maxlength' => $fld['maxsize'],
          '#weight' => $fld['weight'] / 10,
          '#autocomplete_path' => $fld['autocomplete'] ? 'biblio/autocomplete/' . $fld['name'] : '',
          '#description' => check_plain($fld['hint']),
        );
        if ($key == 'biblio_refereed') {
          $field_widget['#options'] = array(
            '' => t('None'),
            'Refereed' => t('Refereed'),
            'Non-Refereed' => t('Non-Refereed'),
            'Does Not Apply' => t('Does Not Apply'),
            'Unknown' => t('Unknown'),
          );
          $field_widget['#description'] = t('If you are not sure, set this to Unknown or Does Not Apply');
        }
        if ($fld['type'] == 'textarea') {

          // Wrap all textarea fields in collapsed field sets in order to save
          // space on the page.
          $field_widget = array(
            '#type' => 'fieldset',
            '#collapsible' => TRUE,
            '#collapsed' => TRUE,
            '#title' => check_plain($fld['title']),
            '#description' => '',
            '#weight' => $fld['weight'] / 10,
            $key => $field_widget,
            'format' => filter_form($node->biblio_formats[$key], 20, array(
              'biblio_formats',
              $key,
            )),
          );
          $key = $fld['name'] . '_field';
        }

        // Embed field directly into the form or into "Other Fields" fieldset.
        if ($main_area) {
          $form[$key] = $field_widget;
          $max_visible_weight = max($max_visible_weight, $field_widget['#weight']);
        }
        elseif (!variable_get('biblio_hide_other_fields', 0)) {
          $form['other_fields'][$key] = $field_widget;
        }
      }
    }

    // Place the 'Other biblio fields' directly below the visible fields.
    $form['other_fields']['#weight'] = $max_visible_weight + 0.1;
    $form['body_field'] = array(
      '#type' => 'fieldset',
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
      '#title' => t('Full Text'),
      '#description' => '',
      '#weight' => $max_visible_weight + 0.2,
    );
    $form['body_field']['body'] = array(
      '#type' => 'textarea',
      '#title' => t('Full Text'),
      '#default_value' => $node->body,
      '#rows' => 10,
      '#required' => FALSE,
      '#description' => t('You may enter a full text or HTML version of the publication here.'),
      '#weight' => 19,
    );

    // Embed the filter form into the "Full Text" fieldset because it applies
    // only to the full text element.
    $form['body_field']['format'] = filter_form($node->format, 20);
  }
  return $form;
}

/**
 * Defines a contributor widget to be used as part of biblio node form.
 *
 * @param object|array $node
 *   An object or node with information about about the biblio item.
 * @param array $fld
 *   An array of information about a field.
 * @param string $auth_category
 *   A string representing the category of author.
 * @param string $biblio_type
 *   A string indicating the type of biblio item.
 * @param bool $other_fieldset
 *   (optional) A logical flag indicating
 *
 * @return array
 *   Associative array with form element keys for this contributor widget.
 */
function _biblio_contributor_widget($node, $fld, $auth_category, $biblio_type, $other_fieldset = FALSE) {
  $init_count = variable_get('biblio_init_auth_count', 4);
  $fldname = $fld['name'];
  $type = str_replace('_', '-', $fldname);
  if (is_object($node)) {
    $contributors = (array) $node->biblio_contributors[$auth_category];
  }
  else {
    $contributors = $node['biblio_contributors'][$auth_category];
  }
  $contributor_count = max($init_count, count($contributors));
  $ctypes = _biblio_get_auth_types($auth_category, $biblio_type);

  // If no author types are available, return nothing for this widget.
  if (!isset($ctypes)) {
    return array();
  }
  $ctypes = db_query('SELECT * FROM {biblio_contributor_type_data} ' . 'WHERE auth_type IN (' . implode(',', $ctypes) . ')');
  while ($ctype = db_fetch_object($ctypes)) {
    $options[$ctype->auth_type] = $ctype->title;
  }

  // Add a wrapper for the choices and more button.
  $wrapper = array(
    '#tree' => TRUE,
    '#type' => 'fieldset',
    '#collapsible' => TRUE,
    '#collapsed' => !$fld['required'] && count($contributors) == 0,
    '#title' => check_plain($fld['title']),
    '#weight' => $fld['weight'] / 10,
    '#description' => t('Enter a single name per line using a format such as "Smith, John K" or "John K Smith" or "J.K. Smith"'),
    '#prefix' => '<div class="clear-block" id="' . $type . '-wrapper">',
    '#suffix' => '</div>',
  );

  // Define a container for just the contributors.
  $wrapper['biblio_contributors'][$auth_category] = array(
    '#prefix' => '<div id="' . $type . '">',
    '#suffix' => '</div>',
    '#theme' => 'biblio_contributors',
    '#id' => $fldname,
    '#hideRole' => count($options) <= 1,
  );

  // Add the current choices to the form.
  $default_values = array(
    'name' => '',
    'cid' => '',
    'auth_type' => key($options),
  );
  for ($delta = 0; $delta < $contributor_count; $delta++) {

    // The contributor already exists.
    if (isset($contributors[$delta])) {
      $values = $contributors[$delta];
    }
    else {
      $values = $default_values;
    }
    $values['rank'] = $delta;
    $wrapper['biblio_contributors'][$auth_category][$delta] = _biblio_contributor_form($delta, $auth_category, $values, $options, $fld['autocomplete']);
  }

  // We name our button 'contrib_more' to avoid conflicts with other modules
  // using AHAH-enabled buttons with the id 'more'.
  $path = 'biblio/js/' . $biblio_type . '/' . $auth_category;
  if ($other_fieldset) {
    $path .= '/1';
  }
  $wrapper[$fldname . '_more'] = array(
    '#type' => 'submit',
    '#value' => t('More @title', array(
      '@title' => $fld['title'],
    )),
    '#description' => t("If there aren't enough boxes above, click here to add more."),
    '#weight' => 1,
    // In the event of no javascript action, execute this submit function.
    '#submit' => array(
      'biblio_more_contributors_submit',
    ),
    '#ahah' => array(
      'path' => $path,
      'wrapper' => $type,
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );
  $form['contributors' . $auth_category . '_wrapper'] = $wrapper;
  return $form;
}

/**
 * Creates contributor form elements for contributor widget and JSON callback.
 *
 * @param $delta
 *
 * @param string $author_category
 *
 * @param array $values
 *
 * @param array $types
 *   (optional)
 * @param bool $autocomplete
 *   (optional) A logical flag indicating whether ...  The default value is TRUE.
 *
 * @return array $form
 *   An associative array of form definition elements.
 */
function _biblio_contributor_form($delta, $auth_category, $values, $types = NULL, $autocomplete = TRUE) {
  $form = array(
    '#tree' => TRUE,
  );

  // Manually set the #parents property of these fields so that their values
  // appear in the $form_state['values']['choice'] array.
  $form['name'] = array(
    '#type' => 'textfield',
    '#title' => t('Name'),
    '#autocomplete_path' => $autocomplete ? 'biblio/autocomplete/contributor' : '',
    '#default_value' => $values['name'],
    '#parents' => array(
      'biblio_contributors',
      $auth_category,
      $delta,
      'name',
    ),
  );
  if (count($types) > 1) {
    $form['auth_type'] = array(
      '#type' => 'select',
      '#title' => t('Type'),
      '#default_value' => $values['auth_type'],
      '#options' => $types,
      '#multiple' => FALSE,
      '#parents' => array(
        'biblio_contributors',
        $auth_category,
        $delta,
        'auth_type',
      ),
    );
  }
  else {
    $form['auth_type'] = array(
      '#type' => 'hidden',
      '#value' => $values['auth_type'],
      '#parents' => array(
        'biblio_contributors',
        $auth_category,
        $delta,
        'auth_type',
      ),
    );
  }
  $form['cid'] = array(
    '#type' => 'hidden',
    '#default_value' => $values['cid'],
    '#parents' => array(
      'biblio_contributors',
      $auth_category,
      $delta,
      'cid',
    ),
  );
  $form['md5'] = array(
    '#type' => 'hidden',
    '#default_value' => $values['md5'],
    '#parents' => array(
      'biblio_contributors',
      $auth_category,
      $delta,
      'md5',
    ),
  );
  $form['rank'] = array(
    '#type' => 'textfield',
    '#size' => 6,
    '#default_value' => $values['rank'],
    '#parents' => array(
      'biblio_contributors',
      $auth_category,
      $delta,
      'rank',
    ),
  );
  return $form;
}

/**
 * Prepares a JSON response for the contributor widget.
 *
 */
function biblio_contributors_js($tid, $auth_category, $other_fields = FALSE) {
  $delta = count($_POST['biblio_contributors'][$auth_category]);

  // Build our new form element.
  $ctypes = _biblio_get_auth_types($auth_category, $tid);
  $ctypes = db_query('SELECT * FROM {biblio_contributor_type_data}
                      WHERE auth_type IN (' . implode(',', $ctypes) . ')');
  while ($ctype = db_fetch_object($ctypes)) {
    $options[$ctype->auth_type] = $ctype->title;
  }
  $default_values = array(
    'name' => '',
    'cid' => '',
    'auth_type' => key($options),
    'rank' => $delta,
  );
  $form_element = _biblio_contributor_form($delta, $auth_category, $default_values, $options);
  drupal_alter('form', $form_element, array(), 'biblio_contributors_js');

  // Build the new form.
  $form_state = array(
    'submitted' => FALSE,
  );
  $form_build_id = $_POST['form_build_id'];

  // Add the new element to the stored form. Without adding the element to the
  // form, Drupal is not aware of this new elements existence and will not
  // process it. We retreive the cached form, add the element, and resave.
  $form = form_get_cache($form_build_id, $form_state);
  if ($other_fields) {
    $form['other_fields']['contributors' . $auth_category . '_wrapper']['biblio_contributors'][$auth_category][$delta] = $form_element;
  }
  else {
    $form['contributors' . $auth_category . '_wrapper']['biblio_contributors'][$auth_category][$delta] = $form_element;
  }
  form_set_cache($form_build_id, $form, $form_state);
  $form += array(
    '#post' => $_POST,
    '#programmed' => FALSE,
  );

  // Rebuild the form.
  $form = form_builder('biblio_node_form', $form, $form_state);

  // Render the new output.
  if ($other_fields) {
    $contributor_form = $form['other_fields']['contributors' . $auth_category . '_wrapper']['biblio_contributors'][$auth_category];
  }
  else {
    $contributor_form = $form['contributors' . $auth_category . '_wrapper']['biblio_contributors'][$auth_category];
  }
  unset($contributor_form['#prefix'], $contributor_form['#suffix']);

  // Prevent duplicate wrappers.
  $contributor_form[$delta]['#attributes']['class'] = empty($contributor_form[$delta]['#attributes']['class']) ? 'ahah-new-content' : $contributor_form[$delta]['#attributes']['class'] . ' ahah-new-content';
  $output = theme('status_messages') . drupal_render($contributor_form);
  print drupal_json(array(
    'status' => TRUE,
    'data' => $output,
  ));
  exit;
}

/**
 * Implementation of hook_validate().
 *
 *
 * Errors should be signaled with form_set_error().
 */
function biblio_form_validate($form, &$form_state) {
  $op = isset($form['#post']['op']) ? $form['#post']['op'] : ($form['#post']['biblio_type'] > 0 ? t('Save') : '');
  switch ($op) {
    case t('Save'):
      if ($form_state['storage']['biblio_type'] == $form_state['values']['biblio_type'] || !empty($form['#node']->biblio_type) && $form['#node']->biblio_type == $form_state['values']['biblio_type']) {
        unset($form_state['storage']);
      }
      else {

        // Clear validation errors that other modules might have set.
        // This assumes that Biblio has a greater weight than those
        // other modules.
        form_set_error(NULL, '', TRUE);
        drupal_get_messages('error');
        $form_state['storage']['biblio_type'] = $form_state['values']['biblio_type'];
        $form_state['submitted'] = TRUE;
        $form_state['rebuild'] = TRUE;
        return;
      }
  }
  if (!empty($node_data)) {
    $form_state['values'] = array_merge($form_state['values'], $node_data[0]);
    $form_state['storage']['biblio_type'] = $node_data[0]['biblio_type'];
    return;
  }
  if (isset($form_state['values']['biblio_keywords'])) {
    module_load_include('inc', 'biblio', 'includes/biblio.keywords');
    if (!is_array($form_state['values']['biblio_keywords'])) {
      $form_state['values']['biblio_keywords'] = biblio_explode_keywords($form_state['values']['biblio_keywords']);
    }
    foreach ($form_state['values']['biblio_keywords'] as $keyword) {
      if (strlen($keyword) > 255) {
        form_set_error('biblio_keywords', t('No single keyword can be greater than 255 characters in length, the word: @kw exceeds this length', array(
          '@kw' => $keyword,
        )));
      }
    }
  }
}

/**
 *
 *
 * @param mixed $year
 *   A variable representing a year or string like 'In Press' or ;Submitted'.
 *
 * @return mixed
 *
 */
function _biblio_numeric_year($year) {
  if (!is_numeric($year)) {
    if (drupal_strtoupper($year) == drupal_strtoupper(t("In Press"))) {
      return 9998;
    }
    if (drupal_strtoupper($year) == drupal_strtoupper(t("Submitted"))) {
      return 9999;
    }
  }
  else {
    return $year;
  }
}

/**
 *
 *
 * @param mixed $year
 *   A variable representing a year value.
 *
 * @reutrn mixed
 *
 */
function _biblio_text_year($year) {
  if ($year == 9998) {
    return check_plain(variable_get('biblio_inpress_year_text', t('In Press')));
  }
  if ($year == 9999) {
    return check_plain(variable_get('biblio_no_year_text', t('Submitted')));
  }
  return $year;
}

/**
 * Prepares a biblio node for submit to database.
 *
 * This function contains code common to both insert and update operations.
 *
 * @param object $node
 *   An object with bibliographic information as well as other elements.
 */
function _biblio_prepare_submit($node) {
  $node->biblio_sort_title = biblio_normalize_title($node->title);
  $node->biblio_year = _biblio_numeric_year($node->biblio_year);
  if (variable_get('biblio_auto_citekey', 1) && empty($node->biblio_citekey)) {
    $node->biblio_citekey = biblio_citekey_generate($node);
  }
  $duplicate = biblio_hash($node);
  if (isset($duplicate) && $duplicate != $node->nid) {

    // if this is a potential duplcate, write the nids of the pre-existing and new nodes
    $dup_map = array(
      'vid' => $duplicate,
      'did' => $node->nid,
    );
    drupal_write_record('biblio_duplicates', $dup_map);
  }
}

/**
 * Implements hook_insert().
 *
 * As a new node is being inserted into the database, we need to do our own
 * database inserts.
 */
function biblio_insert($node) {
  module_load_include('inc', 'biblio', 'includes/biblio.util');
  module_load_include('inc', 'biblio', 'includes/biblio.contributors');
  module_load_include('inc', 'biblio', 'includes/biblio.keywords');
  _biblio_prepare_submit($node);
  biblio_insert_contributors($node);
  biblio_insert_keywords($node);
  $node->biblio_coins = biblio_coins($node);
  drupal_write_record('biblio', $node);
}

/**
 * Implements hook_update().
 *
 * As an existing node is being updated in the database, we need to do our own
 * database updates.
 */
function biblio_update($node) {
  module_load_include('inc', 'biblio', 'includes/biblio.util');
  module_load_include('inc', 'biblio', 'includes/biblio.contributors');
  module_load_include('inc', 'biblio', 'includes/biblio.keywords');
  _biblio_prepare_submit($node);
  biblio_update_contributors($node);
  biblio_update_keywords($node);
  $node->biblio_coins = biblio_coins($node);

  // Update the node in the database:
  if ($node->revision) {
    drupal_write_record('biblio', $node);
  }
  else {
    drupal_write_record('biblio', $node, 'vid');
  }
}

/**
 * Implements hook_delete().
 *
 * When a node is deleted, we need to clean up related tables.
 */
function biblio_delete($node) {
  module_load_include('inc', 'biblio', 'includes/biblio.contributors');
  module_load_include('inc', 'biblio', 'includes/biblio.keywords');

  //first remove data from the biblio table
  db_query('DELETE FROM {biblio} WHERE nid = %d', $node->nid);
  biblio_delete_contributors($node);
  biblio_delete_keywords($node);
}

/**
 * Implements hook_load().
 *
 * This hook is called every time a node is loaded, and allows us to do some loading of our own.
 *
 */
function biblio_load($node) {
  module_load_include('inc', 'biblio', 'includes/biblio.util');
  module_load_include('inc', 'biblio', 'includes/biblio.contributors');
  module_load_include('inc', 'biblio', 'includes/biblio.keywords');
  $additions = db_fetch_object(db_query('SELECT b.*, bt.name as biblio_type_name
                                         FROM {biblio} b
                                         LEFT JOIN {biblio_types} bt on b.biblio_type = bt.tid
                                         WHERE b.vid = %d', $node->vid));
  if ($additions->biblio_formats != NULL) {
    $additions->biblio_formats = unserialize($additions->biblio_formats);
  }
  $additions->biblio_year = _biblio_text_year($additions->biblio_year);
  $additions->biblio_contributors = biblio_load_contributors($node->vid, $additions->biblio_type);
  $additions->biblio_keywords = biblio_load_keywords($node->vid);
  if (empty($additions->biblio_coins)) {
    $additions->biblio_coins = biblio_coins($additions);
  }
  return $additions;
}

/**
 *
 *
 * @param object $node
 *
 *
 * @return string
 *
 */
function biblio_citekey_generate($node) {
  $php = check_plain(variable_get('biblio_citekey_phpcode', ''));
  if (empty($php)) {
    $prefix = variable_get('biblio_citekey_prefix', '');
    $primary_field = variable_get('biblio_citekey_field1', 'nid');
    $secondary_field = variable_get('biblio_citekey_field2', 'nid');
    $citekey = !empty($node->{$primary_field}) ? $node->{$primary_field} : (!empty($node->{$secondary_field}) ? $node->{$secondary_field} : $node->nid);
    return check_plain($prefix . $citekey);
  }
  else {
    ob_start();
    $return = eval($php);
    ob_end_clean();
    return check_plain(strip_tags((string) $return));
  }
}

/**
 * Implements hook_view().
 *
 */
function biblio_view(&$node, $teaser = FALSE, $page = FALSE) {
  if (strlen(trim($node->body))) {
    $node = node_prepare($node, $teaser);
  }
  $style = biblio_get_style();
  $base = variable_get('biblio_base', 'biblio');
  $base_title = check_plain(variable_get('biblio_base_title', 'Biblio'));
  if (variable_get('biblio_fix_isi_links', 0)) {
    biblio_fix_isi_links($node);
  }
  if ($teaser) {
    $node->content['teaser']['#value'] = theme('biblio_style', $node, $base, $style);
  }
  else {
    switch (variable_get('biblio_node_layout', '0')) {
      case 'orig':
      case 'ft':
        $node->content['body']['#value'] = theme('biblio_long', $node, $base, $style);
        break;
      case 'tabular':
      default:
        $node->content['body']['#value'] = theme('biblio_tabular', $node, $base, $teaser);
        break;
    }
  }
  if ($page) {
    drupal_set_breadcrumb(array(
      l(t('Home'), NULL),
      l(drupal_ucfirst($base_title), $base),
    ));
    if (variable_get('biblio_hide_bibtex_braces', 0) && !isset($node->view)) {
      $node->title = biblio_remove_brace($node->title);
      drupal_set_title(filter_xss($node->title, biblio_get_allowed_tags()));
    }
  }
  return $node;
}

/**
 * Implements hook_block().
 *
 * Generates a block containing the latest poll.
 */
function biblio_block($op = 'list', $delta = 0) {
  global $user;
  if (user_access('access content')) {
    if ($op == 'list') {
      $blocks[0]['info'] = t('Most recent publications');
      return $blocks;
    }
    else {
      if ($op == 'view') {

        // Retrieve the latest pubs
        $num_in_block = variable_get('biblio_rowsperblock', 4);
        $block_order = variable_get('biblio_block_order', 'n.created');
        if (variable_get('biblio_view_only_own', 0)) {
          $limit = " AND n.uid = {$user->uid} ";
        }

        //      $query = "SELECT COUNT(n.nid)
        //                  FROM {node} n
        //                  WHERE n.type = 'biblio' AND n.status=1 " . $limit;
        //      $num_rows = db_result(db_query_range(db_rewrite_sql($query), 0, $num_in_block));
        //      if ($num_rows) {
        $query = "SELECT n.nid, n.title\n                    FROM {node} n\n                    LEFT JOIN {biblio} b  on n.vid=b.vid\n                    WHERE n.type = 'biblio' AND n.status=1\n                    ORDER BY {$block_order} DESC";
        $result = db_query_range(db_rewrite_sql($query), 0, $num_in_block);
        $base = variable_get('biblio_base', 'biblio');
        $block['subject'] = t('Recent Publications');
        $block['content'] = '<div class="item-list"><ul>';
        $options['html'] = TRUE;
        while ($pub = db_fetch_object($result)) {
          if (variable_get('biblio_hide_bibtex_braces', 0)) {
            $pub->title = biblio_remove_brace($pub->title);
          }
          $block['content'] .= '<li >' . l(filter_xss($pub->title, biblio_get_allowed_tags()), "node/{$pub->nid}", $options) . '</li>';
        }
        $block['content'] .= '</ul>';
        if (variable_get('biblio_rss', 0)) {
          $block['content'] .= theme('feed_icon', url("{$base}/recent/rss.xml", array(
            'absolute' => TRUE,
          )), t('Recently Added Publications'));
        }
        $block['content'] .= l(t('More...'), $base);
        $block['content'] .= '</div>';

        //      }
        return $block;
      }
    }
  }
}

/**
 *
 *
 */
function biblio_recent_feed() {
  $query = "SELECT *\n            FROM {node} AS n\n            WHERE n.type = 'biblio' AND n.status=1\n            ORDER BY n.created DESC";
  $numberInFeed = variable_get('biblio_rss_number_of_entries', 10);
  $siteName = variable_get('site_name', 'Drupal');
  $base = variable_get('biblio_base', 'biblio');
  $result = db_query_range(db_rewrite_sql($query), 0, $numberInFeed);
  $channel['title'] = $siteName . ' - ' . t("Recently Added Publications");
  $channel['link'] = url($base, array(
    'absolute' => TRUE,
  ));
  $channel['description'] = t("This feed lists the %num most recently added publications on %site", array(
    '%num' => $numberInFeed,
    '%site' => $siteName,
  ));
  $nids = array();
  while ($row = db_fetch_object($result)) {
    $nids[] = $row->nid;
  }
  node_feed($nids, $channel);
}

/**
 * This function creates a feed from a filter type query (i.e. biblio/author/jones)
 *
 * @param $query
 *   The SQL string to be used in the call to db_query.
 * @param array $terms
 *   (optional) The terms that are used to replace any place holders in the query.
 * @param array $rss_info
 *   (optional) Array which contains the title, link and description info for rss feed.
 */
function biblio_filter_feed($query, $terms = NULL, $rss_info = NULL) {
  $base = variable_get('biblio_base', 'biblio');
  $channel['title'] = $rss_info['title'];
  $channel['link'] = url($base . $rss_info['link'], array(
    'absolute' => TRUE,
  ));
  $channel['description'] = $rss_info['description'];
  $nids = array();
  $result = db_query($query, $terms);
  while ($row = db_fetch_object($result)) {
    $nids[] = $row->nid;
  }
  node_feed($nids, $channel);
}

/**
 *
 */
function biblio_get_db_fields() {
  $fields = array();
  $fields[] = 'nid';
  $fields[] = 'vid';
  $fields[] = 'biblio_type';
  $result = db_query('SELECT name FROM {biblio_fields} ');
  while ($field = db_fetch_array($result)) {
    $fields[] = $field['name'];
  }
  return $fields;
}

/**
 * Filter
 *
 * Largely inspired from the footnote module
 *
 * @param string $citekey
 *
 *
 * @return string
 *
 */
function _biblio_citekey_print($citekey) {
  $nid = db_fetch_object(db_query("SELECT nid FROM {biblio} WHERE biblio_citekey = '%s' ORDER BY vid DESC", $citekey));
  if ($nid->nid > 0) {
    $style = biblio_get_style();
    $base = variable_get('biblio_base', 'biblio');
    $node = node_load($nid->nid);
    return theme('biblio_style', $node, $base, $style);
  }
  else {
    return t("Citekey @cite not found", array(
      '@cite' => $citekey,
    ));
  }
}

/**
 * Implements hook_filter_tips().
 *
 * This hook allows filters to provide help text to users during the content
 * editing process. Short tips are provided on the content editing screen, while
 * long tips are provided on a separate linked page. Short tips are optional,
 * but long tips are highly recommended.
 */
function biblio_filter_tips($delta, $format, $long = FALSE) {
  switch ($delta) {
    case 0:
      if ($long) {
        return t('You can cite references directly into texts with <code>&lt;bib&gt;<i>citekey</i>&lt;/bib&gt; <b><i>or</i></b> [bib]<i>citekey</i>[/bib]</code>. This will be replaced with a running number (the publication reference) and the publication referenced by the citekey within the &lt;bib&gt; tags will be printed at the bottom of the page (the reference).');
      }
      else {
        return t('Use &lt;bib&gt;<i>citekey</i>&lt;/bib&gt; <b><i>or</i></b> [bib]<i>citekey</i>[/bib] to insert automatically numbered references.');
      }
      break;
  }
}

/**
 * Implements hook_filter().
 *
 * The bulk of filtering work is done here. This hook is quite complicated, so
 * we'll discuss each operation it defines.
 */
function biblio_filter($op, $delta = 0, $format = -1, $text = '') {

  // The "list" operation provides the module an opportunity to declare both how
  // many filters it defines and a human-readable name for each filter. Note that
  // the returned name should be passed through t() for translation.
  if ($op == 'list') {
    return array(
      0 => t('Biblio module references &lt;bib&gt; <i>or</i> [bib]'),
      1 => t('Biblio module inline references &lt;ibib&gt; <i>or</i> [ibib]'),
    );
  }
  if ($op == 'no cache') {
    return TRUE;
  }

  // All operations besides "list" provide a $delta argument so we know which
  // filter they refer to. We'll switch on that argument now so that we can
  // discuss each filter in turn.
  switch ($delta) {

    // First is the html footnotes filter
    case 0:
      switch ($op) {

        // This description is shown in the administrative interface, unlike the
        // filter tips which are shown in the content editing interface.
        case 'description':
          return t('Use &lt;bib&gt;citekey&lt;/bib&gt; or [bib]citebkey[/bib]to insert automatically numbered references.');

        // We don't need the "prepare" operation for this filter, but it's required
        // to at least return the input text as-is.

        //TODO: May need to escape <fn> if we use HTML filter too, but Footnotes could be first
        case 'prepare':
          return $text;

        // The actual filtering is performed here. The supplied text should be
        // returned, once any necessary substitutions have taken place.
        case 'process':
          $pattern = array(
            '|\\[bib](.*?)\\[/bib]|s',
            '|<bib>(.*?)</bib>|s',
          );

          // This is used with footnote module integration to replace the <bib> tags with <fn> tags
          if (variable_get('biblio_footnotes_integration', 0) && module_exists('footnotes')) {
            $text = preg_replace_callback($pattern, '_biblio_filter_footnote_callback', $text);
            return $text;
          }
          else {
            $text = preg_replace_callback($pattern, '_biblio_filter_replace_callback', $text);

            // Replace tag <footnotes> with the list of footnotes.
            // If tag is not present, by default add the footnotes at the end.
            // Thanks to acp on drupal.org for this idea. see http://drupal.org/node/87226
            $footer = '';
            $footer = _biblio_filter_replace_callback(NULL, 'output footer');
            if (preg_match('/<bibliography(\\/( )?)?>/', $text) > 0) {
              $text = preg_replace('/<bibliography(\\/( )?)?>/', $footer, $text, 1);
              return $text;
            }
            else {
              return $text . "\n\n" . $footer;
            }
          }
      }
      break;
    case 1:
      switch ($op) {

        // This description is shown in the administrative interface, unlike the
        // filter tips which are shown in the content editing interface.
        case 'description':
          return t('Use &lt;ibib&gt;citekey&lt;/ibib&gt; or [ibib]citebkey[/ibib]to insert inline references.');

        // We don't need the "prepare" operation for this filter, but it's required
        // to at least return the input text as-is.

        //TODO: May need to escape <fn> if we use HTML filter too, but Footnotes could be first
        case 'prepare':
          return $text;

        // The actual filtering is performed here. The supplied text should be
        // returned, once any necessary substitutions have taken place.
        case 'process':
          $pattern = array(
            '|\\[ibib](.*?)\\[/ibib]|s',
            '|<ibib>(.*?)</ibib>|s',
          );
          $text = preg_replace_callback($pattern, '_biblio_inline_filter_replace_callback', $text);
          return $text;
      }
      break;
  }
}

/**
 *
 */
function _biblio_inline_filter_replace_callback($matches) {
  $text = _biblio_citekey_print($matches[1]);
  return $text;
}

/**
 *
 */
function _biblio_filter_footnote_callback($matches, $square_brackets = FALSE) {
  if ($square_brackets) {
    $text = '[fn]' . _biblio_citekey_print($matches[1]) . "</fn>";
  }
  else {
    $text = '<fn>' . _biblio_citekey_print($matches[1]) . "</fn>";
  }
  return $text;
}

/**
 * Helper function called from preg_replace_callback() above
 *
 * Uses static vars to temporarily store footnotes found.
 * In my understanding, this is not threadsafe?!
 */
function _biblio_filter_replace_callback($matches, $op = '') {
  static $n = 0;
  static $store_matches = array();
  $str = '';
  if ($op == 'output footer') {
    if ($n > 0) {
      $str = '<hr /><h3>' . t('References') . '</h3>';
      $str .= '<div class="references"><ol>';
      for ($m = 1; $m <= $n; $m++) {
        $str .= '<li id="reference' . $m . '"><a name="ref' . $m . '">' . _biblio_citekey_print($store_matches[$m - 1]) . " </a></li>\n\n";
      }
      $str .= '</ol></div>';
    }
    $n = 0;
    $store_matches = array();
    return $str;
  }

  //default op: act as called by preg_replace_callback()
  $ref = array_search($matches[1], $store_matches);
  if ($ref === FALSE) {
    $n++;
    array_push($store_matches, $matches[1]);

    //$stores_matches[$matches[1]] = $n;
    $ref = $n;
  }
  else {
    $ref++;
  }
  $allowed_tags = array();
  $title = filter_xss($matches[1], biblio_get_allowed_tags());

  //html attribute cannot contain quotes
  $title = str_replace('"', "&quot;", $title);

  //remove newlines. Browsers don't support them anyway and they'll confuse line break converter in filter.module
  $title = str_replace("\n", " ", $title);
  $title = str_replace("\r", "", $title);
  if (module_exists('hovertip')) {
    $text = '<span hovertip="reference' . $ref . '"><a href="#ref' . $ref . '">[' . $ref . ']</a></span>';
    $text .= '<span id="reference' . $ref . '" class="hovertip">' . _biblio_citekey_print($title) . '</span>';
  }
  else {
    $text = '<a href="#ref' . $ref . '" title="Reference ' . $ref . '">[' . $ref . ']</a>';
  }
  return $text;
}

/**
 * Implements hook_taxonomy().
 */
function biblio_taxonomy($op, $type, $array = NULL) {
  if ($op == 'delete' && $type == 'vocabulary' && $array['vid'] == variable_get('biblio_keyword_vocabulary', -1)) {
    variable_del('biblio_keyword_freetagging');
    variable_del('biblio_keyword_vocabulary');
  }
}

/**
 *
 */
function biblio_term_path($term) {
  $base = variable_get('biblio_base', 'biblio');
  if ($term->vid == variable_get('biblio_collection_vocabulary', 0)) {
    return "{$base}/collection/{$term->name}";
  }
  elseif ($term->vid == variable_get('biblio_keyword_vocabulary', 0)) {
    return "{$base}/term_id/{$term->tid}";
  }
  else {
    return;
  }
}

/**
 *
 * @return integer
 *   The node ID of a potential duplicate biblio content.
 */
function biblio_hash($node) {
  static $sums = array();
  $duplicate = NULL;
  if (empty($sums)) {
    $res = db_query("SELECT nid, biblio_md5 FROM {biblio} ");
    while ($md5 = db_fetch_object($res)) {
      $sums[$md5->biblio_md5] = $md5->nid;
    }
  }
  $hash_string = str_replace(' ', '', drupal_strtolower($node->title));
  if (isset($node->biblio_contributors[1][0]['lastname'])) {
    $hash_string .= str_replace(' ', '', drupal_strtolower($node->biblio_contributors[1][0]['lastname']));
  }
  $hash_string .= $node->biblio_year;
  $sum = md5($hash_string);
  if (isset($sums[$sum])) {
    $duplicate = $sums[$sum];
  }
  else {
    $sums[$sum] = $node->nid;
  }
  $node->biblio_md5 = $sum;
  return $duplicate;

  //return the nid of the potential duplicate
}

/**
 * Implements hook_diff().
 *
 * This is a hook provided by the diff module.
 * @param $old_node
 * @param $new_node
 * @return unknown_type
 */
function biblio_diff(&$old_node, &$new_node) {
  module_load_include('inc', 'biblio', 'includes/biblio.contributors');
  $result = array();
  $old_type = db_fetch_object(db_query('SELECT name FROM {biblio_types} where tid = %d', $old_node->biblio_type));
  $new_type = db_fetch_object(db_query('SELECT name FROM {biblio_types} where tid = %d', $new_node->biblio_type));
  $result['biblio_type'] = array(
    '#name' => 'Publication Type',
    '#old' => array(
      $old_type->name,
    ),
    '#new' => array(
      $new_type->name,
    ),
    '#format' => array(
      'show_header' => FALSE,
    ),
  );
  $old_node->biblio_contributors = biblio_load_contributors($old_node->vid);
  $new_node->biblio_contributors = biblio_load_contributors($new_node->vid);
  $db_result = db_query('SELECT b.name, btd.title, bt.weight
                      FROM {biblio_fields} b
                      INNER JOIN {biblio_field_type} bt ON bt.fid=b.fid
                      INNER JOIN {biblio_field_type_data} btd ON btd.ftdid=bt.ftdid
                      WHERE bt.tid=%d ORDER BY weight', $old_node->biblio_type);
  while ($field = db_fetch_object($db_result)) {
    $name = $field->name;
    $old = $new = '';
    if ($field->type == 'contrib_widget') {
      foreach ((array) $old_node->biblio_contributors[$field->fid] as $auth) {
        $old .= $auth['name'] . ' ';
      }
      foreach ((array) $old_node->biblio_contributors[$field->fid] as $auth) {
        $new .= $auth['name'] . ' ';
      }
    }
    else {
      $old = $old_node->{$name};
      $new = $new_node->{$name};
    }
    $result[$name] = array(
      '#name' => $field->title,
      '#old' => explode("\n", $old),
      '#new' => explode("\n", $new),
    );
  }
  return $result;
}

/**
 *
 */
function biblio_token_list($type = 'all') {
  module_load_include('inc', 'biblio', '/includes/biblio.tokens');
  return _biblio_token_list($type);
}

/**
 *
 */
function biblio_token_values($type, $object = NULL) {
  module_load_include('inc', 'biblio', '/includes/biblio.tokens');
  return _biblio_token_values($type, $object);
}

/**
 *
 */
function _biblio_profile_access($user, $type = 'profile') {
  if ($type == 'profile') {
    $key = 'biblio_show_profile';
  }
  else {
    if ($type == 'menu' && $user->uid > 0) {
      $key = 'biblio_my_pubs_menu';
    }
    else {
      return false;
    }
  }

  // if user cannot override site settings or user hasn't yet made its selection, we use site default
  if (!variable_get('biblio_show_user_profile_form', '1') || !isset($user->{$key})) {
    return variable_get($key, '0');
  }
  else {
    return $user->{$key};
  }

  // return user setting
}

/*
 * Helper function to get either the user or system style
 */
function biblio_get_style() {
  global $user;
  if (isset($user->biblio_user_style) && $user->biblio_user_style != "system") {
    return $user->biblio_user_style;
  }
  return module_exists('biblio_citeproc') ? variable_get('biblio_citeproc_style', 'ama.csl') : variable_get('biblio_style', 'cse');
}

/**
 *
 */
function biblio_get_styles() {
  $styles = array();
  if (module_exists('biblio_citeproc')) {
    $result = db_query("SELECT id,filename,parent,title,summary FROM {biblio_citeproc_styles} ORDER BY title ASC");
    while ($style = db_fetch_object($result)) {
      $styles[$style->filename] = $style->title;
    }
  }
  else {
    $dir = drupal_get_path('module', 'biblio') . '/styles';
    $files = file_scan_directory($dir, 'biblio_style_..*.inc$');
    foreach ($files as $file) {
      include_once $file->filename;
      $function = $file->name . '_info';
      if (function_exists($function)) {
        $styles = array_merge($styles, call_user_func($function));

        //build and array of the short and long names
      }
    }
    ksort($styles);
  }
  return $styles;
}

/**
 * Implements hook_views_api().
 */
function biblio_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'biblio') . '/views',
  );
}

/**
 *
 */
function biblio_fix_isi_links(&$node) {
  $isi = check_plain(variable_get('biblio_isi_url', 'http://apps.isiknowledge.com/InboundService.do?Func=Frame&product=WOS&action=retrieve&SrcApp=EndNote&Init=Yes&SrcAuth=ResearchSoft&mode=FullRecord&UT='));
  if (isset($node->biblio_url) && preg_match('/Go\\s*to\\s*ISI/', $node->biblio_url)) {
    $node->biblio_url = str_replace('<Go to ISI>://', $isi, $node->biblio_url);
  }
  if (isset($node->biblio_accession_number) && preg_match('/^ISI:/', $node->biblio_accession_number)) {
    $node->biblio_accession_number = str_replace("ISI:", $isi, $node->biblio_accession_number);
  }
}

/**
 *
 * @return array
 *   An array of allowed HTML tags as values in array.
 *
 */
function biblio_get_allowed_tags() {
  return array(
    'a',
    'b',
    'i',
    'u',
    'sub',
    'sup',
    'span',
  );
}

/**
 *
 */
function biblio_get_title_url_info($node, $base = NULL, $inline = FALSE) {
  $new_window = NULL;
  if (!isset($base)) {
    $base = variable_get('biblio_base', 'biblio');
  }
  if (variable_get('biblio_link_title_url', 0) && !empty($node->biblio_url)) {
    $path = $node->biblio_url;
  }
  else {
    $language = isset($node->language) ? $node->language : '';
    $view_mode = $inline ? 'viewinline' : 'view';
    $node_path = 'node/' . $node->nid;
    $path = $base . '/' . $view_mode . '/' . $node->nid;
    $path_alias = drupal_get_path_alias($node_path, $language);
    $path = $path_alias != $node_path ? $path_alias : drupal_get_path_alias($path, $language);
  }
  if (variable_get('biblio_links_target_new_window', null)) {
    $new_window = array(
      'target' => '_blank',
    );
  }
  return array(
    'link' => $path,
    'options' => array(
      'attributes' => $new_window,
      'html' => TRUE,
    ),
  );
}

/**
 *
 *
 * @param string $type
 *   Possible values include  (can be one of "type_names", "type_map" or "field_map")
 * @param string $format
 *   keys like (tagged, ris, endnote_xml8 etc...)
 *
 * @return array $map
 *
 */
function biblio_get_map($type, $format) {
  $map = unserialize(db_result(db_query("SELECT %s FROM {biblio_type_maps} WHERE format='%s'", array(
    $type,
    $format,
  ))));
  if ($type == 'export_map' && empty($map)) {
    $schema = drupal_get_schema('biblio');
    $fieldnames = array_keys($schema['fields']);
    asort($fieldnames);
    $map = array_fill_keys($fieldnames, 1);
  }
  return $map;
}

/**
 * @param string $type (can be one of "type_names", "type_map" or "field_map")
 * @param string $format (tagged, ris, endnote_xml8 etc...)
 * @param array  $map
 */
function biblio_set_map($type, $format, $map) {
  $map[$type] = serialize($map);
  $map['format'] = $format;
  drupal_write_record('biblio_type_maps', $map, 'format');
}

/**
 * Implemnts hook_reset_map().
 *
 * @param string $type
 *
 * @param string $format
 *
 */
function biblio_reset_map($type, $format) {
  module_invoke_all($format . '_map_reset', $type);
}

/**
 *
 *
 * @param object $node
 *   A node object (passed by reference) populated with biblio type.
 */
function _biblio_export_visibility(&$node) {
  static $visibility = array();
  if (!isset($visibility[$node->biblio_type])) {
    $result = db_query("SELECT bf.name,  bft.common, bft.visible\n                        FROM {biblio_fields} AS bf\n                        INNER JOIN {biblio_field_type} AS bft ON bft.fid=bf.fid\n                        INNER JOIN {biblio_types} AS bt ON bt.tid=bft.tid\n                        WHERE bft.tid={$node->biblio_type}");
    while ($row = db_fetch_array($result)) {
      $fields[$row['name']] = $row['visible'] ? TRUE : FALSE;
    }
    $visibility[$node->biblio_type] = $fields;
  }
  foreach ($visibility[$node->biblio_type] as $field_name => $visible) {
    if (!$visible && isset($node->{$field_name})) {
      unset($node->{$field_name});
    }
  }
}

/**
 *
 *
 * @param string $type_name
 *
 *
 * @return array
 *   An array of definitions for extra fields defined in teh biblio module.
 */
function biblio_content_extra_fields($type_name) {
  if ($type_name == 'biblio') {
    module_load_include('inc', 'biblio', 'includes/content.biblio');
    return _biblio_content_extra_fields($type_name);
  }
}

/**
 * Implements hook_preprocess_page for page display with page.tpl.php.
 */
function biblio_preprocess_page(&$variables) {
  if (isset($variables['node']) && $variables['node']->type == 'biblio') {
    $node = $variables['node'];
    $variables['title'] = filter_xss($node->title, biblio_get_allowed_tags());
  }
}

Functions

Namesort descending Description
biblio_access Implements hook_access().
biblio_autocomplete
biblio_block Implements hook_block().
biblio_citekey_generate
biblio_content_extra_fields
biblio_contributors_js Prepares a JSON response for the contributor widget.
biblio_cron Implements hook_cron().
biblio_delete Implements hook_delete().
biblio_diff Implements hook_diff().
biblio_filter Implements hook_filter().
biblio_filter_clear Implements hook_filter_clear()
biblio_filter_feed This function creates a feed from a filter type query (i.e. biblio/author/jones)
biblio_filter_tips Implements hook_filter_tips().
biblio_fix_isi_links
biblio_form Implements hook_form().
biblio_forms
biblio_form_alter Implements hook_form_alter().
biblio_form_validate Implementation of hook_validate().
biblio_get_allowed_tags
biblio_get_db_fields
biblio_get_map
biblio_get_style
biblio_get_styles
biblio_get_title_url_info
biblio_hash
biblio_help Implements hook_help().
biblio_help_page Generates a page of translated help text applicable to the biblio module.
biblio_init Implements hook_init().
biblio_insert Implements hook_insert().
biblio_link Implements hook_link().
biblio_load Implements hook_load().
biblio_locale Implements hook_locale().
biblio_locale_refresh_fields Refreshes all translatable field strings.
biblio_locale_refresh_types Refreshes all publication type strings.
biblio_menu Implements hook_menu().
biblio_nodeapi Implements hook_nodeapi().
biblio_node_info Implements hook_node_info().
biblio_perm Implements of hook_perm().
biblio_preprocess_page Implements hook_preprocess_page for page display with page.tpl.php.
biblio_recent_feed
biblio_remove_brace Removes curly braces from a string.
biblio_remove_brace_callback Assists in the removal of curly braces with preg_replace_callback().
biblio_reset_map Implemnts hook_reset_map().
biblio_set_map
biblio_taxonomy Implements hook_taxonomy().
biblio_term_path
biblio_theme Implements hook_theme().
biblio_token_list
biblio_token_values
biblio_update Implements hook_update().
biblio_user Implements hook_user().
biblio_user_load Loads a user object based upon user ID.
biblio_user_to_arg Determines actual argument for %biblio_user placeholders in menu paths.
biblio_view Implements hook_view().
biblio_views_api Implements hook_views_api().
_biblio_citekey_print Filter
_biblio_contributor_form Creates contributor form elements for contributor widget and JSON callback.
_biblio_contributor_widget Defines a contributor widget to be used as part of biblio node form.
_biblio_export_visibility
_biblio_filter_footnote_callback
_biblio_filter_replace_callback Helper function called from preg_replace_callback() above
_biblio_get_auth_type Retrieves primary author type based on author category and biblio type.
_biblio_get_auth_types Retrieves author types based upon author category and biblio type.
_biblio_get_field_information Retrieves field information based upon biblio type.
_biblio_inline_filter_replace_callback
_biblio_localize_fields Translates interface field titles and hints.
_biblio_localize_type Translates a publication type for the interface.
_biblio_numeric_year
_biblio_prepare_submit Prepares a biblio node for submit to database.
_biblio_profile_access
_biblio_text_year @reutrn mixed

Constants

Namesort descending Description
BIBLIO_VERSION @file Main file for Drupal module biblio.