Bladeren bron

Merge branch '7.x-3.x' into 525-tv3-obo_speed_colon

Stephen Ficklin 6 jaren geleden
bovenliggende
commit
9077f4d05c

File diff suppressed because it is too large
+ 215 - 324
composer.lock


+ 442 - 0
tests/tripal_chado/fields/ChadoFieldGetValuesListTest.php

@@ -0,0 +1,442 @@
+<?php
+namespace Tests\tripal_chado\fields;
+
+use StatonLab\TripalTestSuite\DBTransaction;
+use StatonLab\TripalTestSuite\TripalTestCase;
+use StatonLab\TripalTestSuite\Database\Factory;
+
+/**
+ * Test ChadoField->getValueList() Method.
+ */
+class ChadoFieldGetValuesListTest extends TripalTestCase {
+  // Uncomment to auto start and rollback db transactions per test method.
+  use DBTransaction;
+
+  // Stores a list of field instances to be tested including their storage method and instance info.
+  private $field_list = NULL;
+
+  /**
+   * Test getValueList for fields based on columns in the base table.
+   *
+   * @dataProvider getBaseFields
+   *
+   * @group fields
+   * @group getValueList
+   */
+  public function testBaseTableColumns($field_name, $bundle_name, $info) {
+    include_once(drupal_get_path('tripal_chado', 'module') . '/includes/TripalFields/ChadoField.inc');
+
+    // Construct the Field instance we want the values for.
+    // Specifying "ChadoField" here ensures we are only testing our
+    // implementation of getValueList() and not the custom version for any
+    // given field.
+    // YOU SHOULD TEST CUSTOM FIELD IMPLEMENTATIONS SEPARATELY.
+    $instance = new \ChadoField($info['field_info'], $info['instance_info']);
+
+    // Retrieve the values.
+    // $values will be an array containing the distinct set of values for this field instance.
+    $values = $instance->getValueList(array('limit' => 5));
+
+    // Ensure we have values returned!
+    $this->assertTrue(
+      is_array($values),
+      t(
+        'No values returned for @field_name (bundle: @bundle_name, bundle base table: @bundle_base_table, chado table: @chado_table, chado column: @chado_column).',
+        array(
+          '@field_name' => $field_name,
+          '@bundle_name' => $bundle_name,
+          '@bundle_base_table' => $info['bundle_base_table'],
+          '@chado_table' => $info['instance_info']['settings']['chado_table'],
+          '@chado_column' => $info['instance_info']['settings']['chado_column'],
+        )
+      )
+    );
+
+    // Ensure there are no more then 5 as specified in the limit above.
+    $this->assertLessThanOrEqual(5, sizeof($values),
+      t('Returned too many results for @field_name.', array('@field_name' => $field_name)));
+
+    // Ensure a known value is in the list.
+    // Note: The following generates fake data with a fixed value for the column this
+    // field is based on. This allows us to check that fixed value is one of those
+    // returned by ChadoField->getValueList().
+    $fake_value = $this->generateFakeData($info['bundle_base_table'], $info['base_schema'], $info['instance_info']['settings']['chado_column']);
+    if ($fake_value !== FALSE) {
+
+      // Re-generate the values...
+      $values = $instance->getValueList(array('limit' => 200));
+
+      // And ensure our fake value is in the returned list.
+      // We can only check this if all the results are returned.
+      // As such, we set the limit quite high above and if we have
+      // less then the limit, we will go ahead with the test.
+      // @note: this tests all fields on TravisCI since there is no pre-existing data.
+      if (sizeof($values) < 200) {
+        $this->assertContains($fake_value, $values, "\nThe following array should but does not contain our fake value ('$fake_value'): '" . implode("', '", $values) . '.');
+      }
+    }
+
+  }
+
+  /**
+   * DataProvider: a list of fields who store their data in the base table of a bundle.
+   *
+   * Each element describes a field instance and consists of:
+   *   - the machine name of the field (e.g. obi__organism).
+   *   - the machine name of the bundle (e.g. bio_data_17).
+   *   - an array of additional information including:
+   *       - instance_info: information about the field instance.
+   *       - field_info: information about the field.
+   *       - bundle: the TripalBundle object.
+   *       - bundle_base_table: if applicable, the chado base table the bundle stores it's data in.
+   *       - base_schema: the Tripal Schema array for the bundle_base_table.
+   */
+  public function getBaseFields() {
+
+    // Retrieve a list of fields to test.
+    // Note: this list is cached to improve performance.
+    $fields = $this->retrieveFieldList();
+
+    return $fields['field_chado_storage']['base'];
+  }
+
+  /**
+   * Test for fields based on columns in the base table that are also foreign keys.
+   *
+   * @dataProvider getBaseFkFields
+   * @group current
+   * @group fields
+   * @group getValueList
+   */
+  public function testBaseTableForeignKey($field_name, $bundle_name, $info) {
+    include_once(drupal_get_path('tripal_chado', 'module') . '/includes/TripalFields/ChadoField.inc');
+
+    // Construct the Field instance we want the values for.
+    // Specifying "ChadoField" here ensures we are only testing our
+    // implementation of getValueList() and not the custom version for any
+    // given field.
+    // YOU SHOULD TEST CUSTOM FIELD IMPLEMENTATIONS SEPARATELY.
+    $instance = new \ChadoField($info['field_info'], $info['instance_info']);
+
+    // Retrieve the values using defaults.
+    // $values will be an array containing the distinct set of values for this field instance.
+    $values = $instance->getValueList(array('limit' => 5));
+
+    // Ensure we have values returned!
+    $this->assertTrue(
+      is_array($values),
+      t(
+        'No values returned for @field_name with no label string set (bundle: @bundle_name, bundle base table: @bundle_base_table, chado table: @chado_table, chado column: @chado_column).',
+        array(
+          '@field_name' => $field_name,
+          '@bundle_name' => $bundle_name,
+          '@bundle_base_table' => $info['bundle_base_table'],
+          '@chado_table' => $info['instance_info']['settings']['chado_table'],
+          '@chado_column' => $info['instance_info']['settings']['chado_column'],
+        )
+      )
+    );
+
+    // Ensure there are no more then 5 as specified in the limit above.
+    $this->assertLessThanOrEqual(5, sizeof($values),
+      t('Returned too many results for @field_name.', array('@field_name' => $field_name)));
+
+    // Ensure it works with a label string set.
+    // Ensure a known value is in the list.
+    // Note: The following generates fake data with a fixed value for the column this
+    // field is based on. This allows us to check that fixed value is one of those
+    // returned by ChadoField->getValueList().
+    $fake_fk_record = $this->generateFakeData($info['bundle_base_table'], $info['base_schema'], $info['instance_info']['settings']['chado_column'], $info['fk_table']);
+    if ($fake_fk_record !== FALSE) {
+
+      // We also want to test the label string functionality.
+      // Grab two columns at random from the related table...
+      $schema = chado_get_schema($info['fk_table']);
+      $fk_table_fields = array_keys($schema['fields']);
+      $use_in_label = array_rand($fk_table_fields, 2);
+      $column1 = $fk_table_fields[$use_in_label[0]];
+      $column2 = $fk_table_fields[$use_in_label[1]];
+      // The label string consists of tokens of the form [column_name].
+      $label_string = '['.$column2.'] (['.$column1.'])';
+
+      // Re-generate the values...
+      $values = $instance->getValueList(array('limit' => 200, 'label_string' => $label_string));
+
+      // And ensure our fake value is in the returned list.
+      // We can only check this if all the results are returned.
+      // As such, we set the limit quite high above and if we have
+      // less then the limit, we will go ahead with the test.
+      // @note: this tests all fields on TravisCI since there is no pre-existing data.
+      if (sizeof($values) < 200) {
+        $fixed_key = $fake_fk_record->{$info['fk_table'].'_id'};
+        $this->assertArrayHasKey($fixed_key, $values, "\nThe following array should but does not contain our fake record: " . print_r($fake_fk_record, TRUE));
+
+        // Now test the label of the fake record option is what we expect
+        // based on the label string we set above.
+        $expected_label = $fake_fk_record->{$column2} . ' (' . $fake_fk_record->{$column1} . ')';
+        $this->assertEquals($expected_label, $values[$fixed_key], "\nThe label should have been '$label_string' with the column values filled in.");
+      }
+    }
+
+  }
+
+  /**
+   * DataProvider: a list of fields who store their data in the base table of a bundle.
+   *
+   * Each element describes a field instance and consists of:
+   *   - the machine name of the field (e.g. obi__organism).
+   *   - the machine name of the bundle (e.g. bio_data_17).
+   *   - an array of additional information including:
+   *       - instance_info: information about the field instance.
+   *       - field_info: information about the field.
+   *       - bundle: the TripalBundle object.
+   *       - bundle_base_table: if applicable, the chado base table the bundle stores it's data in.
+   *       - base_schema: the Tripal Schema array for the bundle_base_table.
+   */
+  public function getBaseFkFields() {
+
+    // Retrieve a list of fields to test.
+    // Note: this list is cached to improve performance.
+    $fields = $this->retrieveFieldList();
+
+    return $fields['field_chado_storage']['foreign key'];
+  }
+
+  /**
+   * Test for fields based on tables besides the base one for the bundle.
+   * CURRENTLY RETRIEVING VALUES FOR THESE TABLES IS NOT SUPPORTED.
+   *
+   * @dataProvider getNonBaseFields
+   *
+   * @group fields
+   * @group getValueList
+   */
+  public function testNonBaseTable($field_name, $bundle_name, $info) {
+    include_once(drupal_get_path('tripal_chado', 'module') . '/includes/TripalFields/ChadoField.inc');
+
+    // Construct the Field instance we want the values for.
+    // Specifying "ChadoField" here ensures we are only testing our
+    // implementation of getValueList() and not the custom version for any
+    // given field.
+    // YOU SHOULD TEST CUSTOM FIELD IMPLEMENTATIONS SEPARATELY.
+    $instance = new \ChadoField($info['field_info'], $info['instance_info']);
+
+    // Supress tripal errors
+    putenv("TRIPAL_SUPPRESS_ERRORS=TRUE");
+    ob_start();
+
+    try {
+
+    // Retrieve the values.
+    // $values will be an array containing the distinct set of values for this field instance.
+    $values = $instance->getValueList(array('limit' => 5));
+
+    // @todo Check that we got the correct warning message.
+    // Currently we can't check this because we need to supress the error in order to keep it from printing
+    // but once we do, we can't access it ;-P
+
+    } catch (Exception $e) {
+      $this->fail("Although we don't support values lists for $field_name, it still shouldn't produce an exception!");
+    }
+
+    // Clean the buffer and unset tripal errors suppression
+    ob_end_clean();
+    putenv("TRIPAL_SUPPRESS_ERRORS");
+
+    $this->assertFalse($values, "We don't support retrieving values for $field_name since it doesn't store data in the base table.");
+
+  }
+
+  /**
+   * DataProvider: a list of fields who store their data in the base table of a bundle.
+   *
+   * Each element describes a field instance and consists of:
+   *   - the machine name of the field (e.g. obi__organism).
+   *   - the machine name of the bundle (e.g. bio_data_17).
+   *   - an array of additional information including:
+   *       - instance_info: information about the field instance.
+   *       - field_info: information about the field.
+   *       - bundle: the TripalBundle object.
+   *       - bundle_base_table: if applicable, the chado base table the bundle stores it's data in.
+   *       - base_schema: the Tripal Schema array for the bundle_base_table.
+   */
+  public function getNonBaseFields() {
+
+    // Retrieve a list of fields to test.
+    // Note: this list is cached to improve performance.
+    $fields = $this->retrieveFieldList();
+
+    return $fields['field_chado_storage']['referring'];
+  }
+
+  /**
+   * Returns a list of Fields sorted by their backend, etc. for use in tests.
+   */
+  private function retrieveFieldList() {
+    if ($this->field_list === NULL) {
+
+      $this->field_list = array();
+
+      // field_info_instances() retrieves a list of all the field instances in the current site,
+      // indexed by the bundle it is attached to.
+      // @todo use fake bundles here to make these tests less dependant upon the current site.
+      $bundles = field_info_instances('TripalEntity');
+      foreach($bundles as $bundle_name => $fields) {
+
+        // Load the bundle object to later determine the chado table.
+        $bundle = tripal_load_bundle_entity(array('name'=> $bundle_name));
+
+        // For each field instance...
+        foreach ($fields as $field_name => $instance_info) {
+          $bundle_base_table = $base_schema = NULL;
+
+          // Load the field info.
+          $field_info = field_info_field($field_name);
+
+          // Determine the storage backend.
+          $storage = $field_info['storage']['type'];
+
+          // If this field stores it's data in chado...
+          // Determine the relationship between this field and the bundle base table.
+          $rel = NULL;
+          if ($storage == 'field_chado_storage') {
+
+            // We need to know the table this field stores it's data in.
+            $bundle_base_table = $bundle->data_table;
+            // and the schema for that table.
+            $base_schema = chado_get_schema($bundle_base_table);
+            // and the table this field stores it's data in.
+            $field_table = $instance_info['settings']['chado_table'];
+            $field_column = $instance_info['settings']['chado_column'];
+
+            // By default we simply assume there is some relationship.
+            $rel = 'referring';
+            $rel_table = NULL;
+            // If the field and bundle store their data in the same table
+            // then it's either a "base" or "foreign key" relationship
+            // based on the schema.
+            if ($bundle_base_table == $field_table) {
+
+              // We assume it's not a foreign key...
+              $rel = 'base';
+              // and then check the schema to see if we're wrong :-)
+              foreach ($base_schema['foreign keys'] as $schema_info) {
+                if (isset($schema_info['columns'][ $field_column ])) {
+                  $rel = 'foreign key';
+                  $rel_table = $schema_info['table'];
+                }
+              }
+            }
+          }
+
+          // Store all the info about bundle, field, instance, schema for use in the test.
+          $info = array(
+            'field_name' => $field_name,
+            'bundle_name' => $bundle_name,
+            'bundle' => $bundle,
+            'bundle_base_table' => $bundle_base_table,
+            'base_schema' => $base_schema,
+            'field_info' => $field_info,
+            'instance_info' => $instance_info,
+            'fk_table' => $rel_table,
+          );
+
+          // Create a unique key.
+          $key = $bundle_name . '--' . $field_name;
+
+          // If this bundle uses chado and we know the fields relationship to the base
+          // chado table, then we want to index the field list by that relationship.
+          if ($rel) {
+            $this->field_list[$storage][$rel][$key] = array(
+              $field_name,
+              $bundle_name,
+              $info
+            );
+          }
+          else {
+            $this->field_list[$storage][$key] = array(
+              $field_name,
+              $bundle_name,
+              $info
+            );
+          }
+
+        }
+      }
+    }
+
+    return $this->field_list;
+  }
+
+  /**
+   * Generate fake data for a given bundle.
+   *
+   * If only the first parameter is provided this function adds fake data to the indicated
+   * chado table. If the third parameter is provided the generated fake data will
+   * have a fixed value for the indicated column.
+   *
+   * @return
+   *   Returns FALSE if it was unable to create fake data.
+   */
+  private function generateFakeData($chado_table, $schema, $fixed_column = FALSE, $fk_table = FALSE) {
+    $faker = \Faker\Factory::create();
+
+    // First, do we have a factory? We can't generate data without one...
+    if (!Factory::exists('chado.'.$chado_table)) {
+      return FALSE;
+    }
+
+    // Create fake data -TripalTestSuite will use faker for all values.
+    if ($fixed_column === FALSE) {
+      factory('chado.'.$chado_table, 50)->create();
+      return TRUE;
+    }
+
+
+    // Attempt to create a fixed fake value.
+    // This needs to match the column type in the chado table and if the column is a
+    // foreign key, this value should match a fake record in the related table.
+    $fake_value = NULL;
+
+    // If we weren't told the related table then we assume this is a simple column (not a foreign key).
+    if ($fk_table === FALSE) {
+      $column_type = $schema[$fixed_column]['type'];
+      if (($column_type == 'int')) {
+        $fake_value = $faker->randomNumber();
+      }
+      elseif (($column_type == 'varchar') OR ($column_type == 'text')) {
+        $fake_value = $faker->words(2,TRUE);
+      }
+
+      if ($fake_value !== NULL) {
+        factory('chado.'.$chado_table)->create(array(
+          $fixed_column => $fake_value,
+        ));
+        return $fake_value;
+      }
+    }
+    // Otherwise, we need to create a fixed fake record in the related table and then
+    // use it in our fake data for the chado table.
+    else {
+      // Create our fixed fake record in the related table.
+      $fake_table_record = factory('chado.'.$fk_table)->create();
+
+      // Again, if we don't have a factory :-( there's nothing we can do.
+      if (!Factory::exists('chado.'.$fk_table)) {
+        return FALSE;
+      }
+
+      // Now create our fake records.
+      if (isset($fake_table_record->{$fk_table.'_id'})) {
+        factory('chado.'.$chado_table)->create(array(
+          $fixed_column => $fake_table_record->{$fk_table.'_id'},
+        ));
+
+        return $fake_table_record;
+      }
+    }
+
+    return FALSE;
+
+  }
+}

