Przeglądaj źródła

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

Stephen Ficklin 7 lat temu
rodzic
commit
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'];
   $field_class = $field['type'];
   if (tripal_load_include_field_class($field_class)) {
   if (tripal_load_include_field_class($field_class)) {
     $field = new $field_class($field, $instance);
     $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 = '';
   $accession = '';
   $term_name = '';
   $term_name = '';
   $term = NULL;
   $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'];
     $vocabulary = $instance['settings']['term_vocabulary'];
     $accession = $instance['settings']['term_accession'];
     $accession = $instance['settings']['term_accession'];
     $term_name = $instance['settings']['term_name'];
     $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
         be associated with a controlled vocabulary term. This field mapping is
         required and cannot be changed');
         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',
     '#type' => 'value',
     '#value' => $vocabulary,
     '#value' => $vocabulary,
   );
   );
-  $element['field_term']['term_name'] = array(
+  $element['term_name'] = array(
     '#type' => 'value',
     '#type' => 'value',
     '#value' => $term_name,
     '#value' => $term_name,
   );
   );
-  $element['field_term']['term_accession'] = array(
+  $element['term_accession'] = array(
     '#type' => 'value',
     '#type' => 'value',
     '#value' => $accession,
     '#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(
   $element['field_term']['details'] = array(
     '#type' => 'item',
     '#type' => 'item',
     '#title' => 'Current Term',
     '#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');
     $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
     $num_terms = 0;
     $num_terms = 0;
     foreach ($terms as $term) {
     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.
       // only one matching term.
       $default = FALSE;
       $default = FALSE;
       $attrs = array();
       $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) {
   if (array_key_exists('clicked_button', $form_state) and $form_state['clicked_button']['#executes_submit_callback'] == TRUE) {
     $has_default = FALSE;
     $has_default = FALSE;
     if ($form_state['values']['term_vocabulary']) {
     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;
       $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
       if (preg_match("/^term-(\d+)$/", $key, $matches) and
           $form_state['input']['term-' . $matches[1]]) {
           $form_state['input']['term-' . $matches[1]]) {
         $cvterm_id = $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));
         $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.
   // The default widget for this field.
   public static $default_widget = 'remote__data_widget';
   public static $default_widget = 'remote__data_widget';
+
   // The default formatter for this field.
   // The default formatter for this field.
   public static $default_formatter = 'remote__data_formatter';
   public static $default_formatter = 'remote__data_formatter';
+
   // The module that manages this field.
   // The module that manages this field.
   public static $module = 'tripal_ws';
   public static $module = 'tripal_ws';
+
   // A list of global settings. These can be accessed within the
   // A list of global settings. These can be accessed within the
   // globalSettingsForm.  When the globalSettingsForm is submitted then
   // globalSettingsForm.  When the globalSettingsForm is submitted then
   // Drupal will automatically change these settings for all fields.
   // Drupal will automatically change these settings for all fields.
@@ -67,18 +70,21 @@ class remote__data extends WebServicesField {
     // attached asynchronously.
     // attached asynchronously.
     'auto_attach' => FALSE,
     'auto_attach' => FALSE,
     // Settings to allow the site admin to set the remote data source info.
     // 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
   // A boolean specifying that users should not be allowed to create
   // fields and instances of this field type through the UI. Such
   // fields and instances of this field type through the UI. Such
   // fields can only be created programmatically with field_create_field()
   // fields can only be created programmatically with field_create_field()
   // and field_create_instance().
   // and field_create_instance().
   public static $no_ui = FALSE;
   public static $no_ui = FALSE;
+
   // A boolean specifying that the field will not contain any data. This
   // A boolean specifying that the field will not contain any data. This
   // should exclude the field from web services or downloads.  An example
   // should exclude the field from web services or downloads.  An example
   // could be a quick search field that appears on the page that redirects
   // 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
     // 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
     // entity_load function doesn't know this field shouldn't be loaded so
     // we need to short-circuit that.
     // we need to short-circuit that.
+    $_SERVER['REQUEST_URI'];
     if (preg_match('/^web-services/', $_SERVER['REQUEST_URI'])) {
     if (preg_match('/^web-services/', $_SERVER['REQUEST_URI'])) {
-      dpm('hi');
       $this->loaded_via_ws = TRUE;
       $this->loaded_via_ws = TRUE;
       return;
       return;
     }
     }
 
 
     // Get the site url from the tripal_sites table.
     // 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()
    * @see WebServicesField::load()
@@ -129,9 +139,7 @@ class remote__data extends WebServicesField {
     );
     );
 
 
     // If this field is being loaded via web services then just return.
     // 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;
       return;
     }
     }
 
 
@@ -160,8 +168,12 @@ class remote__data extends WebServicesField {
       // Save the member information for use later.
       // Save the member information for use later.
       $entity->{$field_name}['und'][$i]['remote_entity'] = $member;
       $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.
       // Next get the the details about this member.
-      $query_field = 'relationship';
       $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
       $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
       $field_data = $this->makeRemoteRequest($query_field_url);
       $field_data = $this->makeRemoteRequest($query_field_url);
       if(!$field_data){
       if(!$field_data){
@@ -169,6 +181,7 @@ class remote__data extends WebServicesField {
         $entity->{$field_name}['und'] = array();
         $entity->{$field_name}['und'] = array();
         $entity->{$field_name}['und'][0] = array(
         $entity->{$field_name}['und'][0] = array(
           'value' => array(),
           'value' => array(),
+          'remote_entity' => array(),
         );
         );
         return;
         return;
       }
       }
@@ -194,7 +207,8 @@ class remote__data extends WebServicesField {
 
 
      // Build the Query and make and substitions needed.
      // Build the Query and make and substitions needed.
      $query_url = $ws_url . '/' . $query;
      $query_url = $ws_url . '/' . $query;
-     $options = array('timeout' => 360);
+
+     $options = array();
      $data = drupal_http_request($query_url, $options);
      $data = drupal_http_request($query_url, $options);
 
 
      if (!$data) {
      if (!$data) {
@@ -232,6 +246,7 @@ class remote__data extends WebServicesField {
    */
    */
   public function instanceSettingsForm() {
   public function instanceSettingsForm() {
     $element = parent::instanceSettingsForm();
     $element = parent::instanceSettingsForm();
+
     // Get the setting for the option for how this widget.
     // Get the setting for the option for how this widget.
     $instance = $this->instance;
     $instance = $this->instance;
     $settings = '';
     $settings = '';
@@ -239,14 +254,17 @@ class remote__data extends WebServicesField {
 
 
     $tokens = array();
     $tokens = array();
     // Get the form info from the bundle about to be saved.
     // 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.
     // Retrieve all available tokens.
-    $tokens = tripal_get_entity_tokens($bundle_info);
+    $tokens = tripal_get_entity_tokens($bundle);
 
 
     $element['data_info'] = array(
     $element['data_info'] = array(
       '#type' => 'fieldset',
       '#type' => 'fieldset',
       '#title' => 'Remote Data Settings',
       '#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,
       '#collapsible' => TRUE,
       '#collapsed' => FALSE,
       '#collapsed' => FALSE,
       '#prefix' => "<div id='set_titles-fieldset'>",
       '#prefix' => "<div id='set_titles-fieldset'>",
@@ -264,14 +282,14 @@ class remote__data extends WebServicesField {
 
 
     $element['data_info']['remote_site'] = array(
     $element['data_info']['remote_site'] = array(
       '#type' => 'select',
       '#type' => 'select',
-      '#title' => t('Site'),
+      '#title' => t('Remote Tripal Site'),
       '#options' => $rows,
       '#options' => $rows,
       '#default_value' => $this->instance['settings']['data_info']['remote_site'],
       '#default_value' => $this->instance['settings']['data_info']['remote_site'],
     );
     );
 
 
     $element['data_info']['query'] = array(
     $element['data_info']['query'] = array(
       '#type' => 'textarea',
       '#type' => 'textarea',
-      '#title' => 'Query',
+      '#title' => 'Query to Execute',
       '#description' => 'Build the query string that should be appended after the url. The tokens
       '#description' => 'Build the query string that should be appended after the url. The tokens
       listed below may be used in your query build.',
       listed below may be used in your query build.',
       '#default_value' => $this->instance['settings']['data_info']['query'],
       '#default_value' => $this->instance['settings']['data_info']['query'],
@@ -280,8 +298,15 @@ class remote__data extends WebServicesField {
     );
     );
     $element['data_info']['rd_field_name'] = array(
     $element['data_info']['rd_field_name'] = array(
       '#type' => 'textfield',
       '#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'],
       '#default_value' => $this->instance['settings']['data_info']['rd_field_name'],
       '#required' => TRUE
       '#required' => TRUE
     );
     );
@@ -310,24 +335,31 @@ class remote__data extends WebServicesField {
       '#default_value' =>  $this->instance['settings']['data_info']['description'],
       '#default_value' =>  $this->instance['settings']['data_info']['description'],
       '#rows' => 1
       '#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
    * @param unknown $form_state
    */
    */
   public function instanceSettingsFormValidate($form, &$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'];
     $settings = $display['settings'];
     $field_name = $this->field['field_name'];
     $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.
     // Get the site name where the data came from.
     $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
     $site_id_ws = $this->instance['settings']['data_info']['remote_site'];
     $site = db_select('tripal_sites', 'ts')
     $site = db_select('tripal_sites', 'ts')
@@ -82,44 +95,127 @@ class remote__data_formatter extends WebServicesFieldFormatter {
       ->fetchObject();
       ->fetchObject();
 
 
     $content = '';
     $content = '';
-    if (count($items) > 0) {
+    if (array_key_exists('label', $items[0]['remote_entity'])) {
       $remote_entity_label = $items[0]['remote_entity']['label'];
       $remote_entity_label = $items[0]['remote_entity']['label'];
       $remote_entity_page = $items[0]['remote_entity']['ItemPage'];
       $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 {
     else {
       $content = t('There is no data about this record from the !site database.',
       $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) {
     foreach ($items as $index => $item) {
       $value = $item['value'];
       $value = $item['value'];
+      if (!$value) {
+        continue;
+      }
       if (is_array($value)) {
       if (is_array($value)) {
         $headers = array('');
         $headers = array('');
 
 
         // If this is a collection then handle it as a list of members.
         // If this is a collection then handle it as a list of members.
         if (array_key_exists('members', $value)) {
         if (array_key_exists('members', $value)) {
           foreach ($value['members'] as $subvalue) {
           foreach ($value['members'] as $subvalue) {
-            $content .= $this->createTable($subvalue);
+            $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
+            $rows[]= array($subvalue);
           }
           }
         }
         }
         else {
         else {
-          $content .= $this->createTable($subvalue);
+          $subvalue = $this->refineSubValue($value, $subfields, $header_label);
+          $rows[]= array($subvalue);
         }
         }
       }
       }
       else {
       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.
     // Return the content for this field.
     $element[0] = array(
     $element[0] = array(
       '#type' => 'markup',
       '#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.
    * 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 = new TripalWebServiceResource($this->base_path);
     $this->resource->setID('error');
     $this->resource->setID('error');
     $this->resource->addContextItem('error', 'rdfs:error');
     $this->resource->addContextItem('error', 'rdfs:error');
+    $this->resource->setType('error');
     $this->resource->addProperty('error', $message);
     $this->resource->addProperty('error', $message);
   }
   }
 
 
@@ -274,21 +275,41 @@ class TripalWebService {
    *   A TripalWebServiceResource instance.
    *   A TripalWebServiceResource instance.
    * @param $term
    * @param $term
    *   The term array.
    *   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
    * @return $key
    *   The key (the term name but with spaces replaced with underscores).
    *   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')) {
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addContextTerm: Please provide a $resource of type 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
     // Make sure the vocab is present
     $vocab = $term['vocabulary'];
     $vocab = $term['vocabulary'];
     $this->addContextVocab($resource, $vocab);
     $this->addContextVocab($resource, $vocab);
 
 
     // Sanitize the term key
     // Sanitize the term key
     $key = $term['name'];
     $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
     // Create the compact IRI
     $compact_iri = $vocab['short_name'] . ':' . $term['accession'];
     $compact_iri = $vocab['short_name'] . ':' . $term['accession'];
@@ -304,6 +325,9 @@ class TripalWebService {
     $resource->addContextItem($key_adj, $compact_iri);
     $resource->addContextItem($key_adj, $compact_iri);
     $resource->addContextItem($compact_iri, $iri);
     $resource->addContextItem($compact_iri, $iri);
 
 
+    if ($key_adj == 'clause subject') {
+      //print_r(debug_backtrace()[2]['function']);
+    }
     return $key_adj;
     return $key_adj;
   }
   }
 
 
@@ -339,16 +363,21 @@ class TripalWebService {
    *   The term array for the key.
    *   The term array for the key.
    * @param $value
    * @param $value
    *   The value to add.
    *   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
    * @return $key
    *   The key (the term name but with spaces replaced with underscores).
    *   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')) {
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
     }
     }
+
     // First add the term
     // First add the term
-    $key = $this->addContextTerm($resource, $term);
+    $key = $this->addContextTerm($resource, $term, $sanitize);
 
 
     // Then add the property.
     // Then add the property.
     $resource->addProperty($key, $value);
     $resource->addProperty($key, $value);
@@ -362,14 +391,19 @@ class TripalWebService {
    *   A TripalWebServiceResource instance.
    *   A TripalWebServiceResource instance.
    * @param $type
    * @param $type
    *   The 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')) {
     if (!is_a($resource, 'TripalWebServiceResource')) {
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
       throw new Exception('addProperty: Please provide a $resource of type TripalWebServiceResource.');
     }
     }
     // First add the term
     // First add the term
-    $key = $this->addContextTerm($resource, $term);
+    $key = $this->addContextTerm($resource, $term, $sanitize);
 
 
     // Then set the type
     // Then set the type
     $resource->setType($key);
     $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] : '';
     $entity_id = (count($this->path) > 1) ? $this->path[1] : '';
     $expfield  = (count($this->path) > 2) ? $this->path[2] : '';
     $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
     // If we have a content type then list all of the entities that belong
     // to it.
     // to it.
     if ($ctype and !$entity_id and !$expfield) {
     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.
     // Get the TripalBundle, TripalTerm and TripalVocab for this type.
     $bundle = tripal_load_bundle_entity(array('label' => $ctype));
     $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
     // 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);
     $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) {
     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
     // Check that the user has access to this entity.  If not then the
     // function call will throw an error.
     // function call will throw an error.
     $this->checkAccess($entity);
     $this->checkAccess($entity);
 
 
-    list($field, $instance, $term) = $this->findField($bundle, $expfield);
 
 
     // Next add in the ID and Type for this resources.
     // Next add in the ID and Type for this resources.
     $this->setResourceType($this->resource, $term);
     $this->setResourceType($this->resource, $term);
     $this->resource->setID(urlencode($term['name']));
     $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);
     $this->addEntityField($term, $entity, $bundle, $field, $instance, $service_path, $expfield);
   }
   }
@@ -110,6 +120,11 @@ class TripalEntityService_v0_1 extends TripalWebService {
     foreach ($instances as $instance) {
     foreach ($instances as $instance) {
       $field_name = $instance['field_name'];
       $field_name = $instance['field_name'];
       $field = field_info_field($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'];
       $vocabulary = $instance['settings']['term_vocabulary'];
       $accession = $instance['settings']['term_accession'];
       $accession = $instance['settings']['term_accession'];
       $temp_term = tripal_get_term_details($vocabulary, $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.
     // in the order they were set for the display.
     $instances = field_info_instances('TripalEntity', $bundle->name);
     $instances = field_info_instances('TripalEntity', $bundle->name);
 
 
+    // Sort the instances by their weight.
     uasort($instances, function($a, $b) {
     uasort($instances, function($a, $b) {
       $a_weight = (is_array($a) && isset($a['widget']['weight'])) ? $a['widget']['weight'] : 0;
       $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;
       $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.
         // that information.
         $items = field_get_items('TripalEntity', $entity, $field_name);
         $items = field_get_items('TripalEntity', $entity, $field_name);
         if ($items and count($items) > 0 and $items[0]['value']) {
         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 {
         else {
           if ($hide_fields == 'show') {
           if ($hide_fields == 'show') {
-            $this->addResourceProperty($this->resource, $term, NULL);
+            $this->addResourceProperty($this->resource, $term, NULL, array('lowercase', 'spacing'));
           }
           }
         }
         }
         continue;
         continue;
@@ -308,17 +324,17 @@ class TripalEntityService_v0_1 extends TripalWebService {
           }
           }
         }
         }
         else {
         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
       // If the value is not an array it's a scalar so add it as is to the
       // response.
       // response.
       else {
       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 ($field['cardinality'] != 1) {
 
 
       // If this is the expanded field page then we need to swap out
       // 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;
         $this->resource = $response;
       }
       }
       else {
       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.
    * Rewrites the keys of a field's items array for use with web services.
    */
    */
   private function sanitizeFieldKeys($resource, $value, $bundle, $service_path) {
   private function sanitizeFieldKeys($resource, $value, $bundle, $service_path) {
+
     // If the entity is set to hide fields that have no values then we
     // If the entity is set to hide fields that have no values then we
     // want to honor that in the web services too.
     // want to honor that in the web services too.
     $hide_fields = tripal_get_bundle_variable('hide_empty_field', $bundle->id, 'hide');
     $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];
           $vocabulary = $matches[1];
           $accession = $matches[2];
           $accession = $matches[2];
           $term = tripal_get_term_details($vocabulary, $accession);
           $term = tripal_get_term_details($vocabulary, $accession);
-          $key = $this->addContextTerm($resource, $term);
+          $key = $this->addContextTerm($resource, $term, array('lowercase', 'spacing'));
 
 
           if (is_array($v)) {
           if (is_array($v)) {
             $temp[$key] = $this->sanitizeFieldKeys($resource, $v, $bundle, $service_path);
             $temp[$key] = $this->sanitizeFieldKeys($resource, $v, $bundle, $service_path);
@@ -489,7 +507,7 @@ class TripalEntityService_v0_1 extends TripalWebService {
       $dir = 'ASC';
       $dir = 'ASC';
 
 
       // If the user provided more than one order statement then those are
       // If the user provided more than one order statement then those are
-      // separated by a semicolong.
+      // separated by a semicolon.
       $items = explode(';', $order_params);
       $items = explode(';', $order_params);
       foreach ($items as $key) {
       foreach ($items as $key) {
 
 
@@ -746,6 +764,9 @@ class TripalEntityService_v0_1 extends TripalWebService {
     $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
     $term = reset($term);
     $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.
     // Set the label for this collection.
     $label = tripal_get_term_details('rdfs', 'label');
     $label = tripal_get_term_details('rdfs', 'label');
     $this->addResourceProperty($this->resource, $label, $bundle->label . " collection");
     $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;
+}