You are here

rdf_format.inc in Taxonomy import/export via XML 5

Same filename and directory in other branches
  1. 5.2 rdf_format.inc
  2. 6.2 rdf_format.inc
  3. 6 rdf_format.inc

Include routines for RDF parsing and taxonomy/term creation.

This lib is included on demand by the taxonomy_xml.module when needed to read or write the RDF syntax.

@author dman (Dan Morrison) http://coders.co.nz/

File

rdf_format.inc
View source
<?php

/**
 * @file Include routines for RDF parsing and taxonomy/term creation.
 * 
 * This lib is included on demand by the taxonomy_xml.module when needed
 * to read or write the RDF syntax.
 * 
 * @author dman (Dan Morrison) http://coders.co.nz/
 */
define('TAXONOMY_XML_RDF_NS', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
define('TAXONOMY_XML_TYPE', TAXONOMY_XML_RDF_NS . 'type');
define('TAXONOMY_XML_RDFS_NS', 'http://www.w3.org/2000/01/rdf-schema#');

// See  http://www.w3.org/2004/12/q/doc/rdf-labels.html
define('TAXONOMY_XML_CONTENTLABEL_NS', 'http://www.w3.org/2004/12/q/contentlabel#');
define('TAXONOMY_XML_CATEGORY', TAXONOMY_XML_CONTENTLABEL_NS . 'Category');
define('TAXONOMY_XML_OWL_NS', 'http://www.w3.org/2002/07/owl#');
define('TAXONOMY_XML_W3C_WN', 'http://www.w3.org/2006/03/wn/wn20/');
define('TAXONOMY_XML_W3C_WN_SCHEMA', TAXONOMY_XML_W3C_WN . 'schema/');
define('TAXONOMY_XML_SKOS_NS', 'http://www.w3.org/2004/02/skos/core#');

/**
 * Read in RDF taxonomies and vocabularies. Create vocabs and terms as needed.
 * 
 * See formats.html readme for information about the RDF input supported.
 * 
 * Targets include :
 *   ICRA      Content Rating  http://www.icra.org/vocabulary/ 
 *   WordNet   Lexicon http: //wordnet.princeton.edu/ 
 *   SUMO   http://www. ontologyportal.org/
 * 
 * ... and the ontologies found at http://www.schemaweb.info/ that implement
 * appropriate parts of the RDF Schema "rdfs" (eg Classes with subclassOf)
 */
function taxonomy_xml_rdf_parse(&$data, $vid) {
  if (!file_exists(drupal_get_path('module', 'taxonomy_xml'))) {
    drupal_set_message(t('This method requires the ARC library to be available. Please check the taxonomy_xml INSTALL.txt'));
    return false;
  }

  // Use ARC parser
  include_once "arc/ARC_rdfxml_parser.php";
  $parser_args = array(
    "bnode_prefix" => "genid",
    "base" => "",
  );
  $parser = new ARC_rdfxml_parser($parser_args);
  $triples = $parser
    ->parse_data($data);
  if (!is_array($triples)) {
    drupal_set_message(t("Problem parsing input %message", array(
      '%message' => $triples,
    )), 'error');
    return false;
  }
  drupal_set_message(t("%count data triples (atomic statements) found in the source RDF doc", array(
    '%count' => count($triples),
  )));

  #dpm($triples);

  // The RDF input may come in several flavours,
  // Resources of the following 'types' may be cast into taxonomy terms for our purposes.
  // That is, an rdf:Class is a Drupal:term
  //
  // Add to this list as needed
  //
  $term_types = array(
    TAXONOMY_XML_RDF_NS . 'Property',
    'http://purl.org/dc/elements/1.1/subject',
    TAXONOMY_XML_RDFS_NS . 'Class',
    TAXONOMY_XML_W3C_WN_SCHEMA . 'Word',
    TAXONOMY_XML_W3C_WN_SCHEMA . 'NounWordSense',
    TAXONOMY_XML_W3C_WN_SCHEMA . 'NounSynset',
    'http://www.w3.org/2004/12/q/contentlabel#Category',
    TAXONOMY_XML_SKOS_NS . 'Concept',
  );

  // A Drupal 'vocabulary' is represented by an owl:Ontology
  // or other similar shaped constructs
  $vocabulary_types = array(
    TAXONOMY_XML_OWL_NS . 'Ontology',
    TAXONOMY_XML_RDF_NS . 'Description',
    'http://www.w3.org/2001/12/Glossary',
  );
  $resources_by_type = taxonomy_xml_convert_triples_to_sorted_objects($triples);

  // The resources are all initialized as data objects.
  // Resource types we expect to be dealing with are just vocabs and terms.
  drupal_set_message(t("Found %count different <strong>kinds</strong> of resources in the input : %types", array(
    '%count' => count($resources_by_type),
    '%types' => join(', ', array_keys($resources_by_type)),
  )));

  #dpm($resources_by_type);

  // Scan the sorted objects for vocabulary definitions
  // Hopefully there's only one vocab per file, but loop anyway
  $vocabularies = array();
  foreach ($vocabulary_types as $vocabulary_type) {
    if (isset($resources_by_type[$vocabulary_type]) && is_array($resources_by_type[$vocabulary_type])) {
      foreach ($resources_by_type[$vocabulary_type] as $uri => &$vocabulary_handle) {
        $vocabularies[$uri] =& $vocabulary_handle;
      }
    }
  }
  drupal_set_message(t("Found %count resources to be used as vocabulary definitions", array(
    '%count' => count($vocabularies),
  )));
  if ($vid == 0) {

    // We've been asked to use the vocab described in the source file.
    if (!$vocabularies) {

      // Create a placeholder.
      $vocabularies[] = array(
        'name' => 'Imported Vocabulary',
      );
    }
  }
  $vid = taxonomy_xml_absorb_vocabulary_definitions($vocabularies);

  // $vocabularies now contains a keyed array of target vocabularies the terms may be put into
  // $vid is the default one (most common is one vocab per input file) to be used unless otherwise defined per-term.
  // Gather the resources that will become terms.
  // Slightly long way (not using array_merge), as I need to merge indexed and by reference
  $terms = array();
  foreach ($term_types as $term_type) {
    if (isset($resources_by_type[$term_type]) && is_array($resources_by_type[$term_type])) {
      foreach ($resources_by_type[$term_type] as $uri => &$term_handle) {

        // Grab name/label early for debugging and indexing
        $predicates = $term_handle->predicates;
        if (isset($predicates['label'])) {
          $term_handle->name = $predicates['label'][0];
        }
        $terms[$uri] =& $term_handle;
      }
    }
  }
  drupal_set_message(t("Found %count resources to be imported as terms into vocabulary %vid", array(
    '%count' => count($terms),
    '%vid' => $vid,
  )));

  // $predicate_synonyms is a translation array to match rdf-speak with Drupal concepts
  $predicate_synonyms = taxonomy_xml_relationship_synonyms();

  //
  // START MAKING TERMS
  //
  foreach ($terms as $uri => &$term) {
    drupal_set_message(t("Reviewing term %uri '%name' and analyzing its properties", array(
      '%uri' => $uri,
      '%name' => $term->name,
    )));
    if (!isset($term->vid)) {

      // This is just a default fallback. Imported terms should really have already chosen their vid.
      $term->vid = $vid;
    }

    // Build term from data
    // Convert all input predicates into attributes on the object the taxonomy.module will understand
    foreach ($term->predicates as $predicate => $values) {
      $original_predicate = $predicate;

      // First translate misc terminology synonyms to the cannonic predicate I use everywhere
      // This allows us to interpret several XML dialects at once
      if (isset($predicate_synonyms[$predicate]) && ($cannonic = $predicate_synonyms[$predicate])) {
        $predicate = $cannonic;
      }

      #drupal_set_message(t("Applying '$predicate' ($predicate) value of ". print_r($values, 1) ." found in $uri"));
      switch ($predicate) {
        case 'type':

        // These are already done. Ignore
        case 'subPropertyOf':

          // Useless, ignore also
          break;
        case TAXONOMY_XML_NAME:
          $term->name = taxonomy_xml_get_literal_string($values);
          break;
        case TAXONOMY_XML_DESCRIPTION:
          $term->description = taxonomy_xml_get_literal_string($values);
          break;
        case TAXONOMY_XML_PARENT:
        case TAXONOMY_XML_RELATED:
        case TAXONOMY_XML_CHILD:

          // A term relationship.
          // Translate each referred item from URI to label or handle,
          // and save to be linked in later
          foreach ($values as $i => $target_uri) {
            $term->predicates[$predicate][$target_uri] = $target_uri;
          }
          break;
        case TAXONOMY_XML_HAS_SYNONYM:
          $term->synonyms_array = isset($term->synonyms_array) ? array_merge($term->synonyms_array, $values) : $values;
          $term->synonyms = join("\n", array_unique($term->synonyms_array));
          break;
        case TAXONOMY_XML_IN_VOCABULARY:

          // This term need to be in the vocabulary referred to by this URI
          // check our known vocabs to see if they are recognised
          // Do we know a vocab with an ID matching this 'isdefinedby' value?
          foreach ($values as $value) {

            // probably just one...
            if ($target_vocab = $vocabularies[$value]) {

              // I know this vocab!
              $term->vid = $target_vocab->vid;
            }
          }
          break;
        case 'unused':

          // Explicitly ignore these
          break;
        default:
          drupal_set_message(t("Dunno what to do with '{$predicate}' value of " . print_r($values, 1) . " found in {$uri}", array(
            '$predicate' => $predicate,
            '%values' => print_r($values, 1),
            '%uri' => $uri,
          )));
      }
    }

    // Look for existing term matching this one and blend the properties
    // Ensure name is valid
    if (!$term->name) {

      // Fallback to a name, identifier derived (roughly) from the URI - not always meaningful, but all we have in some contexts.
      $term->name = basename($uri);
      drupal_set_message(t("Problem, we were unable to find a specific label for the term referred to as %uri. Guessing that %name will be good enough.", array(
        '%uri' => $uri,
        '%name' => $term->name,
      )));
    }
    if (!$term->name) {

      // Should never get here
      drupal_set_message(t("Problem, this term %uri does not have a readable label.", array(
        '%uri' => $uri,
      )));
      next;
    }

    # dpm(array('data to merge' => $term));

    // See if a definition already exisits in the DB. Build on that.
    $existing_term = _taxonomy_xml_get_term_placeholder($term->name, $vid);

    // Merge the old term objects properties into this one. Really just want its tid, but there may be more info I should not lose.
    // New input takes precedence over older data
    foreach ((array) $existing_term as $key => $value) {
      if (!isset($term->{$key})) {
        $term->{$key} = $value;
      }
    }

    // The term object is now as tidy as it can be as a self-contained entity.

    # dpm($term);

    // MAINTAIN IDS
    // Because this is likely to be used with a site-cloning set-up, it would help if we tried to match IDs
    // OTOH, doing so could be very messy for other situations.
    // So,
    //  iff there is no pre-existing term with this id,
    //  create this one as a clone with the old ID.
    // This requires a little DB sneakiness.
    if ($term->internal_id && !taxonomy_get_term($term->internal_id)) {
      $term->tid = $term->internal_id;
      drupal_set_message(t("Doing sneaky import of %term_name re-using the internal id = %term_id", array(
        '%term_name' => $term->name,
        '%term_id' => $term->internal_id,
      )));
      db_query("INSERT INTO {term_data} (tid, name, description, vid, weight) VALUES (%d, '%s', '%s', %d, %d)", $term->tid, $term->name, $term->description, $term->vid, $term->weight);

      // Fudge the sequences table to patch the hack we just did, avoid over-writing later
      $current_id = db_result(db_query("SELECT id FROM {sequences} WHERE name = '%s'", '{term_data}_tid'));
      if ($current_id < $term->tid) {
        db_query("REPLACE INTO {sequences} VALUES ('%s', %d)", '{term_data}_tid', $term->tid);
      }
    }

    #dpm(array("ready to save" => $term));
    $save_term = (array) $term;
    taxonomy_save_term($save_term);

    // Re-retrieve the new term definition, just in case anything extra happened to it during processing
    $new_term = taxonomy_xml_get_term_by_name_from_vocab($term->name, $term->vid);
    if (!$new_term) {
      drupal_set_message(t("It seems like we failed to create and retrieve a term called %term_name", array(
        '%term_name' => $term->name,
      )), 'error');
    }

    // Merge retrieved values back over our main definition so the handles are up-to-date
    foreach ((array) $new_term as $key => $value) {
      $term->{$key} = $value;
    }
  }

  // end term-construction loop;

  #dpm("Saved all, now linking!");

  #dpm($terms);

  // Now the terms are all happily created, create their relationships
  // Couldn't do so until they had all been given tids.
  taxonomy_xml_set_term_relations($terms);
  $term_list = array();
  foreach ($terms as $term) {
    $term_list[] = l($term->name, 'admin/content/taxonomy/edit/term/' . $term->tid);
  }
  drupal_set_message(t('Updated %count term(s)', array(
    '%count' => count($terms),
  )) . ' <i>' . implode(', ', $term_list) . '.</i> ');
  drupal_set_message(t("\n    Finished importing vocabulary %vocab_name. \n    You may now need to <a href='!settings_link'>Review the vocabulary settings</a> \n    or <a href='!list_link'>List the terms</a>", array(
    '%vocab_name' => $vocabulary->name,
    '!settings_link' => url('admin/content/taxonomy/edit/vocabulary/' . $vid),
    '!list_link' => url('admin/content/taxonomy/' . $vid),
  )));
  return count($term_list);
}

/**
 * Compile triple statements into information objects again.
 * 
 * Returns a nested array, Indexed on their URI/id, and grouped by type
 * (references so we can change them) This currently assumes ALL found info
 * objects as terms, which is not quite right
 */
function taxonomy_xml_convert_triples_to_sorted_objects(&$triples) {

  // Triples are boringly granular bits of information.
  // Merge them.
  $resources = array();
  $resources_by_type = array();
  foreach ($triples as $triplenum => $statement) {
    $subject_uri = $statement['s']['uri'];
    if (!isset($resources[$subject_uri])) {

      // Create placeholder if this is the first occurance
      $resources[$subject_uri] = (object) array();
    }
    $subject =& $resources[$subject_uri];

    # dpm(array("Processing a statement about $subject_uri" => $statement));
    switch ($statement['o']['type']) {
      case 'uri':
        $object_uri = $statement['o']['uri'];

        // Also make a placeholder for the object, for convenience
        // It's not much fun referring to something that doesn't exist.
        if (!isset($resources[$object_uri])) {
          $resources[$object_uri] = (object) array();
        }
        $object_val = $object_uri;
        break;
      default:
        $object_val = trim($statement['o']['val']);
    }

    // Placeholders ready, now add this statements info
    // Namespaces are boring, Simplify the predicates
    // TODO - revisit if namespaces are needed
    $predicate = taxonomy_xml_rdf_shortname($statement['p']);
    if (!isset($subject->predicates[$predicate])) {
      $subject->predicates[$predicate] = array();
    }

    // Some properties can be collated, listed
    // Some need to be merged or selected (languages)
    // In this stage of pre-processing, we cannot select which string we need, so gather all values
    if ($statement['o']['type'] == 'literal' && ($lang = $statement['o']['lang'])) {
      $subject->predicates[$predicate][$lang] = $object_val;
    }
    else {

      // Only add uniques, Keeps clutter down
      if (!in_array($object_val, $subject->predicates[$predicate])) {
        $subject->predicates[$predicate][] = $object_val;
      }
    }
    if ($predicate == 'type') {

      // Very important info!
      $subject->type = $object_val;

      // Sort it! (by reference)
      $resources_by_type[$subject->type][$subject_uri] =& $subject;
    }
    if ($predicate == TAXONOMY_XML_NAME) {
      $subject->name = $object_val;
    }

    // This is very memory-intensive for big vocabs. Try to clean up :(
    unset($triples[$triplenum]);
  }

  // Scan the full array for any lost data - just FYI
  $unknown_resources = array();
  foreach ($resources as $uri => &$resource) {
    if (!isset($resource->type)) {
      $unknown_resources[$uri] =& $resource;
    }

    // While we are looping,
    // Make a guess at its original, internal ID
    // grabbing the last numeric bit from the id in the document
    // eg from '#vocab/1' or '#vocabulary:1' or #term33
    // Be very generic and forgiving in the format we look for
    $parts = preg_split('|[^\\d]|', $uri);
    $last_num = array_pop($parts);
    if (is_numeric($last_num)) {
      $resource->internal_id = $last_num;
    }
  }
  if ($unknown_resources) {

    #drupal_set_message(t("Found %count Unsorted (untyped) resources. Not sure what I'll do with these. They are things that have had statements made about them .. that I don't recognise. Probably just extra data found in the input and ignored. %unknown", array('%count' => count($unknown_resources), '%unknown' => join(', ', array_keys($unknown_resources))) ));
  }
  return $resources_by_type;
}

/**
 * Choose a string from an array of language-tagged possibilities
 * 
 * Util func to help read complex RDF statements.
 */
function taxonomy_xml_get_literal_string($values) {
  if (!is_array($values)) {
    return trim($values);
  }

  // May need to choose language
  if (count($values) == 1) {
    $out = array_pop($values);
  }
  else {

    // TODO add language selector
    if ($label = $values['en']) {
      $out = $label;
    }
    else {

      // fine, whatever
      $out = array_pop($values);
    }
  }
  return trim($out);
}

/**
 * Return the shorthand label of a potentially long RDF URI
 * 
 * EG, for http://www.w3.org/1999/02/22-rdf-syntax-ns#Property
 * return 'Property'
 * ... for sanity
 */
function taxonomy_xml_rdf_shortname($uri) {
  $parts = parse_url($uri);
  $shortname = $parts['fragment'] ? $parts['fragment'] : ($parts['query'] ? $parts['query'] : basename($parts['path']));

  // The proper method for guessing simple names is probably documented elsewhere.
  // ... this does the trick for now.
  return $shortname;
}

/**
 * Return an XML/RDF document representing this vocab
 * 
 * I'd like to use ARC libraries, but it doesn't appear to include an RDF
 * serializer output method, only an input parser...
 * 
 * Uses PHP DOM to create DOM document and nodes.
 * 
 * We use namespaces carefully here, although it may create wordy output if the
 * DOM is not optimizing the declarations for us. Still, best to be explicit, it
 * would seem.
 * 
 * The URI used to refer to other resources is based on the source document
 * location, eg
 * http://this.server/taxonomy_xml/{vid}/rdf#{tid}
 * 
 * Preamble should look something like:
 * 
 * <rdf:RDF xmlns:rdf ="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
 *   xmlns: rdfs="http://www.w3.org/2000/01/rdf-schema#"
 *   xmlns: owl="http://www.w3.org/2002/07/owl#"
 *
 */
function taxonomy_xml_rdf_create($vid, $parent = 0, $depth = -1, $max_depth = NULL) {
  $vocabulary = taxonomy_vocabulary_load($vid);
  $domcontainer = taxonomy_xml_rdf_document();
  $dom = $domcontainer->ownerDocument;

  #dpm(array(domcontainer => $domcontainer, dom => $dom));

  // define the vocab
  taxonomy_xml_add_vocab_as_rdf($domcontainer, $vocabulary);

  // and more details?
  // Now start adding terms.
  // They are listed as siblings, not children of the ontology
  $tree = module_invoke('taxonomy', 'get_tree', $vid, $parent, $depth, $max_depth);
  taxonomy_xml_add_terms_as_rdf($domcontainer, $tree);
  $result = $dom
    ->savexml();

  // Minor layout tweak for readability
  $result = preg_replace('|(<[^<]*/[^>]*>)|', "\$1\n", $result);
  $result = preg_replace('|><|', ">\n<", $result);

  # dpm($result);
  return $result;
}

/**
 * Set up an RDF document preamble.
 * Returns a document, also sets the passed handle to the RDF node that content
 * should land in
 * 
 */
function taxonomy_xml_rdf_document() {
  $dom = new domdocument('1.0', 'UTF-8');
  $dom
    ->appendchild($dom
    ->createcomment(htmlentities("\n    This file was created by Drupal taxonomy_xml import/export tool. \n    http://drupal.org/project/taxonomy_xml\n\n    The RDF schema in this file is intended to follow the Working Draft\n    described at http://www.w3.org/TR/wordnet-rdf/ for the notation of\n    thesauri and taxonomies.\n    ")));
  $dom
    ->appendchild($dom
    ->createprocessinginstruction('xml-stylesheet', 'href="render-taxonomy-rdf.xsl" type="text/xsl"'));
  $domcontainer = $dom
    ->createelementns(TAXONOMY_XML_RDF_NS, 'rdf:RDF');

  #  $rdfnode->setattribute('xmlns', TAXONOMY_XML_RDFS_NS);
  $dom
    ->appendchild($domcontainer);

  // Why can't I set more namespaces?
  // By appending a namespaced att, the extra namespaces appear at the top.
  // Otherwise the appear everywhere. There must be a better way
  $domcontainer
    ->setattributens(TAXONOMY_XML_RDFS_NS, 'rdfs:title', "Initializing namespace in PHP is hard");
  $domcontainer
    ->setattributens(TAXONOMY_XML_OWL_NS, 'owl:hack', "Initializing namespace in PHP is hard");

  // Note that one way to get namespaces to work right is by adding new
  // elements to their context asap, not by waiting until after further bits are added.
  return $domcontainer;
}

/**
 * Create a vocabulary definition (just the def, not its terms) and insert it
 * into the given document element.
 * 
 * @param $domcontainer an XML dom document, modified by ref.
 * @param $vocabulary a vocab object
 */
function taxonomy_xml_add_vocab_as_rdf(&$domcontainer, $vocabulary) {
  $dom = $domcontainer->ownerDocument;

  // Describe the vocabulary itself
  $vocabnode = $dom
    ->createelementns(TAXONOMY_XML_OWL_NS, 'owl:Ontology');
  $domcontainer
    ->appendchild($vocabnode);

  // If this was a cannonic vocab, we would use a full URI as identifiers
  $vocabnode
    ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:ID', 'vocabulary-' . $vocabulary->vid);
  $vocabnode
    ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:about', url('taxonomy_xml/' . $vocabulary->vid . '/rdf', NULL, NULL, TRUE));
  $vocabnode
    ->appendchild($dom
    ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:label', htmlentities($vocabulary->name)));
  if ($vocabulary->description) {
    $vocabnode
      ->appendchild($dom
      ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:comment', htmlentities($vocabulary->description)));
  }
  $vocabnode
    ->appendchild($dom
    ->createelementns(TAXONOMY_XML_OWL_NS, 'owl:versionInfo', htmlentities(format_date(time(), 'large'))));
}

/**
 * Given a list of terms, append definitions of them to the passed DOM container
 * 
 * Following w3c, SUMO and Wordnet examples (tho not any explicit instructions,
 * taxonomy terms are modelled as rdfs:Class objects structured using rdfs:
 * subClassOf statements.
 * 
 * Sample from Wordnet:
 * 
 * <Class rdf:about="http://xmlns.com/wordnet/1.6/Cat">
 *   <label>Cat  [ 1 ]</label>
 *   <comment>feline mammal usually having thick soft fur and being unable
 * to roar; domestic cats; wildcats</comment>
 *   <subClassOf>
 *     <Class rdf:about="http://xmlns.com/wordnet/1.6/Feline" />
 *   </subClassOf>
 * </Class>
 * 
 * I'm copying that syntax.
 * 
 * @param $termlist a FLAT array of all terms, internally cross-referenced to
 * each other defining the tree stucture
 */
function taxonomy_xml_add_terms_as_rdf(&$domcontainer, $termlist) {
  if (!$termlist) {
    return;
  }
  $dom = $domcontainer->ownerDocument;
  foreach ($termlist as $term) {
    $termnode = $dom
      ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:Class');
    $termnode
      ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:ID', 'term-' . $term->tid);
    $domcontainer
      ->appendchild($termnode);
    $termnode
      ->appendchild($dom
      ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:label', htmlentities($term->name)));
    if ($term->description) {
      $termnode
        ->appendchild($dom
        ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:comment', htmlentities($term->description)));
    }
    $vocab_ref = $dom
      ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:isDefinedBy');
    $vocab_ref
      ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:resource', '#vocabulary-' . $term->vid);
    $termnode
      ->appendchild($vocab_ref);
    foreach ((array) taxonomy_get_related($term->tid) as $relatedid => $relatedterm) {
      $related_node = $dom
        ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:seeAlso', htmlentities($relatedterm->name));
      $related_node
        ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:resource', '#term-' . $relatedid);
      $termnode
        ->appendchild($related_node);
    }
    $synonyms = taxonomy_get_synonyms($term->tid);

    // TODO - figure out the right syntax for synonym
    // I'm using 'equivalentClass' ... although that's really intended for merging different vocabs.
    foreach ((array) $synonyms as $synonymname) {
      $synonymnode = $parent_node = $dom
        ->createelementns(TAXONOMY_XML_OWL_NS, 'owl:equivalentClass', htmlentities($synonymname));
      $termnode
        ->appendchild($synonymnode);
    }
    foreach ((array) $term->parents as $parentid) {
      $parentlist = array();
      if ($parentid) {
        $parentlist[$parentid] = $parent = taxonomy_get_term($parentid);
        $parent_node = $dom
          ->createelementns(TAXONOMY_XML_RDFS_NS, 'rdfs:subClassOf', htmlentities($parent->name));
        $parent_node
          ->setattributens(TAXONOMY_XML_RDF_NS, 'rdf:resource', '#term-' . $parentid);
        $termnode
          ->appendchild($parent_node);
      }
    }

    # dpm(array('adding term to rdf' => $term));

    #$termnode->appendchild($dom->createcomment(print_r($term, 1)));

    // workaround for large vocabs - extend runtime indefinately
    set_time_limit(10);
  }

  // Done all terms in list
}

Functions

Namesort descending Description
taxonomy_xml_add_terms_as_rdf Given a list of terms, append definitions of them to the passed DOM container
taxonomy_xml_add_vocab_as_rdf Create a vocabulary definition (just the def, not its terms) and insert it into the given document element.
taxonomy_xml_convert_triples_to_sorted_objects Compile triple statements into information objects again.
taxonomy_xml_get_literal_string Choose a string from an array of language-tagged possibilities
taxonomy_xml_rdf_create Return an XML/RDF document representing this vocab
taxonomy_xml_rdf_document Set up an RDF document preamble. Returns a document, also sets the passed handle to the RDF node that content should land in
taxonomy_xml_rdf_parse Read in RDF taxonomies and vocabularies. Create vocabs and terms as needed.
taxonomy_xml_rdf_shortname Return the shorthand label of a potentially long RDF URI

Constants