+ 21 - 0
tripal/includes/TripalFields/TripalField.inc

@@ -745,4 +745,25 @@ class TripalField {
   public function queryOrder($query, $order) {
 
   }
+
+  /**
+   * Used to retrieve a distinct list of values already used for the current field instance.
+   *
+   * @param $keyword
+   *   A string option used to filter the distinct list. This is used when creating an
+   *   autocomplete. For all distinct values, set this to NULL.
+   * @param $options
+   *   An array where options for how to generate this list can be specified. 
+   *   Supported options include:
+   *     - limit: how many results to limit to (Default: 25)
+   *     - label_string: a string with tokens that should be used to generate the
+   *         human-readable values in the returned list.
+   *
+   * @return
+   *   An array of values.
+   */
+  public function getValueList($options = array(), $keyword = NULL) {
+  
+  }
+
 }

+ 0 - 39
tripal/includes/tripal.fields.inc

@@ -82,45 +82,6 @@ function tripal_field_widget_info_alter(&$info) {
 
 }
 
-/**
- * Implements hook_field_views_data();
- */
-function tripal_field_views_data($field) {
-  $data = array();
-
-  // Skip fields that aren't attached to TripalEntity entities.
-  if (!array_key_exists('TripalEntity', $field['bundles'])) {
-    return $data;
-  }
-
-  if (!array_key_exists('tripal_storage_api', $field['storage']['settings'])) {
-    return $data;
-  }
-
-  $field_type = $field['type'];
-  $field_types = tripal_get_field_types();
-
-  // Iterate through the bundles to which this field is attached and
-  // if it is a TripalField field then we'll call the viewsDataAlater function.
-  $bundles = $field['bundles']['TripalEntity'];
-  foreach ($bundles as $bundle_name) {
-    $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
-    if (in_array($field_type, $field_types)) {
-      $tfield = new $field_type($field, $instance);
-
-      // Fields should be associated with the bundle's term identifier
-      // (i.e. [vocab]__[accession].
-      $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
-      $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-      $view_base_id = $term->vocab->vocabulary . '__' . $term->accession;
-
-      $data += $tfield->viewsData($view_base_id);
-    }
-  }
-
-  return $data;
-}
-
 /**
  * Implements hook_field_formatter_info().
  */

