Bläddra i källkod

Remote Fields now working. Still need smarter elements on the field instance form settings

Stephen Ficklin 7 år sedan
förälder
incheckning
49b9fb817b

+ 27 - 20
tripal/includes/tripal.fields.inc

@@ -430,7 +430,7 @@ function tripal_field_instance_settings_form_validate($element, &$form_state, $f
   $field_class = $field['type'];
   if (tripal_load_include_field_class($field_class)) {
     $field = new $field_class($field, $instance);
-    return $field->instanceSettingsForm();
+    return $field->instanceSettingsFormValidate($form, $form_state);
   }
 }
 
@@ -475,7 +475,8 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
   $accession = '';
   $term_name = '';
   $term = NULL;
-  if (array_key_exists('settings', $instance) and array_key_exists('term_vocabulary', $instance['settings'])) {
+  if (array_key_exists('settings', $instance) and
+      array_key_exists('term_vocabulary', $instance['settings'])) {
     $vocabulary = $instance['settings']['term_vocabulary'];
     $accession = $instance['settings']['term_accession'];
     $term_name = $instance['settings']['term_name'];
@@ -536,25 +537,25 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
         be associated with a controlled vocabulary term. This field mapping is
         required and cannot be changed');
   }
-  $element['field_term'] = array(
-    '#type' => 'fieldset',
-    '#title' => 'Controlled Vocabulary Term',
-    '#description' => $description,
-    '#prefix' => '<div id = "tripal-field-term-fieldset">',
-    '#suffix' => '</div>',
-  );
-  $element['field_term']['term_vocabulary'] = array(
+  $element['term_vocabulary'] = array(
     '#type' => 'value',
     '#value' => $vocabulary,
   );
-  $element['field_term']['term_name'] = array(
+  $element['term_name'] = array(
     '#type' => 'value',
     '#value' => $term_name,
   );
-  $element['field_term']['term_accession'] = array(
+  $element['term_accession'] = array(
     '#type' => 'value',
     '#value' => $accession,
   );
+  $element['field_term'] = array(
+    '#type' => 'fieldset',
+    '#title' => 'Controlled Vocabulary Term',
+    '#description' => $description,
+    '#prefix' => '<div id = "tripal-field-term-fieldset">',
+    '#suffix' => '</div>',
+  );
   $element['field_term']['details'] = array(
     '#type' => 'item',
     '#title' => 'Current Term',
@@ -606,7 +607,7 @@ function tripal_field_instance_settings_form_alter_process($element, &$form_stat
     $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
     $num_terms = 0;
     foreach ($terms as $term) {
-      // Save the user a click by setting the default value as 1 if there's
+      // Save the user a click, by setting the default value as 1 if there's
       // only one matching term.
       $default = FALSE;
       $attrs = array();
@@ -653,6 +654,9 @@ function tripal_field_instance_settings_form_alter_validate($form, &$form_state)
   if (array_key_exists('clicked_button', $form_state) and $form_state['clicked_button']['#executes_submit_callback'] == TRUE) {
     $has_default = FALSE;
     if ($form_state['values']['term_vocabulary']) {
+      $form_state['values']['instance']['settings']['term_vocabulary'] = $form_state['values']['term_vocabulary'];
+      $form_state['values']['instance']['settings']['term_accession'] = $form_state['values']['term_accession'];
+      $form_state['values']['instance']['settings']['term_name'] = $form_state['values']['term_name'];
       $has_default = TRUE;
     }
 
@@ -670,14 +674,17 @@ function tripal_field_instance_settings_form_alter_validate($form, &$form_state)
       if (preg_match("/^term-(\d+)$/", $key, $matches) and
           $form_state['input']['term-' . $matches[1]]) {
         $cvterm_id = $matches[1];
-        // TODO: this should not call a Chado function.
+        // TODO: this should not call a Chado function, but the autocomplete
+        // currently uses chado cvterm IDs.
         $term = chado_generate_var('cvterm', array('cvterm_id' => $cvterm_id));
-        $form_state['values']['instance']['settings']['term_vocabulary'] = $term->dbxref_id->db_id->name;
-        $form_state['values']['instance']['settings']['term_accession'] = $term->dbxref_id->accession;
-        $form_state['values']['instance']['settings']['term_name'] = $term->name;
-        $selected_term = TRUE;
-        $num_selected++;
-        $has_default =  TRUE;
+        if ($term) {
+          $form_state['values']['instance']['settings']['term_vocabulary'] = $term->dbxref_id->db_id->name;
+          $form_state['values']['instance']['settings']['term_accession'] = $term->dbxref_id->accession;
+          $form_state['values']['instance']['settings']['term_name'] = $term->name;
+          $selected_term = TRUE;
+          $num_selected++;
+          $has_default =  TRUE;
+        }
       }
     }
 

+ 82 - 40
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -23,10 +23,13 @@ class remote__data extends WebServicesField {
 
   // The default widget for this field.
   public static $default_widget = 'remote__data_widget';
+
   // The default formatter for this field.
   public static $default_formatter = 'remote__data_formatter';
+
   // The module that manages this field.
   public static $module = 'tripal_ws';
+
   // A list of global settings. These can be accessed within the
   // globalSettingsForm.  When the globalSettingsForm is submitted then
   // Drupal will automatically change these settings for all fields.
@@ -67,18 +70,21 @@ class remote__data extends WebServicesField {
     // attached asynchronously.
     'auto_attach' => FALSE,
     // Settings to allow the site admin to set the remote data source info.
-      'data_info' => array(
-        'query' => '',
-        'remote_site' => '',
-        'description' => '',
-        'rd_field_name' => '',
-      ),
+    'data_info' => array(
+      'query' => '',
+      'remote_site' => '',
+      'description' => '',
+      'rd_field_name' => '',
+      'site_logo' => '',
+    ),
   );
+
   // A boolean specifying that users should not be allowed to create
   // fields and instances of this field type through the UI. Such
   // fields can only be created programmatically with field_create_field()
   // and field_create_instance().
   public static $no_ui = FALSE;
+
   // A boolean specifying that the field will not contain any data. This
   // should exclude the field from web services or downloads.  An example
   // could be a quick search field that appears on the page that redirects
@@ -100,19 +106,23 @@ class remote__data extends WebServicesField {
     // is an if statement to not show this field in the web services but the
     // entity_load function doesn't know this field shouldn't be loaded so
     // we need to short-circuit that.
+    $_SERVER['REQUEST_URI'];
     if (preg_match('/^web-services/', $_SERVER['REQUEST_URI'])) {
-      dpm('hi');
       $this->loaded_via_ws = TRUE;
       return;
     }
 
     // Get the site url from the tripal_sites table.
-    $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
-    $this->remote_site = db_select('tripal_sites', 'ts')
-      ->fields('ts')
-      ->condition('ts.id', $site_id_ws)
-      ->execute()
-      ->fetchObject();
+    if (array_key_exists('data_info', $instance['settings'])) {
+      $site_id_ws = $instance['settings']['data_info']['remote_site'];
+      if ($site_id_ws) {
+        $this->remote_site = db_select('tripal_sites', 'ts')
+          ->fields('ts')
+          ->condition('ts.id', $site_id_ws)
+          ->execute()
+          ->fetchObject();
+      }
+    }
   }
   /**
    * @see WebServicesField::load()
@@ -129,9 +139,7 @@ class remote__data extends WebServicesField {
     );
 
     // If this field is being loaded via web services then just return.
-    dpm($this->loaded_via_ws);
-    return;
-    if ($this->loaded_via_ws) {
+    if ($this->loaded_via_ws == TRUE) {
       return;
     }
 
@@ -160,8 +168,12 @@ class remote__data extends WebServicesField {
       // Save the member information for use later.
       $entity->{$field_name}['und'][$i]['remote_entity'] = $member;
 
+      // Separate the query_field if it has subfields.
+      $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
+      $subfields = explode(',', $rd_field_name);
+      $query_field = $subfields[0];
+
       // Next get the the details about this member.
-      $query_field = 'relationship';
       $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
       $field_data = $this->makeRemoteRequest($query_field_url);
       if(!$field_data){
@@ -169,6 +181,7 @@ class remote__data extends WebServicesField {
         $entity->{$field_name}['und'] = array();
         $entity->{$field_name}['und'][0] = array(
           'value' => array(),
+          'remote_entity' => array(),
         );
         return;
       }
@@ -194,7 +207,8 @@ class remote__data extends WebServicesField {
 
      // Build the Query and make and substitions needed.
      $query_url = $ws_url . '/' . $query;
-     $options = array('timeout' => 360);
+
+     $options = array();
      $data = drupal_http_request($query_url, $options);
 
      if (!$data) {
@@ -232,6 +246,7 @@ class remote__data extends WebServicesField {
    */
   public function instanceSettingsForm() {
     $element = parent::instanceSettingsForm();
+
     // Get the setting for the option for how this widget.
     $instance = $this->instance;
     $settings = '';
@@ -239,14 +254,17 @@ class remote__data extends WebServicesField {
 
     $tokens = array();
     // Get the form info from the bundle about to be saved.
-    $bundle_info = tripal_load_bundle_entity(array($instance['bundle']));
+    $bundle = tripal_load_bundle_entity(array('name' => $instance['bundle']));
     // Retrieve all available tokens.
-    $tokens = tripal_get_entity_tokens($bundle_info);
+    $tokens = tripal_get_entity_tokens($bundle);
 
     $element['data_info'] = array(
       '#type' => 'fieldset',
       '#title' => 'Remote Data Settings',
-      '#description' => 'Provide the site name, query and description for the remote data source.',
+      '#description' => 'These settings allow you to provide a Tripal web
+        services query to identify content on another Tripal site and display
+        that here within this field.  You must specify the query to execute and
+        the field to display.',
       '#collapsible' => TRUE,
       '#collapsed' => FALSE,
       '#prefix' => "<div id='set_titles-fieldset'>",
@@ -264,14 +282,14 @@ class remote__data extends WebServicesField {
 
     $element['data_info']['remote_site'] = array(
       '#type' => 'select',
-      '#title' => t('Site'),
+      '#title' => t('Remote Tripal Site'),
       '#options' => $rows,
       '#default_value' => $this->instance['settings']['data_info']['remote_site'],
     );
 
     $element['data_info']['query'] = array(
       '#type' => 'textarea',
-      '#title' => 'Query',
+      '#title' => 'Query to Execute',
       '#description' => 'Build the query string that should be appended after the url. The tokens
       listed below may be used in your query build.',
       '#default_value' => $this->instance['settings']['data_info']['query'],
@@ -280,8 +298,15 @@ class remote__data extends WebServicesField {
     );
     $element['data_info']['rd_field_name'] = array(
       '#type' => 'textfield',
-      '#title' => 'Field',
-      '#description' => 'Name of the field you would like to display.',
+      '#title' => 'Field to Display',
+      '#description' => 'The results returned by the query should match
+        entities (or records) from the selected remote site.  That entity
+        will have multiple fields. Only one remote field can be shown by
+        this field. Please enter the name of the field you would like
+        to display.  Some fields have "subfields".  You can display a subfield
+        rather than the entire field by entering a comma-separated sequence
+        of subfields.  For example, for relationships, you may only want to
+        show the "clause", therefore, the entry here would be: realtionship,clause.',
       '#default_value' => $this->instance['settings']['data_info']['rd_field_name'],
       '#required' => TRUE
     );
@@ -310,24 +335,31 @@ class remote__data extends WebServicesField {
       '#default_value' =>  $this->instance['settings']['data_info']['description'],
       '#rows' => 1
     );
-    /*$element['test_button']['data'] = array(
-      '#prefix' => '<div class="returned-data">',
-      '#suffix' => '</div>',
-      '#type' => 'container',
-        //…
-    );
 
-    $element['test_button'] = array(
-      '#type' => 'button',
-      '#value' => t('Test Query'),
-      '#ajax' => array(
-        'wrapper' => 'data-id',
-        'callback' => 'tripal_ws_url_query_test',
+    $fid = $this->instance['settings']['data_info']['site_logo'] ? $this->instance['settings']['data_info']['site_logo'] : NULL;
+    $file = NULL;
+    if ($fid) {
+      $file = file_load($fid);
+    }
+    $element['data_info']['site_logo'] = array(
+      '#title' => 'Remote Site Logo',
+      '#description' => t('When data is taken from a remote site and shown to the user,
+         the site from which the data was retrieved is indicated.  If you would like to
+         include the logo for the remote site, please upload an image here.'),
+      '#type' => 'managed_file',
+      '#default_value' => $file ? $file->fid : NULL,
+      '#theme' => 'image_widget',
+      '#attached' => array(
+        'css' => array(
+          'image-preview' => drupal_get_path('module', 'image') . '/image.css',
+        ),
       ),
-        //…
-    );*/
+      'preview' => array(
+        '#markup' => theme('image_style', array('style_name' => 'thumbnail', 'path' => $file ? $file->uri : '')),
+      ),
+    );
 
-      return $element;
+    return $element;
   }
 
  /**
@@ -336,6 +368,16 @@ class remote__data extends WebServicesField {
    * @param unknown $form_state
    */
   public function instanceSettingsFormValidate($form, &$form_state) {
+    $site_logo = $form_state['values']['instance']['settings']['data_info']['site_logo'];
+
+    // If we have a site logo then add usage information.
+    if ($site_logo) {
+      $file = file_load($site_logo);
+      $file_usage  = file_usage_list($file);
+      if (!array_key_exists('tripal_ws', $file_usage)) {
+        file_usage_add($file, 'tripal_ws', 'site-logo', 1);
+      }
+    }
   }
 
 

+ 105 - 9
tripal_ws/includes/TripalFields/remote__data/remote__data_formatter.inc

@@ -73,6 +73,19 @@ class remote__data_formatter extends WebServicesFieldFormatter {
     $settings = $display['settings'];
     $field_name = $this->field['field_name'];
 
+    // Get any subfields and the header label.  Shift the array because the
+    // results should already be the value of the fisrt entry.
+    $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
+    $subfields = explode(',', $rd_field_name);
+    $header_label = $this->getHeaderLabel($subfields);
+    array_shift($subfields);
+
+    // Get the site logo if one is provided
+    $site_logo = $this->instance['settings']['data_info']['site_logo'];
+    if ($site_logo) {
+      $site_logo = file_load($site_logo);
+    }
+
     // Get the site name where the data came from.
     $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
     $site = db_select('tripal_sites', 'ts')
@@ -82,44 +95,127 @@ class remote__data_formatter extends WebServicesFieldFormatter {
       ->fetchObject();
 
     $content = '';
-    if (count($items) > 0) {
+    if (array_key_exists('label', $items[0]['remote_entity'])) {
       $remote_entity_label = $items[0]['remote_entity']['label'];
       $remote_entity_page = $items[0]['remote_entity']['ItemPage'];
-      $content = t('The data below about !label was obtained from the !site database.',
-        array('!label' => l($remote_entity_label, $remote_entity_page),
-          '!site' => l($site->name, $site->url)));
+      $content = t('This content provided by !site.',
+        array('!site' => l($site->name, $site->url, array('attributes' => array("target" => '_blank')))));
+      if ($site_logo) {
+        $content .= '<br><img class="tripal-remote--data-field-logo" src="' . file_create_url($site_logo->uri) . '">';
+      }
+      $content .= '<br>' . t('View !data on %site',
+        array('!data' => l('this content', $remote_entity_page, array('attributes' => array('target' => '_blank'))),
+               '%site' => $site->name));
     }
     else {
       $content = t('There is no data about this record from the !site database.',
-          array('!site' => l($site->name, $site->url)));
+          array('!site' => l($site->name, $site->uri)));
     }
+    $rows = array();
     foreach ($items as $index => $item) {
       $value = $item['value'];
+      if (!$value) {
+        continue;
+      }
       if (is_array($value)) {
         $headers = array('');
 
         // If this is a collection then handle it as a list of members.
         if (array_key_exists('members', $value)) {
           foreach ($value['members'] as $subvalue) {
-            $content .= $this->createTable($subvalue);
+            $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
+            $rows[]= array($subvalue);
           }
         }
         else {
-          $content .= $this->createTable($subvalue);
+          $subvalue = $this->refineSubValue($value, $subfields, $header_label);
+          $rows[]= array($subvalue);
         }
       }
       else {
-        $content .= $this->createDL($value);
+        $rows[] = array($value);
+      }
+    }
+
+    $has_sub_tables = FALSE;
+    for ($i = 0; $i < count($rows); $i++) {
+      if (is_array($rows[$i][0])) {
+        $rows[$i][0] = $this->createTable($rows[$i]);
+        $has_sub_tables = TRUE;
+      }
+    }
+
+    // If we don't have  tables for each row then we'll put everything into
+    // a table.
+    if (!$has_sub_tables) {
+      $headers = array($header_label . '(s)');
+      $content .= theme_table(array(
+        'header' => $headers,
+        'rows' => $rows,
+        'attributes' => array(
+          'class' => 'tripal-remote--data-field-table',
+        ),
+        'sticky' => FALSE,
+        'caption' => "",
+        'colgroups' => array(),
+        'empty' => 'There are no results.',
+      ));
+    }
+    else {
+      for ($i = 0; $i < count($rows); $i++) {
+        if (count($rows) > 1) {
+          $content .= '<p class="tripal-remote--data-field-table-label">' . $header_label . ' ' . ($i + 1) . '</p>';
+        }
+        $content .= $rows[$i][0];
       }
     }
 
     // Return the content for this field.
     $element[0] = array(
       '#type' => 'markup',
-      '#markup' => $content,
+      '#markup' => '<div class="tripal-remote--data-field">' . $content . '</div>',
     );
   }
 
+
+  /**
+   * Retrieves the header label given the subfields criteria.
+   *
+   * @param $subfields
+   *   An array of the sequence of subfields.
+   */
+  private function getHeaderLabel($subfields) {
+     $subfield = array_shift($subfields);
+     $header_label = ucwords(preg_replace('/_/', ' ', $subfield));
+     if (count($subfields) > 0) {
+       $header_label .= ' ' . $this->getHeaderLabel($subfields);
+     }
+     return $header_label;
+  }
+  /**
+   * Adjusts the items array to contain only the section/subsection desired.
+   *
+   * The field settings can indicate a field with sub fields that should
+   * be displayed (e.g. organism,genus or relationship,clause).  We want
+   * to adjust the item to only include what the user requested.
+   *
+   * @param $values
+   * @param $subfields
+   */
+  private function refineSubValue($values, $subfields) {
+    $subfield = array_shift($subfields);
+    if (array_key_exists($subfield, $values)) {
+      if (is_array($values[$subfield]) and count($subfields) > 0) {
+        return $this->refineSubvalue($values[$subfield], $subfields);
+      }
+      else {
+        return $values[$subfield];
+      }
+    }
+    else {
+      return $values;
+    }
+  }
   /**
    * A recursive function for displaying an item in a table.
    *

+ 41 - 7
tripal_ws/includes/TripalWebService.inc

@@ -249,6 +249,7 @@ class TripalWebService {
     $this->resource = new TripalWebServiceResource($this->base_path);
     $this->resource->setID('error');
     $this->resource->addContextItem('error', 'rdfs:error');
+    $this->resource->setType('error');
     $this->resource->addProperty('error', $message);
   }
 
@@ -274,21 +275,41 @@ class TripalWebService {
    *   A TripalWebServiceResource instance.
    * @param $term
    *   The term array.
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
    * @return $key
    *   The key (the term name but with spaces replaced with underscores).
    */
-  public function addContextTerm($resource, $term) {
+  public function addContextTerm($resource, $term, $sanitize = array()) {
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addContextTerm: Please provide a $resource of type TripalWebServiceResource.');
     }
 
+    if (!$term) {
+      $backtrace = debug_backtrace();
+      throw new Exception('addContextTerm: Please provide a non NUll or non empty $term.');
+
+    }
+    if (!$term['name']) {
+      throw new Exception('addContextTerm: The provided term does not have a name: ' . print_r($term, TRUE));
+    }
+
     // Make sure the vocab is present
     $vocab = $term['vocabulary'];
     $this->addContextVocab($resource, $vocab);
 
     // Sanitize the term key
     $key = $term['name'];
-    $key_adj = strtolower(preg_replace('/ /', '_', $term['name']));
+    $key_adj = $key;
+    if (in_array('spacing', $sanitize)) {
+      $key_adj = preg_replace('/ /', '_', $key_adj);
+    }
+    if (in_array('lowercase', $sanitize)) {
+      $key_adj = strtolower($key_adj);
+    }
 
     // Create the compact IRI
     $compact_iri = $vocab['short_name'] . ':' . $term['accession'];
@@ -304,6 +325,9 @@ class TripalWebService {
     $resource->addContextItem($key_adj, $compact_iri);
     $resource->addContextItem($compact_iri, $iri);
 
+    if ($key_adj == 'clause subject') {
+      //print_r(debug_backtrace()[2]['function']);
+    }
     return $key_adj;
   }
 
@@ -339,16 +363,21 @@ class TripalWebService {
    *   The term array for the key.
    * @param $value
    *   The value to add.
-   *
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
    * @return $key
    *   The key (the term name but with spaces replaced with underscores).
    */
-  public function addResourceProperty($resource, $term, $value) {
+  public function addResourceProperty($resource, $term, $value, $sanitize = array()) {
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
     }
+
     // First add the term
-    $key = $this->addContextTerm($resource, $term);
+    $key = $this->addContextTerm($resource, $term, $sanitize);
 
     // Then add the property.
     $resource->addProperty($key, $value);
@@ -362,14 +391,19 @@ class TripalWebService {
    *   A TripalWebServiceResource instance.
    * @param $type
    *   The type
+   * @param $santize
+   *   An array of keywords indicating how to santize the key.  By default,
+   *   no sanitizing occurs.  The two valid types are 'lowercase', and 'spacing'
+   *   where 'lowercase' converts the term name to all lowercase and
+   *   'spacing' replaces any spaces with underscores.
    */
-  public function setResourceType($resource, $term) {
+  public function setResourceType($resource, $term, $sanitize = array('spacing')) {
 
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
     }
     // First add the term
-    $key = $this->addContextTerm($resource, $term);
+    $key = $this->addContextTerm($resource, $term, $sanitize);
 
     // Then set the type
     $resource->setType($key);

+ 41 - 20
tripal_ws/includes/TripalWebService/TripalEntityService_v0_1.inc

@@ -40,6 +40,14 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $entity_id = (count($this->path) > 1) ? $this->path[1] : '';
     $expfield  = (count($this->path) > 2) ? $this->path[2] : '';
 
+    // is this a valid content type?
+    if ($ctype) {
+      $bundle = tripal_load_bundle_entity(array('label' => $ctype));
+      if (!$bundle) {
+        throw new Exception('Invalid content type: ' . $ctype);
+      }
+    }
+
     // If we have a content type then list all of the entities that belong
     // to it.
     if ($ctype and !$entity_id and !$expfield) {
@@ -67,35 +75,37 @@ class TripalEntityService_v0_1 extends TripalWebService {
 
     // Get the TripalBundle, TripalTerm and TripalVocab for this type.
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
-    $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
-    $term = reset($term);
-    $vocab = $term->vocab;
 
-    // Convert the term to a simple array
-    $term = tripal_get_term_details($term->vocab->vocabulary, $term->accession);
+    // Find the field that matches the field name provided by the user.
+    list($field, $instance, $term) = $this->findField($bundle, $expfield);
+
+    if (!$field) {
+      throw new Exception("Could not find a matching field for the name: $expfield");
+    }
 
     // Get the TripalEntity
-    $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id));
+    $entity = tripal_load_entity('TripalEntity', array('id' => $entity_id), FALSE, array($field['id']));
     $entity = reset($entity);
 
-    // If we couldn't match this field argument to a field and entity then return
+    // If we couldn't find the entity then fail.
     if (!$entity) {
-      throw new Exception("Cannot find this record.");
+      throw new Exception("Cannot find the record with id $entity_id.");
     }
 
     // Check that the user has access to this entity.  If not then the
     // function call will throw an error.
     $this->checkAccess($entity);
 
-    list($field, $instance, $term) = $this->findField($bundle, $expfield);
 
     // Next add in the ID and Type for this resources.
     $this->setResourceType($this->resource, $term);
     $this->resource->setID(urlencode($term['name']));
 
-    // Attach the field and then add it's values to the response.
-    field_attach_load($entity->type, array($entity->id => $entity),
-        FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+    if (!property_exists($entity, $field['field_name'])) {
+      // Attach the field and then add its values to the response.
+      field_attach_load($entity->type, array($entity->id => $entity),
+          FIELD_LOAD_CURRENT, array('field_id' => $field['id']));
+    }
 
     $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path, $expfield);
   }
@@ -110,6 +120,11 @@ class TripalEntityService_v0_1 extends TripalWebService {
     foreach ($instances as $instance) {
       $field_name = $instance['field_name'];
       $field = field_info_field($field_name);
+      $field_type = $field['type'];
+      // Skip fields of remote data.
+      if ($field_type == 'remote__data') {
+        continue;
+      }
       $vocabulary = $instance['settings']['term_vocabulary'];
       $accession = $instance['settings']['term_accession'];
       $temp_term = tripal_get_term_details($vocabulary, $accession);
@@ -197,6 +212,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
     // in the order they were set for the display.
     $instances = field_info_instances('TripalEntity', $bundle->name);
 
+    // Sort the instances by their weight.
     uasort($instances, function($a, $b) {
       $a_weight = (is_array($a) && isset($a['widget']['weight'])) ? $a['widget']['weight'] : 0;
       $b_weight = (is_array($b) && isset($b['widget']['weight'])) ? $b['widget']['weight'] : 0;
@@ -246,11 +262,11 @@ class TripalEntityService_v0_1 extends TripalWebService {
         // that information.
         $items = field_get_items('TripalEntity', $entity, $field_name);
         if ($items and count($items) > 0 and $items[0]['value']) {
-          $this->addResourceProperty($this->resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']));
+         $this->addResourceProperty($this->resource, $term, $service_path . '/' . $entity->id . '/' . urlencode($term['name']), array('lowercase', 'spacing'));
         }
         else {
           if ($hide_fields == 'show') {
-            $this->addResourceProperty($this->resource, $term, NULL);
+            $this->addResourceProperty($this->resource, $term, NULL, array('lowercase', 'spacing'));
           }
         }
         continue;
@@ -308,17 +324,17 @@ class TripalEntityService_v0_1 extends TripalWebService {
           }
         }
         else {
-          $this->addResourceProperty($this->resource, $term, $values[0]);
+          $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
         }
       }
       // If the value is not an array it's a scalar so add it as is to the
       // response.
       else {
-        $this->addResourceProperty($this->resource, $term, $values[0]);
+        $this->addResourceProperty($this->resource, $term, $values[0], array('lowercase', 'spacing'));
       }
     }
 
-    // If the field cardinality is > 1
+    // If the field cardinality is > 1 or -1 (for unlimited)
     if ($field['cardinality'] != 1) {
 
       // If this is the expanded field page then we need to swap out
@@ -344,7 +360,8 @@ class TripalEntityService_v0_1 extends TripalWebService {
         $this->resource = $response;
       }
       else {
-        $this->resource->addProperty($key, $response);
+        //$this->resource->addProperty($key, $response);
+        $this->addResourceProperty($this->resource, $term, $response, array('lowercase', 'spacing'));
       }
     }
   }
@@ -353,6 +370,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
    * Rewrites the keys of a field's items array for use with web services.
    */
   private function sanitizeFieldKeys($resource, $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');
@@ -374,7 +392,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
           $vocabulary = $matches[1];
           $accession = $matches[2];
           $term = tripal_get_term_details($vocabulary, $accession);
-          $key = $this->addContextTerm($resource, $term);
+          $key = $this->addContextTerm($resource, $term, array('lowercase', 'spacing'));
 
           if (is_array($v)) {
             $temp[$key] = $this->sanitizeFieldKeys($resource, $v, $bundle, $service_path);
@@ -489,7 +507,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $dir = 'ASC';
 
       // If the user provided more than one order statement then those are
-      // separated by a semicolong.
+      // separated by a semicolon.
       $items = explode(';', $order_params);
       foreach ($items as $key) {
 
@@ -746,6 +764,9 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = reset($term);
 
+    // Convert term to a simple array
+    $term = tripal_get_term_details($term->vocab->vocabulary, $term->accession);
+
     // Set the label for this collection.
     $label = tripal_get_term_details('rdfs', 'label');
     $this->addResourceProperty($this->resource, $label, $bundle->label . " collection");

+ 0 - 15
tripal_ws/includes/tripal_ws.fields.inc

@@ -93,18 +93,3 @@ function tripal_ws_bundle_create_user_field($new_field, $bundle) {
     ));
   }
 }
-/**
- * Implements hook_ws_field_formatter_info_alter()
- *
- * Adds the remote__data field to every formatter so that existing formatters
- * can be used for remote data (provided that remote data is compatible).
- *
- */
-function tripal_ws_field_formatter_info_alter(&$info) {
-  foreach ($info as $formatter_type => $details) {
-    if (array_key_exists('TripalFieldFormatter', $details)) {
-      $info[$formatter_type]['field types'][] = 'remote__data';
-    }
-  }
-  dpm($info);
-}

+ 33 - 0
tripal_ws/theme/css/tripal_ws.css

@@ -0,0 +1,33 @@
+.tripal-remote--data-field {
+  max-height: 400px;
+  overflow: scroll;
+}
+.tripal-remote--data-field-logo {
+  max-height: 100px;
+}
+
+.tripal-remote--data-field-dl dt {
+  float: left;
+  padding-right: 5px;
+}
+
+.tripal-remote--data-field-table tr {
+  background: none;
+}
+.tripal-remote--data-field-table td {
+  border-left: none;
+  border-right: none;
+  text-align: left;
+  vertical-align: top;
+}
+
+.tripal-remote--data-field-table th{
+  border: none;
+}
+.tripal-remote--data-field-table tr:last-child td {
+  border-bottom: none;
+}
+
+.tripal-remote--data-field-table-label {
+  font-weight: bold;
+}