Преглед на файлове

Merge branch '7.x-3.x' into 423-tv3-remote_data_field

Stephen Ficklin преди 6 години
родител
ревизия
772031d06f
променени са 43 файла, в които са добавени 1316 реда и са изтрити 339 реда
  1. 1 1
      legacy/tripal_phylogeny/tripal_phylogeny.info
  2. 231 61
      tripal/api/tripal.entities.api.inc
  3. 1 0
      tripal/api/tripal.jobs.api.inc
  4. 16 1
      tripal/api/tripal.notice.api.inc
  5. 28 1
      tripal/api/tripal.terms.api.inc
  6. 55 5
      tripal/includes/TripalBundleUIController.inc
  7. 40 44
      tripal/includes/TripalEntityController.inc
  8. 1 2
      tripal/includes/TripalEntityUIController.inc
  9. 11 1
      tripal/includes/TripalFields/TripalFieldWidget.inc
  10. 3 3
      tripal/includes/TripalImporter.inc
  11. 1 1
      tripal/includes/TripalJob.inc
  12. 7 6
      tripal/includes/tripal.bulk_update.inc
  13. 22 0
      tripal/includes/tripal.field_storage.inc
  14. 37 5
      tripal/includes/tripal.fields.inc
  15. 1 0
      tripal_chado/api/tripal_chado.api.inc
  16. 17 3
      tripal_chado/api/tripal_chado.query.api.inc
  17. 4 1
      tripal_chado/api/tripal_chado.variables.api.inc
  18. 13 2
      tripal_chado/includes/TripalFields/chado_linker__prop/chado_linker__prop_widget.inc
  19. 8 0
      tripal_chado/includes/TripalFields/data__accession/data__accession_widget.inc
  20. 14 3
      tripal_chado/includes/TripalFields/data__sequence/data__sequence_widget.inc
  21. 201 0
      tripal_chado/includes/TripalFields/efo__array_design/efo__array_design.inc
  22. 37 0
      tripal_chado/includes/TripalFields/efo__array_design/efo__array_design_formatter.inc
  23. 75 0
      tripal_chado/includes/TripalFields/efo__array_design/efo__array_design_widget.inc
  24. 0 4
      tripal_chado/includes/TripalFields/local__contact/local__contact_widget.inc
  25. 2 2
      tripal_chado/includes/TripalFields/obi__organism/obi__organism.inc
  26. 145 0
      tripal_chado/includes/TripalFields/operation__analysis/operation__analysis.inc
  27. 33 0
      tripal_chado/includes/TripalFields/operation__analysis/operation__analysis_formatter.inc
  28. 72 0
      tripal_chado/includes/TripalFields/operation__analysis/operation__analysis_widget.inc
  29. 2 2
      tripal_chado/includes/TripalFields/sbo__database_cross_reference/sbo__database_cross_reference_widget.inc
  30. 6 6
      tripal_chado/includes/TripalFields/schema__additional_type/schema__additional_type_widget.inc
  31. 1 1
      tripal_chado/includes/TripalFields/schema__alternate_name/schema__alternate_name_widget.inc
  32. 1 1
      tripal_chado/includes/TripalFields/schema__publication/schema__publication_widget.inc
  33. 2 2
      tripal_chado/includes/TripalFields/sio__annotation/sio__annotation_widget.inc
  34. 5 7
      tripal_chado/includes/loaders/tripal_chado.pub_importer_PMID.inc
  35. 22 8
      tripal_chado/includes/loaders/tripal_chado.pub_importers.inc
  36. 20 8
      tripal_chado/includes/tripal_chado.field_storage.inc
  37. 158 6
      tripal_chado/includes/tripal_chado.fields.inc
  38. 6 1
      tripal_chado/includes/tripal_chado.phylotree.inc
  39. 1 0
      tripal_chado/includes/tripal_chado.pub_search.inc
  40. 8 3
      tripal_chado/tripal_chado.module
  41. 3 142
      tripal_ws/includes/TripalFields/remote__data/remote__data_widget.inc
  42. 0 5
      tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc
  43. 5 1
      tripal_ws/tripal_ws.module

+ 1 - 1
legacy/tripal_phylogeny/tripal_phylogeny.info

@@ -2,7 +2,7 @@ name = Tripal Phylotree
 description = Supports the phylogeny tables of Chado by providing pages for viewing and editing of phylotrees. Table list at: http://gmod.org/wiki/chado_phylotree_Module. This module was added to Tripal through collaboration with the LegumeInformation group (http://http://legumeinfo.org/)
 core = 7.x
 project = tripal_phylogeny
-package = Tripal Extensions
+package = Tripal v2 Legacy
 version = 7.x-3.0-rc2
 dependencies[] = tripal_core
 dependencies[] = tripal_cv

+ 231 - 61
tripal/api/tripal.entities.api.inc

