Browse Source

Fixed lots of bugs with web services and added new update function for tripal_chado

Stephen Ficklin 7 years ago
parent
commit
cab8852f14

+ 0 - 1
tripal/api/tripal.fields.api.inc

@@ -43,7 +43,6 @@ function hook_bundle_fields_info_alter(&$info, $bundle, $term) {
 function hook_bundle_instances_info_alter(&$info, $bundle, $term) {
 
 }
-
 /**
  * Retrieves a list of TripalField types.
  *

+ 54 - 7
tripal/api/tripal.terms.api.inc

@@ -116,7 +116,26 @@ function hook_vocab_get_term($vocabulary, $accession) {
   // See the tripal_chado_vocab_get_term() function for an example.
 
 }
-
+/**
+ * Hook used by the default term storage backend to provide details for a vocab.
+ *
+ * This hook is called by the tripal_entity module to retrieve information
+ * about the vocabulary from the storage backend.  It must return an array with
+ * a set of keys.
+ *
+ * @param $vocabulary
+ *   The vocabulary of the vocabulary in which the term is found.
+ *
+ * @return
+ *   An array with at least the following keys:
+ *     - name : The full name of the vocabulary.
+ *     - short_name : The short name abbreviation for the vocabulary.
+ *     - description : A brief description of the vocabulary.
+ *     - url : (optional) A URL for the online resources for the vocabulary.
+ */
+function hook_vocab_get_vocabulary($vocabulary) {
+  // See the tripal_chado_vocab_get_vocabulary() function for an example.
+}
 /**
  * Hook used by the default term storage backend to add new terms.
  *
@@ -175,15 +194,10 @@ function tripal_add_term($details) {
   }
 }
 
+
 /**
  * Retrieves full information about a vocabulary term.
  *
- * Vocabularies are stored in a database backend.  Tripal has no requirements
- * for how terms are stored.  By default, the tripal_chado modules provides
- * storage for vocabularies and terms. This function will call the
- * hook_vocab_get_term() function for the database backend that is housing the
- * vocabularies and allow it to return the details about the term.
- *
  * @param $vocabulary
  *   The vocabulary of the vocabulary in which the term is found.
  * @param $accession
@@ -217,3 +231,36 @@ function tripal_get_term_details($vocabulary, $accession) {
     }
   }
 }
+/**
+ * Retrieves full information about a vocabulary.
+ *
+ * Vocabularies are stored in a database backend.  Tripal has no requirements
+ * for how terms are stored.  By default, the tripal_chado modules provides
+ * storage for vocabularies and terms. This function will call the
+ * hook_vocab_get_term() function for the database backend that is housing the
+ * vocabularies and allow it to return the details about the term.
+ *
+ * @param $vocabulary
+ *   The vocabulary of the vocabulary in which the term is found.
+ *
+ * @return
+ *   An array with at least the following keys:
+ *     - name : The full name of the vocabulary.
+ *     - short_name : The short name abbreviation for the vocabulary.
+ *     - description : A brief description of the vocabulary.
+ *     - url : (optional) A URL for the online resources for the vocabulary.
+ */
+function tripal_get_vocabulary_details($vocabulary) {
+  // TODO: we need some sort of administrative interface that lets the user
+  // switch to the desired vocabulary type. For now, we'll just use the
+  // first one in the list.
+  $stores = module_invoke_all('vocab_storage_info');
+  if (is_array($stores) and count($stores) > 0) {
+    $keys = array_keys($stores);
+    $module = $stores[$keys[0]]['module'];
+    $function = $module . '_vocab_get_vocabulary';
+    if (function_exists($function)) {
+      return $function($vocabulary);
+    }
+  }
+}

+ 55 - 0
tripal/includes/tripal.term_lookup.inc

@@ -25,6 +25,61 @@ function tripal_vocabulary_lookup_form_submit($form, $form_state) {
 
 }
 
+function tripal_vocabulary_lookup_page($vocabulary) {
+
+  $vocab = tripal_get_vocabulary_details($vocabulary);
+
+  // If we can't find the term then just return a message.
+  if (!$vocab) {
+    drupal_set_message('The vocabulary cannot be found on this site', 'error');
+    return '';
+  }
+
+  $headers = array();
+  $rows = array();
+  $vocab_name = $vocab['name'];
+  if ($vocab['url']) {
+    $vocab_name = l($vocab['name'], $vocab['url'], array('attributes' => array('target' => '_blank')));
+  }
+  $short_name = $vocab['short_name'];
+  $vocab_desc = $vocab['description'];
+  $rows[] = array(
+    array(
+      'data' => 'Name',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $vocab_name,
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Short Name',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $short_name,
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Description',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $vocab_desc,
+  );
+
+  $table = array(
+    'header' => $headers,
+    'rows' => $rows,
+    'attributes' => array(),
+    'sticky' => FALSE,
+    'caption' => 'Vocabulary details',
+    'colgroups' => array(),
+    'empty' => '',
+  );
+  $content =  theme_table($table);
+  return $content;
+}
 /**
  *
  * @param $vocabulary

+ 13 - 13
tripal/tripal.module

@@ -121,17 +121,6 @@ function tripal_menu() {
     'file path' => drupal_get_path('module', 'tripal'),
   );
 
-//   $items['admin/tripal/loaders/obo_loader'] = array(
-//     'title' => 'OBO Controlled Vocabulary Loader',
-//     'description' => t("Import vocabularies and terms in OBO format."),
-//     'access arguments' => array('administer tripal'),
-//     'page callback' => 'drupal_get_form',
-//     'page arguments' => array('tripal_vocabulary_import_form'),
-//     'file' => 'includes/tripal.admin.inc',
-//     'file path' => drupal_get_path('module', 'tripal'),
-//     'type' => MENU_NORMAL_ITEM,
-//   );
-
   $items['admin/tripal/extension'] = array(
     'title' => 'Extensions',
     'description' => t("Configuration and management pages for Tripal extension modules."),
@@ -286,9 +275,20 @@ function tripal_menu() {
 //     'type' => MENU_NORMAL_ITEM,
 //   );
 
-  $items['cv/lookup/%/%'] = array(
+  $items['cv/lookup/%'] = array(
     'title' => 'Vocabulary Lookup',
-    'description' => t("Provides a tool to discover controlled vocabularies and their terms used by this site."),
+    'description' => t("Provides a tool to discover controlled vocabularies"),
+    'access arguments' => array('access content'),
+    'page callback' => 'tripal_vocabulary_lookup_page',
+    'page arguments' => array(2),
+    'file' => 'includes/tripal.term_lookup.inc',
+    'file path' => drupal_get_path('module', 'tripal'),
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['cv/lookup/%/%'] = array(
+    'title' => 'Vocabulary Term Lookup',
+    'description' => t("Provides a tool to discover controlled vocabularies terms used by this site."),
     'access arguments' => array('access content'),
     'page callback' => 'tripal_vocabulary_lookup_term_page',
     'page arguments' => array(2, 3),

+ 114 - 7
tripal_chado/includes/tripal_chado.semweb.inc

@@ -19,9 +19,11 @@ function tripal_chado_populate_chado_semweb_table() {
   // inserted.
 
   // Now set defaults!
+  tripal_chado_populate_vocab_DC();
   tripal_chado_populate_vocab_EDAM();
   tripal_chado_populate_vocab_ERO();
   tripal_chado_populate_vocab_FOAF();
+  tripal_chado_populate_vocab_HYDRA();
   tripal_chado_populate_vocab_IAO();
   tripal_chado_populate_vocab_LOCAL();
   tripal_chado_populate_vocab_NCBITAXON();
@@ -31,6 +33,7 @@ function tripal_chado_populate_chado_semweb_table() {
   tripal_chado_populate_vocab_SBO();
   tripal_chado_populate_vocab_SCHEMA();
   tripal_chado_populate_vocab_SIO();
+  tripal_chado_populate_vocab_SO();
   tripal_chado_populate_vocab_SWO();
   tripal_chado_populate_vocab_TAXRANK();
   tripal_chado_populate_vocab_TCONTACT();
@@ -55,7 +58,59 @@ function tripal_chado_populate_vocab_FOAF() {
     'Friend of a Friend. A dictionary of people-related terms that can be used in structured data).'
   );
 }
+/**
+ * Adds the Hydra vocabulary
+ */
+function tripal_chado_populate_vocab_HYDRA() {
+
+  tripal_insert_db(array(
+    'name' => 'hydra',
+    'description' => 'A Vocabulary for Hypermedia-Driven Web APIs',
+    'url' => 'https://www.hydra-cg.com/spec/latest/core/',
+    'urlprefix' => 'https://www.hydra-cg.com/spec/latest/core/#{db}:{accession}',
+  ));
+  tripal_insert_cv(
+    'hydra',
+    'A Vocabulary for Hypermedia-Driven Web APIs.'
+  );
 
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:Collection',
+    'name' => 'Collection',
+    'cv_name' => 'hydra',
+    'definition' => 'A collection holding references to a number of related resources.',
+  ));
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:member',
+    'name' => 'member',
+    'cv_name' => 'hydra',
+    'definition' => 'A member of the collection',
+  ));
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:description',
+    'name' => 'description',
+    'cv_name' => 'hydra',
+    'definition' => 'A description.',
+  ));
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:totalItems',
+    'name' => 'totalItems',
+    'cv_name' => 'hydra',
+    'definition' => 'The total number of items referenced by a collection.',
+  ));
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:title',
+    'name' => 'title',
+    'cv_name' => 'hydra',
+    'definition' => 'A title, often used along with a description.',
+  ));
+  $name = tripal_insert_cvterm(array(
+    'id' => 'hydra:PartialCollectionView',
+    'name' => 'PartialCollectionView',
+    'cv_name' => 'hydra',
+    'definition' => 'A PartialCollectionView describes a partial view of a Collection. Multiple PartialCollectionViews can be connected with the the next/previous properties to allow a client to retrieve all members of the collection.',
+  ));
+}
 /**
  * Adds the RDFS database and terms.
  */
