Browse Source

Basic web services for TripalEntity entities with JSON+LD, Hydra is working

Stephen Ficklin 9 years ago
parent
commit
12d200ca06

+ 21 - 1
tripal_chado/includes/tripal_chado.entity.inc

@@ -96,7 +96,7 @@ function tripal_chado_entity_update($entity, $type) { }
  *
  * Implements hook_entity_delete().
  */
-function tripal_chaddo_entity_delete($entity, $type) {
+function tripal_chado_entity_delete($entity, $type) {
   $record = db_select('chado_entity', 'ce')
     ->fields('ce', array('chado_entity_id', 'data_table', 'record_id'))
     ->condition('entity_id', $entity->id)
@@ -145,3 +145,23 @@ function tripal_chado_entity_access($op, $entity = NULL, $account = NULL) {
   }
   return FALSE;
 }
+
+/**
+ * Implements hook_tripal_default_title_format().
+ */
+function tripal_chado_tripal_default_title_format($entity, $available_tokens) {
+  $format = array();
+
+  // Load the term associated with this Tripal Content type.
+  $term = entity_load('TripalTerm', array('id' => $entity->term_id));
+  $term = reset($term);
+
+  // For organism titles  we want the genus and species with no comma separation.
+  if ($term->name == 'organism') {
+    $format[] = array(
+      'format' => '[organism__genus]-[organism__species]',
+      'weight' => -5
+    );
+  }
+  return $format;
+}

+ 119 - 84
tripal_chado/tripal_chado.module

@@ -80,7 +80,8 @@ function tripal_chado_field_info() {
       'description' => t('This record may have any number of properties. Use
           this field to first add the type.'),
       'default_widget' => 'tripal_chado_kvproperty_adder_widget',
-      'default_formatter' => 'tripal_chado_kvproperty_adder_formatter',
+      //'default_formatter' => 'tripal_chado_kvproperty_adder_formatter',
+      'default_formatter' => 'hidden',
       'settings' => array(),
       'storage' => array(
         'type' => 'field_chado_storage',
@@ -196,16 +197,6 @@ function tripal_chado_field_formatter_info() {
  * @param $field
  */
 function hook_chado_field_alter(&$field) {
-  // TODO: add example code for how to use this hook.
-}
-
-/**
- * Implements hook_chado_field_alter().
- *
- * This function adds the custom formatters and widgets to many of the Chado
- * tables.  This way Tripal users get a nice set of already usable fields.
- */
-function tripal_chado_chado_field_alter(&$field) {
 
   if (!array_key_exists('field_settings', $field)) {
     return;
@@ -217,49 +208,6 @@ function tripal_chado_chado_field_alter(&$field) {
   }
   // Here we provide new field types and widgets for FK fields
   // and fields that need special attention.
-  if ($field['field_settings']['chado_column'] =='organism_id') {
-    $field['field_type'] = 'organism_id';
-    $field['widget_type'] = 'tripal_chado_organism_select_widget';
-    $field['label'] = 'Organism';
-    $field['description'] = 'Select an organism.';
-  }
-  else if ($field['field_settings']['chado_column'] =='dbxref_id') {
-    $field['field_type'] = 'dbxref_id';
-    $field['widget_type'] = 'tripal_chado_primary_dbxref_widget';
-    $field['label'] = 'Primary Cross Reference';;
-    $field['description'] = 'This record can be cross-referenced with a
-    record in another online database. The primary reference is for the
-    most prominent reference.  At a minimum, the database and accession
-    must be provided.  To remove a set reference, change the database
-    field to "Select a Database".';
-  }
-  else if ($field['field_settings']['chado_table'] == 'feature' and
-    $field['field_settings']['chado_column'] == 'md5checksum') {
-    $field['field_type'] = 'md5checksum';
-    $field['widget_type'] = 'tripal_chado_md5checksum_checkbox_widget';
-    $field['label'] = 'MD5 Checksum';
-    $field['description'] = 'Generating MD5 checksum for the sequence.';
-  }
-  else if ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'seqlen') {
-    $field['field_type'] = 'seqlen';
-    $field['widget_type'] = 'tripal_chado_seqlen_hidden_widget';
-    $field['label'] = 'Seqlen';
-    $field['description'] = 'The length of the residues.';
-  }
-  else if ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'residues') {
-    $field['field_type'] = 'residues';
-    $field['widget_type'] = 'tripal_chado_residues_textarea_widget';
-    $field['label'] = 'Residues';
-    $field['description'] = 'Please provide an IUPAC compatible residues for this feature. Spaces and new lines are allowed.';
-  }
-  else if ($field['label'] == 'Timeaccessioned') {
-    $field['label'] = 'Time Accessioned';
-    $field['description'] = 'Please enter the time that this record was first added to the database.';
-  }
-  else if ($field['label'] == 'Timelastmodified') {
-    $field['label'] = 'Time Last Modified';
-    $field['description'] = 'Please enter the time that this record was last modified. The default is the current time.';
-  }
 }
 
 /**
@@ -705,14 +653,25 @@ function tripal_chado_get_table_column_field_default($table_name, $schema, $colu
   $details = $schema['fields'][$column_name];
 
   // Create an array with information about this field.
-  $field_info = array(
+  $field = array(
     'field_type' => '',
     'widget_type' => '',
     'field_settings' => array(
       'chado_table' => $table_name,
       'chado_column' => $column_name,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+    'widget_settings' => array(
+      'display_label' => 1
     ),
-    'widget_settings' => array('display_label' => 1),
     'description' => '',
     'label' => ucwords(preg_replace('/_/', ' ', $column_name)),
     'is_required' => 0,
@@ -721,60 +680,136 @@ function tripal_chado_get_table_column_field_default($table_name, $schema, $colu
   // Alter the field info array depending on the column details.
   switch($details['type']) {
     case 'char':
-      $field_info['field_type'] = 'text';
-      $field_info['widget_type'] = 'text_textfield';
-      $field_info['field_settings']['max_length'] = $details['length'];
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textfield';
+      $field['field_settings']['max_length'] = $details['length'];
       break;
     case 'varchar':
-      $field_info['field_type'] = 'text';
-      $field_info['widget_type'] = 'text_textfield';
-      $field_info['field_settings']['max_length'] = $details['length'];
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textfield';
+      $field['field_settings']['max_length'] = $details['length'];
       break;
     case 'text':
-      $field_info['field_type'] = 'text';
-      $field_info['widget_type'] = 'text_textarea';
-      $field_info['field_settings']['max_length'] = 17179869184;
-      $field_info['field_settings']['text_processing'] = 1;
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textarea';
+      $field['field_settings']['max_length'] = 17179869184;
+      $field['field_settings']['text_processing'] = 1;
       break;
     case 'blob':
       // not sure how to support a blob field.
       continue;
       break;
     case 'int':
-      $field_info['field_type'] = 'number_integer';
-      $field_info['widget_type'] = 'number';
+      $field['field_type'] = 'number_integer';
+      $field['widget_type'] = 'number';
       break;
     case 'float':
-      $field_info['field_type'] = 'number_float';
-      $field_info['widget_type'] = 'number';
-      $field_info['field_settings']['precision'] = 10;
-      $field_info['field_settings']['scale'] = 2;
-      $field_info['field_settings']['decimal_separator'] = '.';
+      $field['field_type'] = 'number_float';
+      $field['widget_type'] = 'number';
+      $field['field_settings']['precision'] = 10;
+      $field['field_settings']['scale'] = 2;
+      $field['field_settings']['decimal_separator'] = '.';
       break;
     case 'numeric':
-      $field_info['field_type'] = 'number_decimal';
-      $field_info['widget_type'] = 'number';
+      $field['field_type'] = 'number_decimal';
+      $field['widget_type'] = 'number';
       break;
     case 'serial':
       // Serial fields are most likely not needed as a field.
       break;
     case 'boolean':
-      $field_info['field_type'] = 'list_boolean';
-      $field_info['widget_type'] = 'options_onoff';
-      $field_info['field_settings']['allowed_values'] = array(0 => "No", 1 => "Yes");
+      $field['field_type'] = 'list_boolean';
+      $field['widget_type'] = 'options_onoff';
+      $field['field_settings']['allowed_values'] = array(0 => "No", 1 => "Yes");
       break;
     case 'datetime':
       // Use the Drupal Date and Date API to create the field/widget
-      $field_info['field_type'] = 'datetime';
-      $field_info['widget_type'] = 'date_select';
-      $field_info['widget_settings']['increment'] = 1;
-      $field_info['widget_settings']['tz_handling'] = 'none';
-      $field_info['widget_settings']['collapsible'] = TRUE;
+      $field['field_type'] = 'datetime';
+      $field['widget_type'] = 'date_select';
+      $field['widget_settings']['increment'] = 1;
+      $field['widget_settings']['tz_handling'] = 'none';
+      $field['widget_settings']['collapsible'] = TRUE;
 
       // TODO: Add settings so that the minutes increment by 1.
       // And turn off the timezone, as the Chado field doesn't support it.
       break;
   }
 
-  return $field_info;
+  // Set some default semantic web information
+  if ($column_name == 'name') {
+    $field['field_settings']['semantic_web']['type'] = 'name';
+    $field['field_settings']['semantic_web']['ns'] = 'foaf';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
+  }
+  if ($column_name == 'description' or $column_name == 'definition' or
+      $column_name == 'comment') {
+    $field['field_settings']['semantic_web']['type'] = 'description';
+    $field['field_settings']['semantic_web']['ns'] = 'hydra';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://www.w3.org/ns/hydra/core#';
+  }
+
+  //
+  // GENERIC COLUMNS
+  //
+  if ($field['field_settings']['chado_column'] =='organism_id') {
+    $field['field_type'] = 'organism_id';
+    $field['widget_type'] = 'tripal_chado_organism_select_widget';
+    $field['label'] = 'Organism';
+    $field['description'] = 'Select an organism.';
+  }
+  elseif ($field['field_settings']['chado_column'] =='dbxref_id') {
+    $field['field_type'] = 'dbxref_id';
+    $field['widget_type'] = 'tripal_chado_primary_dbxref_widget';
+    $field['label'] = 'Primary Cross Reference';;
+    $field['description'] = 'This record can be cross-referenced with a ' .
+      'record in another online database. The primary reference is for the ' .
+      'most prominent reference.  At a minimum, the database and accession ' .
+      'must be provided.  To remove a set reference, change the database ' .
+      'field to "Select a Database".';
+  }
+  elseif ($field['label'] == 'Timeaccessioned') {
+    $field['label'] = 'Time Accessioned';
+    $field['description'] = 'Please enter the time that this record was first added to the database.';
+  }
+  elseif ($field['label'] == 'Timelastmodified') {
+    $field['label'] = 'Time Last Modified';
+    $field['description'] = 'Please enter the time that this record was last modified. The default is the current time.';
+  }
+  //
+  // ORGANISM TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'organism' and $field['field_settings']['chado_column'] == 'comment') {
+    $field['label'] = 'Description';
+  }
+  //
+  // FEATURE TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'uniquename') {
+    $field['field_type'] = 'text';
+    $field['widget_type'] = 'text_textfield';
+    $field['field_settings']['text_processing'] = 0;
+    $field['field_settings']['semantic_web']['type'] = 'name';
+    $field['field_settings']['semantic_web']['ns'] = 'foaf';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'md5checksum') {
+    $field['field_type'] = 'md5checksum';
+    $field['widget_type'] = 'tripal_chado_md5checksum_checkbox_widget';
+    $field['label'] = 'MD5 Checksum';
+    $field['description'] = 'Generating MD5 checksum for the sequence.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'seqlen') {
+    $field['field_type'] = 'seqlen';
+    $field['widget_type'] = 'tripal_chado_seqlen_hidden_widget';
+    $field['label'] = 'Seqlen';
+    $field['description'] = 'The length of the residues.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'residues') {
+    $field['field_type'] = 'residues';
+    $field['widget_type'] = 'tripal_chado_residues_textarea_widget';
+    $field['label'] = 'Residues';
+    $field['description'] = 'Please provide an IUPAC compatible residues for this feature. Spaces and new lines are allowed.';
+  }
+
+  return $field;
 }

+ 2 - 14
tripal_entities/api/tripal_entities.api.inc

@@ -195,13 +195,6 @@ function tripal_get_bundle_variable($variable_name, $bundle_id, $default = FALSE
 
   // Warn if we can't find the tripal_variable.
   if (!$variable) {
-//     tripal_report_error(
-//       'trpbundle_var',
-//       TRIPAL_WARNING,
-//       'Unable to fetch tripal bundle variable value due to missing tripal variable (%var).',
-//       array('%var' => $variable_name)
-//     );
-//     return FALSE;
     return $default;
   }
 
@@ -215,12 +208,6 @@ function tripal_get_bundle_variable($variable_name, $bundle_id, $default = FALSE
 
   // Warn if the value appears to be empty.
   if (!$value) {
-//     tripal_report_error(
-//       'trpbundle_var',
-//       TRIPAL_WARNING,
-//       'Unable to fetch tripal bundle variable (%var) value.',
-//       array('%var' => $variable_name)
-//     );
     return $default;
   }
 
@@ -645,5 +632,6 @@ function hook_vocab_select_term_form_validate($form, &$form_state) {
  *   cannot be found.
  */
 function hook_vocab_get_term($namespace, $accession) {
-
+  // See the tripal_chado_vocab_get_term() function for an example.
 }
+

+ 12 - 12
tripal_entities/includes/TripalBundleUIController.inc

@@ -198,7 +198,7 @@ function tripal_entities_tripal_bundle_form($form, &$form_state, $entityDataType
   //-------------------------
   $url_pattern = tripal_get_bundle_variable('url_format', $entity_type->id, '');
   if (!$url_pattern) $url_pattern = str_replace(' ', '', $term->name) . '/[TripalEntity__entity_id]';
-  
+
   $form['url'] = array(
     '#type' => 'fieldset',
     '#title' => t('URL Path options'),
@@ -207,12 +207,12 @@ function tripal_entities_tripal_bundle_form($form, &$form_state, $entityDataType
     '#tree' => TRUE,
     '#group' => 'additional_settings',
   );
-  
+
   $form['url']['explanation'] = array(
     '#type' => 'item',
-    '#markup' => t('<p>The pattern below is used to specify the URL of %type content pages. 
+    '#markup' => t('<p>The pattern below is used to specify the URL of %type content pages.
     This allows you to present more friendly, informative URLs to your user.</p>
-    <p><strong>You must choose a combination of tokens that results in a unique path for 
+    <p><strong>You must choose a combination of tokens that results in a unique path for
     each page!</strong></p>',
     array('%type' => $entity_type->label)),
   );
@@ -220,14 +220,14 @@ function tripal_entities_tripal_bundle_form($form, &$form_state, $entityDataType
   $form['url']['url_pattern'] = array(
     '#type' => 'textarea',
     '#title' => t('URL Alias Pattern'),
-    '#description' => t('You may rearrange elements in this text box to customize the url 
-      alias. The available tokens are listed below. <strong>Make sure the pattern forms a 
+    '#description' => t('You may rearrange elements in this text box to customize the url
+      alias. The available tokens are listed below. <strong>Make sure the pattern forms a
       valid, unique URL</strong>. Leave this field blank to use the original path.'),
     '#default_value' => $url_pattern,
     '#required' => TRUE,
     '#rows' => 1
   );
-  
+
   $tokens = tripal_get_tokens($entity_type, array('required only' => TRUE));
   $form['url']['tokens'] = array(
     '#type' => 'hidden',
@@ -292,7 +292,7 @@ function tripal_entities_tripal_bundle_form_validate($form, $form_state) {
       array('%type' => $form_state['build_info']['args'][0]->label));
     form_set_error('set_titles][title_format', $msg);
   }
-  
+
   // PART 2: URL Alias'
   if ($form_state['values']['url']['url_pattern']) {
     $tokens_available = unserialize($form_state['values']['url']['tokens']);
@@ -322,11 +322,11 @@ function tripal_entities_tripal_bundle_form_validate($form, $form_state) {
  * Submit: Tripal content type edit form.
  */
 function tripal_entities_tripal_bundle_form_submit($form, &$form_state) {
-  
+
   $bundle_entity = $form_state['build_info']['args'][0];
-  
+
   if ($form_state['triggering_element']['#value'] == 'Save Content Type') {
-    
+
     // Save the label.
     $bundle_entity->label = $form_state['values']['label'];
     $bundle_entity->save();
@@ -339,7 +339,7 @@ function tripal_entities_tripal_bundle_form_submit($form, &$form_state) {
       $bundle_entity,
       $form_state['values']['set_titles']['title_format']
     );
-    
+
     // Save the URL alias pattern if it's set.
     if ($form_state['values']['url']['url_pattern']) {
       tripal_set_bundle_variable('url_format', $bundle_entity->id, $form_state['values']['url']['url_pattern']);

+ 0 - 2
tripal_entities/includes/TripalTermController.inc

@@ -17,8 +17,6 @@ class TripalTermController extends EntityAPIController {
 
   /**
    * Delete a single entity.
-   *
-   * Really a convenience function for deleteMultiple().
    */
   public function delete($entity) {
     $transaction = db_transaction();

+ 2 - 35
tripal_entities/tripal_entities.module

@@ -178,38 +178,6 @@ function tripal_entities_theme($existing, $type, $theme, $path) {
 
   );
 }
-
-/**
- * https://api.drupal.org/api/drupal/modules!rdf!rdf.module/group/rdf/7
- */
-function tripal_entities_rdf_mapping() {
-  return array();
-/*   return array(
-    'type' => 'tripal_entity',
-    'bundle' => 'gene',
-    'mapping' => array(
-      'rdftype' => array('sioc:Item', 'foaf:Document'),
-      'title' => array(
-        'predicates' => array('dc:title'),
-      ),
-      'uid' => array(
-        'predicates' => array('sioc:has_creator'),
-        'type' => 'rel',
-      ),
-      'name' => array(
-        'predicates' => array('foaf:name'),
-      ),
-      'uniquename' => array(
-        'predicates' => array('foaf:name'),
-      ),
-      'organism_id' => array(
-        'predicates' => array('sioc:has_parent'),
-        'type' => 'rel'
-      )
-    ),
-  ); */
-}
-
 // http://www.bluespark.com/blog/drupal-entities-part-3-programming-hello-drupal-entity
 // http://dikini.net/31.08.2010/entities_bundles_fields_and_field_instances
 /**
@@ -454,9 +422,8 @@ function tripal_entities_entity_info_alter(&$entity_info){
 /**
  * Menu argument loader; Load a tripal data type by string.
  *
- *
- * This function is not meant to be used as an API function. It is only mean
- * for use in the menu to resolve the %tripal_entity wildcard.
+ * This function is not meant to be used as an API function. It is only meant
+ * for use in the menu to resolve the %tripal_bundle wildcard.
  *
  * @param $type
  *   The machine-readable name of a tripal data type to load.

+ 1 - 2
tripal_fields_layout/tripal_fields_layout.module

@@ -11,7 +11,6 @@ function tripal_fields_layout_form_field_ui_field_edit_form_alter(&$form, &$form
   // For entity fields added by Tripal Entities we don't want the
   // the end-user to change the cardinality and the required fields
   // such that record can't be saved in Chado.
-  $dbs = tripal_fields_layout_get_db_names_for_published_vocabularies();
   if (in_array($form['#instance']['entity_type'], $dbs)) {
     $form['field']['cardinality']['#access'] = FALSE;
     $form['instance']['required']['#access'] = FALSE;
@@ -267,7 +266,7 @@ function tripal_fields_layout_form_field_ui_display_overview_form_panel_configur
       'effect' => 'fade'
     )
   );
-  
+
   $form['te_configure_panels']['panel_items'] = array (
     '#tree' => TRUE,
     '#prefix' => '<div id="tripal-fields-layout-panel-setting">',

+ 233 - 103
tripal_ws/includes/tripal_ws.rest.inc

@@ -8,6 +8,9 @@ function tripal_ws_rest() {
   global $base_url;
   $ws_args = func_get_args();
 
+  // The web services should never be cached.
+  drupal_page_is_cacheable(FALSE);
+
   // Set some initial variables.
   $response = array();
   $status = 'success';
@@ -35,6 +38,7 @@ function tripal_ws_rest() {
         tripal_ws_handle_content_service($api_url, $response, $ws_args);
         break;
       case 'vocab':
+        tripal_ws_handle_vocab_service($api_url, $response, $ws_args);
         break;
       default:
         tripal_ws_handle_no_service($api_url, $response);
@@ -64,71 +68,6 @@ function tripal_ws_rest() {
   print drupal_json_encode($response);
 }
 
-/**
- * Provides the Hydra compatible apiDocumentation page that describes this API.
- *
- * @param $api_url
- * @param $response
- */
-function tripal_ws_handle_doc_service($api_url, &$response) {
-  // First, add the vocabularies used into the @context section.
-  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
-  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
-
-  // Next add in the ID for tihs resource.
-  $site_name = variable_get('site_name', '');
-  $response['@id'] = $api_url . '/ws-doc/';
-  $response['title'] =  $site_name . ": RESTful Web Services API";
-  $response['entrypoint'] = $api_url;
-  $response['description'] = "A fully queryable REST API using JSON-LD and " .
-    "discoverable using the WC3 Hydra specification.";
-
-  // Lastly, add in the terms used into the @context section.
-  $response['@context']['title'] = 'hydra:title';
-  $response['@context']['entrypoint'] = array(
-    "@id" => "hydra:entrypoint",
-    "@type" => "@id",
-  );
-  $response['@context']['description'] = 'hydra:description';
-}
-
-/**
- * This function specifies the types of resources avaiable via the API.
- *
- * @param $api_url
- * @param $response
- * @param $ws_args
- */
-function tripal_ws_handle_no_service($api_url, &$response) {
-
-  // Add in the term's will use for keys to the context.
-  $response['@context']['itemListElement'] = 'schema:itemListElement';
-  $response['@context']['position'] = 'schema:position';
-  $response['@context']['item'] = 'schema:item';
-  $response['@context']['name'] = 'foaf:name';
-  $response['@context']['description'] = 'dc:description';
-  $response['@context']['numberOfItems'] = 'schema:numberOfItems';
-  $response['@context']['itemListOrder'] = 'schema:itemListOrder';
-  $response['@context']['numberOfItems'] = 'schema:numberOfItems';
-  $response['@context']['ListItem'] = 'schema:ListItem';
-
-  // Start the list.
-  $response['@type'] = 'ItemList';
-  $response['itemListOrder'] = 'ItemListOrderAscending';
-  $response['numberOfItems'] = 0;
-  $response['name'] = 'Services';
-  $response['itemListElement'][] = array(
-    '@type' => 'ListItem',
-    'position' => 1,
-    'item' => array(
-      '@id' => $api_url . '/data/',
-      '@type' => $vocab->namespace . ':' . $term->accession,
-      'name' => $term->name,
-      'description' => 'This service provides acesss to the biological and ' .
-        'anciallary data available on this site.',
-    ),
-  );
-}
 
 /**
  *
@@ -156,7 +95,64 @@ function tripal_ws_handle_content_service($api_url, &$response, $ws_args) {
     tripal_ws_get_content($api_url, $response, $ws_args, $ctype, $entity_id);
   }
 }
+/**
+ *
+ * @param $api_url
+ * @param $response
+ * @param $ws_args
+ */
+function tripal_ws_handle_vocab_service($api_url, &$response, $ws_args) {
+
+  // Get the vocab name.
+  $namespace = (count($ws_args) > 1) ? $ws_args[1] : '';
+  $accession = (count($ws_args) > 2) ? $ws_args[2] : '';
+
+  // If we have no $namespace type then list all of the available vocabs.
+  if (!$namespace) {
+    tripal_ws_get_vocabs($api_url, $response);
+  }
+  // If we don't have a $namespace then show a paged list of terms.
+  else if ($namespace and !$accession) {
+  }
+  // If we have a content type and an entity ID then show the entity
+  else {
+  }
+}
+
+/**
+ *
+ * @param $api_url
+ * @param $response
+ */
+function tripal_ws_get_vocabs($api_url, &$response) {
+  // First, add the vocabularies used into the @context section.
+  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
+
+  // Next add in the ID for tihs resource.
+  $response['@id'] = $api_url . '/vocab';
 
+  // Start the list.
+  $response['@type'] = 'Collection';
+  $response['totalItems'] = 0;
+  $response['label'] = 'Content Types';
+  $response['member'] = array();
+
+  // TODO: determine how to get the list of in-house terms that are used
+  // on the site.  This should really only include terms that are used
+  // as TripalEntity bundle types and that aren't part of another published
+  // vocabulary.
+
+
+  //$response['totalItems'] = $i;
+
+  // Lastly, add in the terms used into the @context section.
+  $response['@context']['Collection'] = 'hydra:Collection';
+  $response['@context']['totalItems'] = 'hydra:totalItems';
+  $response['@context']['member'] = 'hydra:member';
+  $response['@context']['label'] = 'rdfs:label';
+  $response['@context']['description'] = 'hydra:description';
+}
 /**
  * Provides a collection (list) of all of the content types.
  *
@@ -176,6 +172,7 @@ function tripal_ws_get_content_types($api_url, &$response) {
   $response['@type'] = 'Collection';
   $response['totalItems'] = 0;
   $response['label'] = 'Content Types';
+  $response['member'] = array();
 
   // Get the list of published terms (these are the bundle IDs)
   $bundles = db_select('tripal_bundle', 'tb')
@@ -184,7 +181,8 @@ function tripal_ws_get_content_types($api_url, &$response) {
     ->execute();
   $terms = array();
 
-  // Iterate through the terms and add the mas a entry in the collection.
+  // Iterate through the terms and add an entry in the collection.
+  $i = 0;
   while ($bundle = $bundles->fetchObject()) {
     $entity =  entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = reset($entity);
@@ -242,16 +240,24 @@ function tripal_ws_get_content_type($api_url, &$response, $ws_args, $ctype) {
 
   // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
   $bundle = tripal_load_bundle_entity(array('label' => $ctype));
-  $entity =  entity_load('TripalTerm', array('id' => $bundle->term_id));
-  $term = reset($entity);
+  $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
+  $term = reset($term);
   $vocab = $term->vocab;
 
+  if (!array_key_exists($vocab->namespace, $response['@context'])) {
+    // If there is no URL prefix then use this API's vocabulary API
+    if ($term->urlprefix) {
+      $response['@context'][$vocab->namespace] = $term->urlprefix;
+    }
+    else {
+      $response['@context'][$vocab->namespace] = $api_url . '/vocab/' . $vocab->namespace . '/';
+    }
+  }
+
   // Start the list.
   $response['@type'] = 'Collection';
   $response['totalItems'] = 0;
-  // TODO: perhaps we should also have a plural label and get rid of the word
-  // 'records'.
-  $response['label'] = $bundle->label . " records";
+  $response['label'] = $bundle->label . " collection";
 
   // Get the list of entities for this bundle.
   $query = new EntityFieldQuery;
@@ -267,7 +273,7 @@ function tripal_ws_get_content_type($api_url, &$response, $ws_args, $ctype) {
     $entities = entity_load('TripalEntity', array_keys($results['TripalEntity']));
     foreach ($entities as $entity) {
       $response['member'][] = array(
-        '@id' => $api_url . '/content/' . $ctype . '/' .  $bundle->id,
+        '@id' => $api_url . '/content/' . $ctype . '/' .  $entity->id,
         '@type' => $vocab->namespace . ':' . $term->accession,
         'label' => $entity->title,
         'itemPage' => url('/bio-data/' . $entity->id, array('absolute' => TRUE)),
@@ -301,52 +307,176 @@ function tripal_ws_get_content($api_url, &$response, $ws_args, $ctype, $entity_i
 
   // Get the TripalBundle, TripalTerm and TripalVocab type for this type.
   $bundle = tripal_load_bundle_entity(array('label' => $ctype));
-  $entity =  entity_load('TripalTerm', array('id' => $bundle->term_id));
-  $term = reset($entity);
+  $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
+  $term = reset($term);
   $vocab = $term->vocab;
 
-  // Get the TripalEntity
+  if (!array_key_exists($vocab->namespace, $response['@context'])) {
+    // If there is no URL prefix then use this API's vocabulary API
+    if ($term->urlprefix) {
+      $response['@context'][$vocab->namespace] = $term->urlprefix;
+    }
+    else {
+      $response['@context'][$vocab->namespace] = $api_url . '/vocab/' . $vocab->namespace . '/';
+    }
+  }
+
+  // Get the TripalEntity and attach all the fields.
   $entity = entity_load('TripalEntity', array('id' => $entity_id));
+  field_attach_load('TripalEntity', $entity);
   $entity = reset($entity);
 
+
   // Next add in the ID and Type for this resources.
   $response['@id'] = $api_url . '/content/' . $ctype . '/' . $entity_id;
-  $response['@type'] = $vocab->namespace . ':' . $term->accession,;
-  $response['totalItems'] = 0;
-  // TODO: perhaps we should also have a plural label and get rid of the word
-  // 'records'.
-  $response['label'] = $bundle->label . " records";
+  $response['@type'] = $vocab->namespace . ':' . $term->accession;
+  $response['label'] = $entity->title;
+  $response['itemPage'] = url('/bio-data/' . $bundle->id, array('absolute' => TRUE));
+
+  // Get information about the fields attached to this bundle and sort them
+  // in the order they were set for the display.
+  // TODO: should we allow for custom ordering of fields for web services
+  // or use the default display ordering?
+  $fields = field_info_instances('TripalEntity', $bundle->name);
+  uasort($fields, function($a, $b) {
+    $a_weight = (is_array($a) && isset($a['display']['default']['weight'])) ? $a['display']['default']['weight'] : 0;
+    $b_weight = (is_array($b) && isset($b['display']['default']['weight'])) ? $b['display']['default']['weight'] : 0;
+
+    if ($a_weight == $b_weight) {
+      return 0;
+    }
+    return ($a_weight < $b_weight) ? -1 : 1;
+  });
+
+  // Iterate throught the fields and add each value to the response.
+  //$response['fields'] = $fields;
+  foreach ($fields as $field_name => $field) {
+    $field_value = $entity->$field_name;
+
+    // Get the semantic web settings for this field
+    $field_type = '';
+    if (array_key_exists('semantic_web', $field['settings'])) {
+      $field_type = $field['settings']['semantic_web']['type'];
+      if ($field_type) {
+        $ns = $field['settings']['semantic_web']['ns'];
+        $nsurl = $field['settings']['semantic_web']['nsurl'];
+        $response['@context'][$ns] = $nsurl;
+        $response['@context'][$field['label']] = $ns . ':' .$field_type;
+      }
+    }
 
-  // Get the list of entities for this bundle.
-  $query = new EntityFieldQuery;
-  $query->entityCondition('entity_type', 'TripalEntity')
-  ->entityCondition('bundle', $bundle->name)
-  ->propertyOrderBy('title', 'DESC')
-  ->pager(10);
+    // TODO: need a way to hide fields.
 
-  // Iterate through the entities and add them to the list.
-  $results = $query->execute();
-  $i = 0;
-  if (isset($results['TripalEntity'])) {
-    $entities = entity_load('TripalEntity', array_keys($results['TripalEntity']));
-    foreach ($entities as $entity) {
-      $response['member'][] = array(
-        '@id' => $api_url . '/content/' . $ctype . '/' .  $bundle->id,
-        '@type' => $vocab->namespace . ':' . $term->accession,
-        'label' => $entity->title,
-        'itemPage' => url('/bio-data/' . $bundle->id, array('absolute' => TRUE)),
-      );
-      $i++;
+    // Get the values based on cardinality
+    $items = field_get_items('TripalEntity', $entity, $field_name);
+    $values = '';
+    if (array_key_exists('und', $field_value) and count($field_value['und']) == 1) {
+      $values = $field_value['und'][0]['value'];
+    }
+    // If cardinality is greater than 1 then the value should be an array
+    else {
+      $values = array();
+      for ($i = 0; $i < count($field_value); $i++) {
+        $values[] = $field_value['und'][$i]['value'];
+      }
     }
+    $response[$field['label']] = $values;
   }
-  $response['totalItems'] = $i;
+
+ //$response['fields'] = $fields;
 
   // Lastly, add in the terms used into the @context section.
-  $response['@context']['Collection'] = 'hydra:Collection';
-  $response['@context']['totalItems'] = 'hydra:totalItems';
-  $response['@context']['member'] = 'hydra:member';
   $response['@context']['label'] = 'rdfs:label';
   $response['@context']['itemPage'] = 'schema:itemPage';
+}
+
+
+
+/**
+ * Provides the Hydra compatible apiDocumentation page that describes this API.
+ *
+ * @param $api_url
+ * @param $response
+ */
+function tripal_ws_handle_doc_service($api_url, &$response) {
+  // First, add the vocabularies used into the @context section.
+  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
 
+  // Next add in the ID for tihs resource.
+  $site_name = variable_get('site_name', '');
+  $response['@id'] = $api_url . '/doc/';
+  $response['title'] =  $site_name . ": RESTful Web Services API";
+  $response['entrypoint'] = $api_url;
+  $response['description'] = "A fully queryable REST API using JSON-LD and " .
+      "discoverable using the WC3 Hydra specification.";
+
+  // Lastly, add in the terms used into the @context section.
+  $response['@context']['title'] = 'hydra:title';
+  $response['@context']['entrypoint'] = array(
+    "@id" => "hydra:entrypoint",
+    "@type" => "@id",
+  );
+  $response['@context']['description'] = 'hydra:description';
 }
 
+/**
+ * This function specifies the types of resources avaiable via the API.
+ *
+ * @param $api_url
+ * @param $response
+ * @param $ws_args
+ */
+function tripal_ws_handle_no_service($api_url, &$response) {
+
+  // First, add the vocabularies used into the @context section.
+  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
+  $response['@context']['dc'] = 'http://purl.org/dc/dcmitype/';
+  $response['@context']['schema'] = 'https://schema.org/';
+
+  // Next add in the ID for tihs resource.
+  $response['@id'] = $api_url;
+
+
+  // Start the list.
+  $response['@type'] = 'Collection';
+  $response['totalItems'] = 0;
+  $response['label'] = 'Services';
+  $response['member'] = array();
+
+  // Start the list.
+  $response['member'][] = array(
+    '@id' => $api_url . '/content/',
+    '@type' => 'Service',
+    'label' => 'Content Types',
+    'description' => 'Provides acesss to the biological and ' .
+      'ancilliary data available on this site. Each content type ' .
+      'represents biological data that is defined in a controlled vocabulary '.
+      '(e.g. Sequence Ontology term: gene (SO:0000704)).',
+  );
+  $response['member'][] = array(
+    '@id' => $api_url . '/doc/',
+    '@type' => 'Service',
+    'label' => 'API Documentation',
+    'description' => 'The WC3 Hydra compatible documentation for this API.',
+  );
+  $response['member'][] = array(
+    '@id' => $api_url . '/vocab/',
+    '@type' => 'Service',
+    'label' => 'Vocabulary',
+    'description' => 'Defines in-house locally defined vocabulary terms that ' .
+      'have been added specifically for this site.  These terms are typically ' .
+      'added because no other appropriate term exists in another community-vetted '.
+      'controlled vocabulary.',
+  );
+
+  $response['totalItems'] = count($response['member']);
+
+  $response['@context']['Collection'] = 'hydra:Collection';
+  $response['@context']['totalItems'] = 'hydra:totalItems';
+  $response['@context']['member'] = 'hydra:member';
+  $response['@context']['Service'] = 'dc:Service';
+  $response['@context']['label'] = 'rdfs:label';
+  $response['@context']['description'] = 'hydra:description';
+}

+ 0 - 1
tripal_ws/tripal_ws.info

@@ -10,5 +10,4 @@ dependencies[] = tripal_core
 dependencies[] = tripal_views
 dependencies[] = tripal_db
 dependencies[] = tripal_cv
-dependencies[] = tripal_fields_layout
 dependencies[] = tripal_entities