You are here

taxonomy_xml.module in Taxonomy import/export via XML 5.2

taxonomy_xml.module This module makes it possible to import and export taxonomies as XML documents.

File

taxonomy_xml.module
View source
<?php

/**
* Rewritten 2008 dman (Dan Morrison) http://coders.co.nz/ 
 Copyright (c) 2007  Nicolas Haase <nicolas.haase@team.ourbrisbane.com>  
 Copyright (c) 2006  Sami Khan <sami@etopian.com>
 Copyright (c) 2005  Sheldon Rampton <sheldon@prwatch.org>
                                                                               
   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.
*/

/** 
 * @file taxonomy_xml.module
 * This module makes it possible to import and export taxonomies
 * as XML documents.
 */

/**
 * The keywords used in the expected source CSV files
 * These words based on one selected govt vocabulary, many others are likely.
 * 
 * A hash table of SYNONYMS for these terms is included in this module for
 * compatability with other syntaxes found in the wild.
 * @see taxonomy_xml_relationship_synonyms()
 * 
 * Refer also to ISO2788 for a description of the use of these structural
 * relations 
 * "http://www.ontopia.net/topicmaps/materials/tm-vs-thesauri.html#sect-thesauri"
 * 
 * 
 * 
 * An example Thesaurus source file may be the text version of 
 * 
 * Thesaurus for Graphic Materials I: Subject Terms [Library of Congress]
 * 
 * "http://www.loc.gov/rr/print/tgm1/downloadtgm1.html" 
 */
define('TAXONOMY_XML_RELATED', 'Related Terms');
define('TAXONOMY_XML_PARENT', 'Broader Terms');
define('TAXONOMY_XML_CHILD', 'Narrower Terms');
define('TAXONOMY_XML_HAS_SYNONYM', 'Used for');
define('TAXONOMY_XML_SYNONYM_OF', 'Use');
define('TAXONOMY_XML_DESCRIPTION', 'Definition');
define('TAXONOMY_XML_IN_VOCABULARY', 'Part of');
define('TAXONOMY_XML_NAME', 'name');

/**
 * Implementation of hook_help().
 */
function taxonomy_xml_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Makes it possible to import and export taxonomy terms via XML.');
    case 'admin/content/taxonomy/import':
      return t("\n        <p>\n        You can upload a vocabulary and/or taxonomy terms from a properly-formatted document. See the file formats.html in the module directory for more information.\n        </p><p>\n        If you want to add the terms to an existing vocabulary, \n        use the \"Target vocabulary\" selector below. \n        If you select \"determined by source file,\" the add to vocabulary will be specified by the XML document itself. \n        <br/>\n        Drupal-specific configurations (such as the related node types, or the free-tagging property) will \n        probably not be imported, so you'll want to review the new vocabulary edit page when it's done.\n        </p><p>\n        To avoid duplications, already-existing terms will not be added.\n        It's highly unlikely that term IDs can be meaningfully shared between systems, so the\n        tid field is NOT retained. Instead, terms are looked up by name (string match) and vocabulary when looking for existing terms.\n        Just because you've duplicated a vocabulary, does not mean it's identically numbered as the original. \n        They will usually get new, unique IDs.\n        </p>\n      ");
    case 'admin/content/taxonomy/export':
      return t("You can export XML documents for each vocabulary and its terms in this website's !taxonomies. Choose the vocabulary from the list below.", array(
        '!taxonomies' => l(t("taxonomies"), "admin/help/taxonomy"),
      ));
    case 'admin/help#taxonomy_xml':
      return t("This module makes it possible to import and export vocabularies and taxonomy terms in several formats (requires taxonomy.module). " . "Once installed and enabled, it module provides a list of downloadable XML documents for each vocabulary at !downloads. To import a vocabulary, use !upload.", array(
        '!downloads' => l(t("taxonomy XML"), "admin/taxonomy/export"),
        '!upload' => l("administer &raquo; categories &raquo; import", "admin/taxonomy/import", array(), NULL, NULL, FALSE, TRUE),
      ));
  }
}