@@ -64,7 +119,7 @@ function tripal_chado_populate_vocab_RDFS() {
     'name' => 'rdfs',
     'description' => 'Resource Description Framework Schema',
     'url' => 'https://www.w3.org/TR/rdf-schema/',
-    'urlprefix' => 'https://www.w3.org/TR/rdf-schema/#ch_',
+    'urlprefix' => 'https://www.w3.org/TR/rdf-schema/#ch_{accession}',
   ));
   tripal_insert_cv(
     'rdfs',
@@ -99,7 +154,6 @@ function tripal_chado_populate_vocab_SCHEMA() {
     'Schema.org. Schema.org is sponsored by Google, Microsoft, Yahoo and Yandex. The vocabularies are developed by an open community process.'
   );
 
-
   $term = tripal_insert_cvterm(array(
     'id' => 'schema:name',
     'name' => 'name',
@@ -172,6 +226,13 @@ function tripal_chado_populate_vocab_SCHEMA() {
       tripal_associate_chado_semweb_term($table, 'type_id', $term);
     }
   }
+
+  $term = tripal_insert_cvterm(array(
+    'id' => 'schema:ItemPage',
+    'name' => 'ItemPage',
+    'cv_name' => 'schema',
+    'definition' => 'A page devoted to a single item, such as a particular product or hotel.',
+  ));
 }
 
 /**
@@ -217,6 +278,41 @@ function tripal_chado_populate_vocab_SIO() {
   ));
   tripal_associate_chado_semweb_term('cvterm', 'cv_id', $term);
 }
+
+/**
+ * Adds the details for the SO vocab and db.
+ */
+function tripal_chado_populate_vocab_SO() {
+  tripal_insert_db(array(
+    'name' => 'SO',
+    'description' => 'The sequence ontology.',
+    'url' => 'http://www.sequenceontology.org/',
+    'urlprefix' => 'http://www.sequenceontology.org/browser/current_svn/term/{db}:{accession}',
+  ));
+  tripal_insert_cv('sequence', 'The sequence ontology.');
+}
+
+/**
+ * Adds the DC database.
+ */
+function tripal_chado_populate_vocab_DC() {
+  tripal_insert_db(array(
+    'name' => 'dc',
+    'description' => 'DCMI Metadata Terms.',
+    'url' => 'http://purl.org/dc/dcmitype/',
+    'urlprefix' => 'http://purl.org/dc/terms/{accession}',
+  ));
+  tripal_insert_cv(
+    'dc',
+    'DCMI Metadata Terms.'
+  );
+  $term = tripal_insert_cvterm(array(
+    'id' => 'dc:Service',
+    'name' => 'Service',
+    'cv_name' => 'dc',
+    'definition' => 'A system that provides one or more functions.',
+  ));
+}
 /**
  * Adds the EDAM database and terms.
  */
@@ -518,16 +614,19 @@ function tripal_chado_populate_vocab_IAO() {
  * ontology.
  */
 function tripal_chado_populate_vocab_LOCAL() {
+  global $base_path;
 
   tripal_insert_db(array(
     'name' => 'null',
-    'description' => 'No online database.'
+    'description' => 'No online database.',
+    'url' => $base_path . 'cv/lookup/null',
+    'urlprefix' => $base_path. 'cv/lookup/{db}/{accession}',
   ));
   tripal_insert_db(array(
     'name' => 'local',
     'description' => 'Terms created for this site.',
-    'url' => '/cv/lookup',
-    'urlprefix' => '/cv/lookup/{db}/{accession}',
+    'url' => $base_path . 'cv/lookup/local',
+    'urlprefix' => $base_path . 'cv/lookup/{db}/{accession}',
   ));
 
 
@@ -786,7 +885,7 @@ function tripal_chado_populate_vocab_LOCAL() {
   // TODO: these probably have real terms we can use.
 
   $term = tripal_insert_cvterm(array(
-    'id' => 'locak:rank',
+    'id' => 'local:rank',
     'name' => 'rank',
     'definition' => 'A taxonmic rank',
     'cv_name' => 'local',
@@ -1345,7 +1444,7 @@ function tripal_chado_populate_vocab_UO() {
     'url' => 'http://purl.obolibrary.org/obo/uo',
     'urlprefix' => 'http://purl.obolibrary.org/obo/TAXRANK_',
   ));
-  tripal_insert_cv('uo','Units of Measurement Ontology');
+  tripal_insert_cv('uo', 'Units of Measurement Ontology');
 
   $term = tripal_insert_cvterm(array(
     'id' => 'UO:0000000',
@@ -1361,6 +1460,14 @@ function tripal_chado_populate_vocab_UO() {
  */
 function tripal_chado_populate_vocab_TAXRANK() {
 
+  tripal_insert_db(array(
+    'name' => 'TAXRANK',
+    'description' => 'A vocabulary of taxonomic ranks (species, family, phylum, etc)',
+    'url' => 'http://www.obofoundry.org/ontology/taxrank.html',
+    'urlprefix' => 'http://purl.obolibrary.org/obo/{db}_{accession}',
+  ));
+  tripal_insert_cv('taxonomic_rank', 'A vocabulary of taxonomic ranks (species, family, phylum, etc)');
+
   $term = tripal_get_cvterm(array('id' => 'TAXRANK:0000005'));
   tripal_associate_chado_semweb_term('organism', 'genus', $term);
 

+ 27 - 0
tripal_chado/includes/tripal_chado.vocab_storage.inc

@@ -15,6 +15,33 @@ function tripal_chado_vocab_storage_info() {
     ),
   );
 }
+/**
+ * Implements hook_vocab_get_vocabulary().
+ *
+ * This hook is created by the Tripal module and is not a Drupal hook.
+ */
+function tripal_chado_vocab_get_vocabulary($vocabulary) {
+  // It's possible that Chado is not available (i.e. it gets renamed
+  // for copying) but Tripal has already been prepared and the
+  // entities exist.  If this is the case we don't want to run the
+  // commands below.
+  if (!chado_table_exists('cv')) {
+    return FALSE;
+  }
+  $sql = "
+     SELECT DB.name as name, CV.name as short_name, DB.description, DB.url
+     FROM {db} DB
+      INNER JOIN {dbxref} DBX on DBX.db_id = DB.db_id
+      INNER JOIN {cvterm} CVT on CVT.dbxref_id = DBX.dbxref_id
+      INNER JOIN {cv} CV on CV.cv_id = CVT.cv_id
+     WHERE
+      DB.name = :name
+     LIMIT 1 OFFSET 0
+  ";
+  $result = chado_query($sql, array(':name' => $vocabulary));
+  $result = $result->fetchAssoc();
+  return $result;
+}
 
 /**
  * Implements hook_vocab_get_term().

+ 122 - 0
tripal_chado/tripal_chado.install

@@ -873,4 +873,126 @@ function tripal_chado_update_7304() {
     $error = $e->getMessage();
     throw new DrupalUpdateException('Could not perform update: '. $error);
   }
+}
+/**
+ * Adding missing cv/db details and cvterms.
+ */
+function tripal_chado_update_7305() {
+  try {
+    tripal_insert_db(array(
+      'name' => 'rdfs',
+      'description' => 'Resource Description Framework Schema',
+      'url' => 'https://www.w3.org/TR/rdf-schema/',
+      'urlprefix' => 'https://www.w3.org/TR/rdf-schema/#ch_{accession}',
+    ));
+    tripal_insert_cv('rdfs', 'Resource Description Framework Schema');
+    tripal_insert_db(array(
+      'name' => 'SO',
+      'description' => 'The sequence ontology.',
+      'url' => 'http://www.sequenceontology.org/',
+      'urlprefix' => 'http://www.sequenceontology.org/browser/current_svn/term/{db}:{accession}',
+    ));
+    tripal_insert_cv('sequence', 'The sequence ontology.');
+    tripal_insert_db(array(
+      'name' => 'TAXRANK',
+      'description' => 'A vocabulary of taxonomic ranks (species, family, phylum, etc)',
+      'url' => 'http://www.obofoundry.org/ontology/taxrank.html',
+      'urlprefix' => 'http://purl.obolibrary.org/obo/{db}_{accession}',
+    ));
+    tripal_insert_cv('taxonomic_rank', 'A vocabulary of taxonomic ranks (species, family, phylum, etc)');
+    tripal_insert_db(array(
+      'name' => 'hydra',
+      'description' => 'A Vocabulary for Hypermedia-Driven Web APIs',
+      'url' => 'https://www.hydra-cg.com/spec/latest/core/',
+      'urlprefix' => 'https://www.hydra-cg.com/spec/latest/core/#{db}:{accession}',
+    ));
+    tripal_insert_cv(
+      'hydra',
+      'A Vocabulary for Hypermedia-Driven Web APIs.'
+    );
+    tripal_insert_db(array(
+      'name' => 'dc',
+      'description' => 'DCMI Metadata Terms.',
+      'url' => 'http://purl.org/dc/dcmitype/',
+      'urlprefix' => 'http://purl.org/dc/terms/{accession}',
+    ));
+    tripal_insert_cv(
+      'dc',
+      'DCMI Metadata Terms.'
+    );
+    $term = tripal_insert_cvterm(array(
+      'id' => 'dc:Service',
+      'name' => 'Service',
+      'cv_name' => 'dc',
+      'definition' => 'A system that provides one or more functions.',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:Collection',
+      'name' => 'Collection',
+      'cv_name' => 'hydra',
+      'definition' => 'A collection holding references to a number of related resources.',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:member',
+      'name' => 'member',
+      'cv_name' => 'hydra',
+      'definition' => 'A member of the collection',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:description',
+      'name' => 'description',
+      'cv_name' => 'hydra',
+      'definition' => 'A description.',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:totalItems',
+      'name' => 'totalItems',
+      'cv_name' => 'hydra',
+      'definition' => 'The total number of items referenced by a collection.',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:title',
+      'name' => 'title',
+      'cv_name' => 'hydra',
+      'definition' => 'A title, often used along with a description.',
+    ));
+    $name = tripal_insert_cvterm(array(
+      'id' => 'hydra:PartialCollectionView',
+      'name' => 'PartialCollectionView',
+      'cv_name' => 'hydra',
+      'definition' => 'A PartialCollectionView describes a partial view of a Collection. Multiple PartialCollectionViews can be connected with the the next/previous properties to allow a client to retrieve all members of the collection.',
+    ));
+    $term = tripal_insert_cvterm(array(
+      'id' => 'schema:ItemPage',
+      'name' => 'ItemPage',
+      'cv_name' => 'schema',
+      'definition' => 'A page devoted to a single item, such as a particular product or hotel.',
+    ));
+    global $base_path;
+
+    tripal_insert_db(array(
+      'name' => 'null',
+      'description' => 'No online database.',
+      'url' => $base_path . 'cv/lookup/null',
+      'urlprefix' => $base_path. 'cv/lookup/{db}/{accession}',
+    ));
+    tripal_insert_db(array(
+      'name' => 'local',
+      'description' => 'Terms created for this site.',
+      'url' => $base_path . 'cv/lookup/local',
+      'urlprefix' => $base_path . 'cv/lookup/{db}/{accession}',
+    ));
+    $term = tripal_insert_cvterm(array(
+      'id' => 'local:rank',
+      'name' => 'rank',
+      'definition' => 'A taxonmic rank',
+      'cv_name' => 'local',
+    ));
+
+  }
+  catch (\PDOException $e) {
+    $transaction->rollback();
+    $error = $e->getMessage();
+    throw new DrupalUpdateException('Could not perform update: '. $error);
+  }
 }

+ 2 - 1
tripal_ws/includes/TripalWebService.inc

@@ -250,7 +250,8 @@ class TripalWebService {
    *   The error message to report.
    */
   public function setError($message) {
-    $this->resource = new TripalWebServiceResource();
+    $this->resource = new TripalWebServiceResource($this->base_path);
+    $this->resource->setID('error');
     $this->resource->addContextItem('error', 'rdfs:error');
     $this->resource->addProperty('error', $message);
   }

+ 84 - 73
tripal_ws/includes/TripalWebService/TripalEntityService_v0_1.inc

@@ -91,7 +91,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
     field_attach_load($entity->type, array($entity->id => $entity),
         FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
 
-    $this->addEntityField($key_adj, $entity, $field, $instance, $service_path, $expfield);
+    $this->addEntityField($key_adj, $term, $entity, $bundle, $field, $instance, $service_path, $expfield);
   }
 
   /**
@@ -127,19 +127,20 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $vocab = $term->vocab;
 
     // Add the vocabulary to the context.
-    $this->resource->addContextItem($vocab->vocabulary, $term->urlprefix);
     $this->resource->addContextItem($term->name, $term->url);
 
     // Get the TripalEntity
     $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
     $entity = reset($entity);
 
+    $itemPage = tripal_get_term_details('schema', 'ItemPage');
+    $label = tripal_get_term_details('rdfs', 'label');
     $this->resource->setID($entity_id);
     $this->resource->setType($term->name);
-    $this->resource->addContextItem('label', 'rdfs:label');
-    $this->resource->addContextItem('itemPage', 'schema:itemPage');
+    $this->resource->addContextItem('label', $label['url']);
+    $this->resource->addContextItem('ItemPage', $itemPage['url']);
     $this->resource->addProperty('label', $entity->title);
-    $this->resource->addProperty('itemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
+    $this->resource->addProperty('ItemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
 
     $this->addEntityFields($entity, $bundle, $term, $service_path);
 
@@ -152,6 +153,10 @@ class TripalEntityService_v0_1 extends TripalWebService {
    */
   private function addEntityFields($entity, $bundle, $term, $service_path) {
 
+    // If the entity is set to hide fields that have no values then we
+    // want to honor that in the web services too.
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+
     // Get information about the fields attached to this bundle and sort them
     // in the order they were set for the display.
     $instances = field_info_instances('TripalEntity', $bundle->name);
@@ -185,56 +190,49 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $vocabulary = $instance['settings']['term_vocabulary'];
       $accession = $instance['settings']['term_accession'];
       $term = tripal_get_term_details($vocabulary, $accession);
-      if ($term) {
-        $key = $term['name'];
-        $key_adj = strtolower(preg_replace('/ /', '_', $key));
-        // The term schema:url also points to a recource so we need
-        // to make sure we set the type to be '@id'.
-        if ($vocabulary == 'schema' and $accession == 'url') {
-          $this->resource->addContextItem($key_adj, array(
-            '@id' => $term['url'],
-            '@type' => '@id',
-          ));
-        }
-        else {
-          $this->resource->addContextItem($key_adj, $term['url']);
-        }
-      }
-      else {
+      if (!$term) {
         continue;
       }
+      $key = $term['name'];
+      $key_adj = strtolower(preg_replace('/ /', '_', $key));
 
       // If this field should not be attached by default then just add a link
       // so that the caller can get the information separately.
       $instance_settings = $instance['settings'];
       if (array_key_exists('auto_attach', $instance['settings']) and
           $instance_settings['auto_attach'] == FALSE) {
-        $this->resource->addContextItem($key_adj, array(
-          '@id' => '', //$response['@context'][$key_adj],
-          '@type' => '@id'
-        ));
         // Add a URL only if there are values. If there are no values then
         // don't add a URL which would make the end-user think they can get
         // that information.
         $items = field_get_items('TripalEntity', $entity, $field_name);
         if ($items and count($items) > 0 and $items[0]['value']) {
-          $this->resource->addProperty($key_adj, $service_path . '/' . $entity->id . '/' . urlencode($key));
+          $this->resource->addContextItem($key_adj, $term['url']);
+          $this->resource->addProperty($key_adj, $service_path . '/' . $entity->id . '/' . urlencode($term['name']));
         }
         else {
-          $this->resource->addProperty($key_adj, NULL);
+          if ($hide_fields == 'show') {
+            $this->resource->addContextItem($key_adj, $term['url']);
+            $this->resource->addProperty($key_adj, NULL);
+          }
         }
         continue;
       }
 
       // Get the details for this field for the JSON-LD response.
-      $this->addEntityField($key_adj, $entity, $field, $instance, $service_path);
+      $this->addEntityField($key_adj, $term, $entity, $bundle, $field, $instance, $service_path);
     }
   }
 
   /**
    * Adds the field as a property of the entity resource.
    */
-  private function addEntityField($key, $entity, $field, $instance, $service_path, $expfield = NULL) {
+  private function addEntityField($key, $term, $entity, $bundle, $field, $instance,
+      $service_path, $expfield = NULL) {
+
+    // If the entity is set to hide fields that have no values then we
+    // want to honor that in the web services too.
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
+
     // Get the field  settings.
     $field_name = $field['field_name'];
     $field_settings = $field['settings'];
@@ -252,11 +250,16 @@ class TripalEntityService_v0_1 extends TripalWebService {
 
     $values = array();
     for ($i = 0; $i < count($items); $i++) {
-      $values[$i] = $this->sanitizeFieldKeys($items[$i]['value'], $service_path);
+      $values[$i] = $this->sanitizeFieldKeys($items[$i]['value'], $bundle, $service_path);
+    }
+
+    if ($hide_fields == 'hide' and empty($values[0])) {
+      return;
     }
 
     // If the field cardinality is 1
-    if ($field[cardinality] == 1) {
+    if ($field['cardinality'] == 1) {
+
       // If the value is an array and this is the field page then all of those
       // key/value pairs should be added directly to the response.
       if (is_array($values[0])) {
@@ -266,41 +269,46 @@ class TripalEntityService_v0_1 extends TripalWebService {
           }
         }
         else {
+          $this->resource->addContextItem($key, $term['url']);
           $this->resource->addProperty($key, $values[0]);
         }
       }
       // If the value is not an array it's a scalar so add it as is to the
       // response.
       else {
+        $this->resource->addContextItem($key, $term['url']);
         $this->resource->addProperty($key, $values[0]);
       }
     }
 
     // If the field cardinality is > 1
-    if ($field[cardinality] != 1) {
+    if ($field['cardinality'] != 1) {
 
       // If this is the expanded field page then we need to swap out
       // the resource for a collection.
-      if ($expfield) {
-        $this->resource = new TripalWebServiceCollection($service_path . '/' . urlencode($expfield));
-        $this->resource->addContextItem('label', 'rdfs:label');
-        $this->resource->addProperty('label', $instance['label']);
-        foreach ($values as $delta => $element) {
-          $member = new TripalWebServiceResource($service_path);
-          foreach ($element as $key => $value) {
-            $member->addContextItem($key, '');
-            $member->addProperty($key, $value);
-          }
-          $this->resource->addMember($member);
+      $response = new TripalWebServiceCollection($service_path . '/' . urlencode($expfield));
+      $label = tripal_get_term_details('rdfs', 'label');
+      $response->addContextItem('label', $label['url']);
+      $response->addProperty('label', $instance['label']);
+      $i = 0;
+      foreach ($values as $delta => $element) {
+        $member = new TripalWebServiceResource($service_path . '/' . urlencode($expfield));
+        $member->setID($i);
+        // Add the context of the parent resource because all of the keys
+        // were santizied and set to match the proper context.
+        $member->setContext($this->resource);
+        $member->setType($key);
+        foreach ($element as $key => $value) {
+          $member->addProperty($key, $value);
         }
+        $response->addMember($member);
+        $i++;
+      }
+      if ($expfield) {
+        $this->resource = $response;
       }
       else {
-        $response[$key] = array(
-          '@type' => 'Collection',
-          'totalItems' => count($values),
-          'label' => $instance['label'],
-          'member' => $values,
-        );
+        $this->resource->addProperty($key, $response);
       }
     }
   }
@@ -308,7 +316,10 @@ class TripalEntityService_v0_1 extends TripalWebService {
   /**
    * Rewrites the keys of a field's items array for use with web services.
    */
-  private function sanitizeFieldKeys($value, $service_path) {
+  private function sanitizeFieldKeys($value, $bundle, $service_path) {
+    // If the entity is set to hide fields that have no values then we
+    // want to honor that in the web services too.
+    $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
 
     $new_value = '';
     // If the value is an array rather than a scalar then map the sub elements
@@ -316,32 +327,29 @@ class TripalEntityService_v0_1 extends TripalWebService {
     if (is_array($value)) {
       $temp = array();
       foreach ($value as $k => $v) {
+        // exclude fields that have no values so we can hide them
+        if (empty($v) and $hide_fields == 'hide') {
+          continue;
+        }
         $matches = array();
         if (preg_match('/^(.+):(.+)$/', $k, $matches)) {
           $vocabulary = $matches[1];
           $accession = $matches[2];
           $term = tripal_get_term_details($vocabulary, $accession);
+
           $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
           if (is_array($v)) {
-            $temp[$key_adj] = $this->sanitizeFieldKeys($v, $service_path);
+            $temp[$key_adj] = $this->sanitizeFieldKeys($v, $bundle, $service_path);
           }
           else {
             $temp[$key_adj] = $v !== "" ? $v : NULL;
           }
-          // The term schema:url also points to a recource so we need
-          // to make sure we set the type to be '@id'.
-          if ($vocabulary == 'schema' and $accession == 'url') {
-            $this->resource->addContextItem($key_adj, array(
-              '@id' => $term['url'],
-              '@type' => '@id',
-            ));
-          }
-          else {
-            $this->resource->addContextItem($key_adj, $term['url']);
-          }
+          $this->resource->addContextItem($key_adj, $term['url']);
+
         }
         else {
-          $temp[$k] = $v;
+          // TODO: this is an error, if we get here then we have
+          // a key that isn't using the proper format... what to do?
         }
       }
       $new_value = $temp;
@@ -368,7 +376,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
     }
     foreach ($items as $key => $value) {
       if (is_array($value)) {
-        $this->sanitizeFieldEntity($items[$key], $response, $api_url);
+        $this->sanitizeFieldEntity($items[$key], $service_path);
         continue;
       }
 
@@ -378,7 +386,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
           $item_entity = tripal_load_entity($item_etype, array($item_eid));
           $item_entity = reset($item_entity);
           $bundle = tripal_load_bundle_entity(array('name' => $item_entity->bundle));
-          $items['@id'] = url($api_url . '/content/' . $bundle->label . '/' . $item_eid, array('absolute' => TRUE));
+          $items['@id'] = $this->getServicePath() . '/' . urlencode($bundle->label) . '/' . $item_eid;
         }
         unset($items['entity']);
       }
@@ -390,8 +398,9 @@ class TripalEntityService_v0_1 extends TripalWebService {
    */
   private function doContentTypeList($ctype) {
     $service_path = $this->getServicePath() . '/' . urlencode($ctype);
+    $label = tripal_get_term_details('rdfs', 'label');
     $this->resource = new TripalWebServiceCollection($service_path);
-    $this->resource->addContextItem('label', 'rdfs:label');
+    $this->resource->addContextItem('label', $label['url']);
 
     // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
@@ -567,8 +576,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
     // Now perform the query.
     $results = $query->execute();
 
-    //$this->resource->initPager($num_records, $params['limit']);
-    $this->resource->initPager($num_records, $limit);
+    $this->resource->initPager($num_records, $limit, $page);
 
     // Iterate through the entities and add them to the list.
     foreach ($results['TripalEntity'] as $entity_id => $stub) {
@@ -582,14 +590,16 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $query->condition('TE.id', $entity_id);
       $entity = $query->execute()->fetchObject();
 
+      $itemPage = tripal_get_term_details('schema', 'ItemPage');
+      $label = tripal_get_term_details('rdfs', 'label');
       $member = new TripalWebServiceResource($service_path);
-      $member->addContextItem('label', 'rdfs:label');
-      $member->addContextItem('itemPage', 'schema:itemPage');
+      $member->addContextItem('label', $label['url']);
+      $member->addContextItem('ItemPage', $itemPage['url']);
       $member->addContextItem($term->name, $term->url);
       $member->setID($entity->id);
       $member->setType($term->name);
       $member->addProperty('label', $entity->title);
-      $member->addProperty('itemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
+      $member->addProperty('ItemPage', url('/bio_data/' . $entity->id, array('absolute' => TRUE)));
       $this->resource->addMember($member);
     }
   }
@@ -599,8 +609,9 @@ class TripalEntityService_v0_1 extends TripalWebService {
    */
   private function doAllTypesList() {
     $service_path = $this->getServicePath();
+    $label = tripal_get_term_details('rdfs', 'label');
     $this->resource = new TripalWebServiceCollection($service_path);
-    $this->resource->addContextItem('label', 'rdfs:label');
+    $this->resource->addContextItem('label', $label['url']);
     $this->resource->addProperty('label', 'Content Types');
 
     // Get the list of published terms (these are the bundle IDs)
@@ -624,7 +635,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
       }
       $member = new TripalWebServiceResource($service_path);
       $member->addContextItem($term->name, $term->url);
-      $member->addContextItem('label', 'rdfs:label');
+      $member->addContextItem('label', $label['url']);
       $member->addContextItem('description', 'hydra:description');
       $member->setID(urlencode($bundle->label));
       $member->setType($term->name);

+ 15 - 7
tripal_ws/includes/TripalWebServiceCollection.inc

@@ -26,6 +26,11 @@ class TripalWebServiceCollection extends TripalWebServiceResource {
    */
   protected $itemsPerPage;
 
+  /**
+   * The current page of the pager.
+   */
+  protected $page;
+
 
   /**
    * Implements the constructor.
@@ -36,9 +41,12 @@ class TripalWebServiceCollection extends TripalWebServiceResource {
   public function __construct($service_path) {
     parent::__construct($service_path);
     $this->members = array();
-    $this->addContextItem('Collection', "hydra:Collection");
-    $this->addContextItem('totalItems', "hydra:totalItems");
-    $this->addContextItem('member', "member");
+    $term = tripal_get_term_details('hydra', 'Collection');
+    $this->addContextItem('Collection', $term['url']);
+    $term = tripal_get_term_details('hydra', 'totalItems');
+    $this->addContextItem('totalItems', $term['url']);
+    $term = tripal_get_term_details('hydra', 'member');
+    $this->addContextItem('member', $term['url']);
     parent::setType('Collection');
 
     // If the totalItems is set to -1 then this means paging is turned off and
@@ -58,10 +66,11 @@ class TripalWebServiceCollection extends TripalWebServiceResource {
    * @param $path
    *   The path
    */
-  public function initPager($totalItems, $itemsPerPage, $path) {
+  public function initPager($totalItems, $itemsPerPage, $page) {
     $this->doPaging = TRUE;
     $this->totalItems = $totalItems;
     $this->itemsPerPage = $itemsPerPage;
+    $this->page = $page;
   }
 
   /**
@@ -104,7 +113,7 @@ class TripalWebServiceCollection extends TripalWebServiceResource {
 
       $data['totalItems'] = $this->totalItems;
       $total_pages = ceil($this->totalItems / $this->itemsPerPage);
-      $page = array_key_exists('page', $this->params) ? $this->params['page'] : 1;
+      $page = $this->page;
       $limit = $this->itemsPerPage;
 
       if ($this->totalItems > 0) {
@@ -123,10 +132,9 @@ class TripalWebServiceCollection extends TripalWebServiceResource {
           $data['view']['next'] = $this->service_path . '?' . implode('&', array("page=$next", "limit=$limit"));
         }
       }
-
     }
     else {
-      $data['totalItems'] = count($member_data);
+      $data['totalItems'] = count($this->members);
     }
 
     $member_data = array();

+ 24 - 4
tripal_ws/includes/TripalWebServiceResource.inc

@@ -42,10 +42,17 @@ class TripalWebServiceResource {
 
     // First, add the RDFS and Hydra vocabularies to the context.  All Tripal
     // web services should use these.
-    $this->addContextItem('rdfs', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#');
-    $this->addContextItem('hydra', 'http://www.w3.org/ns/hydra/core#');
-    $this->addContextItem('dc', 'http://purl.org/dc/dcmitype/');
-    $this->addContextItem('schema', 'https://schema.org/');
+    $vocab = tripal_get_vocabulary_details('rdfs');
+    $this->addContextItem('rdfs', $vocab['url']);
+
+    $vocab = tripal_get_vocabulary_details('hydra');
+    $this->addContextItem('hydra', $vocab['url']);
+
+    $vocab = tripal_get_vocabulary_details('dc');
+    $this->addContextItem('dc', $vocab['url']);
+
+    $vocab = tripal_get_vocabulary_details('schema');
+    $this->addContextItem('schema', $vocab['url']);
 
     $this->data['@id'] = $service_path;
     $this->data['@type'] = '';
@@ -228,4 +235,17 @@ class TripalWebServiceResource {
       return $this->context;
   }
 
+  /**
+   * Copies the context from a given TripalWebService resource to this
+   * resource.
+   *
+   * @param $resource
+   */
+  public function setContext($resource) {
+    if (!is_a($resource, 'TripalWebServiceResource')) {
+      throw new Exception("The \$resource argument must be an instance of a TripalWebServiceResource.");
+    }
+    $this->context = $resource->getContext();
+  }
+
 }

+ 1 - 1
tripal_ws/tripal_ws.module

@@ -305,7 +305,7 @@ function tripal_ws_load_remote_entity($site_id, $api_version, $ctype, $id) {
   drupal_set_title($response['label']);
 
   // Attribute this data to the proper source.
-  $source_url = l($response['label'], $response['itemPage'], array('attributes' => array('target' => '_blank')));
+  $source_url = l($response['label'], $response['ItemPage'], array('attributes' => array('target' => '_blank')));
   $content = '<div><strong>Source:</strong> ' . $site->name . ': ' . $source_url . '</div>';
 
   // Fake an entity so we can display this content using the same