@@ -358,8 +358,7 @@ function tripal_load_bundle_entity($values) {
 }
 
 /**
- * Allows a module to write to the admin notification table
- * during the cron run.
+ * Allows a module to write to the admin notification table.
  *
  * @param $title
  *   A generic phrase indicating what the notification is for.
@@ -628,6 +627,8 @@ function tripal_get_content_type($bundle_name) {
  *
  * @param $bundle_name
  *   The name of the bundle to refresh (e.g. bio_data_4).
+ * @param $term
+ *   The term object for the bundle.
  *
  * @return
  *   The array of field instance names that were added.
@@ -635,38 +636,34 @@ function tripal_get_content_type($bundle_name) {
  * @ingroup tripal_entities_api
  */
 function tripal_create_bundle_fields($bundle, $term) {
-
   $added = array();
 
   // Allow modules to add fields to the new bundle.
   $modules = module_implements('bundle_fields_info');
-  $info = array();
+  $field_info = array();
   foreach ($modules as $module) {
     $function = $module . '_bundle_fields_info';
     $temp = $function('TripalEntity', $bundle);
     if (is_array($temp)) {
       // TODO: it would be good to check this array to make sure it follows
       // protocol.  It would help identify potential errors.
-      $info = array_merge($info, $temp);
+      $field_info = array_merge($field_info, $temp);
     }
   }
 
   // Allow modules to alter which fields should be attached to content
   // types they create.
-  drupal_alter('bundle_fields_info', $info, $bundle, $term);
+  drupal_alter('bundle_fields_info', $field_info, $bundle, $term);
 
   // Iterate through all of the fields and create them.
-  foreach ($info as $field_name => $details) {
+  foreach ($field_info as $field_name => $details) {
     $field_type = $details['type'];
 
-    // TODO: make sure the field term exits. If not then
-    // skip it.
-
     // If the field already exists then skip it.
     $field = field_info_field($details['field_name']);
     if ($field) {
       continue;
-    }
+    }    
 
     // Create the field.
     $field = field_create_field($details);
@@ -678,35 +675,99 @@ function tripal_create_bundle_fields($bundle, $term) {
 
   // Allow modules to add instances to the new bundle.
   $modules = module_implements('bundle_instances_info');
-  $info = array();
+  $instance_info = array();
   foreach ($modules as $module) {
     $function = $module . '_bundle_instances_info';
     $temp = $function('TripalEntity', $bundle);
     if (is_array($temp)) {
       // TODO: it would be good to check this array to make sure it follows
       // protocol.  It would help identify potential errors.
-      $info = array_merge($info, $temp);
+      $instance_info = array_merge($instance_info, $temp);
     }
   }
 
   // Allow modules to alter which fields should be attached to content
   // types they create.
-  drupal_alter('bundle_instances_info', $info, $bundle, $term);
-
+  drupal_alter('bundle_instances_info', $instance_info, $bundle, $term);
+  
+  // Get the list of existing instances
+  $existing_instances = field_info_instances('TripalEntity', $bundle->name);
+  
   // Iterate through all of the field instances and create them.
-  foreach ($info as $field_name => $details) {
+  foreach ($instance_info as $instance_name => $details) {
+    
+    // Make sure the instance has a term. If not, report it and skip the field.
+    if (!array_key_exists('term_vocabulary', $details['settings'])) {
+      tripal_report_error('tripal_fields', TRIPAL_WARNING,
+        'The field instance, !field, is missing the "term_vocabulary" setting. The field instance cannot be added. Please check the field settings.',
+        ['!field' => $instance_name], ['drupal_set_message' => TRUE]);
+        continue;
+    }
+    if (!array_key_exists('term_accession', $details['settings'])) {
+      tripal_report_error('tripal_fields', TRIPAL_WARNING,
+        'The field instance, !field, is missing the "term_accession" setting. The field instance cannot be added. Please check the field settings.',
+        ['!field' => $instance_name], ['drupal_set_message' => TRUE]);
+        continue;}
+    
+    // Make sure the term exists. If not, skip the field instance and 
+    // report an error.
+    $field_term_id = $details['settings']['term_vocabulary'] . ':' . $details['settings']['term_accession'];    
+    $field_term = tripal_get_term_details($details['settings']['term_vocabulary'], $details['settings']['term_accession']);
+    if (!$field_term) {
+      tripal_report_error('tripal_fields', TRIPAL_WARNING, 
+        'The term, !term, for the field, !field, does not exist in the database. The  ' .
+        'field instance cannot be added. Please make sure the term is correct and add it if necessary.',
+        ['!term' => $field_term_id,
+          '!field' => $instance_name],
+        ['drupal_set_message' => TRUE]);
+      continue;
+    }
+    
+    // Make sure the term is not used for any other existing field instance.
+    $skip = TRUE;
+    foreach ($existing_instances as $existing_name => $existing_instance) {
+      // If this instance term is the same as this exsiting term and the 
+      // instance name is not the same then we have a problem.
+      $existing_term_id = $existing_instance['settings']['term_vocabulary'] . ':' . $existing_instance['settings']['term_accession'];
+      $existing_field = field_info_field($existing_name);
+      if ($existing_term_id == $field_term_id and $instance_name != $existing_name) {
+        tripal_report_error('tripal_fields', TRIPAL_WARNING,
+          'The field, !field, uses a term, !term, that is already in use on this content type. The ' .
+          'field instance cannot be added.',
+          ['!term' => $existing_term_id,
+            '!field' => $instance_name],
+          ['drupal_set_message' => TRUE]);
+        $skip = TRUE;
+      }
+      
+      // If the instance term is the same as this exsting term but the storage
+      // types are different then we have a problem.
+      $existing_storage = $existing_field['storage']['type'];
+      $this_field_storage = $field_info[$details['field_name']]['storage']['type'];
+      if ($existing_term_id == $field_term_id and $existing_storage != $this_field_storage) {
+        tripal_report_error('tripal_fields', TRIPAL_WARNING,
+          'The field, !field, provided by the storage type, !type, uses a term, !term, that is already in use on this content type and provided by another storage backend. The ' .
+          'field instance cannot be added.  Perhaps, consider a different term and adjust the data in the database.',
+          ['!term' => $existing_term_id,
+           '!type' => $this_field_storage,
+           '!field' => $instance_name],
+          ['drupal_set_message' => TRUE]);
+        $skip = TRUE;
+      }
+    }
+    if ($skip) {
+      continue;
+    }
 
     // If the field is already attached to this bundle then skip it.
-    $field = field_info_field($details['field_name']);
-    if ($field and array_key_exists('bundles', $field) and
-        array_key_exists('TripalEntity', $field['bundles']) and
-        in_array($bundle->name, $field['bundles']['TripalEntity'])) {
+    if (array_key_exists($instance_name, $existing_instances)) {
       continue;
     }
 
     // Create the field instance.
     $instance = field_create_instance($details);
-    $added[] = $field_name;
+    $existing_instances[$instance_name] = $instance;
+    $added[] = $instance_name;
   }
   return $added;
 }
@@ -1008,14 +1069,14 @@ function tripal_get_default_title_format($bundle) {
 /**
  * Returns an array of tokens based on Tripal Entity Fields.
  *
- * @param TripalBundle $entity
+ * @param TripalBundle $bundle
  *    The bundle entity for which you want tokens.
  * @return
  *    An array of tokens where the key is the machine_name of the token.
  *
  * @ingroup tripal_entities_api
  */
-function tripal_get_entity_tokens($entity, $options = array()) {
+function tripal_get_entity_tokens($bundle, $options = array()) {
   $tokens = array();
 
   // Set default options.
@@ -1024,51 +1085,132 @@ function tripal_get_entity_tokens($entity, $options = array()) {
 
   if ($options['include id']) {
     $token = '[TripalBundle__bundle_id]';
-    $tokens[$token] = array(
+    $tokens[$token] = [
       'label' => 'Bundle ID',
       'description' => 'The unique identifier for this Tripal Content Type.',
       'token' => $token,
       'field_name' => NULL,
       'required' => TRUE
-    );
+    ];
 
     $token = '[TripalEntity__entity_id]';
-    $tokens[$token] = array(
+    $tokens[$token] = [
       'label' => 'Content/Entity ID',
       'description' => 'The unique identifier for an individual piece of Tripal Content.',
       'token' => $token,
       'field_name' => NULL,
       'required' => TRUE
-    );
+    ];
   }
 
-  $fields = field_info_instances('TripalEntity', $entity->name);
-  foreach ($fields as $f) {
-
-    // Build the token from the field information.
-    $token = '[' . $f['field_name'] . ']';
-    $current_token = array(
-      'label' => $f['label'],
-      'description' => $f['description'],
-      'token' => $token,
-      'field_name' => $f['field_name'],
-      'required' => $f['required']
-    );
-
-    // If the required only option is set then we only want to add
-    // required fields to the token list.
-    if ($options['required only'] AND $current_token['required']) {
-      $tokens[$token] = $current_token;
+  $instances = field_info_instances('TripalEntity', $bundle->name);
+  foreach ($instances as $instance_name => $instance) {
+    
+    if (!$instance['required'] and $options['required only']) {
+      continue;
     }
-    // If the required only option is not set then add everything.
-    elseif (!$options['required only']) {
-      $tokens[$token] = $current_token;
+    
+    $use_field = FALSE;
+   
+    // Iterate through the TripalEntity fields and see if they have
+    // sub-elements, if so, add those as tokens too.
+    $field_name = $instance['field_name'];
+    if ($instance['entity_type'] == 'TripalEntity') {
+      if (tripal_load_include_field_class($field_name)) {
+        $field = field_info_field($field_name);
+        $field_obj = new $field_name($field, $instance);
+        $element_info = $field_obj->elementInfo();
+        $term_id = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
+        if ($element_info and 
+            array_key_exists($term_id, $element_info) and 
+            array_key_exists('elements', $element_info[$term_id]) and count($element_info[$term_id]['elements']) > 0) {
+          $elements = $element_info[$term_id]['elements'];
+          _tripal_get_entity_tokens_for_elements($instance, $field_name, $elements, $tokens, $options);
+        }
+        else {
+          $use_field = TRUE;
+        }
+      }
+      else {
+        $use_field = TRUE;
+      }
+    }
+    else {
+      $use_field = TRUE;
+    }
+    
+    // If we have no elements to add then just add the field as is.
+    if ($use_field) {
+      // Build the token from the field information.
+      $token = '[' . $instance['field_name'] . ']';
+      $tokens[$token] = [
+        'label' => $instance['label'],
+        'description' => $instance['description'],
+        'token' => $token,
+        'field_name' => $instance['field_name'],
+        'required' => $instance['required']
+      ];
     }
   }
 
   return $tokens;
 }
 
+/**
+ * A recursive helper function to get tokens for element sub fields.
+ * 
+ * @param $instance
+ *   A original field instance object.
+ * @param $parent
+ *   The name of the parent. The first time this is called outside of 
+ *   recursion this should be the field name.
+ * @param $elements
+ *   The array of elements to process.
+ * @param $tokens
+ *   The array of tokens to be added to.
+ */
+function _tripal_get_entity_tokens_for_elements($instance, $parent, $elements, &$tokens, $options) {
+  
+  // Iterate through all of the elements and add tokens for each one.
+  foreach ($elements as $child_term_id => $details) {
+    
+    // We don't need to add the entity element.
+    if ($child_term_id == 'entity') {
+      continue;
+    }
+    
+    // Skip elements that aren't required.
+    $required = array_key_exists('required', $details) ? $details['required'] : FALSE;
+    if (!$required and $options['required only']) {
+      continue;
+    }
+    $token = '[' . $parent . ',' . $child_term_id . ']'; 
+    $label = $child_term_id;
+    if (array_key_exists('name', $details)) {
+      $label = $details['name'];
+    }
+    elseif (preg_match('/:/', $child_term_id)) {
+      list($vocabulary, $accession) = explode(':', $child_term_id);
+      $term = tripal_get_term_details($vocabulary, $accession);
+      $label = $term['name'];
+    }
+
+    // Add the token!
+    $tokens[$token] = [
+      'label' => $label,
+      'description' => array_key_exists('description', $details) ? $details['description'] : '',
+      'token' => $token,
+      'field_name' => $instance['field_name'],
+      'required' => $required
+    ];
+    
+    // Recurse to include sub elements
+    if (array_key_exists('elements', $details)) {
+      _tripal_get_entity_tokens_for_elements($instance, $parent . ',' . $child_term_id, 
+        $details['elements'], $tokens, $options);
+    }
+  }
+}
 /**
  * Replace all Tripal Tokens in a given string.
  *
@@ -1089,8 +1231,8 @@ function tripal_get_entity_tokens($entity, $options = array()) {
  */
 function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL) {
   // Determine which tokens were used in the format string
-  $used_tokens = array();
-  if (preg_match_all('/\[\w+\]/', $string, $matches)) {
+  $used_tokens = [];
+  if (preg_match_all('/\[.*?\]/', $string, $matches)) {
     $used_tokens = $matches[0];
   }
 
@@ -1105,10 +1247,13 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
   // all synced entities causes extreme slowness, so we'll only attach
   // the necessary fields for replacing tokens.
   $attach_fields = array();
-  foreach($used_tokens as $token) {
-    $field_name = str_replace(array('.','[',']'), array('__','',''), $token);
 
-    if (!property_exists($entity, $field_name)) {
+  foreach($used_tokens as $token) {
+    $token = preg_replace('/[\[\]]/', '', $token);
+    $elements = explode(',', $token);
+    $field_name = array_shift($elements);
+    //$field_name = str_replace(array('.','[',']'), array('__','',''), $field_name);
+    if (!property_exists($entity, $field_name) or empty($entity->{$field_name})) {
       $field = field_info_field($field_name);
       $storage = $field['storage'];
       $attach_fields[$storage['type']]['storage'] = $storage;
@@ -1130,20 +1275,28 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
     module_invoke($storage['module'], 'field_storage_load', 'TripalEntity',
         $entities, FIELD_LOAD_CURRENT, $field_ids, array());
   }
-
+  
   // Now that all necessary fields are attached process the tokens.
   foreach($used_tokens as $token) {
-    $field_name = str_replace(array('.','[',']'), array('__','',''), $token);
+    $token = preg_replace('/[\[\]]/', '', $token);
+    $elements = explode(',', $token);
+    $field_name = array_shift($elements);
     $value = '';
-
+    
     if (property_exists($entity, $field_name)) {
+      $value = '';
       // Note: there is a memory leak in field_get_items() so we can't use it
       // here or bulk publishing will slowly erode memory.
-      //$field_value = field_get_items('TripalEntity', $entity, $field_name);
-      if (array_key_exists(0, $entity->{$field_name}['und'])) {
+      // $field_value = field_get_items('TripalEntity', $entity, $field_name);
+      if (array_key_exists('und', $entity->{$field_name}) and 
+          array_key_exists(0, $entity->{$field_name}['und'])) {
         $value = $entity->{$field_name}['und'][0]['value'];
-      }
-      // TODO: deal with the value when it is not a scalar.
+        // If the value is an array it means we have sub elements and we can
+        // descend through the array to look for matching value.
+        if (is_array($value) and count($elements) > 0) {
+          $value = _tripal_replace_entity_tokens_for_elements($elements, $value);
+        }
+      }    
     }
     // The TripalBundle__bundle_id is a special token for substituting the
     // bundle id.
@@ -1164,16 +1317,33 @@ function tripal_replace_entity_tokens($string, &$entity, $bundle_entity = NULL)
 
     // We can't support tokens that have multiple elements (i.e. in an array).
     if (is_array($value)) {
-      $string = str_replace($token, '', $string);
+      $string = str_replace('[' . $token . ']', '', $string);
     }
     else {
-      $string = str_replace($token, $value, $string);
+      $string = str_replace('[' . $token . ']', $value, $string);
     }
   }
 
   return $string;
 }
 
+/**
+ * A helper function for tripal_replace_entity_tokens to get token values.
+ * 
+ * This helper function is used when the tokens are from subelements.
+ * @param $entity
+ */
+function _tripal_replace_entity_tokens_for_elements($elements, $values) {
+  $term_id = array_shift($elements);
+  $value = $values[$term_id];
+  if (count($elements) == 0) {
+    return $value;
+  }
+  else {
+    _tripal_replace_entity_tokens_for_elements($elements, $value);
+  }
+}
+
 /**
  * Formats the tokens for display.
  *

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

@@ -415,6 +415,7 @@ function tripal_launch_job($do_parallel = 0, $job_id = NULL, $max_jobs = -1, $si
 
     // Run the job
     $callback = $job->getCallback();
+    print date('Y-m-d H:i:s') . ": Job ID " . $job_id . ".\n";
     print date('Y-m-d H:i:s') .": Calling: $callback(" . implode(", ", $string_args) . ")\n";
     try {
       $job->run();

+ 16 - 1
tripal/api/tripal.notice.api.inc

@@ -58,6 +58,8 @@ define('TRIPAL_DEBUG',7);
  *   An array of options. Some available options include:
  *     - print: prints the error message to the terminal screen. Useful when
  *       display is the command-line
+ *     - drupal_set_message: set to TRUE to call drupal_set_message with the 
+ *       same error.
  *
  * @ingroup tripal_notify_api
  */
@@ -113,7 +115,7 @@ function tripal_report_error($type, $severity, $message, $variables = array(), $
     watchdog($type, $message, $variables, $severity);
   }
   catch (Exception $e) {
-    print "CRITICAL (TRIPAL): Unable to register error message with watchdog: " . $e->getMessage(). "\n.";
+    print "CRITICAL (TRIPAL): Unable to add error message with watchdog: " . $e->getMessage(). "\n.";
     $options['print'] = TRUE;
   }
 
@@ -129,6 +131,19 @@ function tripal_report_error($type, $severity, $message, $variables = array(), $
   if (isset($options['print'])) {
     print $severity_string . ' (' . strtoupper($type) . '): ' . $print_message . "\n";
   }
+  
+  if (isset($options['drupal_set_message'])) {
+    if (in_array($severity, [TRIPAL_CRITICAL, TRIPAL_ERROR])) {
+      $status = 'error';
+    }
+    elseif ($severity == TRIPAL_WARNING) {
+      $status = 'warning';
+    }
+    else {
+      $status = 'status';
+    }
+    drupal_set_message($print_message, $status);
+  }
 
   // Print to the Tripal error log but only if the severity is not info.
   if (($severity != TRIPAL_INFO)) {

+ 28 - 1
tripal/api/tripal.terms.api.inc

@@ -389,7 +389,34 @@ function tripal_get_term_details($vocabulary, $accession) {
     $module = $stores[$keys[0]]['module'];
     $function = $module . '_vocab_get_term';
     if (function_exists($function)) {
-      return $function($vocabulary, $accession);
+      $term = $function($vocabulary, $accession);
+      
+      // Make sure the term has a URL. If it does not, then use the Tripal
+      // interface as the URL for the term.
+      $url_missing = FALSE;
+      if (!$term['url']) {
+        $url_missing = TRUE;
+        $term['url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
+      }
+      if (!$term['vocabulary']['sw_url']) {
+        $url_missing = TRUE;
+        $term['vocabulary']['sw_url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
+      }
+      // Let the user know that the vocabulary is missing.
+      if ($url_missing) {
+        tripal_add_notification(
+          "Missing CV term URL", 
+          t("The controlled vocabulary, %vocab, is missing a URL. Tripal will handle " .
+            "this by linking to the cv/lookup page of this site. However, the correct " .
+            "should be updated for this site", 
+            ['%vocab' => $term['vocabulary']['short_name']]),
+          'Controlled Vocabularies', 
+          NULL, 
+          'mising-vocab-' . $term['vocabulary']['short_name']
+        );
+      }
+      
+      return $term;
     }
   }
 }

+ 55 - 5
tripal/includes/TripalBundleUIController.inc

@@ -56,6 +56,55 @@ class TripalBundleUIController extends EntityDefaultUIController {
 
     return $forms;
   }
+  
+  /**
+   * Renders the Bundle overview table
+   */
+  public function overviewTable($conditions = array()) {
+    $entities = entity_load($this->entityType, FALSE, $conditions);
+
+    // Sort the entities by label.
+    $sorted = [];    
+    foreach ($entities as $entity) {
+      $sorted[$entity->label] = $entity;
+    }
+    ksort($sorted, SORT_STRING|SORT_FLAG_CASE);
+    
+    $rows = array();
+    foreach ($sorted as $entity) {
+      // Get the term for this content type
+      $additional_cols = [$entity->term->name . ' (' . l($entity->accession, 'cv/lookup/' . $entity->term->vocab->vocabulary . '/' . $entity->term->accession) . ')'];
+      $rows[] = $this->overviewTableRow($conditions, 
+        entity_id($this->entityType, $entity), $entity,
+        $additional_cols);
+    }
+    // Assemble the right table header.
+    $header = array(t('Label'));
+    if (!empty($this->entityInfo['exportable'])) {
+      $header[] = t('Status');
+    }
+    $header[] = array(
+      'data' => t('Term'),
+    );
+    // Add operations with the right colspan.
+    $field_ui = !empty($this->entityInfo['bundle of']) && module_exists('field_ui');
+    $exportable = !empty($this->entityInfo['exportable']);
+    $colspan = 3;
+    $colspan = $field_ui ? $colspan + 2 : $colspan;
+    $colspan = $exportable ? $colspan + 1 : $colspan;
+    $header[] = array(
+      'data' => t('Operations'),
+      'colspan' => $colspan,
+    );
+    
+    $render = array(
+      '#theme' => 'table',
+      '#header' => $header,
+      '#rows' => $rows,
+      '#empty' => t('None.'),
+    );
+    return $render;
+  }
 
 }
 
@@ -298,7 +347,8 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
   $form['url']['token_display'] = array(
     '#type' => 'fieldset',
     '#title' => t('Available Tokens'),
-    '#description' => t('Copy the token and paste it into the "URL Alias Pattern" text field above.'),
+    '#description' => t('Copy the token and paste it into the "URL Alias Pattern" ' . 
+      'text field above. Please choose tokens that will guarantee a unique URL.'),
     '#collapsible' => TRUE,
     '#collapsed' => TRUE
   );
@@ -439,8 +489,8 @@ function tripal_tripal_bundle_form_submit($form, &$form_state) {
       $includes = array(
         module_load_include('inc', 'tripal', 'includes/tripal.bulk_update'),
       );
-      tripal_add_job('Update all aliases', 'tripal', 'tripal_update_all_urls_and_titles', $args,
-        $user->uid, 10, $includes);
+      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal', 
+        'tripal_update_all_urls_and_titles', $args, $user->uid, 10, $includes);
     }
     elseif ($trigger == 'Bulk update all aliases'){
       $update = $form_state['input']['url']['url_pattern'];
@@ -453,8 +503,8 @@ function tripal_tripal_bundle_form_submit($form, &$form_state) {
       $includes = array(
         module_load_include('inc', 'tripal', 'includes/tripal.bulk_update'),
       );
-      tripal_add_job('Update all aliases', 'tripal', 'tripal_update_all_urls_and_titles', $args,
-        $user->uid, 10, $includes);
+      tripal_add_job('Update all aliases for content type: ' . $bundle->label, 'tripal', 
+        'tripal_update_all_urls_and_titles', $args, $user->uid, 10, $includes);
     }
 
     $form_state['redirect'] = 'admin/structure/bio_data';

+ 40 - 44
tripal/includes/TripalEntityController.inc

@@ -47,7 +47,6 @@ class TripalEntityController extends EntityAPIController {
       $function = $module . '_entity_create';
       $function($entity, $values['type']);
     }
-
     return $entity;
 
   }
@@ -100,30 +99,23 @@ class TripalEntityController extends EntityAPIController {
    * Sets the title for an entity.
    *
    * @param $entity
+   *   The entity whose title should be changed.
    * @param $title
+   *   The title to use. It can contain tokens the correspond to field values.
+   *   Token should be be compatible with those returned by 
+   *   tripal_get_entity_tokens().
    */
   public function setTitle($entity, $title = NULL) {
-
+    
+    $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
+    
     // If no title was supplied then we should try to generate one using the
     // default format set by admins.
-    if (!$title) {
-      // Load the TripalBundle entity for this TripalEntity.
-      // Get the format for the title based on the bundle of the entity.
-      // And then replace all the tokens with values from the entity fields.
-      $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
+    if (!$title) {     
       $title = tripal_get_title_format($bundle_entity);
-      $title = tripal_replace_entity_tokens($title, $entity, $bundle_entity);
-    }
-    // Check if the passed alias has tokens.
-    if($title && (preg_match_all("/\[[^\]]*\]/", $title, $bundle_tokens))) {
-
-      // Load the TripalBundle entity for this TripalEntity.
-      $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
-
-      // And then replace all the tokens with values from the entity fields.
-      $title = tripal_replace_entity_tokens($title, $entity, $bundle_entity);
     }
-    // As long as we were able to determine a title, we should update it ;-).
+    $title = tripal_replace_entity_tokens($title, $entity, $bundle_entity);
+    
     if ($title) {
       db_update('tripal_entity')
         ->fields(array(
@@ -136,6 +128,13 @@ class TripalEntityController extends EntityAPIController {
 
   /**
    * Sets the URL alias for an entity.
+   * 
+   * @param $entity
+   *   The entity whose URL alias should be changed.
+   * @param $alias
+   *   The alias to use. It can contain tokens the correspond to field values.
+   *   Token should be be compatible with those returned by 
+   *   tripal_get_entity_tokens().
    */
   public function setAlias($entity, $alias = NULL) {
     $source_url = "bio_data/$entity->id";
@@ -145,12 +144,10 @@ class TripalEntityController extends EntityAPIController {
     if (!$alias) {
 
       // Load the TripalBundle entity for this TripalEntity.
-      $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
-
       // First get the format for the url alias based on the bundle of the entity.
+      // Then replace all the tokens with values from the entity fields.
+      $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
       $alias = tripal_get_bundle_variable('url_format', $bundle_entity->id);
-
-      // And then replace all the tokens with values from the entity fields.
       $alias = tripal_replace_entity_tokens($alias, $entity, $bundle_entity);
     }
 
@@ -158,31 +155,28 @@ class TripalEntityController extends EntityAPIController {
     // the term name and entity id.
     if (!$alias) {
 
-      // Load the term for this TripalEntity.
+      // Load the term for this TripalEntity. Set a default based on the term 
+      // name and entity id. Then replace all the tokens with values from 
+      // the entity fields.
       $term = entity_load('TripalTerm', array('id' => $entity->term_id));
       $term = reset($term);
-
-      // Set a default based on the term name and entity id.
       $alias = str_replace(' ', '', $term->name) . '/[TripalEntity__entity_id]';
-
-      // And then replace all the tokens with values from the entity fields.
       $alias = tripal_replace_entity_tokens($alias, $entity, $bundle_entity);
     }
-    // Check if the passed alias has tokens.
+    
+    // Check if the passed alias has tokens. Load the TripalBundle entity for 
+    // this TripalEntity. Then replace all the tokens with values from the 
+    // entity fields.
     if($alias && (preg_match_all("/\[[^\]]*\]/", $alias, $bundle_tokens))) {
-
-      // Load the TripalBundle entity for this TripalEntity.
       $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
-
-      // And then replace all the tokens with values from the entity fields.
       $alias = tripal_replace_entity_tokens($alias, $entity, $bundle_entity);
     }
 
     // Make sure the alias doesn't contain spaces.
-    $alias = preg_replace('/\s+/','-',$alias);
+    //$alias = preg_replace('/\s+/','-',$alias);
     // Or any non alpha numeric characters.
-    $alias = preg_replace('/[^a-zA-Z0-9\-\/]/','',$alias);
-    $alias = preg_replace('/_/','-',$alias);
+    //$alias = preg_replace('/[^a-zA-Z0-9\-\/]/','',$alias);
+    //$alias = preg_replace('/_/','-',$alias);
 
     if ($alias) {
       // Determine if this alias has already been used.
@@ -210,10 +204,7 @@ class TripalEntityController extends EntityAPIController {
           'language' => 'und',
         );
 
-//        path_delete(array('source' => $source_url));
-//        $path = array('source' => $source_url, 'alias' => $alias);
-//        path_save($path);
-        //Now check if an entry with the source url for this entity already
+        // Now check if an entry with the source url for this entity already
         // exists. This is an issue when updating existing url aliases. To avoid
         // creating 404s existing aliases need to be updated and a redirect
         // created to handle the old alias.
@@ -246,15 +237,14 @@ class TripalEntityController extends EntityAPIController {
               ->condition('source', $source_url, '=')
               ->condition('pid', $ea->pid, '=')
               ->execute();
-
           }
         }
         else {
           drupal_write_record('url_alias', $values);
         }
       }
-      // If there is only one alias matching then it might just be that we already
-      // assigned this alias to this entity in a previous save.
+      // If there is only one alias matching then it might just be that we 
+      // already assigned this alias to this entity in a previous save.
       elseif ($num_aliases == 1) {
 
         $bundle_entity = tripal_load_bundle_entity(array('name' => $entity->bundle));
@@ -360,6 +350,9 @@ class TripalEntityController extends EntityAPIController {
         $invocation = 'entity_update';
         $pkeys = array('id');
       }
+      if (property_exists($entity, 'publish') and $entity->publish == TRUE) {
+        $invocation = 'entity_publish';
+      }
 
       // Invoke hook_entity_presave().
       module_invoke_all('entity_presave', $entity, $entity->type);
@@ -389,11 +382,14 @@ class TripalEntityController extends EntityAPIController {
       // Now we need to either insert or update the fields which are
       // attached to this entity. We use the same primary_keys logic
       // to determine whether to update or insert, and which hook we
-      // need to invoke.
+      // need to invoke.  We do not attach fields when publishing an entity.
+      // This is because a field may have default values and if so, those fields
+      // will be attached and the storage backend may then try to insert
+      // fields which should not be inserted because they already exist.
       if ($invocation == 'entity_insert') {
         field_attach_insert('TripalEntity', $entity);
       }
-      else {
+      if ($invocation == 'entity_update') {
         field_attach_update('TripalEntity', $entity);
       }
 

+ 1 - 2
tripal/includes/TripalEntityUIController.inc

@@ -316,7 +316,6 @@ function tripal_view_entity($entity, $view_mode = 'full') {
        $query->propertyCondition('status', 0);
      }
    }
-   //$query->propertyOrderBy('created', 'DESC');
 
    // Find out the total number of records and determine what page we're on, and
    // initialize the pager.
@@ -797,4 +796,4 @@ function tripal_entity_form_ajax_callback($form, $form_state) {
    }
    return FALSE;
 
- }
+ }

+ 11 - 1
tripal/includes/TripalFields/TripalFieldWidget.inc

@@ -108,8 +108,8 @@ class TripalFieldWidget {
     );
     $widget['#field'] = $this->field;
     $widget['#instance'] = $this->instance;
-    $widget['#element_validate'] = array('tripal_field_widget_form_validate');
     $widget['#theme'] = 'tripal_field_default';
+    $widget['#element_validate'] = array('tripal_field_widget_form_validate');
   }
 
   /**
@@ -125,6 +125,16 @@ class TripalFieldWidget {
 
   }
 
+  /**
+   * Performs validation of the widget form when setting defaults.
+   *
+   * Use this validate to ensure that form values are entered correctly when
+   * a user edits the defaults on the field edit page (available from the
+   * "managed fields" section of the Content type page. 
+   */
+  public function validateDefaults($element, $form, &$form_state, $langcode, $delta) {
+    
+  }
 
   /**
    * Performs extra commands when the entity form is submitted.

+ 3 - 3
tripal/includes/TripalImporter.inc

@@ -302,7 +302,7 @@ class TripalImporter {
           foreach ($fids as $fid) {
             $file = file_load($fid);
             $arguments['files'][] = array(
-              'file_path' => base_path() . drupal_realpath($file->uri),
+              'file_path' => drupal_realpath($file->uri),
               'fid' => $fid
             );
             $has_file++;
@@ -313,14 +313,14 @@ class TripalImporter {
           $fid = $file_details['fid'];
           $file = file_load($fid);
           $arguments['files'][] = array(
-            'file_path' => base_path() . drupal_realpath($file->uri),
+            'file_path' => drupal_realpath($file->uri),
             'fid' => $fid
           );
           $has_file++;
 
           // For backwards compatibility add the old 'file' element.
           $arguments['file'] = array(
-            'file_path' => base_path() . drupal_realpath($file->uri),
+            'file_path' => drupal_realpath($file->uri),
             'fid' => $fid
           );
         }

+ 1 - 1
tripal/includes/TripalJob.inc

@@ -160,7 +160,7 @@ class TripalJob {
     }
 
     $includes = $details['includes'];
-    if ($path and is_array($path)) {
+    if ($includes and is_array($includes)) {
       foreach ($includes as $path) {
         $full_path = $_SERVER['DOCUMENT_ROOT'] . base_path() . $path;
         if (!empty($path)) {

+ 7 - 6
tripal/includes/tripal.bulk_update.inc

@@ -8,6 +8,7 @@
  * @param $type
  */
 function tripal_update_all_urls_and_titles($bundle_id, $update, $type) {
+
   // Load all the entity_ids.
   $entity_table = 'chado_'.$bundle_id;
   $entities = db_select($entity_table, 'e')
@@ -15,16 +16,16 @@ function tripal_update_all_urls_and_titles($bundle_id, $update, $type) {
     ->orderBy('entity_id', 'ASC')
     ->execute();
   $num_entities = $entities->rowCount();
+  
   // Parse the $update variable for tokens and load those tokens.
-  preg_match_all("/\[[^\]]*\]/", $update, $bundle_tokens);
+  preg_match_all("'/\[.*?\]/'", $update, $bundle_tokens);
 
   $fields = array();
   foreach ($bundle_tokens as $bundle_token) {
-    foreach ($bundle_token as $token) {
-      $string = str_replace(array('[', ']'), '', $token);
-      $field_array = field_info_field($string);
-      $fields[] = $field_array['id'];
-    }
+    $elements = explode(',', $token);
+    $field_name = array_shift($elements);
+    $field_array = field_info_field($field_name);
+    $fields[] = $field_array['id'];
   }
 
   $i = 1;

+ 22 - 0
tripal/includes/tripal.field_storage.inc

@@ -76,10 +76,18 @@ function tripal_field_storage_query($query) {
   $select->join('tripal_bundle', 'TB', 'TE.bundle = TB.name');
   $select->fields('TE', array('id'));
   $select->fields('TB', array('name'));
+  
+  // Apply any entity condition filters.
+  if ($query->entityConditions) {
+    if (array_key_exists('bundle', $query->entityConditions)) {
+      $select->condition('TB.name', $query->entityConditions['bundle']['value']);
+    }
+  }
 
   // Add in any filters to the query.
   foreach ($query->fieldConditions as $index => $condition) {
     $field = $condition['field'];
+    
     // Skip conditions that don't belong to this storage type.
     if ($field['storage']['type'] != 'tripal_no_storage') {
       continue;
@@ -107,6 +115,20 @@ function tripal_field_storage_query($query) {
       $select->orderBy('TB.label', $direction);
     }
   }
+  
+  // Add a range of records to retrieve
+  if (isset($query->range)) {
+    $select->range($query->range['start'], $query->range['length']);
+  }
+  
+  // Only include records that are deleted.  Tripal doesn't keep track of
+  // records that are deleted that need purging separately so we can do nothing
+  // with this.
+  if (isset($query->deleted)) {
+    // There won't ever be field data marked as deleted so just created a 
+    // condition that always evaluates to false.
+    $select->where('1=0');
+  }
 
   // Perform the query and return the results.
   $entities = $select->execute();

+ 37 - 5
tripal/includes/tripal.fields.inc

@@ -265,16 +265,27 @@ function tripal_form_field_ui_field_overview_form_alter(&$form, &$form_state, $f
     $field = field_info_field($field_name);
     $instance = field_info_instance('TripalEntity', $field_name, $form['#bundle']);
     
+    // Warn users if a field is missing a term.
+    if ($instance and $instance['entity_type'] == 'TripalEntity' and 
+        array_key_exists('settings', $instance) and is_array($instance['settings']) and
+        (!array_key_exists('term_vocabulary', $instance['settings']) or !$instance['settings']['term_vocabulary'])) {
+      tripal_report_error('tripal_fields', TRIPAL_WARNING,
+          'The field, !field, is missing a controlled vocabulary term. Please edit the field and set a term, otherwise this field may not work properly.',
+          ['!field' => $field_name],
+          ['drupal_set_message' => TRUE]);
+    }
     // Warn users if any of the terms are not unique.
     if ($instance and array_key_exists('settings', $instance) and is_array($instance['settings']) and 
         array_key_exists('term_vocabulary', $instance['settings'])) {
       $term = $instance['settings']['term_vocabulary'] . ':' . $instance['settings']['term_accession'];
       if (array_key_exists($term, $used_terms)) {
         $used_terms[$term][] = $field_name;
-        drupal_set_message(t('The term !term is in use by multiple fields: !fields. 
-          This is not allowed. Every field must have a different controlled vocabulary term. 
-          Please correct the term assignments.', 
-          array('!term' => $term, '!fields' => implode(', ', $used_terms[$term]))), 'error');
+        tripal_report_error('tripal_fields', TRIPAL_WARNING,
+          'The term !term is in use by multiple fields: !fields. ' .
+          'This is not allowed. Every field must have a different controlled vocabulary term. ' . 
+          'Please correct the term assignments.',
+          ['!term' => $term, '!fields' => implode(', ', $used_terms[$term])], 
+          ['drupal_set_message' => TRUE]);
       }
       $used_terms[$term][] = $field_name;
     }
@@ -734,6 +745,19 @@ function tripal_field_instance_settings_form_alter_validate($form, &$form_state)
         }
       }
     }
+    
+    // Make sure this term is not already used.    
+    $bundle_name = $form_state['values']['instance']['bundle'];
+    $existing_instances = field_info_instances('TripalEntity', $bundle_name);
+    $field_term_id = $form_state['values']['instance']['settings']['term_vocabulary'] . ':' . $form_state['values']['instance']['settings']['term_accession'];
+    $field_name = $form_state['values']['instance']['field_name'];  
+    foreach ($existing_instances as $existing_name => $existing_instance) {
+      $existing_term_id = $existing_instance['settings']['term_vocabulary'] . ':' . $existing_instance['settings']['term_accession'];
+      if ($existing_term_id == $field_term_id and $field_name != $existing_name) {       
+        form_set_error('term-', t('The term, !term, is already in use on this content type.  A term can only be used once per content type. Please choose a different term.',
+          ['!term' => $field_term_id]));
+      }
+    }
 
     if ($num_selected > 1) {
       form_set_error('term-', 'Please select only one term
@@ -771,7 +795,15 @@ function tripal_field_widget_form_validate($element, &$form_state, $form) {
   tripal_load_include_field_class($widget_class);
   if (class_exists($widget_class)) {
     $widget = new $widget_class($field, $instance);
-    $widget->validate($element, $form, $form_state, $langcode, $delta);
+    
+    // Set the validation function for this field widget depending on the
+    // form displaying the widget.
+    if ($form['#form_id'] == 'field_ui_field_edit_form') {
+      $widget->validateDefaults($element, $form, $form_state, $langcode, $delta);
+    }
+    else {
+      $widget->validate($element, $form, $form_state, $langcode, $delta);
+    }
   }
 }
 

+ 1 - 0
tripal_chado/api/tripal_chado.api.inc

@@ -225,6 +225,7 @@ function chado_publish_records($values, $job_id = NULL) {
         // can deal with it.
         'chado_record' => chado_generate_var($table, array($pkey_field => $record_id)),
         'chado_record_id' => $record_id,
+        'publish' => TRUE,
       ));
       $entity = $entity->save();
       if (!$entity) {

+ 17 - 3
tripal_chado/api/tripal_chado.query.api.inc

@@ -1646,7 +1646,9 @@ function chado_select_record_check_value_type(&$op, &$value, $type) {
  *            F.uniquename = :feature_uniquename";
  * $args = array( ':feature_uniquename' => $form_state['values']['uniquename'] );
  * $result = chado_query($sql, $args);
- * foreach ($result as $r) { [Do something with the records here] }
+ * while ($r = $results->fetchObject()) { 
+ *   // Do something with the record object $r
+ * }
  * @endcode
  *
  * @ingroup tripal_chado_query_api
@@ -2065,7 +2067,19 @@ function chado_db_select($table, $alias = NULL, array $options = array()) {
   if (empty($options['target'])) {
     $options['target'] = 'default';
   }
-  $conninfo = Database::getConnectionInfo();
-  $conn = new ChadoDatabaseConnection($conninfo['default']);
+   
+  // We only want one connection for chado_db_select, so the first time we
+  // create it, we'll save it in the $GLOBALS array for use next time this
+  // function is called. If we don't do this, then the function will
+  // open too many connections and cause the database server to block.
+  $conn = NULL;
+  if (!array_key_exists('chado_db_select_connnection', $GLOBALS)) {
+    $conninfo = Database::getConnectionInfo();
+    $conn = new ChadoDatabaseConnection($conninfo['default']);
+    $GLOBALS['chado_db_select_connnection'] = $conn;
+  }
+  else {
+    $conn = $GLOBALS['chado_db_select_connnection'];
+  }
   return $conn->select($table, $alias, $options);
 }

+ 4 - 1
tripal_chado/api/tripal_chado.variables.api.inc

@@ -788,9 +788,12 @@ function chado_expand_var($object, $type, $to_expand, $table_options = array())
       }
       $foreign_table_desc = chado_get_schema($foreign_table);
 
-      // TODO: if we don't get a foreign_table (which could happen of a custom 
+      // If we don't get a foreign_table (which could happen of a custom 
       // table is not correctly defined or the table name is mispelled then we 
       // should return gracefully.
+      if(!is_array($foreign_table_desc)) {
+        return $object;
+      }
 
       // BASE CASE: If it's connected to the base table via a FK constraint
       // then we have all the information needed to expand it now.

+ 13 - 2
tripal_chado/includes/TripalFields/chado_linker__prop/chado_linker__prop_widget.inc

@@ -7,7 +7,7 @@ class chado_linker__prop_widget extends ChadoFieldWidget {
   // The list of field types for which this formatter is appropriate.
   public static $field_types = array('chado_linker__prop');
 
-  /**
+    /**
    *
    * @see TripalFieldWidget::form()
    */
@@ -109,6 +109,17 @@ class chado_linker__prop_widget extends ChadoFieldWidget {
       '#value' => $rank,
     );
   }
+  
+  /**
+   * @see TripalFieldWidget::validateDefaults()
+   */
+  public function validateDefaults($element, $form, &$form_state, $langcode, $delta) {
+    $field_name = $this->field['field_name'];
+    $field_table = $this->instance['settings']['chado_table'];
+    
+    $value = $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__value'];
+    $form_state['values'][$field_name]['und'][$delta]['value'] = $value;    
+  }
 
   /**
    *
@@ -121,7 +132,7 @@ class chado_linker__prop_widget extends ChadoFieldWidget {
     $field_table = $this->instance['settings']['chado_table'];
     $chado_column = $this->instance['settings']['chado_column'];
     $instance = $this->instance;
-
+       
     $schema = chado_get_schema($field_table);
     $pkey = $schema['primary key'][0];
     $base_table = $this->instance['settings']['base_table'];

+ 8 - 0
tripal_chado/includes/TripalFields/data__accession/data__accession_widget.inc

@@ -94,6 +94,14 @@ class data__accession_widget extends ChadoFieldWidget {
     $dbxref_id = $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__dbxref_id'];
     $db_id = $form_state['values'][$field_name]['und'][$delta]['db_id'];
     $accession = $form_state['values'][$field_name]['und'][$delta]['accession'];
+    
+    // Is this field required?
+    if ($element['#required'] and !$db_id) {
+      form_set_error($field_name . '][und][0][db_id', "A database for the accession must be provided.");
+    }
+    if ($element['#required'] and !$accession) {
+      form_set_error($field_name . '][und][0][accession', "An accession number must be provided.");
+    }
 
     // If user did not select a database, we want to remove dbxref_id from the
     // field. We use '__NULL__' because this field is part of the base table

+ 14 - 3
tripal_chado/includes/TripalFields/data__sequence/data__sequence_widget.inc

@@ -45,8 +45,19 @@ class data__sequence_widget extends ChadoFieldWidget {
       '#cols' => 30,
     );
   }
-
-
+  
+  /**
+   * @see TripalFieldWidget::validateDefaults()
+   */
+  public function validateDefaults($element, $form, &$form_state, $langcode, $delta) {
+    $field_name = $this->field['field_name'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+    
+    $value = $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__' . $field_column];
+    $form_state['values'][$field_name]['und'][$delta]['value'] = $value;
+  }
+  
   /**
    *
    * @see TripalFieldWidget::submit()
@@ -55,7 +66,7 @@ class data__sequence_widget extends ChadoFieldWidget {
     $field_name = $this->field['field_name'];
     $field_table = $this->instance['settings']['chado_table'];
     $field_column = $this->instance['settings']['chado_column'];
-
+    
     // Remove any white spaces.
     $residues = $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__' . $field_column];
     if ($residues) {

+ 201 - 0
tripal_chado/includes/TripalFields/efo__array_design/efo__array_design.inc

@@ -0,0 +1,201 @@
+<?php
+
+class efo__array_design extends ChadoField {
+
+  // The default lable for this field.
+  public static $default_label = 'Array Design';
+
+  // The default description for this field.
+  public static $description = 'An instrument design which describes the design of the array.';
+
+  // Provide a list of instance specific settings. These can be access within
+  // the instanceSettingsForm.  When the instanceSettingsForm is submitted
+  // then Drupal with automatically change these settings for the instnace.
+  // It is recommended to put settings at the instance level whenever possible.
+  // If you override this variable in a child class be sure to replicate the
+  // term_name, term_vocab, term_accession and term_fixed keys as these are
+  // required for all TripalFields.
+  public static $default_instance_settings  = array(
+    // The short name for the vocabulary (e.g. shcema, SO, GO, PATO, etc.).
+    'term_vocabulary' => 'EFO',
+    // The name of the term.
+    'term_name' => 'array design',
+    // The unique ID (i.e. accession) of the term.
+    'term_accession' => '0000269',
+    // Set to TRUE if the site admin is allowed to change the term
+    // type. This will create form elements when editing the field instance
+    // to allow the site admin to change the term settings above.
+    'term_fixed' => FALSE,
+  );
+
+  // The default widget for this field.
+  public static $default_widget = 'efo__array_design_widget';
+
+  // The default formatter for this field.
+  public static $default_formatter = 'efo__array_design_formatter';
+
+
+  /**
+   * @see TripalField::validate()
+   */
+  public function validate($entity_type, $entity, $langcode, $items, &$errors) {
+
+    // If we don't have an entity then we don't want to validate.  The case
+    // where this could happen is when a user is editing the field settings
+    // and trying to set a default value. In that case there's no entity and
+    // we don't want to validate.  There will always be an entity for creation
+    // and update operations of a content type.
+    if (!$entity) {
+      return;
+    }
+    $settings = $this->field['settings'];
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+    
+    // Get the field values.
+    foreach ($items as $delta => $values) {
+
+      // Get the field values.
+      $arraydesign_id = $values[$linker_field];
+      if (!$arraydesign_id or $arraydesign_id == 0) {
+        $errors[$field_name]['und'][0][] = array(
+          'message' =>  t("Please specify an array design."),
+          'error' => 'efo__array_design'
+        );
+      }
+    }
+  }
+
+  /**
+   * @see TripalField::load()
+   */
+  public function load($entity) {
+
+    $record = $entity->chado_record;
+    $settings = $this->instance['settings'];
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Get the terms for each of the keys for the 'values' property.
+    $name_term = chado_get_semweb_term('arraydesign', 'name');
+    $version_term = chado_get_semweb_term('arraydesign', 'version');
+
+    // Set some defaults for the empty record.
+    $entity->{$field_name}['und'][0] = array(
+      'value' => array(),
+    );
+
+    if (!$record) {
+      return;
+    }
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+    $entity->{$field_name}['und'][0]['value'] = array(
+      $name_term => $record->{$field_column}->name,
+      $version_term => $record->{$field_column}->version,
+    );
+    $entity->{$field_name}['und'][0][$linker_field] = $record->{$field_column}->arraydesign_id;
+
+    // Is there a published entity for this arraydesign?
+    if (property_exists($record->{$field_column}, 'entity_id')) {
+      $entity->{$field_name}['und'][0]['value']['entity'] = 'TripalEntity:' . $record->{$field_column}->entity_id;
+    }
+  }
+
+  
+  /**
+   * @see TripalField::elementInfo()
+   */
+  public function elementInfo() {
+    $field_term = $this->getFieldTermID();
+
+    $name_term = chado_get_semweb_term('arraydesign', 'name');
+    $version_term = chado_get_semweb_term('arraydesign', 'version');
+    
+    return array(
+      $field_term => array(
+        'operations' => array('eq', 'contains', 'starts'),
+        'sortable' => TRUE,
+        'searchable' => TRUE,
+        'readonly' => FALSE,
+        'type' => 'xs:complexType',
+        'elements' => array(
+          $name_term => array(
+            'searchable' => TRUE,
+            'name' => 'name',
+            'operations' => array('eq', 'ne', 'contains', 'starts'),
+            'sortable' => FALSE,
+            'type' => 'xs:string',
+            'readonly' => TRUE,
+            'required' => FALSE,
+          ),
+          $version_term => array(
+            'searchable' => TRUE,
+            'name' => 'version',
+            'operations' => array('eq', 'ne'),
+            'sortable' => TRUE,
+            'readonly' => FALSE,
+            'type' => 'xs:string',
+            'required' => TRUE,
+          ),
+          'entity' => array(
+            'searchable' => FALSE,
+          ),
+        ),
+      ),
+    );
+  }
+
+  /**
+   * @see ChadoField::query()
+   */
+  public function query($query, $condition) {
+    $alias = $this->field['field_name'];
+    $operator = $condition['operator'];
+
+    $field_term_id = $this->getFieldTermID();
+    $name_term = $field_term_id . ',' . chado_get_semweb_term('arraydesign', 'name');
+    $version_term = $field_term_id . ',' . chado_get_semweb_term('arraydesign', 'version');
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'arraydesign', $alias, "base.arraydesign_id = $alias.arraydesign_id");
+
+    // If the column is the field name then we're during a search on the full
+    // scientific name.
+    if ($condition['column'] == $field_term_id or 
+        $condition['column'] == $name_term) {      
+      $query->condition("$alias.name", $condition['value'], $operator);
+    }
+    // If the column is a subfield.
+    if ($condition['column'] == $version_term) {
+      $query->condition("$alias.version", $condition['value'], $operator);
+    }
+  }
+
+  /**
+   * @see ChadoField::queryOrder()
+   */
+  public function queryOrder($query, $order) {
+    $alias = $this->field['field_name'];
+
+    $field_term_id = $this->getFieldTermID();
+    $name_term = $field_term_id . ',' . chado_get_semweb_term('arraydesign', 'name');
+    $version_term = $field_term_id . ',' . chado_get_semweb_term('arraydesign', 'version');
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'arraydesign', $alias, "base.arraydesign_id = $alias.arraydesign_id");
+
+    // Now perform the sort.
+    if ($order['column'] == $name_term) {
+      $query->orderBy("$alias.name", $order['direction']);
+    }
+    if ($order['column'] == $version_term) {
+      $query->orderBy("$alias.version", $order['direction']);
+    }
+  }
+}

+ 37 - 0
tripal_chado/includes/TripalFields/efo__array_design/efo__array_design_formatter.inc

@@ -0,0 +1,37 @@
+<?php
+
+class efo__array_design_formatter extends ChadoFieldFormatter {
+
+  // The default lable for this field.
+  public static $default_label = 'Array Design';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('efo__array_design');
+
+  /**
+   * @see TripalFieldFormatter::view()
+   */
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+    if (count($items) > 0) {
+      
+      $name_term = chado_get_semweb_term('arraydesign', 'name');
+      $version_term = chado_get_semweb_term('arraydesign', 'version');
+      
+      $content = $items[0]['value'][$name_term];
+      if (isset($items[0]['value'][$version_term])) {
+        $content . '(' . $items[0]['value'][$version_term] . ')';
+      }
+      if (array_key_exists('entity', $items[0]['value'])) {
+        list($entity_type, $entity_id) = explode(':', $items[0]['value']['entity']);
+        $content = l($content, 'bio_data/' . $entity_id);
+      }
+
+      // The cardinality of this field is 1 so we don't have to
+      // iterate through the items array, as there will never be more than 1.
+      $element[0] = array(
+        '#type' => 'markup',
+        '#markup' => $content,
+      );
+    }
+  }
+}

+ 75 - 0
tripal_chado/includes/TripalFields/efo__array_design/efo__array_design_widget.inc

@@ -0,0 +1,75 @@
+<?php
+
+class efo__array_design_widget extends ChadoFieldWidget {
+
+  // The default lable for this field.
+  public static $default_label = 'Array Design';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = array('efo__array_design');
+
+
+  /**
+   * @see TripalFieldWidget::form()
+   */
+  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
+
+    parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
+
+    $settings = $this->field['settings'];
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Set the linker field appropriately.
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+
+    $arraydesign_id = 0;
+    if (count($items) > 0 and array_key_exists($linker_field, $items[0])) {
+      $arraydesign_id = $items[0][$linker_field];
+    }
+
+    $widget['value'] = array(
+      '#type' => 'value',
+      '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
+    );
+    $sql = "SELECT arraydesign_id, name, version FROM {arraydesign} ORDER BY name";
+    $results = chado_query($sql);
+    $options = ['' => '- Select an array design -'];
+    while ($r = $results->fetchObject()) {
+      $options[$r->arraydesign_id] = $r->name;
+      if ($r->version) {
+        $options[$r->arraydesign_id] = $r->name . '(' . $r->version . ')';
+      }
+    }
+    $widget[$linker_field] = array(
+      '#type' => 'select',
+      '#title' => $element['#title'],
+      '#description' => $element['#description'],
+      '#options' => $options,
+      '#default_value' => $arraydesign_id,
+      '#required' => $element['#required'],
+      '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
+      '#delta' => $delta,
+    );
+  }
+
+  /**
+   * @see TripalFieldWidget::validate()
+   */
+  public function validate($element, $form, &$form_state, $langcode, $delta) {
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Set the linker field appropriately.
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+    
+    // Make sure the value is set to the organism_id
+    $arraydesign_id = $form_state['values'][$field_name]['und'][0][$linker_field];
+    $form_state['values'][$field_name]['und'][0]['value'] = $arraydesign_id;
+  }
+}

+ 0 - 4
tripal_chado/includes/TripalFields/local__contact/local__contact_widget.inc

@@ -57,10 +57,6 @@ class local__contact_widget extends ChadoFieldWidget {
       '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
     );
 
-    $widget['chado-' . $field_table . '__' . $pkey] = array(
-      '#type' => 'value',
-      '#default_value' => $record_id,
-    );
     $widget[$linker_field] = array(
       '#type' => 'value',
       '#default_value' => $contact_id,

+ 2 - 2
tripal_chado/includes/TripalFields/obi__organism/obi__organism.inc

@@ -31,10 +31,10 @@ class obi__organism extends ChadoField {
   );
 
   // The default widget for this field.
-  public static $default_widget = 'OBI__organism_widget';
+  public static $default_widget = 'obi__organism_widget';
 
   // The default formatter for this field.
-  public static $default_formatter = 'OBI__organism_formatter';
+  public static $default_formatter = 'obi__organism_formatter';
 
 
   /**

+ 145 - 0
tripal_chado/includes/TripalFields/operation__analysis/operation__analysis.inc

@@ -0,0 +1,145 @@
+<?php
+
+class operation__analysis extends ChadoField {
+
+  // The default lable for this field.
+  public static $default_label = 'Analysis';
+
+  // The default description for this field.
+  public static $description = 'Application of analytical methods to existing data of a specific type.';
+
+  // Provide a list of instance specific settings. These can be access within
+  // the instanceSettingsForm.  When the instanceSettingsForm is submitted
+  // then Drupal with automatically change these settings for the instnace.
+  // It is recommended to put settings at the instance level whenever possible.
+  // If you override this variable in a child class be sure to replicate the
+  // term_name, term_vocab, term_accession and term_fixed keys as these are
+  // required for all TripalFields.
+  public static $default_instance_settings  = [
+    // The short name for the vocabulary (e.g. shcema, SO, GO, PATO, etc.).
+    'term_vocabulary' => 'operation',
+    // The name of the term.
+    'term_name' => 'Analysis',
+    // The unique ID (i.e. accession) of the term.
+    'term_accession' => '2945',
+    // Set to TRUE if the site admin is allowed to change the term
+    // type. This will create form elements when editing the field instance
+    // to allow the site admin to change the term settings above.
+    'term_fixed' => FALSE,
+  ];
+
+  // The default widget for this field.
+  public static $default_widget = 'operation__analysis_widget';
+
+  // The default formatter for this field.
+  public static $default_formatter = 'operation__analysis_formatter';
+
+
+  /**
+   * @see TripalField::load()
+   */
+  public function load($entity) {
+    $record = $entity->chado_record;
+    $settings = $this->instance['settings'];
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Get the terms for each of the keys for the 'values' property.
+    $name_term = chado_get_semweb_term('analysis', 'name');
+
+    // Set some defaults for the empty record.
+    $entity->{$field_name}['und'][0] = [
+      'value' => [],
+    ];
+
+    if (!$record or !$record->analysis_id) {
+      return;
+    }
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+    $entity->{$field_name}['und'][0]['value'] = [
+      $name_term => $record->{$field_column}->name,
+    ];
+    $entity->{$field_name}['und'][0][$linker_field] = $record->{$field_column}->analysis_id;
+
+    // Is there a published entity for this analysis?
+    if (property_exists($record->{$field_column}, 'entity_id')) {
+      $entity->{$field_name}['und'][0]['value']['entity'] = 'TripalEntity:' . $record->{$field_column}->entity_id;
+    }
+  }
+
+  
+  /**
+   * @see TripalField::elementInfo()
+   */
+  public function elementInfo() {
+    $field_term = $this->getFieldTermID();
+
+    $name_term = chado_get_semweb_term('analysis', 'name');
+    
+    return [
+      $field_term => [
+        'operations' => ['eq', 'contains', 'starts'],
+        'sortable' => TRUE,
+        'searchable' => TRUE,
+        'readonly' => FALSE,
+        'type' => 'xs:complexType',
+        'elements' => [
+          $name_term => [
+            'searchable' => TRUE,
+            'name' => 'name',
+            'operations' => ['eq', 'ne', 'contains', 'starts'],
+            'sortable' => FALSE,
+            'type' => 'xs:string',
+            'readonly' => TRUE,
+            'required' => FALSE,
+          ],
+          'entity' => [
+            'searchable' => FALSE,
+          ],
+        ],
+      ],
+    ];
+  }
+
+  /**
+   * @see ChadoField::query()
+   */
+  public function query($query, $condition) {
+    $alias = $this->field['field_name'];
+    $operator = $condition['operator'];
+
+    $field_term_id = $this->getFieldTermID();
+    $name_term = $field_term_id . ',' . chado_get_semweb_term('analysis', 'name');
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'analysis', $alias, "base.analysis_id = $alias.analysis_id");
+
+    // If the column is the field name then we're during a search on the full
+    // scientific name.
+    if ($condition['column'] == $field_term_id or 
+        $condition['column'] == $name_term) {      
+      $query->condition("$alias.name", $condition['value'], $operator);
+    }
+  }
+
+  /**
+   * @see ChadoField::queryOrder()
+   */
+  public function queryOrder($query, $order) {
+    $alias = $this->field['field_name'];
+
+    $field_term_id = $this->getFieldTermID();
+    $name_term = $field_term_id . ',' . chado_get_semweb_term('analysis', 'name');
+
+    // Join to the organism table for this field.
+    $this->queryJoinOnce($query, 'analysis', $alias, "base.analysis_id = $alias.analysis_id");
+
+    // Now perform the sort.
+    if ($order['column'] == $name_term) {
+      $query->orderBy("$alias.name", $order['direction']);
+    }
+  }
+}

+ 33 - 0
tripal_chado/includes/TripalFields/operation__analysis/operation__analysis_formatter.inc

@@ -0,0 +1,33 @@
+<?php
+
+class operation__analysis_formatter extends ChadoFieldFormatter {
+
+  // The default lable for this field.
+  public static $default_label = 'Analysis';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = ['operation__analysis'];
+
+  /**
+   * @see TripalFieldFormatter::view()
+   */
+  public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
+    if (count($items) > 0) {
+      
+      $name_term = chado_get_semweb_term('analysis', 'name');
+      
+      $content = $items[0]['value'][$name_term];
+      if (array_key_exists('entity', $items[0]['value'])) {
+        list($entity_type, $entity_id) = explode(':', $items[0]['value']['entity']);
+        $content = l($content, 'bio_data/' . $entity_id);
+      }
+
+      // The cardinality of this field is 1 so we don't have to
+      // iterate through the items array, as there will never be more than 1.
+      $element[0] = [
+        '#type' => 'markup',
+        '#markup' => $content,
+      ];
+    }
+  }
+}

+ 72 - 0
tripal_chado/includes/TripalFields/operation__analysis/operation__analysis_widget.inc

@@ -0,0 +1,72 @@
+<?php
+
+class operation__analysis_widget extends ChadoFieldWidget {
+
+  // The default lable for this field.
+  public static $default_label = 'Analysis';
+
+  // The list of field types for which this formatter is appropriate.
+  public static $field_types = ['operation__analysis'];
+
+
+  /**
+   * @see TripalFieldWidget::form()
+   */
+  public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
+
+    parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
+
+    $settings = $this->field['settings'];
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Set the linker field appropriately.
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+
+    $analysis_id = 0;
+    if (count($items) > 0 and array_key_exists($linker_field, $items[0])) {
+      $analysis_id = $items[0][$linker_field];
+    }
+
+    $widget['value'] = [
+      '#type' => 'value',
+      '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
+    ];
+    $sql = "SELECT analysis_id, name FROM {analysis} ORDER BY name";
+    $results = chado_query($sql);
+    $options = ['' => '- Select an analysis -'];
+    while ($r = $results->fetchObject()) {
+      $options[$r->analysis_id] = $r->name;
+    }
+    $widget[$linker_field] = [
+      '#type' => 'select',
+      '#title' => $element['#title'],
+      '#description' => $element['#description'],
+      '#options' => $options,
+      '#default_value' => $analysis_id,
+      '#required' => $element['#required'],
+      '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
+      '#delta' => $delta,
+    ];
+  }
+
+  /**
+   * @see TripalFieldWidget::validate()
+   */
+  public function validate($element, $form, &$form_state, $langcode, $delta) {
+
+    $field_name = $this->field['field_name'];
+    $field_type = $this->field['type'];
+    $field_table = $this->instance['settings']['chado_table'];
+    $field_column = $this->instance['settings']['chado_column'];
+
+    // Set the linker field appropriately.
+    $linker_field = 'chado-' . $field_table . '__' . $field_column;
+    
+    // Make sure the value is set to the organism_id
+    $analysis_id = $form_state['values'][$field_name]['und'][0][$linker_field];
+    $form_state['values'][$field_name]['und'][0]['value'] = $analysis_id;
+  }
+}

+ 2 - 2
tripal_chado/includes/TripalFields/sbo__database_cross_reference/sbo__database_cross_reference_widget.inc

@@ -99,7 +99,7 @@ class sbo__database_cross_reference_widget extends ChadoFieldWidget {
       '#disabled' => $db_id ? FALSE : TRUE,
     );
   }
-
+    
   /**
    * @see TripalFieldWidget::validate()
    */
@@ -111,7 +111,7 @@ class sbo__database_cross_reference_widget extends ChadoFieldWidget {
     $field_table = $this->instance['settings']['chado_table'];
     $field_column = $this->instance['settings']['chado_column'];
     $base_table = $this->instance['settings']['base_table'];
-
+    
     $schema = chado_get_schema($table_name);
     $pkey = $schema['primary key'][0];
     $fkeys = array_values($schema['foreign keys'][$base_table]['columns']);

+ 6 - 6
tripal_chado/includes/TripalFields/schema__additional_type/schema__additional_type_widget.inc

@@ -19,8 +19,8 @@ class schema__additional_type_widget extends ChadoFieldWidget {
     $field_type = $this->field['type'];
     $field_table = $this->instance['settings']['chado_table'];
     $field_column = $this->instance['settings']['chado_column'];
-    $vocabulary = $this->instance['settings']['vocabulary'];
-    $parent_term = $this->instance['settings']['parent_term'];
+    $enforce_vocabulary = $this->instance['settings']['vocabulary'];
+    $enforce_parent_term = $this->instance['settings']['parent_term'];
     
     $linker_field = 'chado-' . $field_table . '__' . $field_column;
 
@@ -44,8 +44,8 @@ class schema__additional_type_widget extends ChadoFieldWidget {
     // If a parent_term is provided then use that to get the options
     $options = array();
     $options[] = 'Select a type';
-    if ($parent_term) {
-      list ($vocabulary, $accession) = explode(':', $parent_term);
+    if ($enforce_parent_term) {
+      list ($vocabulary, $accession) = explode(':', $enforce_parent_term);
       if ($vocabulary and $accession) {
         $sql = "
           SELECT
@@ -69,8 +69,8 @@ class schema__additional_type_widget extends ChadoFieldWidget {
         }
       }
     }
-    elseif ($vocabulary) {
-      $cv = chado_get_cv(array('name' => $vocabulary));
+    elseif ($enforce_vocabulary) {
+      $cv = chado_get_cv(array('name' => $enforce_vocabulary));
       $options = chado_get_cvterm_select_options($cv->cv_id);
     }
     // If no vocabulary or parent term are provided then just give a generic

+ 1 - 1
tripal_chado/includes/TripalFields/schema__alternate_name/schema__alternate_name_widget.inc

@@ -123,6 +123,7 @@ class schema__alternate_name_widget extends ChadoFieldWidget {
       '#required' => $element['#required'],
     );
   }
+    
   /**
    * @see TripalFieldWidget::validate()
    */
@@ -139,7 +140,6 @@ class schema__alternate_name_widget extends ChadoFieldWidget {
     $fkeys = array_values($schema['foreign keys'][$base_table]['columns']);
     $fkey = $fkeys[0];
 
-
     $pub_id = $form_state['values'][$field_name]['und'][$delta]['chado-' . $table_name . '__pub_id'];
     $syn_name = $form_state['values'][$field_name]['und'][$delta]['name'];
     $syn_type = $form_state['values'][$field_name]['und'][$delta]['type_id'];

+ 1 - 1
tripal_chado/includes/TripalFields/schema__publication/schema__publication_widget.inc

@@ -150,7 +150,7 @@ class schema__publication_widget extends ChadoFieldWidget {
     // it out so that the Chado field_storage infrastructure won't try to
     // write a record.
     if (!$title) {
-      $form_state['values'][$field_name]['und'][$delta]['value'] = '';
+      $form_state['values'][$field_name]['und'][$delta]['value'] = 'delete_me';
       $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__' . $fkey] = '';
       $form_state['values'][$field_name]['und'][$delta]['chado-' . $field_table . '__pub_id'] = '';
     }

+ 2 - 2
tripal_chado/includes/TripalFields/sio__annotation/sio__annotation_widget.inc

@@ -174,7 +174,7 @@ class sio__annotation_widget extends ChadoFieldWidget {
       );
     }
   }
-
+ 
   /**
    *
    * @see TripalFieldWidget::submit()
@@ -185,7 +185,7 @@ class sio__annotation_widget extends ChadoFieldWidget {
     $field_table = $this->instance['settings']['chado_table'];
     $field_column = $this->instance['settings']['chado_column'];
     $base_table = $this->instance['settings']['base_table'];
-
+    
     // Get the FK that links to the base record.
     $schema = chado_get_schema($field_table);
     $pkey = $schema['primary key'][0];

+ 5 - 7
tripal_chado/includes/loaders/tripal_chado.pub_importer_PMID.inc

@@ -356,7 +356,7 @@ function tripal_pub_PMID_parse_pubxml($pub_xml) {
   if (!$pub_xml) {
     return $pub;
   }
-
+  
   // read the XML and iterate through it.
   $xml = new XMLReader();
   $xml->xml(trim($pub_xml));
@@ -512,9 +512,7 @@ function tripal_pub_PMID_parse_article($xml, &$pub) {
           tripal_pub_PMID_parse_journal($xml, $pub);
           break;
         case 'ArticleTitle':
-          $xml->read();
-          // remoave any trailing period from the title
-          $pub['Title'] = trim(preg_replace('/\.$/', '', $xml->value));
+          $pub['Title'] = $xml->readString();
           break;
         case 'Abstract':
           tripal_pub_PMID_parse_abstract($xml, $pub);
@@ -675,14 +673,14 @@ function tripal_pub_PMID_parse_abstract($xml, &$pub) {
       switch ($element) {
         case 'AbstractText':
           $label = $xml->getAttribute('Label');
-          $xml->read();
+          $value = $xml->readString();
           if ($label) {
-            $part = "<p><b>$label</b></br>" . $xml->value . '</p>';
+            $part = "<p><b>$label</b></br>" . $value . '</p>';
             $abstract .= $part;
             $pub['Structured Abstract Part'][] = $part;
           }
           else {
-            $abstract .= '<p>' . $xml->value . '</p>';
+            $abstract .= "<p>" . $value . "</p>";
           }
           break;
         case 'CopyrightInformation':

+ 22 - 8
tripal_chado/includes/loaders/tripal_chado.pub_importers.inc

@@ -74,10 +74,8 @@ function tripal_pub_importers_list() {
       <li>Create a new importer by clicking the 'New Importer' link above, and after saving it should appear in the list below.  Click the
           link labeled 'Import Pubs' to schedule a job to import the publications</li>
       <li>The first method only performs the import once.  However, you can schedule the
-          importer to run peridically by adding a cron job. See the " .
-     l("Pub Module help instructions", "admin/tripal/legacy/tripal_pub/help") . " to learn how to
-     set the importers to run automatically.") . '</li>
-     </ol><br>';
+          importer to run peridically by adding a cron job. </li> 
+     </ol><br>");
 
 
   $table = array(
@@ -125,9 +123,13 @@ function tripal_pub_importer_setup_page($action = 'new', $pub_import_id = NULL)
     drupal_set_message(t('If you want to create contact pages for authors, you must first ') . l(t('load the Tripal Contact Ontology'), 'admin/tripal/loaders/chado_vocabs/obo_loader'), 'error');
   }
 
+  /**
+   * Commenting the AGL message out until we get AGL working again.
+   * spf 6/22/2018
   if(!extension_loaded ('yaz')){
     drupal_set_message(t('<b>Note:</b> In order to create an importer using the USDA National Agricultural Library (AGL) you must install the yaz libraries. See the ') . l(t('Pub Module help page'), 'admin/tripal/legacy/tripal_pub/help') . ' for assistance.  If you do not want to use AGL you can ignore this warning.', 'warning');
   }
+  */
 
   // generate the search form
   $form = drupal_get_form('tripal_pub_importer_setup_form',  $pub_import_id, $action);
@@ -137,9 +139,9 @@ function tripal_pub_importer_setup_page($action = 'new', $pub_import_id = NULL)
 
   // retrieve any results
   if (array_key_exists('tripal_pub_import', $_SESSION)) {
-    $remote_db = $_SESSION['tripal_pub_import']['remote_db'];
-    $num_criteria = $_SESSION['tripal_pub_import']['num_criteria'];
-    $days = $_SESSION['tripal_pub_import']['days'];
+    $remote_db = array_key_exists('remote_db', $_SESSION['tripal_pub_import']) ? $_SESSION['tripal_pub_import']['remote_db'] : '';
+    $num_criteria = array_key_exists('num_criteria', $_SESSION['tripal_pub_import']) ? $_SESSION['tripal_pub_import']['num_criteria'] : '';
+    $days = array_key_exists('days', $_SESSION['tripal_pub_import']) ? $_SESSION['tripal_pub_import']['days'] : '';
 
     $search_array = array();
     $search_array['remote_db'] = $remote_db;
@@ -371,6 +373,11 @@ function tripal_pub_importer_setup_form($form, &$form_state = NULL, $pub_import_
   if (!$remote_db) {
     $remote_db = 'PMID';
   }
+  /**
+   * Removing AGL option until we get it fixed
+   * spf 6/22/2018
+   */
+  unset($remote_dbs['AGL']);
   $form['themed_element']['remote_db'] = array(
     '#title' => t('Remote Database'),
     '#type' => 'select',
@@ -639,7 +646,7 @@ function tripal_pub_importer_setup_form_validate($form, &$form_state) {
     }
 
     if (!$is_phrase) {
-      if (preg_match('/and/i', $search_terms) and preg_match('/or/i', $search_terms)) {
+      if (preg_match('/\sand\s/i', $search_terms) and preg_match('/\sor\s/i', $search_terms)) {
         form_set_error("search_terms-$i", "You may use 'AND' or 'OR' but cannot use both. Add a new entry below with the same scope for the other conunction.");
         $_SESSION['tripal_pub_import']['perform_search'] = 0;
       }
@@ -1537,6 +1544,13 @@ function tripal_execute_pub_importer($import_id, $job_id = NULL) {
         chado_node_sync_records('contact');
       }
     }
+    // For Tripal v3 we want to publish the new publications
+    else {
+      $bundle = tripal_load_bundle_entity(['label' => 'Publication']);
+      if ($bundle) {
+        chado_publish_records(['bundle_name' => $bundle->name], $job_id);
+      }
+    }
     tripal_set_job_progress($job_id, '100');
   }
   catch (Exception $e) {

+ 20 - 8
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -42,12 +42,10 @@ function tripal_chado_field_storage_write($entity_type, $entity, $op, $fields) {
   $base_pkey = $base_schema['primary key'][0];
 
   // Convert the fields into a key/value list of fields and their values.
-  $field_vals = tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity);
-// dpm($field_vals);
-
-  // First, write the record for the base table.  If we have a record id then
-  // this is an update and we need to set the primary key.  If not, then this
-  // is an insert and we need to set the type_id if the table supports it.
+  list($field_vals, $field_items) = tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity);
+  // dpm($field_vals);  
+  
+  // First, write the record for the base table.
   $values = $field_vals[$base_table];
   if ($record_id) {
     $values[$base_pkey] = $record_id;
@@ -55,8 +53,10 @@ function tripal_chado_field_storage_write($entity_type, $entity, $op, $fields) {
   elseif ($type_field and !$linker) {
     $values[$type_field] = $cvterm->cvterm_id;
   }
-
   $base_record_id = tripal_chado_field_storage_write_table($base_table, $values, $base_table);
+  if (!$base_record_id) {
+    throw new Exception('Unable to write fields to Chado: ' . print_r($field_items, TRUE));
+  }
 
   // If this is an insert then add the chado_entity record.
   if ($op == FIELD_STORAGE_INSERT) {
@@ -381,6 +381,7 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
 function tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity) {
   $all_fields = array();
   $base_fields = array();
+  $field_items = array();
 
   // Iterate through all of the fields and organize them into a
   // new fields array keyed by the table name
@@ -405,6 +406,7 @@ function tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $e
     // are multi-valued.
     $items = field_get_items($entity_type, $entity, $field_name);
     $temp = array();
+    $field_items[$field_name] = $items;
     foreach ($items as $delta => $item) {
 
       // A field may have multiple items. The field can use items
@@ -453,7 +455,7 @@ function tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $e
   }
 
   $all_fields = array_merge($base_fields, $all_fields);
-  return $all_fields;
+  return [$all_fields, $field_items];
 }
 
 /**
@@ -728,6 +730,16 @@ function tripal_chado_field_storage_query($query) {
       } // end foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
     } // end if ($sort['type'] == 'field') {
   } // end foreach ($query->order as $index => $sort) {
+  
+  
+  // Only include records that are deleted.  Tripal doesn't keep track of
+  // records that are deleted that need purging separately so we can do nothing
+  // with this.
+  if (property_exists($query, 'deleted') and $query->deleted) {
+    // There won't ever be field data marked as deleted so just created a
+    // condition that always evaluates to false.
+    $cquery->where('1=0');
+  }
 
  //dpm($cquery->__toString());
  //dpm($cquery->getArguments());

+ 158 - 6
tripal_chado/includes/tripal_chado.fields.inc

@@ -532,6 +532,17 @@ function tripal_chado_bundle_fields_info_custom(&$info, $details, $entity_type,
           'type' => 'field_chado_storage',
         ),
       );
+      $field_name = 'efo__array_design';
+      $field_type = 'efo__array_design';
+      $info[$field_name] = array(
+        'field_name' => $field_name,
+        'type' => $field_type,
+        'cardinality' => 1,
+        'locked' => FALSE,
+        'storage' => array(
+          'type' => 'field_chado_storage',
+        ),
+      );
     }
   
   // For the pub_id field in the base table.
@@ -555,6 +566,20 @@ function tripal_chado_bundle_fields_info_custom(&$info, $details, $entity_type,
     );
   } 
 
+  // Analysis Id
+  if (array_key_exists('analysis_id', $schema['fields'])) {
+    $field_name = 'operation__analysis';
+    $field_type = 'operation__analysis';
+    $info[$field_name] = array(
+      'field_name' => $field_name,
+      'type' => $field_type,
+      'cardinality' => 1,
+      'locked' => FALSE,
+      'storage' => array(
+        'type' => 'field_chado_storage',
+      ),
+    );
+  }
 }
 
 /**
@@ -702,8 +727,24 @@ function tripal_chado_bundle_fields_info_linker(&$info, $details, $entity_type,
       }
 
       $field_name = strtolower(preg_replace('/[^\w]/','_', $term->dbxref_id->db_id->name . '__' . $term->name));
-      $field_name = substr($field_name, 0, 32);
+
+      // The field name can only be 32 chars, but if our name is longer we need
+      // to add some random chars to ensure we don't have naming conflicts 
+      // with other terms (e.g. mitochondrial_genetic_code and 
+      // mitochondrial_genetic_code_name)
+      if (strlen($field_name) >= 32) {
+        $field_name = substr($field_name, 0, 20) . '_' . $term->cvterm_id;
+      }
       $field_type = 'chado_linker__prop';
+      
+      // Don't try to add a property that uses the same term as another field.
+      if (array_key_exists($field_name, $info)) {
+        tripal_report_error('chado_fields', TRIPAL_WARNING, 
+          'A field of type !type already exists, yet a property wants to use the same term. The property cannot be added.',
+         ['!type' => $field_name],
+         ['drupal_set_message' => TRUE]);
+        continue;
+      }
       $info[$field_name] = array(
         'field_name' => $field_name,
         'type' => $field_type,
@@ -1016,11 +1057,11 @@ function tripal_chado_bundle_instances_info_base(&$info, $entity_type, $bundle,
     }
     if ($base_info['label'] == 'Timeaccessioned') {
       $base_info['label'] = 'Time Accessioned';
-      $base_info['description'] = 'Please enter the time that this record was first added to the database.';
+      $base_info['description'] = 'The time that this record was first added to the database.';
     }
     if ($base_info['label'] == 'Timelastmodified') {
       $base_info['label'] = 'Time Last Modified';
-      $base_info['description'] = 'Please enter the time that this record was last modified. The default is the current time.';
+      $base_info['description'] = 'The time that this record was last modified. The default is the current time.';
     }
 
     // Sometimes the boolean fields are listed as integer.  We need to
@@ -1178,6 +1219,8 @@ function tripal_chado_bundle_instances_info_base(&$info, $entity_type, $bundle,
       if ($column_name == 'name') {
         $base_info['widget']['type'] = 'text_textfield';
         $base_info['settings']['text_processing'] = '0';
+        $base_info['required'] = TRUE;
+        $base_info['description'] = 'A unique name for this assay..';
       }
       if ($column_name == 'protcol_id') {
         $base_info['label'] = 'Protocol';
@@ -1186,20 +1229,24 @@ function tripal_chado_bundle_instances_info_base(&$info, $entity_type, $bundle,
         $base_info['label'] = 'Array Batch Identifier';
         $base_info['widget']['type'] = 'text_textfield';
         $base_info['settings']['text_processing'] = '0';
+        $base_info['description'] = 'A unique identifier for the array batch.';
       }
       if ($column_name == 'operator_id') {
         $base_info['label'] = 'Operator';
+        $base_info['description'] = 'The individual who performed the assay.';
       }
       if ($column_name == 'arrayidentifier') {
         $base_info['label'] = 'Array Identifier';
         $base_info['widget']['type'] = 'text_textfield';
         $base_info['settings']['text_processing'] = '0';
+        $base_info['description'] = 'A unique alternate identifier for the array.';
       }
       if ($column_name == 'arraydesign_id') {
         $base_info['label'] = 'Array Design';
       }
       if ($column_name == 'assaydate') {
         $base_info['label'] = 'Assay Date';
+        $base_info['description'] = 'The date the assay was performed';
       }
     }
     //
@@ -1287,6 +1334,9 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
         'base_table' => $table_name,
         'vocabulary' => $default_vocab,
         'parent_term' => $parent_term,
+        'term_vocabulary' => 'schema',
+        'term_name' => 'additionalType',
+        'term_accession' => 'additionalType',
       ),
       'widget' => array(
         'type' => 'schema__additional_type_widget',
@@ -1527,6 +1577,11 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
   // BASE DBXREF
   if (array_key_exists('dbxref_id', $schema['fields'])) {
     $field_name = 'data__accession';
+    $required = FALSE;
+    if (array_key_exists('not null', $schema['fields']['dbxref_id']) and
+        $schema['fields']['dbxref_id']['not null']) {
+      $required = TRUE;
+    }
     $info[$field_name] = array(
       'field_name' => $field_name,
       'entity_type' => $entity_type,
@@ -1534,7 +1589,7 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
       'label' => 'Accession',
       'description' => 'This field specifies the unique stable accession (ID) for
         this record. It requires that this site have a database entry.',
-      'required' => FALSE,
+      'required' => $required,
       'settings' => array(
         'auto_attach' => TRUE,
         'chado_table' => $table_name,
@@ -1772,6 +1827,9 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
         'chado_table' => 'organism',
         'chado_column' => 'type_id',
         'base_table' => 'organism',
+        'term_vocabulary' => 'TAXRANK',
+        'term_name' => 'infraspecific_taxon',
+        'term_accession' => '0000046',
       ),
       'widget' => array(
         'type' => 'taxrank__infraspecific_taxon_widget',
@@ -1973,6 +2031,9 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
         'chado_table' => $table_name,
         'chado_column' => 'pub_id',
         'base_table' => $table_name,
+        'term_accession' => 'publication',
+        'term_vocabulary' => 'schema',
+        'term_name' => 'publication',
       ),
       'widget' => array(
         'type' => 'schema__publication_widget',
@@ -2027,7 +2088,80 @@ function tripal_chado_bundle_instances_info_custom(&$info, $entity_type, $bundle
         ),
       ),
     );
+    
+    $field_name = 'efo__array_design';
+    $info[$field_name] =  array(
+      'field_name' => $field_name,
+      'entity_type' => $entity_type,
+      'bundle' => $bundle->name,
+      'label' => 'Array design',
+      'description' => 'An instrument design which describes the design of the array.',
+      'required' => TRUE,
+      'settings' => array(
+        'auto_attach' => TRUE,
+        'chado_table' => 'assay',
+        'chado_column' => 'arraydesign_id',
+        'base_table' => 'assay',
+        'term_vocabulary' => 'EFO',
+        'term_name' => 'array design',
+        'term_accession' => '0000269',
+        
+      ),
+      'widget' => array(
+        'type' => 'efo__array_design_widget',
+        'settings' => array(
+          'display_label' => 1,
+        ),
+      ),
+      'display' => array(
+        'default' => array(
+          'label' => 'inline',
+          'type' => 'efo__array_design_formatter',
+          'settings' => array(),
+        ),
+      ),
+    );
   } 
+  // Analysis Id
+  if (array_key_exists('analysis_id', $schema['fields'])) {
+    $field_name = 'operation__analysis';
+    $is_required = FALSE;
+    if (array_key_exists('not null', $schema['fields']['analysis_id']) and
+        $schema['fields']['analysis_id']['not null']) {
+      $is_required = TRUE;
+    }
+    $info[$field_name] =  array(
+      'field_name' => $field_name,
+      'entity_type' => $entity_type,
+      'bundle' => $bundle->name,
+      'label' => 'Analysis',
+      'description' => 'Application of analytical methods to existing data of a specific type.',
+      'required' => $is_required,
+      'settings' => array(
+        'auto_attach' => TRUE,
+        'chado_table' => $table_name,
+        'chado_column' => 'analysis_id',
+        'base_table' => $table_name,
+        'term_vocabulary' => 'operation',
+        'term_name' => 'Analysis',
+        'term_accession' => '2945',
+        
+      ),
+      'widget' => array(
+        'type' => 'operation__analysis_widget',
+        'settings' => array(
+          'display_label' => 0,
+        ),
+      ),
+      'display' => array(
+        'default' => array(
+          'label' => 'inline',
+          'type' => 'operation__analysis_formatter',
+          'settings' => array(),
+        ),
+      ),
+    );
+  }
 }
 
 /**
@@ -2095,6 +2229,9 @@ function tripal_chado_bundle_instances_info_linker(&$info, $entity_type, $bundle
         'chado_table' => $dbxref_table,
         'chado_column' => $pkey,
         'base_table' => $table_name,
+        'term_vocabulary' => 'SBO',
+        'term_name' => 'Database Cross Reference',
+        'term_accession' => '0000554',
       ),
       'widget' => array(
         'type' => 'sbo__database_cross_reference_widget',
@@ -2377,7 +2514,13 @@ function tripal_chado_bundle_instances_info_linker(&$info, $entity_type, $bundle
          }
 
          $field_name = strtolower(preg_replace('/[^\w]/','_', $term->dbxref_id->db_id->name . '__' . $term->name));
-         $field_name = substr($field_name, 0, 32);
+         // The field name can only be 32 chars, but if our name is longer we need
+         // to add some random chars to ensure we don't have naming conflicts
+         // with other terms (e.g. mitochondrial_genetic_code and
+         // mitochondrial_genetic_code_name)
+         if (strlen($field_name) >= 32) {
+           $field_name = substr($field_name, 0, 20) . '_' . $term->cvterm_id;
+         }
          $info[$field_name] = array(
            'field_name' => $field_name,
            'entity_type' => $entity_type,
@@ -2448,6 +2591,9 @@ function tripal_chado_bundle_instances_info_linker(&$info, $entity_type, $bundle
         'chado_table' => $term_table,
         'chado_column' => $pkey,
         'base_table' => $table_name,
+        'term_vocabulary' => 'SIO',
+        'term_name' => 'annotation',
+        'term_accession' => '001166',
       ),
       'widget' => array(
         'type' => 'sio__annotation_widget',
@@ -2483,6 +2629,9 @@ function tripal_chado_bundle_instances_info_linker(&$info, $entity_type, $bundle
         'chado_table' => $pub_table,
         'chado_column' => $pkey,
         'base_table' => $table_name,
+        'term_accession' => 'publication',
+        'term_vocabulary' => 'schema',
+        'term_name' => 'publication',
       ),
       'widget' => array(
         'type' => 'schema__publication_widget',
@@ -2555,6 +2704,9 @@ function tripal_chado_bundle_instances_info_linker(&$info, $entity_type, $bundle
         'chado_table' => $rel_table,
         'chado_column' => $pkey,
         'base_table' => $table_name,
+        'term_vocabulary' => 'SBO',
+        'term_name' => 'Relationship',
+        'term_accession' => '0000374',
       ),
       'widget' => array(
         'type' => 'sbo__relationship_widget',
@@ -2947,4 +3099,4 @@ function tripal_chado_field_views_data_alter(&$result, $field, $module) {
       }
     }
   }
-}
+}

+ 6 - 1
tripal_chado/includes/tripal_chado.phylotree.inc

@@ -5,6 +5,11 @@
  * @param $phylotree
  */
 function tripal_phylogeny_prepare_tree_viewer($phylotree) {
+  
+  // If the phylotree is not provided then just return;
+  if (!$phylotree) {
+    tripal_report_error('tripal_phylotree', TRIPAL_ERROR, 'tripal_phylogeny_prepare_tree_viewer: must provide a $phylotree argument.');
+  }
 
   // Don't prepare for viewing more than once.
   if (property_exists($phylotree, 'prepared_to_view') and
@@ -24,7 +29,7 @@ function tripal_phylogeny_prepare_tree_viewer($phylotree) {
 
   // Don't show tick marks for the taxonomy tree.
   $skip_ticks = 0;
-  if ($phylotree->type_id->name == 'taxonomy' or $phylotree->type_id->name == 'Species tree') {
+  if (!is_null($phylotree->type_id) and ($phylotree->type_id->name == 'taxonomy' or $phylotree->type_id->name == 'Species tree')) {
     $skip_ticks = 1;
   }
 

+ 1 - 0
tripal_chado/includes/tripal_chado.pub_search.inc

@@ -308,6 +308,7 @@ function tripal_chado_pub_search_form($form, &$form_state) {
       '#type'          => 'textfield',
       '#default_value' => $search_terms,
       '#required'      => FALSE,
+      '#size' => 35,
     );
     $form['criteria'][$i]["scope-$i"] = array(
       '#type'          => 'select',

+ 8 - 3
tripal_chado/tripal_chado.module

@@ -407,7 +407,7 @@ function tripal_chado_menu() {
   );
   $items['admin/tripal/loaders/pub/edit/%'] = array(
     'page callback' => 'tripal_pub_importer_setup_page',
-    'page arguments' => array(5),
+    'page arguments' => array('edit', 5),
     'access arguments' => array('load tripal data'),
     'type ' => MENU_CALLBACK,
     'file' => 'includes/loaders/tripal_chado.pub_importers.inc',
@@ -1054,7 +1054,7 @@ function tripal_chado_form_field_ui_field_edit_form_alter(&$form, &$form_state,
 
     // If this is a Chado field we need to preserve the Chado elements of the
     // settings or they will be lost if a user edits the field settings.
-    if (array_key_Exists('chado_table', $form['#instance']['settings'])) {
+    if (array_key_exists('chado_table', $form['#instance']['settings'])) {
       $form['instance']['settings']['base_table'] = array(
         '#type' => 'value',
         '#value' => $form['#instance']['settings']['base_table'],
@@ -1183,7 +1183,12 @@ function tripal_feature_match_features_page($id) {
     }
     $table_attrs = array('class' => 'tripal-data-table');
     $output = "<p>The following features match the name '$id'.</p>";
-    $output .= theme_table($header, $rows, $table_attrs, $caption);
+    $output .= theme_table(array(
+      'header' => $header, 
+      'rows' => $rows, 
+      'attributes' => $table_attrs, 
+      'caption' => $caption
+    ));
     return $output;
   }
 

+ 3 - 142
tripal_ws/includes/TripalFields/remote__data/remote__data_widget.inc

@@ -6,152 +6,13 @@ class remote__data_widget extends WebServicesFieldWidget {
   public static $default_label = 'Remote Data';
   // The list of field types for which this formatter is appropriate.
   public static $field_types = array('remote__data');
+ 
   /**
-   * Provides the form for editing of this field.
-   *
-   * This function corresponds to the hook_field_widget_form()
-   * function of the Drupal Field API.
-   *
-   * This form is diplayed when the user creates a new entity or edits an
-   * existing entity.  If the field is attached to the entity then the form
-   * provided by this function will be displayed.
-   *
-   * At a minimum, the form must have a 'value' element.  For Tripal, the
-   * 'value' element of a field always corresponds to the value that is
-   * presented to the end-user either directly on the page (with formatting)
-   * or via web services, or some other mechanism.  However, the 'value' is
-   * sometimes not enough for a field.  For example, the Tripal Chado module
-   * maps fields to table columns and sometimes those columns are foreign keys
-   * therefore, the Tripal Chado modules does not just use the 'value' but adds
-   * additional elements to help link records via FKs.  But even in this case
-   * the 'value' element must always be present in the return form and in such
-   * cases it's value should be set equal to that added in the 'load' function.
-   *
-   * @param $widget
-   * @param $form
-   *   The form structure where widgets are being attached to. This might be a
-   *   full form structure, or a sub-element of a larger form.
-   * @param $form_state
-   *   An associative array containing the current state of the form.
-   * @param $langcode
-   *   The language associated with $items.
-   * @param $items
-   *   Array of default values for this field.
-   * @param $delta
-   *   The order of this item in the array of subelements (0, 1, 2, etc).
-   * @param $element
-   * A form element array containing basic properties for the widget:
-   *  - #entity_type: The name of the entity the field is attached to.
-   *  - #bundle: The name of the field bundle the field is contained in.
-   *  - #field_name: The name of the field.
-   *  - #language: The language the field is being edited in.
-   *  - #field_parents: The 'parents' space for the field in the form. Most
-   *    widgets can simply overlook this property. This identifies the location
-   *    where the field values are placed within $form_state['values'], and is
-   *    used to access processing information for the field through the
-   *    field_form_get_state() and field_form_set_state() functions.
-   *  - #columns: A list of field storage columns of the field.
-   *  - #title: The sanitized element label for the field instance, ready for
-   *    output.
-   *  - #description: The sanitized element description for the field instance,
-   *    ready for output.
-   *  - #required: A Boolean indicating whether the element value is required;
-   *    for required multiple value fields, only the first widget's values are
-   *    required.
-   *  - #delta: The order of this item in the array of subelements; see
-   *    $delta above
+   * @see TripalFieldWidget::form()
    */
   public function form(&$widget, &$form, &$form_state, $langcode, $items, $delta, $element) {
     parent::form($widget, $form, $form_state, $langcode, $items, $delta, $element);
-    // Get the field settings.
-   /* $field_name = $this->field['field_name'];
-    $field_type = $this->field['type'];
-
-    // Get the setting for the option for how this widget.
-    $instance = $this->instance;
-    $settings = '';
-    $site_list = '';
-
-    $tokens = array();
-    // Get the form info from the bundle about to be saved.
-    $bundle_info = tripal_load_bundle_entity(array('name' => $form_state['build_info']['args']['0']['bundle']));
-    // Retrieve all available tokens.
-    $tokens = tripal_get_entity_tokens($bundle_info);
-    // If the field already has a value then it will come through the $items
-    // array.  This happens when editing an existing record.
-
-    // FORM PROPER
-    $widget['#prefix'] =  "<span id='$field_name-remote_data-$delta'>";
-    $widget['#suffix'] =  "</span>";
-
-    $widget['value'] = array(
-      '#type' => 'value',
-      '#value' => array_key_exists($delta, $items) ? $items[$delta]['value'] : '',
-    );
-
-    $widget['data_info'] = array(
-      '#type' => 'fieldset',
-      '#title' => 'Remote Data Settings',
-      '#description' => 'Provide the site name, query and description for the remote data source.',
-      '#collapsible' => TRUE,
-      '#collapsed' => FALSE,
-      '#prefix' => "<div id='set_titles-fieldset'>",
-      '#suffix' => '</div>',
-    );
-
-    // Get the site info from the tripal_sites table.
-      // Get the field groups associated with this bundle.
-    $sites = db_select('tripal_sites', 's')
-      ->fields('s')
-      ->execute()->fetchAll();
-
-    foreach ($sites as $site) {
-      $rows[] = $site->name;
-    }
-
-    $widget['data_info']['site'] = array(
-      '#type' => 'select',
-      '#title' => t('Site'),
-      '#options' => $rows,
-      '#default_value' => $site_list,
-    );
-
-    $widget['data_info']['query'] = array(
-      '#type' => 'textarea',
-      '#title' => 'Query',
-      '#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'],
-      '#rows' => 5
-    );
-
-    $widget['set_titles']['token_display']['tokens'] = array(
-      '#type' => 'hidden',
-      '#value' => serialize($tokens)
-    );
-
-    $widget['data_info']['token_display'] = array(
-      '#type' => 'fieldset',
-      '#title' => 'Available Tokens',
-      '#description' => 'Copy the token and paste it into the "Query" text field above.',
-      '#collapsible' => TRUE,
-      '#collapsed' => TRUE
-    );
-
-    $widget['data_info']['token_display']['content'] = array(
-      '#type' => 'item',
-      '#markup' => theme_token_list($tokens),
-    );
-
-    $widget['data_info']['description'] = array(
-      '#type' => 'textarea',
-      '#title' => 'Description',
-      '#description' => 'Describe the data being pulled in.',
-      '#default_value' =>  $this->instance['settings']['data_info']['description'],
-      '#rows' => 1
-    );
-*/
-    //TODO Add test button to ensure query returns info.
+    
   }
   /**
    * Performs validation of the widgetForm.

+ 0 - 5
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -1000,11 +1000,6 @@ class TripalContentService_v0_1 extends TripalWebService {
       $member->addContextItem($accession, 'vocab:' . $accession);
       $member->setType($accession);
 
-      // Make sure the term has a URL.
-      $url = $term['url'];
-      if (!$url) {
-        throw new Exception(t('Missing a URL for the term: @term.', array('@term' => $term['vocabulary']['short_name'] . ':' . $term['accession'])));
-      }
       $this->addResourceProperty($member, $label, $bundle->label . ' Collection');
       $member->addContextItem('description', 'rdfs:comment');
       // Get the bundle description. If no description is provided then

+ 5 - 1
tripal_ws/tripal_ws.module

@@ -438,8 +438,12 @@ function tripal_ws_load_remote_entity($site_id, $api_version, $ctype, $id) {
 }
 
 function tripal_ws_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
+  // Don't let the user change the cardinality of web services fields
   if ($form['#instance']['entity_type'] == 'TripalEntity') {
+    if ($form['#field']['storage']['type'] == 'field_tripal_ws_storage') {
       $form['field']['cardinality']['#access'] = FALSE;
+      $form['instance']['required']['#access'] = FALSE;
+    }
   }
 }
 
@@ -472,4 +476,4 @@ function tripal_ws_entity_info_alter(&$entity_info) {
       'custom settings' => FALSE,
     ),
   );
-}
+}