/** 
 * Implementation of hook_menu: Define menu links.
 * 
 * @note See hook_menu for a description of parameters and return values.
 */
function taxonomy_xml_menu($may_cache) {
  if (!module_exists('taxonomy')) {
    return;
  }
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/content/taxonomy/export',
      'title' => t('Export'),
      'access' => user_access('administer taxonomy'),
      'callback' => 'taxonomy_xml_export',
      'type' => MENU_LOCAL_TASK,
    );
    $items[] = array(
      'path' => 'admin/content/taxonomy/import',
      'title' => t('Import'),
      'access' => user_access('administer taxonomy'),
      'callback' => 'taxonomy_xml_import',
      'type' => MENU_LOCAL_TASK,
    );
  }
  $items[] = array(
    'path' => 'taxonomy_xml',
    'title' => t('Taxonomy XML'),
    'callback' => 'taxonomy_xml_file',
    'access' => true,
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * taxonomy_xml_export
 * 
 * Outputs an unordered list of all available vocabularies for export
 * 
 * @return An unordered HTML list
 */
function taxonomy_xml_export() {

  // return the list of vocabularies
  $output = '';
  $vocabularies = module_invoke('taxonomy', 'get_vocabularies');
  if (empty($vocabularies)) {
    $output .= t('There are no vocabularies present');
  }
  else {
    foreach ($vocabularies as $vocabulary) {
      $vocablist[$vocabulary->vid] = l($vocabulary->name, "taxonomy_xml/{$vocabulary->vid}");
      $vocablist[$vocabulary->vid] .= ' ' . l("RDF", "taxonomy_xml/{$vocabulary->vid}/rdf");
    }
    $output = theme_item_list($vocablist);
  }
  return $output;
}

/**
 * taxonomy_xml_file
 * 
 * Return a flat file representation of the requested vocab
 * 
 * Default format is the original custom Drupal XML file.
 */
function taxonomy_xml_file($vid, $format = 'xml') {

  // Retrieving Vocabulary name
  $vocabulary = taxonomy_vocabulary_load($vid);
  $vname = strtolower(str_replace(' ', '_', trim($vocabulary->name)));
  unset($vocabulary);
  switch ($format) {
    case 'xml':
      require_once 'xml_format.inc';
      $file = taxonomy_xml_xml_create($vid);
      break;
    case 'rdf':
      require_once 'rdf_format.inc';
      $file = taxonomy_xml_rdf_create($vid);
      break;
  }
  if (!empty($_SERVER['HTTP_USER_AGENT']) && (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE 5.5') || strpos($_SERVER['HTTP_USER_AGENT'], 'Opera'))) {
    header('Content-Type: application/dummy');
  }
  else {
    header('Content-Type: application/octet-stream');
  }
  if (headers_sent()) {
    echo 'Some data has already been output to browser, can\'t send file';
  }
  header('Content-Length: ' . strlen($file));
  header("Content-Disposition: attachment; filename=taxonomy_{$vname}.{$format}");
  echo $file;
}

/**
 * Menu callback for the import page.
 */
function taxonomy_xml_import() {
  return drupal_get_form('taxonomy_xml_import_form');
}

/**
 * Imports the actual XML.
 */
function taxonomy_xml_import_form_validate($form_id, $form_values) {
  if ($file = file_check_upload('xml')) {
    $fd = fopen($file->filepath, "rb");
    if (!$fd) {
      form_set_error('xml', t('Vocabulary import failed: file %filename cannot be read.', array(
        '%filename' => $file->filename,
      )));
    }
    else {
      $info = fstat($fd);
      $len = $info["size"];
      $text = fread($fd, $len);
      fclose($fd);
      drupal_set_message(t('Loaded file %filename. Now processing it.', array(
        '%filename' => $file->filename,
      )));
      $form_values['file'] = $file;
      taxonomy_xml_invoke_import($text, $form_values);
    }
  }
  else {
    form_set_error('xml', t('Vocabulary import failed: file was not uploaded.'));
  }
}

/**
 * Builds the import form.
 */
function taxonomy_xml_import_form() {
  $formats = taxonomy_xml_formats();
  $vocs[0] = t('[Determined by source file]');
  foreach (module_invoke('taxonomy', 'get_vocabularies') as $vid => $voc) {
    $vocs[$vid] = $voc->name;
  }
  $vocs[-1] = t('[Create new]');
  $form['vid'] = array(
    '#type' => 'select',
    '#title' => t('Target vocabulary'),
    '#default_value' => 0,
    '#options' => $vocs,
    '#description' => t('The vocabulary into which terms should be loaded.'),
  );

  /*
    $form['term'] = array(
      '#type' => 'checkbox',
      '#title' => ('Select a term from the vocabulary.'),
      '#description' => t("Allows a selection of a term in the vocabulary to be the parent of the imported taxonomy."),
    );*/
  $form['xml'] = array(
    '#type' => 'file',
    '#title' => t('File to import'),
    '#description' => t('Click "Browse..." to select an XML document to upload.'),
  );
  $form['format'] = array(
    '#type' => 'select',
    '#title' => t('Format of file'),
    '#default_value' => 'xml_format',
    '#options' => $formats,
  );
  $form['duplicate'] = array(
    '#type' => 'checkbox',
    '#title' => t('Allow duplicate terms'),
    '#description' => t('If you want to keep the same term in different positions in the vocabulary hierarchy, check this'),
    '#default_value' => 0,
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Import'),
  );
  $form['#attributes'] = array(
    'enctype' => 'multipart/form-data',
  );
  return $form;
}

/**
 * Do the actual importing from the given string, pased on the parameters passed
 * from the form.
 */
function taxonomy_xml_invoke_import($text, $form_values) {
  $vid = $form_values['vid'];
  if ($vid == -1) {

    // Requested to create new vocab.
    $vocabulary = _taxonomy_xml_get_vocabulary_placeholder(basename($form_values['file']->filename));
    $vid = $vocabulary->vid;
  }

  // Sitemenu implodes when I try to do bulk updates? Or is it pathauto
  // TODO figure this out
  module_disable(array(
    'sitemenu',
    'pathauto',
  ));

  // Conditionally include and invoke the appropriate format library
  $incfile = dirname(drupal_get_filename('module', 'taxonomy_xml')) . '/' . $form_values['format'] . '.inc';
  include_once $incfile;
  $format_name = preg_replace('/_format$/', '', $form_values['format']);
  $funcname = "taxonomy_xml_{$format_name}_parse";
  if (function_exists($funcname)) {
    $success = $funcname($text, $vid, NULL, $form_values['duplicate']);
    if ($success) {
      drupal_set_message(t("Taxonomy Import successful. %success terms created in vocabulary ", array(
        '%success' => $success,
      )));
    }
    else {
      drupal_set_message(t("Failed to import any new terms. This may be due to syntax or formattings errors in the import file.", array()), 'error');
    }
  }
  else {
    drupal_set_message("Unavailable format. {$funcname} was not found in formatting library {$incfile}.", 'error');
  }
}

/**
 * Return a list of available file formats.
 * 
 * Scan the module directory for appropriate inc files.
 * @return 
 *  Array (
 *   [csv_format]   => CSV
 *   [rdf_format]   => RDF
 *   [xml_format]   => XML
 *  )
 */
function taxonomy_xml_formats() {
  $incs = file_scan_directory(dirname(drupal_get_filename('module', 'taxonomy_xml')), '.*_format.inc');
  $formats = array();
  foreach ($incs as $file) {
    $formats[$file->name] = strtoupper(preg_replace('/_format$/', '', $file->name));
  }
  return $formats;
}

/** 
 * Either fetch the named vocab if it exists, or create and return a useful
 * placeholder.
 * 
 * @return the new vocab object.
 */
function _taxonomy_xml_get_vocabulary_placeholder($name) {
  if ($vocabulary = taxonomy_xml_get_vocabulary_by_name($name)) {

    // The node type array for a vocab has the type name as the key and the value set to 1
    // so we have to slightly modify the array or it will save wrong later
    $node_types = explode(',', $vocabulary->nodes);
    $vocabulary->nodes = array();
    foreach ($node_types as $type) {
      $vocabulary->nodes[$type] = 1;
    }
    return $vocabulary;
  }

  // Create new vocab
  $vocabulary = array(
    'name' => $name,
    'relations' => TRUE,
    'hierarchy' => 2,
  );
  module_invoke('taxonomy', 'save_vocabulary', $vocabulary);

  // Need to retrieve it from DB again - the result isn't given back to us.
  $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE LOWER('%s') LIKE LOWER(name)", $vocabulary['name']));
  $vocabulary = taxonomy_vocabulary_load($vid);
  drupal_set_message(t('Created vocabulary %vid %vocabname to put these terms into. You probably want to <a href="!vocablink">go edit it now</a>.', array(
    '%vocabname' => $vocabulary->name,
    '%vid' => $vid,
    '!vocablink' => url('admin/content/taxonomy/edit/vocabulary/' . $vid),
  )));
  return $vocabulary;
}

/**
 * Use the vocabs defined as resources in the input to find or create vocabulary
 * definitions.
 * 
 * @param $vocabularies An array of vocabulary definition objects, extracted
 * from the XML. Modified with their deduced or new vid values by reference
 * 
 * $vocabularies = array(
 *   '#vocabulary-3' => stdClass Object
 *     'name' => "Countries",
 *     'predicates'  => array(
 *       'description' => array( 0 => "A list of countries" ),
 *       'version'  => array( 0 => "2008-08-08" ),
 *     )
 *   )
 * )
 * 
 * All 'predicates' will be compressed into properties. EG in the above example, 
 * ['#vocabulary-3']['predicates']['description'][0]
 * is flattened to 
 * ['#vocabulary-3']['description']
 * 
 * Either input format is fine.
 * 
 * 
 */
function taxonomy_xml_absorb_vocabulary_definitions(&$vocabularies) {

  // See if we can match this definition against the given vid
  // - then on name.
  // If neither seems to exist, make a new one.
  if (is_array($vocabularies)) {

    # dpm(array("The vocabulary definition(s) found in the input file is ", $vocabularies));

    // There may be more than one vocab def per file, although this is unlikely
    foreach ($vocabularies as $vocabid => &$vocab) {

      // Merge all predicate data into a simpler array, re-tagging the attributes as needed
      // - there's not a lot of metadata about vocabs we know how to import, but try anyway - for expansion
      taxonomy_xml_merge_predicates_into_attributes($vocab);
      $target_vocab = NULL;

      // Deduce the given vocab definitions vid, if given as a value
      if (isset($vocab->vid)) {
        $vocab->internal_id = $vocab->vid;
        drupal_set_message(t("Found a vocabulary definition in the input, called {$vocabid}. vid={$vocab->internal_id}"));

        // Try to maintain old Vocabulary IDs
        // Check if it's a good number to write into
        // If the input defines a vid BUT there is already a non-matching vocab with that number, we need a new number
        // If it DOES seem to match, we are safe
        $target_vocab = taxonomy_vocabulary_load($vocab->internal_id);
      }

      #dpm(array('loaded vocab' => $target_vocab, 'parsed vocab' => $vocab));
      if (!empty($target_vocab) && $target_vocab->name == $vocab->name) {

        // Looks like a great match
        $vocab->vid = $vocab->internal_id;
        drupal_set_message(t("Found matching target vocabulary '%vocab_name' vid=%vocab_vid", array(
          '%vocab_name' => $vocab->name,
          '%vocab_vid' => $vocab->vid,
        )));
      }
      else {
        if ($target_vocab) {
          drupal_set_message(t("The vocab ID given in the input file (%vocab_vid) conflicts with an existing vocabulary. We need a different ID... ", array(
            '%vocab_vid' => $vocab->vid,
          )));
        }
        unset($vocab->vid);

        // Vocab with this id exists, but is called something else - Do not merge with it
        // Look for a match by name instead
        if ($target_vocab = taxonomy_xml_get_vocabulary_by_name($vocab->name)) {

          // Found a local vocab called the same as the input vocab. That's a good enough match for us.
          $vocab->vid = $target_vocab->vid;
          drupal_set_message(t("Found a target vocabulary already in the database, matching by name '%name' vid=%vid . This will be used, but not updated.", array(
            '%name' => $vocab->name,
            '%vid' => $vocab->vid,
          )));
        }
      }

      // Have we found a target vocab yet?
      if (empty($vocab->vid)) {

        // Make a brand new one from the imported definition
        $vocab = _taxonomy_xml_get_vocabulary_placeholder($vocab->name);

        // Built a vocabulary from input details. Now save it
        // dpm($vocab);
        $vocab_array = (array) $vocab;
        taxonomy_save_vocabulary($vocab_array);
        $vocab = taxonomy_vocabulary_load($vocab_array['vid']);
        drupal_set_message(t("Made a new Drupal vocabulary definition from data found in the input. Vocab is called: '%name' : %description ", array(
          '%name' => $vocab->name,
          '%description' => $vocab->description,
        )));

        #dpm($vocab);
      }
    }

    // end looping through found vocabs
  }
  else {
    drupal_set_message("The document provided no recognisible vocabulary definitions");
  }

  // This is the default (last found) vid. Probably should not be used, but we may have to make a guess.
  // Either an input file contains just one vocab - in which case this will be right,
  // or the input file contains multiple vocabularies - in which case the terms damn well ought to be tagged with which vocab to use.
  return isset($vocab->vid) ? $vocab->vid : NULL;
}

/**
 * Merge all predicate data into a simpler array, re-tagging
 * the attributes as needed
 * 
 * @param $object - an object containing a 'predicates' array. For each
 * predicate, a cannonically named attribute will be attached to the object.
 */
function taxonomy_xml_merge_predicates_into_attributes(&$object) {
  $predicate_synonyms = taxonomy_xml_relationship_synonyms();
  foreach ($object->predicates as $predicate => $vals) {
    $predicate = isset($predicate_synonyms[$predicate]) ? $predicate_synonyms[$predicate] : $predicate;
    $object->{$predicate} = array_pop($vals);
  }
  if (empty($object->description) && isset($object->{TAXONOMY_XML_DESCRIPTION})) {
    $object->description = $object->{TAXONOMY_XML_DESCRIPTION};
  }
  return $object;
}

/** 
 * Either fetch the named term if it exists, or return a useful placeholder.
 * 
 * The returned term has a 'synonyms_array' because that's easier to work with
 * than string concats in odd places.
 */
function _taxonomy_xml_get_term_placeholder($name, $vid = 0) {

  #dpm("Getting placeholder '$name' vocab:$vid");
  if ($name) {
    $term = taxonomy_xml_get_term_by_name_from_vocab($name, $vid);
  }
  else {

    // Assert input is OK. Just paranoia
    drupal_set_message(t("Asked to make a term with no name ... are you sure?"), 'error');
  }
  if (!$term) {
    $term = (object) array(
      'name' => $name,
      'vid' => $vid,
      'description' => '',
      'weight' => 0,
      'predicates' => array(),
      'synonyms_array' => array(),
    );
  }
  else {
    drupal_set_message(t("A term called '!name' already exists. Updating information onto it.", array(
      '!name' => l($term->name, 'admin/content/taxonomy/edit/term/' . $term->tid),
    )));
  }
  return $term;
}

/**
 * Given a list of terms, set  the related-terms and structure, and save again
 * 
 * Helper function for bulk processes.
 * 
 * The terms are currently indexed by either URI or name. The reference arrays
 * refer to either the URI or name. Scan the current array for the objects
 * (terms) being linked to.
 * 
 * Input would look (in part) like this:
 * 
 * $terms = array(
 *   '#123' => (
 *      'name' => 'hotels',
 *      'tid' => 23, 
 *      'predicates' => (
 *        'See Also' => ['#135', 'camping']
 *        'Broader Term' => ['accomodation']
 *      )
 *    )
 *   '#135' => ( 'name' => 'motels', 'tid' => 35 )
 *   '#145' => ( 'name' => 'camping', 'tid' => 37 )
 *   'accomodation' => ( 'name' => 'accomodation', 'tid' => 11 )
 * )
 * 
 * The process will read the 'predicates', figure out what they mean, figure out
 * which other term is being referenced, and create properties on the term
 * object.
 * 
 * And will return the term objects with appropriate Drupal attributes
 *
 *   '#123' => (
 *      'name' => 'hotels',
 *      'nid' => 23,
 *      'parent' => 11,
 *      'relations' => array(35, 37),
 *    )
 * 
 * Note that the key need have no relation with the nid, 
 * and may be a full string, which will work just as well.
 * The above shows an example of both, although that would be rare in the one import.
 *
 * 
 * Relationships cannot be created if the target term is not included in the
 * $terms list itself. If we are adding to an existing vocab, doing a partial
 * merge, the target terms should have already been retrieved from the database
 * and included in the complete list.
 * 
 * 
 * @param $terms an indexed array of existing taxonomy term objects, possibly
 * referring to each other by id.
 */
function taxonomy_xml_set_term_relations(&$terms) {
  drupal_set_message(t("Now connecting all known term relations and hierarchy links"));
  foreach ($terms as $uri => $term) {
    $name = $term->name;

    // Go through all and add relationships
    // The linking earlier may have given us some duplicates if the source had redundant info, so filter for uniques
    if (isset($term->predicates[TAXONOMY_XML_PARENT]) && is_array($term->predicates[TAXONOMY_XML_PARENT])) {
      foreach (array_unique($term->predicates[TAXONOMY_XML_PARENT]) as $key => $termname) {
        if ($termname) {
          $parent = $terms[$termname];
          if ($termname == $uri) {
            drupal_set_message(t("Not setting %name as a child of itself", array(
              '%name' => $term->name,
            )));
            continue;
          }
          if ($parent && isset($parent->tid)) {
            drupal_set_message(t("!name # %tid is a child of !parent # %ptid", array(
              '!name' => l($term->name, 'admin/content/taxonomy/edit/term/' . $term->tid),
              '%tid' => $term->tid,
              '!parent' => l($parent->name, 'admin/content/taxonomy/edit/term/' . $parent->tid),
              '%ptid' => $parent->tid,
            )));
            $term->parent[$parent->tid] = $parent->tid;
          }
          else {
            drupal_set_message(t("Couldn't find the parent called %termname for %name # %tid", array(
              '%termname' => $termname,
              '%name' => $name,
              '%tid' => $term->tid,
            )));
          }
        }
      }
    }

    #else{drupal_set_message(" $name ". $term->tid ." has no parent term");}
    if (isset($term->predicates[TAXONOMY_XML_RELATED]) && is_array($term->predicates[TAXONOMY_XML_RELATED])) {
      foreach (array_unique($term->predicates[TAXONOMY_XML_RELATED]) as $key => $termname) {
        if ($termname) {
          $related = $terms[$termname];
          if ($related) {
            $term->relations[] = $related->tid;
            drupal_set_message("Term " . $term->name . " " . $term->tid . " is related to {$related->name} " . $related->tid);
          }
          else {
            drupal_set_message(t("Couldn't find the term called '%termname' to link to '%name' as being related. This relationship will be discarded. ", array(
              '%name' => $name,
              '%termname' => $termname,
              '%debug' => print_r(array_keys($terms), 1),
            )));

            # dpm(array('available terms' => array_keys($terms)));
          }
        }
      }
    }
    if (!empty($term->synonyms_array)) {
      $term->synonyms = join("\n", array_unique($term->synonyms_array));
    }

    #     dpm(array('Saving' => $term));
    $save_term = (array) $term;
    taxonomy_save_term($save_term);
  }
}

/**
 * Fetch a vocabulary by name.
 * Utility Func extending taxonomy.module
 * 
 * @return Vocabulary object. NULL if not found
 */
function taxonomy_xml_get_vocabulary_by_name($name) {
  $vs = taxonomy_get_vocabularies();
  foreach ($vs as $voc) {
    if ($voc->name == $name) {
      return $voc;
    }
  }
}

/**
 * Get a term by name from the given vocab.
 * 
 * Loads the related terms and parent links as well.
 *
 * @return Term object. NULL if not found
 */
function taxonomy_xml_get_term_by_name_from_vocab($name, $vid) {
  $matched_terms = taxonomy_get_term_by_name($name);

  // Check it's the right vocab
  while (($term = array_pop($matched_terms)) && $term->vid != $vid) {
    continue;
  }
  if (!$term) {
    return NULL;
  }

  // What? there is no full taxonomy_term_load()?
  $term->parent = taxonomy_get_parents($term->tid);
  $term->relations = taxonomy_get_related($term->tid);
  $term->synonyms_array = taxonomy_get_synonyms($term->tid);
  return $term;
}

/**
 * Return an array of alternative wordings that may be used in the input files.
 * 
 * Add to this as needed, Referring to the globals defined at the top of the
 * module.
 * Different input files use different words to express the same concept. This
 * array tries to translate the meanings down into the core concepts used
 * internally.
 * The reason that this list is so big and messy is because all the different
 * academic sources I've researched just use different terminology to say the
 * same thing.
 * 
 * See ISO 2788 for notes on expressing thesauri.
 * or SKOS http://www.w3.org/2004/02/skos/vocabs
 * 
 * or an alternative glossary:
 * "http://www.boxesandarrows.com/view/controlled_vocabularies_a_glosso_thesaurus"
 * 
 * Each of these terms are predicates that would make up a 'triple' statement.
 * For a geographical taxonomy, a sample could be:
 * 
 * Subject, Predicate, Object
 * 
 * "United States of America", "Narrower", "Iowa"
 * "United States of America", "Broader", "North America"
 * "United States of America", "AKA", "USA"
 * "The States", "See", "United States of America"
 */
function taxonomy_xml_relationship_synonyms() {
  return array(
    'Related Terms' => TAXONOMY_XML_RELATED,
    'Related' => TAXONOMY_XML_RELATED,
    'related' => TAXONOMY_XML_RELATED,
    # SKOS
    'RT' => TAXONOMY_XML_RELATED,
    # ISO2788
    'seeAlso' => TAXONOMY_XML_RELATED,
    'Broader Terms' => TAXONOMY_XML_PARENT,
    'Broader' => TAXONOMY_XML_PARENT,
    'broader' => TAXONOMY_XML_PARENT,
    # SKOS
    'Broad Term' => TAXONOMY_XML_PARENT,
    'BT' => TAXONOMY_XML_PARENT,
    # ISO2788
    'subClassOf' => TAXONOMY_XML_PARENT,
    # rdfs
    'SubClassOf' => TAXONOMY_XML_PARENT,
    # contentlabel
    'ChildOf' => TAXONOMY_XML_PARENT,
    'hypernym' => TAXONOMY_XML_PARENT,
    'hyponymOf' => TAXONOMY_XML_PARENT,
    'Narrower Terms' => TAXONOMY_XML_CHILD,
    'Narrower' => TAXONOMY_XML_CHILD,
    'narrower' => TAXONOMY_XML_CHILD,
    # SKOS
    'NT' => TAXONOMY_XML_CHILD,
    # ISO2788
    'ParentOf' => TAXONOMY_XML_CHILD,
    'hyponym' => TAXONOMY_XML_CHILD,
    'Description' => TAXONOMY_XML_DESCRIPTION,
    'definition' => TAXONOMY_XML_DESCRIPTION,
    # SKOS
    'Definition' => TAXONOMY_XML_DESCRIPTION,
    'comment' => TAXONOMY_XML_DESCRIPTION,
    'gloss' => TAXONOMY_XML_DESCRIPTION,
    'Scope Note' => TAXONOMY_XML_DESCRIPTION,
    'note' => TAXONOMY_XML_DESCRIPTION,
    # SKOS
    'SN' => TAXONOMY_XML_DESCRIPTION,
    # ISO2788
    'Used for' => TAXONOMY_XML_HAS_SYNONYM,
    'AKA' => TAXONOMY_XML_HAS_SYNONYM,
    'synonym' => TAXONOMY_XML_HAS_SYNONYM,
    'altLabel' => TAXONOMY_XML_HAS_SYNONYM,
    # SKOS
    'equivalentClass' => TAXONOMY_XML_HAS_SYNONYM,
    'See' => TAXONOMY_XML_SYNONYM_OF,
    'USE' => TAXONOMY_XML_SYNONYM_OF,
    # ISO2788
    'Use' => TAXONOMY_XML_SYNONYM_OF,
    'related' => TAXONOMY_XML_RELATED,
    'seeAlso' => TAXONOMY_XML_RELATED,
    'memberMeronymOf' => TAXONOMY_XML_RELATED,
    'Part of' => TAXONOMY_XML_IN_VOCABULARY,
    'belongs-to-facet' => TAXONOMY_XML_IN_VOCABULARY,
    'isDefinedBy' => TAXONOMY_XML_IN_VOCABULARY,
    # rdfs
    'inScheme' => TAXONOMY_XML_IN_VOCABULARY,
    # SKOS
    'name' => TAXONOMY_XML_NAME,
    'lexicalForm' => TAXONOMY_XML_NAME,
    'label' => TAXONOMY_XML_NAME,
    'scientific name' => TAXONOMY_XML_NAME,
    'prefLabel' => TAXONOMY_XML_NAME,
    #SKOS
    'hasDescriptor' => 'unused',
  );

  // By listing the deliberately unused attributes the parser finds,
  // we can still be alerted to other unrecognised tags found in the input.
  // Perhaps they could be used. Otherwise the unused ones cause too much noise.
}

/**
 * Backport this renamed function to assist code patching along branches.
 * In D6 taxonomy_get_vocabulary became taxonomy_vocabulary_load
 */
if (!function_exists('taxonomy_vocabulary_load')) {
  function taxonomy_vocabulary_load($vid) {
    return taxonomy_get_vocabulary($vid);
  }
}

Functions

Namesort descending Description
taxonomy_xml_absorb_vocabulary_definitions Use the vocabs defined as resources in the input to find or create vocabulary definitions.
taxonomy_xml_export taxonomy_xml_export
taxonomy_xml_file taxonomy_xml_file
taxonomy_xml_formats Return a list of available file formats.
taxonomy_xml_get_term_by_name_from_vocab Get a term by name from the given vocab.
taxonomy_xml_get_vocabulary_by_name Fetch a vocabulary by name. Utility Func extending taxonomy.module
taxonomy_xml_help Implementation of hook_help().
taxonomy_xml_import Menu callback for the import page.
taxonomy_xml_import_form Builds the import form.
taxonomy_xml_import_form_validate Imports the actual XML.
taxonomy_xml_invoke_import Do the actual importing from the given string, pased on the parameters passed from the form.
taxonomy_xml_menu Implementation of hook_menu: Define menu links.
taxonomy_xml_merge_predicates_into_attributes Merge all predicate data into a simpler array, re-tagging the attributes as needed
taxonomy_xml_relationship_synonyms Return an array of alternative wordings that may be used in the input files.
taxonomy_xml_set_term_relations Given a list of terms, set the related-terms and structure, and save again
_taxonomy_xml_get_term_placeholder Either fetch the named term if it exists, or return a useful placeholder.
_taxonomy_xml_get_vocabulary_placeholder Either fetch the named vocab if it exists, or create and return a useful placeholder.

Constants

Namesort descending Description
TAXONOMY_XML_CHILD
TAXONOMY_XML_DESCRIPTION
TAXONOMY_XML_HAS_SYNONYM
TAXONOMY_XML_IN_VOCABULARY
TAXONOMY_XML_NAME
TAXONOMY_XML_PARENT
TAXONOMY_XML_RELATED The keywords used in the expected source CSV files These words based on one selected govt vocabulary, many others are likely.
TAXONOMY_XML_SYNONYM_OF