+ 67 - 34
tripal/tripal.views.inc

@@ -28,9 +28,11 @@ function tripal_views_plugins() {
  */
 function tripal_views_data() {
   $data = array();
-  // Job Management System
+  // Job Management System.
   tripal_views_data_jobs($data);
-  tripal_views_data_tripal_entity($data);
+  // Add all TripalEntity bundles. 
+  tripal_views_data_tripal_bundles($data);
+  // Add all the TripalFields for each bundle.
   tripal_views_data_fields($data);
 
   $data['views']['tripal_area_collections'] = array(
@@ -116,60 +118,91 @@ function tripal_views_data_alter(&$data) {
  * Integreates the Tripal fields with Views.
  */
 function tripal_views_data_fields(&$data) {
-
+  
+  // Get the bundle details so we only look this up once.
+  $all_bundles = [];
+  $sql = "
+    SELECT TB.name, TV.vocabulary, TT.accession
+    FROM {tripal_bundle} TB
+      INNER JOIN {tripal_term} TT on TT.id = TB.term_id
+      INNER JOIN {tripal_vocab} TV on TV.id = TT.vocab_id
+  ";
+  $results = db_query($sql);
+  while ($bundle = $results->fetchObject()) {
+    $all_bundles[$bundle->name] = $bundle;
+  }
+  
+  // Get all of the TripalField types.
+  $tripal_field_types = tripal_get_field_types();
+  
   // Iterate through the fields.
   $fields = field_info_fields();
   foreach ($fields as $field) {
+    $field_type = $field['type'];
 
     // Skip fields that aren't attached to TripalEntity entities.
     if (!array_key_exists('TripalEntity', $field['bundles'])) {
       continue;
     }
 
-    // Fields that don't connect to the Tripal Storage API should be added differently
+    // Fields that don't connect to the Tripal Storage API should be added 
+    // differently
     if (!array_key_exists('tripal_storage_api', $field['storage']['settings'])) {
       continue;
-    }
+    }    
+
+    // Get the field data for views.
+    $fdata = [];    
 
-    // TODO: do we really need this hook? Just substitute the code here
-    // for what that hook does... it's only called in one plac.e
-    // Call the hook_field_views_data() but only for the tripal module.
-    // Otherwise the other modules will expect that this is an SQL-based
-    // view.
-    $result = (array) module_invoke('tripal', 'field_views_data', $field);
-
-    // Set defaults for the field if no data array was returned.
-    if (empty($result)) {
-      // Iterate through the bundles to which this field is attached and
-      // if it is a TripalField field then we'll call the viewsData function.
-      $bundles = $field['bundles']['TripalEntity'];
-      $result = array();
-      foreach ($bundles as $bundle_name) {
-        $field_name = $field['field_name'];
-
-        // Fields should be associated with the bundle's term identifier
-        // (i.e. [vocab]__[accession].
-        $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
-        $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-        $view_base_id = $term->vocab->vocabulary . '__' . $term->accession;
-
-        $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
+    // Iterate through the bundles to which this field is attached. If the field
+    // is attached to only one bundle then it comes as a scalar and we need
+    // to make it an array.
+    $bundles = $field['bundles']['TripalEntity'];
+    if (!is_array($bundles)) {
+      $bundles = [$bundles];
+    }
+    foreach ($bundles as $bundle_name) {
+      
+      // Sometimes a field may be attached to a bundle that may have been
+      // deleted.  Let's skip those.
+      if (!in_array($bundle_name, array_keys($all_bundles))) {
+        continue;
+      }        
+
+      // Fields should be associated with the bundle's term identifier
+      // (i.e. [vocab]__[accession].
+      $vocabulary = $all_bundles[$bundle_name]->vocabulary;
+      $accession = $all_bundles[$bundle_name]->accession;
+      $view_base_id = $vocabulary . '__' . $accession;
+            
+      // Fields that aren't a TripalField class should be handled using the 
+      // generic TripalField::viewsData function.
+      $instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
+      if (!in_array($field_type, $tripal_field_types)) {
         $tfield = new TripalField($field, $instance);
-        $result += $tfield->viewsData($view_base_id);
+        $fdata += $tfield->viewsData($view_base_id);
+      }
+      // Fields that are a TripalField class can call the viewsData function
+      // for that class.
+      else {
+        $tfield = new $field_type($field, $instance);
+        $fdata += $tfield->viewsData($view_base_id);
       }
     }
 
-    drupal_alter('field_views_data', $result, $field, $module);
+    // Call the hook_field_views_data_alter function.
+    drupal_alter('field_views_data', $fdata, $field);
 
-    if (is_array($result)) {
-      $data = drupal_array_merge_deep($result, $data);
+    if (is_array($fdata)) {
+      $data = drupal_array_merge_deep($fdata, $data);
     }
   }
 }
+
 /**
- * Integrates the TripalEntity entities with Drupal Views.
+ * Integrates the TripalEntity bundles with Drupal Views.
  */
-function tripal_views_data_tripal_entity(&$data) {
+function tripal_views_data_tripal_bundles(&$data) {
 
   // Get the list of all of the bundles (entity types) and add them
   // as "base tables" for views.

+ 165 - 0
tripal_chado/includes/TripalFields/ChadoField.inc

@@ -149,6 +149,171 @@ class ChadoField extends TripalField {
     }
   }
 
+  /**
+   * Used to retrieve a distinct list of values already used for the current field instance.
+   *
+   * @param $keyword
+   *   A string option used to filter the distinct list. This is used when creating an
+   *   autocomplete. For all distinct values, set this to NULL.
+   * @param $options
+   *   An array where options for how to generate this list can be specified. 
+   *   Supported options include:
+   *     - limit: how many results to limit to (Default: 25)
+   *     - label_string: a string with tokens that should be used to generate the
+   *         human-readable values in the returned list.
+   *
+   * The following example shows you how to pull all the value list for a specific instance 
+   * of a field.
+   * @code
+      // In this example we want the values for the obi__organism field
+      // attached to the Tripal Content Type with a machine name of bio_data_17:
+      $field_name = 'obi__organism';
+      $bundle_name = 'bio_data_17';
+
+      // The following two calls get information about the field we want the values for.
+      $field_info = field_info_field($field_name);
+      $instance_info = field_info_instance('TripalEntity', $field_name, $bundle_name);
+      // Construct the Field instance we want the values for.
+      $instance = new ChadoField($field_info, $instance_info);
+
+      // Retrieve the values.
+      // $values will be an array containing the distinct set of values for this field instance.
+      $values = $instance->getValueList();
+   * @endcode
+   *
+   * @return
+   *   An array of values.
+   */
+  public function getValueList($options = array(), $keyword = NULL) {
+    $values = array();
+
+    // Set some defaults.
+    $options['limit'] = (isset($options['limit'])) ? $options['limit'] : 25;
+    $options['label_string'] = (isset($options['label_string'])) ? $options['label_string'] : '';
+
+    // Make sure we know the chado table and column.
+    // If not, we can't give them a list *shrugs*.
+    if (!isset($this->instance['settings']['chado_table']) OR !isset($this->instance['settings']['chado_column'])) {
+      tripal_report_error(
+        'TripalField',
+        TRIPAL_WARNING,
+        'Values List: Unable to generate a values list for %field_name since we don\'t know it\'s chado table/column.',
+        array('%field_name' => $this->instance['field_name'])
+      );
+      return FALSE;
+    }
+
+    // First get some important info about the chado table.column this field is attached to.
+    $chado_table = $this->instance['settings']['chado_table'];
+    $chado_column = $this->instance['settings']['chado_column'];
+    $base_table = $this->instance['settings']['base_table'];
+    $bschema = chado_get_schema($base_table);
+
+    // Now build the distinct query.
+    if ($chado_table == $base_table) {
+
+      // Is the current column a foreign key to another table?
+      $is_fk = FALSE;
+      $fk_table = $fk_column = NULL;
+      foreach ($bschema['foreign keys'] as $k => $v) {
+        if (isset($v['columns'][$chado_column])) {
+          $is_fk = TRUE;
+          $fk_table = $v['table'];
+          $fk_column = $v['columns'][$chado_column];
+        }
+      }
+
+      // Check if this column is a foreign key to another one.
+      // If so we would like to travel through the relationship
+      // to capture a better human-readable option.
+      if ($is_fk) {
+
+        // Determine the query.
+        $sql = "SELECT base.$chado_column as id, fk.*
+                FROM {".$chado_table."} base
+                LEFT JOIN {".$fk_table."} fk ON base.$chado_column=fk.$fk_column
+                GROUP BY base.$chado_column, fk.$fk_column
+                LIMIT ".$options['limit'];
+
+        // Choose a default label string, if needed.
+        if (empty($options['label_string'])) {
+          $fkschema = chado_get_schema($fk_table);
+          if (isset($fkschema['fields']['name'])) {
+            $options['label_string'] = '[name]';
+          }
+          elseif (isset($fkschema['fields']['uniquename'])) {
+            $options['label_string'] = '[uniquename]';
+          }
+          elseif (isset($fkschema['fields']['accession'])) {
+            $options['label_string'] = '[accession]';
+          }
+          elseif (isset($fkschema['fields']['title'])) {
+            $options['label_string'] = '[title]';
+          }
+          elseif ($fk_table == 'organism') {
+            $options['label_string'] = '[genus] [species]';
+          }
+          else {
+            tripal_report_error(
+              'TripalField',
+              TRIPAL_WARNING,
+              'Values List: Unable to generate a default human-readable label for %field_name since there is no name/uniquename column. Please set the options[label_string].',
+              array('%field_name' => $this->instance['field_name'])
+            );
+            return FALSE;
+          }
+        }
+      }
+      // Not a foreign key, so just make the key and value from the base table.
+      else {
+        $sql = "SELECT $chado_column as id, $chado_column
+                FROM {".$chado_table."} base
+                GROUP BY $chado_column
+                LIMIT ".$options['limit'];
+
+        // Choose a default label string, if needed.
+        if (empty($options['label_string'])) {
+          $options['label_string'] = '[' . $chado_column . ']';
+        }
+      }
+    }
+    else {
+      tripal_report_error(
+        'TripalField',
+        TRIPAL_WARNING,
+        'Unable to retrieve a values list for %field_name since it is not a direct column in %base',
+        array('%field_name' => $this->instance['field_name'], '%base' => $base_table)
+      );
+      return FALSE;
+    }
+
+    $results = chado_query($sql);
+
+    // Pre-process the label string for better performance.
+    // Each token is enclosed in square brackets and should be the name of a chado column.
+    preg_match_all('/\[(\w+)\]/', $options['label_string'], $matches);
+    $tokens = $matches[1];
+
+    foreach ($results as $r) {
+      // Determine the label using the label_string option.
+      $label = $options['label_string'];
+      $replace = array();
+      foreach ($tokens as $column) {
+        if (isset($r->{$column})) {
+          $replace[ "[$column]" ] = $r->{$column};
+        }
+        else {
+          $replace[ "[$column]" ] = "";
+        }
+      }
+
+      // Set the value.
+      $values[$r->id] = strtr($options['label_string'], $replace);
+    }
+
+    return $values;
+  }
+
   /**
    * @see TripalField::instanceSettingsForm()
    */

+ 1 - 1
tripal_chado/includes/tripal_chado.fields.inc

@@ -3194,7 +3194,7 @@ function tripal_chado_form_tripalbundle_form_alter(&$form, $form_state) {
 /**
  * Implements hook_field_views_data_alter();
  */
-function tripal_chado_field_views_data_alter(&$result, $field, $module) {
+function tripal_chado_field_views_data_alter(&$result, $field) {
 
   // This module creates the data__image field for managing images on
   // bio_data content types. But we want it to render correctly

+ 0 - 131
tripal_chado/tripal_chado.views.inc

@@ -22,137 +22,6 @@ function tripal_chado_views_data() {
   return $data;
 }
 
-// /**
-//  * Implements hook_views_data_alter().
-//  */
-// function tripal_chado_views_data_alter(&$data) {
-
-//   // Adds integration for chado-based fields.
-//   tripal_chado_add_field_views_data($data);
-
-//   return $data;
-// }
-
-// /**
-//  * Adds integration for chado-based fields.
-//  *
-//  * We can't use hook_field_view_data since this only works when the
-//  * storage engine is of type 'field_sql_storage' and of course,
-//  * ours is not. Thus we create our own implementation of field_views_data()
-//  * for our storage engine.
-//  */
-// function tripal_chado_add_field_views_data(&$data) {
-//   foreach (field_info_fields() as $field) {
-//     if ($field['storage']['type'] != 'field_chado_storage') {
-//       continue;
-//     }
-
-//     $field_name = $field['field_name'];
-//     $field_type = $field['type'];
-
-
-//     // Currently, we only handle integration of chado fields with TripalEntity.
-//     // @todo: extend this to work with other entities in the future.
-//     if (isset($field['bundles']['TripalEntity']) AND isset($field['settings']['chado_column'])) {
-
-//       // We currently don't support prop tables for views integration due
-//       // in part to the multiple values but also b/c we can't indicate which
-//       // type of property to show. Thus, instead of warning the user,
-//       // we just won't integrate it at this time.
-//       // @todo: Handle property fields.
-//       if (preg_match('/prop$/', $field['settings']['chado_table'])) {
-//         continue;
-//       }
-
-//       // Get some information about the chado table in order to make good
-//       // default choices for handlers.
-//       $table_desc = chado_get_schema($field['settings']['chado_table']);
-//       $field_defn = $table_desc['fields'][ $field['settings']['chado_column'] ];
-
-//       // We also need to know if this field is a foreign key.
-//       $fk_defn = FALSE;
-//       foreach ($table_desc['foreign keys'] as $details) {
-//         foreach ($details['columns'] as $left_field => $right_field) {
-//           if ($left_field == $field['settings']['chado_column']) {
-//             $fk_defn = array(
-//               'left_table' => $field['settings']['chado_table'],
-//               'left_field' => $left_field,
-//               'right_table' => $details['table'],
-//               'right_field' => $right_field,
-//             );
-//           }
-//         }
-//       }
-
-//       // Unfortunatly we can't use the field label since that is set at the
-//       // instance level and fields are integrated at the field level (independant of bundle).
-//       // Thus we will simply make the most readable and informative field name we can.
-//       $data['tripal_entity'][$field_name]['title'] = ucfirst(str_replace('_',' ',$field['settings']['chado_table']))
-//         . ': ' .ucfirst(str_replace('_',' ',$field['settings']['chado_column']));
-
-//       // The help should be 'Appears in: TripalEntity: gene, organism'
-//       // so that users know where they can use it. This requires a little extra work since all
-//       // we have access to at this point is bio_data_2, bio_data_4 but since that's not very
-//       // informative, extra work is worth it ;-).
-//       $entity_info = entity_get_info('TripalEntity');
-//       $bundle_labels = array();
-//       foreach ($field['bundles']['TripalEntity'] as $bundle_id) {
-//         $bundle_labels[] = $entity_info['bundles'][$bundle_id]['label'];
-//       }
-//       $data['tripal_entity'][$field_name]['help'] = 'Appears in: TripalEntity:' . implode(', ', $bundle_labels);
-
-//       // Define the field.
-//       $data['tripal_entity'][$field_name]['field']['chado_field'] = $field['settings']['chado_column'];
-//       $data['tripal_entity'][$field_name]['field']['chado_table'] = $field['settings']['chado_table'];
-//       $data['tripal_entity'][$field_name]['field']['field_name'] = $field['field_name'];
-//       $data['tripal_entity'][$field_name]['field']['entity_table'] = 'tripal_entity';
-//       $data['tripal_entity'][$field_name]['field']['entity_type'] = 'TripalEntity';
-//       $data['tripal_entity'][$field_name]['field']['bundles'] = $field['bundles']['TripalEntity'];
-//       $data['tripal_entity'][$field_name]['field']['handler'] = 'chado_views_handler_field';
-//       $data['tripal_entity'][$field_name]['field']['click sortable'] = FALSE;
-
-//       // Define the Filter.
-//       $data['tripal_entity'][$field_name]['filter']['chado_field'] = $field['settings']['chado_column'];
-//       $data['tripal_entity'][$field_name]['filter']['chado_table'] = $field['settings']['chado_table'];
-//       $data['tripal_entity'][$field_name]['filter']['field_name'] = $field['field_name'];
-//       $data['tripal_entity'][$field_name]['filter']['entity_table'] = 'tripal_entity';
-//       $data['tripal_entity'][$field_name]['filter']['entity_type'] = 'TripalEntity';
-//       $data['tripal_entity'][$field_name]['filter']['bundles'] = $field['bundles']['TripalEntity'];
-//       $data['tripal_entity'][$field_name]['filter']['handler'] = 'chado_views_handler_filter_string';
-
-//       // Define sorting.
-//       $data['tripal_entity'][$field_name]['sort']['chado_field'] = $field['settings']['chado_column'];
-//       $data['tripal_entity'][$field_name]['sort']['chado_table'] = $field['settings']['chado_table'];
-//       $data['tripal_entity'][$field_name]['sort']['field_name'] = $field['field_name'];
-//       $data['tripal_entity'][$field_name]['sort']['entity_table'] = 'tripal_entity';
-//       $data['tripal_entity'][$field_name]['sort']['entity_type'] = 'TripalEntity';
-//       $data['tripal_entity'][$field_name]['sort']['bundles'] = $field['bundles']['TripalEntity'];
-//       $data['tripal_entity'][$field_name]['sort']['handler'] = 'chado_views_handler_sort';
-
-//       // Specify special handlers.
-//       if ($fk_defn) {
-//         $data['tripal_entity'][$field_name]['filter']['handler'] = 'chado_views_handler_filter_fk';
-//         $data['tripal_entity'][$field_name]['filter']['foreign_key'] = $fk_defn;
-//       }
-//       if ($field_defn['type'] == 'boolean') {
-//         $data['tripal_entity'][$field_name]['filter']['handler'] = 'chado_views_handler_filter_boolean';
-//         $data['tripal_entity'][$field_name]['filter']['label'] = $field['settings']['chado_column'];
-//         $data['tripal_entity'][$field_name]['filter']['type'] = 'yes-no';
-//       }
-//       elseif ($field_defn['type'] == 'datetime') {
-//         $data['tripal_entity'][$field_name]['filter']['handler'] = 'chado_views_handler_filter_date';
-//       }
-
-//       // Allow the fields to alter the default selections from above.
-//       tripal_load_include_field_type($field_type);
-//       if (preg_match('/^chado/', $field_type) and class_exists($field_type)) {
-//         $field_obj = new $field_type($field);
-//         $field_obj->views_data_alter($data['tripal_entity'][$field_name], $field, $entity_info);
-//       }
-//     }
-//   }
-// }
-
 /**
  * Provides the data array for the tripal custom tables management
  *

+ 2 - 2
tripal_ds/tripal_ds.module

@@ -167,14 +167,14 @@ function tripal_ds_ds_field_settings_alter(&$field_settings, $form, $form_state)
 function tripal_ds_bundle_menu_item($bundle_name, $field_label, $field_name, $entity_type){
   //Check the record does not already exist
   $tripal_ds_rows = db_select('tripal_ds', 'ds')
-    ->fields('ds', array('tripal_ds_field_name', 'tripal_ds_field_label'))
+    ->fields('ds', array('tripal_ds_field_name', 'tripal_ds_field_label', 'bundle'))
     ->condition('bundle', $bundle_name, '=')
     ->condition('tripal_ds_field_label', $field_label, '=')
     ->condition('tripal_ds_field_name', $field_name, '=')
     ->execute()->fetchAll();
   if(!empty($tripal_ds_rows)){
     foreach ($tripal_ds_rows as $tripal_ds_row){
-      if(($field_label == $tripal_ds_row->tripal_ds_field_label) && ($field_name == $tripal_ds_row->tripal_ds_field_name) && ($bundle_name == $tripal_ds_rows->bundle)) {
+      if(($field_label == $tripal_ds_row->tripal_ds_field_label) && ($field_name == $tripal_ds_row->tripal_ds_field_name) && ($bundle_name == $tripal_ds_row->bundle)) {
         // Do not write the field to the table
         drupal_set_message("Could not update the bundle menu because that field already exists.", 'error');
       }

+ 12 - 5
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -153,6 +153,15 @@ class remote__data extends WebServicesField {
 
     // Make the request.
     $data = $this->makeRemoteRequest($query_str);
+    $context = [];
+    if (is_array($data['@context'])) {
+      $contenxt = $data['@context'];
+    }
+    else {
+      $context = json_decode(file_get_contents($data['@context']), TRUE);
+      $context = $context['@context'];
+    }
+
     if(!$data){
       $entity->{$field_name}['und'][0]['value'] = 'ERROR: there was a problem retrieving content for this field.';
       $entity->{$field_name}['und'][0]['admin_message'] =  "The remote service returned no data.";
@@ -201,7 +210,7 @@ class remote__data extends WebServicesField {
       $query_field = $subfields[0];
 
       // Next get the the details about this member.
-      $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
+      $query_field_url =  $context[$content_type] . '/' . $remote_entity_id . '/' . $query_field;
       $field_data = $this->makeRemoteRequest($query_field_url);
 
       // If we encounter any type of error, we'll reset the field and return.
@@ -408,10 +417,8 @@ class remote__data extends WebServicesField {
     return $element;
   }
 
- /**
-   *
-   * @param unknown $form
-   * @param unknown $form_state
+  /**
+   * @see TripalField::instanceSettingsFormValidate()
    */
   public function instanceSettingsFormValidate($form, &$form_state) {
     $site_logo = $form_state['values']['instance']['settings']['data_info']['site_logo'];

+ 1 - 4
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -10,10 +10,7 @@ class TripalContentService_v0_1 extends TripalWebService {
   /**
    * A bit of text to describe what this service provides.
    */
-  public static $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)).';
+  public static $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)).';
 
   /**
    * A machine-readable type for this service. This name must be unique

Some files were not shown because too many files changed in this diff