Browse Source

Fixed merge conflict. Experimenting with entity-level field validation

Stephen Ficklin 9 years ago
parent
commit
a7ed0666f8

+ 75 - 57
legacy/tripal_core/includes/tripal_core.toc.inc

@@ -1,16 +1,16 @@
 <?php
 
 /**
- * 
+ *
  */
 function tripal_core_node_toc_form($form, &$form_state, $node) {
-  
+
   // Get info about this content type
   $all_types = node_type_get_types();
   $type_info = $all_types[$node->type];
-  
+
   $form["#tree"] = TRUE;
-  
+
   $form["instructions"] = array(
     '#type' => 'fieldset',
     '#collapsed' => TRUE,
@@ -18,28 +18,28 @@ function tripal_core_node_toc_form($form, &$form_state, $node) {
     '#title' => 'Instructions',
   );
   $admin_link = l(
-    $type_info->name . " TOC administrative page", 
+    $type_info->name . " TOC administrative page",
     "admin/tripal/chado/" . $type_info->module . "/" . $node->type . "toc",
     array('attributes' => array('target' => '_blank'))
   );
   $form["instructions"]["main"] = array(
     '#markup' => '<p>' . t("Below is a list of the titles of
-      content panes that can appear on this page.  These titles appear in the 
-      the following order in the Table of Contents (TOC). You may rename 
+      content panes that can appear on this page.  These titles appear in the
+      the following order in the Table of Contents (TOC). You may rename
       the titles or drag and drop them to change the order.  <b>Any changes will
       only apply to this page</b>. If you would like to make changes apply to multiple
       pages of the same tpye, please visit the $admin_link. ") . '</p>' .
       '<p>' . t('The list below shows all possible content panes that can appear.
-      However, those without content are automatically hidden and do not 
+      However, those without content are automatically hidden and do not
       appear in the TOC.' . '</p>'),
   );
-  
+
   $form['node'] = array(
     '#type' => 'value',
     '#value' => $node,
   );
-  
-  // Get the content array for this node, then pass it through the 
+
+  // Get the content array for this node, then pass it through the
   // tripal_core_node_view_alter which generates the TOC.  After that
   // we can use the $build array to build the form. We have to add
   // a 'tripal_toc_mode' to the $node because we need to give the mode
@@ -91,7 +91,7 @@ function tripal_core_node_toc_form($form, &$form_state, $node) {
     }
   }
   $form['toc_items']['#theme'] = 'tripal_node_toc_items_table';
-  
+
   $form['submit'] = array(
     '#type' => 'submit',
     '#name' => 'toc_submit',
@@ -102,11 +102,11 @@ function tripal_core_node_toc_form($form, &$form_state, $node) {
     '#name' => 'toc_unset',
     '#value' => t('Unset Node Customizations'),
   );
-  
+
   // Check to see if this node's TOC is specifically being managed.
   $sql = "SELECT count(*) FROM {tripal_toc} where nid = :nid";
   $managed_items = db_query($sql, array(':nid' => $node->nid))->fetchField();
-  
+
   if ($managed_items > 0) {
     $form['is_managed'] = array(
       '#markup' => '<p><font color="red">' .
@@ -116,12 +116,12 @@ function tripal_core_node_toc_form($form, &$form_state, $node) {
         customizations and default to the content type settings.') . '</p>',
     );
   }
-  
-  
+
+
   return $form;
 }
 /**
- * 
+ *
  * @param $variables
  */
 function theme_tripal_node_toc_items_table($variables) {
@@ -137,7 +137,7 @@ function theme_tripal_node_toc_items_table($variables) {
 
   // Build the table header.
   $headers = array('Content Pane Name', 'Hide', 'Weight');
-  
+
   // Format the form elements as rows in the table.
   $rows = array();
   foreach ($toc_items as $key => $item) {
@@ -150,7 +150,7 @@ function theme_tripal_node_toc_items_table($variables) {
       'class' => array('draggable'),
     );
   }
-  
+
   // Theme and return the table.
   $table = array(
     'header' => $headers,
@@ -166,7 +166,7 @@ function theme_tripal_node_toc_items_table($variables) {
 }
 
 /**
- * 
+ *
  * @param $a
  * @param $b
  */
@@ -202,16 +202,16 @@ function tripal_core_node_toc_form_validate($form, &$form_state) {
 function tripal_core_node_toc_form_submit($form, &$form_state) {
   $toc_items = $form_state['values']['toc_items'];
   $node      = $form_state['values']['node'];
-  
-  if ($form_state['clicked_button']['#name'] == "toc_submit") { 
+
+  if ($form_state['clicked_button']['#name'] == "toc_submit") {
     $transaction = db_transaction();
     try {
       // First delete any settings for this node
       db_delete('tripal_toc')
         ->condition('nid', $node->nid)
         ->execute();
-  
-      // Second add in any new settings for this node 
+
+      // Second add in any new settings for this node
       foreach ($toc_items as $toc_id => $item) {
         db_insert('tripal_toc')
           ->fields(array(
@@ -250,7 +250,7 @@ function tripal_core_node_toc_form_submit($form, &$form_state) {
 
 /**
  * To be called by tripal_core_node_view_alter() to generate the TOC.
- * 
+ *
  * @param $build
  *   The build array passed to hook_node_view_alter()
  *
@@ -262,10 +262,10 @@ function tripal_core_node_view_build_toc(&$build) {
   if ($build['#view_mode'] != 'full' OR !array_key_exists('#tripal_generic_node_template', $build)) {
     return;
   }
-  
+
   $node_type = $build["#node"]->type;
   $nid = $build["#node"]->nid;
-  
+
   // The mode alters the format of the build array. There are three types of
   // modes: "display", "manage_node", "manage_type".  If "display" is provided
   // then the build array is formatted for the display of the content.
@@ -277,29 +277,29 @@ function tripal_core_node_view_build_toc(&$build) {
   // specific nodes and therefore should not be used when managing the
   // TOC for a content type.
   $mode = isset($build["#node"]->tripal_toc_mode) ? $build["#node"]->tripal_toc_mode : "display";
-  
-  
+
+
   $cache = cache_get("theme_registry:$theme", 'cache');
   $node = $build['#node'];
   $toc = array();
   $toc_html = '';
-  
+
   // If we are looking at a Tripal node template then we want to
   // make some changes to each pane of content so that we can associate
   // a table of contents and add administrator and curator messages.
   if ($build['#tripal_generic_node_template'] == TRUE) {
-  
+
     // Iterate through all the elements of the $build array and for those
     // that are wanting to provide content for this node.
     $markup = array();
     foreach ($build as $key => $value) {
       $value = $build[$key];
-      
+
       // Skip the body element as the Tripal node types do not use it.
       if ($key == 'body') {
         continue;
       }
-  
+
       // Skip the table of contents and links as those will be placed elsewhere.
       if (preg_match('/^#/', $key) or $key == 'tripal_toc' or $key == 'links') {
         continue;
@@ -337,6 +337,24 @@ function tripal_core_node_view_build_toc(&$build) {
             continue;
           }
 
+          // This field supports tokens, so we need to perform the substitutions
+          // if one is needed. Get the tokens and format
+          $base_table = preg_replace('/^chado_(.*)$/', '\1', $node_type);
+          $tokens = chado_node_generate_tokens($base_table);
+          $markup = $element['#markup'];
+          // Determine which tokens were used in the format string
+          if (preg_match_all('/\[[^]]+\]/', $markup, $used_tokens)) {
+            // Get the value for each token used
+            foreach ($used_tokens[0] as $token) {
+              $token_info = $tokens[$token];
+              if (!empty($token_info)) {
+                $value = chado_get_token_value($token_info, $node);
+                $markup = str_replace($token, $value, $markup);
+              }
+            }
+            $element['#markup'] = $markup;
+          }
+
           // Add the link to the TOC
           $parts = explode("|", $element['#markup']);
           if (count($parts) == 2) {
@@ -346,7 +364,7 @@ function tripal_core_node_view_build_toc(&$build) {
             $toc[$weight][$parts[0]] = "<div id=\"$toc_item_id\" class=\"tripal_toc_list_item\">" . $element['#markup'] . "</div>";
           }
 
-          // Add to the build array but do not add markup. This way 
+          // Add to the build array but do not add markup. This way
           // when the TOC is managed by the node 'TOC' menu these links can
           // be ordered as well.
           $build[$toc_item_id]['#toc_handled'] = TRUE;
@@ -414,7 +432,7 @@ function tripal_core_node_view_build_toc(&$build) {
         unset($build["field_resource_titles"]);
         continue;
       } // end if ($mode != "manage_type" and $key == "field_resource_blocks") {
-  
+
       // Skip any keys we may have already handled. This is the case for
       // the field_resource_blocks where we removed the old CCK fields
       // and added new ones.  We don't want these new ones to be processed
@@ -449,11 +467,11 @@ function tripal_core_node_view_build_toc(&$build) {
       if (array_key_exists('#tripal_toc_id', $build[$key])) {
         $toc_item_id = $build[$key]['#tripal_toc_id'];
       }
-      
+
       // Get any overrides for this key.
       $overrides = tripal_core_get_toc_overrides($nid, $toc_item_id, $node_type, $mode);
 
-      
+
       // If the element should be hidden then unset this key the build
       // array continue to the next one
       if ($mode == "display" and $overrides['hide'] == 1) {
@@ -472,7 +490,7 @@ function tripal_core_node_view_build_toc(&$build) {
       // GET THE MARKUP FOR EACH ELEMENT
       //-----------------------
       $markup = '';
-  
+
       // find the markup. Some fields will have a '#markup' and others, such
       // as CCK elements may have a set of '#markup' elements organized by
       // numerical keys.
@@ -497,13 +515,13 @@ function tripal_core_node_view_build_toc(&$build) {
         '#weight' => $weight,
         '#hide' => $hide,
       );
-      
-  
+
+
         // if we still don't have markup then skip this one
       if (!$markup) {
         continue;
       }
-  
+
       //-----------------------
       // FIND THE TEMPLATE PATH
       //-----------------------
@@ -512,9 +530,9 @@ function tripal_core_node_view_build_toc(&$build) {
       if (!array_key_exists('#tripal_template_show', $build[$key]) or
         $build[$key]['#tripal_template_show'] == TRUE) {
         if ($cache and array_key_exists($key, $cache->data) and array_key_exists('path', $cache->data[$key])) {
-    
+
           $path = $cache->data[$key]['path'] . '/' . $key . '.tpl.php';
-    
+
           $path = tripal_set_message("Administrators, you can
             customize the way the content above is presented.  Tripal provides a template
             file for each pane of content.  To customize, copy the template file to your
@@ -526,7 +544,7 @@ function tripal_core_node_view_build_toc(&$build) {
           );
         }
       }
-    
+
       //-----------------------
       // ADD THIS PANE TO THE TOC BY ORDER OF WEIGHT
       //-----------------------
@@ -550,11 +568,11 @@ function tripal_core_node_view_build_toc(&$build) {
           </div>
         </div>
       ";
-  
+
       $build[$key]['#markup'] = $updated_markup;
     } // end foreach ($build as $key => $value) {
   } // end if ($build['#tripal_generic_node_template'] == TRUE) {
-  
+
   //-----------------------
   // BUILD THE TABLE OF CONTENTS LINKS
   //-----------------------
@@ -572,7 +590,7 @@ function tripal_core_node_view_build_toc(&$build) {
 }
 
 /**
- * 
+ *
  * @param $build
  */
 function tripal_core_get_toc_overrides($nid, $key, $node_type, $mode) {
@@ -580,7 +598,7 @@ function tripal_core_get_toc_overrides($nid, $key, $node_type, $mode) {
   $override_title = '';
   $override_weight = '';
   $override_hide = 0;
-  
+
   if ($mode != "manage_type") {
     // First look to see if the node has customizations for this item.
     $toc_item_overrides = db_select('tripal_toc', 'tc')
@@ -600,7 +618,7 @@ function tripal_core_get_toc_overrides($nid, $key, $node_type, $mode) {
       );
     }
   }
-  
+
   // If there are no specific node customizations then look to see if there
   // are customizations for this content type.
   $toc_item_overrides = db_select('tripal_toc', 'tc')
@@ -632,7 +650,7 @@ function tripal_core_content_type_toc_form($form, &$form_state, $content_type) {
   // Get the type details
   $all_types = node_type_get_types();
   $type_info = $all_types[$content_type];
-  
+
   $form["#tree"] = TRUE;
 
   // Get a single node of this type so we can get all the possible content for it
@@ -640,13 +658,13 @@ function tripal_core_content_type_toc_form($form, &$form_state, $content_type) {
   $nid = db_query($sql, array(':type' => $content_type))->fetchField();
   if (!$nid) {
     $form["not_available"] = array(
-      '#markup' => t('Please sync at least one %type_name record. A node 
-          must exist before customizations to the Table of Contents (TOC) can 
+      '#markup' => t('Please sync at least one %type_name record. A node
+          must exist before customizations to the Table of Contents (TOC) can
           be performed.', array('%type_name' => $type_info->name)),
     );
     return $form;
-  } 
-  
+  }
+
   // Load the node
   $node = node_load($nid);
 
@@ -656,12 +674,12 @@ function tripal_core_content_type_toc_form($form, &$form_state, $content_type) {
   // a 'tripal_toc_mode' to the $node because we need to give the mode
   // to the tripal_core_node_view_build_toc function.
   $node->tripal_toc_mode = 'manage_type';
-  
+
   node_build_content($node);
   $build = $node->content;
   $build["#node"] = $node;
   tripal_core_node_view_alter($build);
-  
+
   $form["instructions"] = array(
     '#type' => 'fieldset',
     '#collapsed' => TRUE,
@@ -680,7 +698,7 @@ function tripal_core_content_type_toc_form($form, &$form_state, $content_type) {
       panes that can appear. However, those without content are automatically
       hidden and do not appear in the TOC.' . '</p>'),
   );
-  
+
   $form['content_type'] = array(
     '#type' => 'value',
     '#value' => $content_type,

+ 41 - 0
legacy/tripal_core/tripal_core.module

@@ -220,3 +220,44 @@ function tripal_core_node_view($node, $view_mode, $langcode) {
     }
   }
 }
+
+/**
+ * Adds support for tokens in the field_resource_links field.
+ *
+ * The field_resource_links field is a special field that can be manually
+ * added by the site admin for providing links on the Tripal TOC sidebar.
+ * Using tokens will allow for creation of custom links. This function
+ * will add a fieldset contiaining the list of appropriate tokens for the
+ * content type.
+ *
+ * @param unknown $element
+ * @param unknown $form_state
+ * @param unknown $context
+ */
+function tripal_core_field_widget_form_alter(&$element, &$form_state, $context) {
+
+  // If the name of the field is 'field_resource_links' then we want to
+  // add a fieldset of tokens.
+  if ($element['#field_name'] == 'field_resource_links') {
+
+    // Add the tokens fieldset to the last element.
+    $num_elements = count($context['items']);
+    if ($num_elements == $element['#delta']) {
+      $bundle = $element['#bundle'];
+      $base_table = preg_replace('/^chado_(.*)$/', '\1', $bundle);
+      $tokens = chado_node_generate_tokens($base_table);
+      $token_list = chado_node_format_tokens($tokens);
+      $element['tokens'] = array(
+        '#type' => 'fieldset',
+        '#title' => 'Available tokens',
+        '#collapsible' => TRUE,
+        '#collapsed' => TRUE,
+        '#weight' => 100
+      );
+      $element['tokens']['tokens_table'] = array(
+        '#type' => 'item',
+        '#markup' => $token_list
+      );
+    }
+  }
+}

+ 156 - 9
tripal/api/tripal.entities.api.inc

@@ -20,7 +20,7 @@ function tripal_load_term_entity($values) {
 
   if ($namespace and $accession) {
     $query = db_select('tripal_term', 'tt');
-    $query->join('tripal_vocab' ,'tv', 'tv.id = tt.vocab_id');
+    $query->join('tripal_vocab', 'tv', 'tv.id = tt.vocab_id');
     $query->fields('tt', array('id'))
       ->fields('tv', array('namespace'))
       ->condition('tv.namespace', $namespace)
@@ -282,6 +282,7 @@ function tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $b
   );
   field_create_instance($field_instance);
 }
+
 /**
  * Allows a module to make changes to an entity object after creation.
  *
@@ -294,24 +295,127 @@ function tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $b
 function hook_entity_create(&$entity, $entity_type) {
 
 }
+
+
 /**
- * Allows a module to add fields to a bundle.
+ * Adds fields to a bundle type.
  *
- * This function is called after the bundle is created and allows any module
- * to add fields to it.
+ * When a new bundle (Tripal content type) is created, the tripal module
+ * allows any other module the implements this hook to add fields too it. This
+ * hook is called automatically.
  *
  * @param $entity_type
- *   The entity type (e.g. TripalEntity).
+ *   The type of entity (e.g. 'TripalEntity')
  * @param $bundle
- *   A TripalBundle object.
+ *   An instance of a TripalBundle object.  This is the bundle to which
+ *   the fields will be added.
  * @param $term
- *   A TripalTerm object.
+ *   An instance of a TripalTerm object. Each TripalBundle is associated with
+ *   a controlled vocabulary term. This is the term object for the bundle.
  *
  * @return
- *   TRUE on success, FALSE on failure.
+ *   There is no return value.
  */
 function hook_add_bundle_fields($entity_type, $bundle, $term) {
 
+  //
+  // The example code below is derived from the tripal_chado modules and
+  // adds a 'synonym' field to the bundle.
+  //
+
+  $bundle_name = $bundle->name;
+
+  // This array will hold details that map the bundle to tables in Chado.
+  $bundle_data = array();
+
+  // Get the cvterm that corresponds to this TripalTerm object.
+  $vocab = entity_load('TripalVocab', array($term->vocab_id));
+  $vocab = reset($vocab);
+  $match = array(
+    'dbxref_id' => array(
+      'db_id' => array(
+        'name' => $vocab->namespace,
+      ),
+      'accession' => $term->accession
+    ),
+  );
+  $cvterm = chado_generate_var('cvterm', $match);
+
+
+  // Get the default table that this term maps to.
+  $default = db_select('tripal_cv_defaults', 't')
+  ->fields('t')
+  ->condition('cv_id', $cvterm->cv_id->cv_id)
+  ->execute()
+  ->fetchObject();
+  if ($default) {
+    $bundle_data = array(
+      'cv_id' => $cvterm->cv_id->cv_id,
+      'cvterm_id' => $cvterm->cvterm_id,
+      'data_table' => $default->table_name,
+      'type_table' => $default->table_name,
+      'field' =>  $default->field_name,
+    );
+  }
+  else {
+    return;
+  }
+
+  // Formulate the name of the synonym table.
+  $syn_table = $bundle_data['data_table'] . '_synonym';
+
+  // Don't add a field to this bundle if
+  if (!chado_table_exists($syn_table)) {
+    return;
+  }
+
+  $field_name = $syn_table;
+  $schema = chado_get_schema($syn_table);
+  $pkey = $schema['primary key'][0];
+
+
+  // Tripal wants to map fields to a controlled vocabulary terms
+  // (e.g SO:0000704 for a 'gene').  This is not required, but is highly
+  // recommended to support mashups of data via web services.
+  $semantic_web = array(
+    // The type is the term from a vocabulary that desribes this field..
+    'type' => '',
+    // The namepsace for the vocabulary (e.g. 'foaf').
+    'ns' => '',
+    // The URL for the namespace.  It must be that the type can be
+    // appended to the URL.
+    'nsurl' => '',
+  );
+
+  // The Tripal Chado module needs to know what Chado table and field
+  // this field maps to, as well as the base table that it maps to
+  // if this field will map to a record in a linking table.
+  $field_settings = array(
+    // The Chado table that this field maps to.
+    'chado_table' => $syn_table,
+    // The column in the chado table that this field maps to.
+    'chado_column' => $pkey,
+    // The base table that this field is connected to.
+    'base_table' => $base_table,
+    'semantic_web' => $semantic_web,
+  );
+
+  // Finally set the field values.
+  $field_info = array(
+    'field_type' => 'synonym',
+    'widget_type' => 'tripal_fields_synonym_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Synonyms',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'storage' => 'field_chado_storage',
+    'field_settings' => $field_settings,
+  );
+
+  // Call the Tripal API function tripal_add_bundle_field to complete the
+  // addition of the field to the bundle.
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
 }
 
 /**
@@ -368,6 +472,10 @@ function tripal_set_bundle_variable($variable_name, $bundle_id, $value) {
 
   $variable = tripal_get_variable($variable_name);
 
+  if (!$variable) {
+    return FALSE;
+  }
+
   // And then we need to write the new format to the tripal_bundle_variables table.
   $record = array(
     'bundle_id' => $bundle_id,
@@ -785,7 +893,7 @@ function hook_vocab_import_form_submit($form, &$form_state) {
  *   An array with at least the following keys:
  *     namespace : The namespace of the vocabulary.
  *     accession : The name unique ID of the term.
- *     url_prefix : The URL by which terms (accessions) can be appended.
+ *     url : The URL for the term.
  *     name : The name of the term.
  *     definition : The term's description.
  *   any other keys may be added as desired. Returns NULL if the term
@@ -795,3 +903,42 @@ function hook_vocab_get_term($namespace, $accession) {
   // See the tripal_chado_vocab_get_term() function for an example.
 }
 
+/**
+ * Retrieves full information about a vocabulary term.
+ *
+ * Vocabularies are stored in a database backend.  Tripal has no requirements
+ * for how terms are stored.  By default, the tripal_chado modules provides
+ * storage for vocabularies and terms. This function will call the
+ * hook_vocab_get_term() function for the database backend that is housing the
+ * vocabularies and allow it to return the details about the term.
+ *
+ * @param $namespace
+ *   The namespace of the vocabulary in which the term is found.
+ * @param $accession
+ *   The unique identifier (accession) for this term.
+ *
+ * @return
+ *   An array with at least the following keys:
+ *     namespace : The namespace of the vocabulary.
+ *     accession : The name unique ID of the term.
+ *     url : The URL for the term.
+ *     name : The name of the term.
+ *     definition : The term's description.
+ *   any other keys may be added as desired. Returns NULL if the term
+ *   cannot be found.
+ */
+function tripal_get_term_details($namespace, $accession) {
+  // TODO: we need some sort of administrative interface that lets the user
+  // switch to the desired vocabulary type. For now, we'll just use the
+  // first one in the list.
+  $stores = module_invoke_all('vocab_storage_info');
+  if (is_array($stores) and count($stores) > 0) {
+    $keys = array_keys($stores);
+    $module = $stores[$keys[0]]['module'];
+    $function = $module . '_vocab_get_term';
+    if (function_exists($function)) {
+      return $function($vocab->namespace, $this->accession);
+    }
+  }
+}
+

+ 40 - 9
tripal/includes/TripalBundleUIController.inc

@@ -98,15 +98,46 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
   );
 
   if ($term) {
+
+    $rows = array(
+      array(
+        array(
+          'header' => TRUE,
+          'data' => 'Vocabulary',
+          'class' => array('side-header')
+        ),
+        $vocab->namespace
+      ),
+      array(
+        array(
+          'header' => TRUE,
+          'data' => 'Term',
+          'class' => array('side-header')
+        ),
+        $term->name
+      ),
+      array(
+        array(
+          'header' => TRUE,
+          'data' => 'Definition',
+          'class' => array('side-header')
+        ),
+        $term->definition
+      )
+    );
+    $table_vars = array(
+      'header' => array(),
+      'rows' => $rows,
+      'attributes' => array(),
+      'caption' => '',
+      'sticky' => FALSE,
+      'colgroups' => array(),
+      'empty' => '',
+    );
     $form['term'] = array(
-      '#type' => 'markup',
-      '#markup' => theme('table', array(
-        'header' => array(),
-        'rows' => array(
-          array(array('header' => TRUE, 'data' => 'Vocabulary', 'class' => array('side-header')), $vocab->namespace),
-          array(array('header' => TRUE, 'data' => 'Term', 'class' => array('side-header')), $term->name),
-        )
-      ))
+      '#type' => 'item',
+      '#title' => t('Vocabulary Term'),
+      '#markup' => theme_table($table_vars)
     );
   }
 
@@ -443,7 +474,7 @@ function tripal_admin_add_type_form_submit($form, &$form_state) {
       }
     }
     else {
-      drupal_set_message('This type already exists.', 'warning');
+      drupal_set_message("The term '$accession' already exists as a content type.", 'warning');
     }
   }
 }

+ 2 - 2
tripal/includes/TripalTerm.inc

@@ -13,7 +13,7 @@ class TripalTerm extends Entity {
 
     // Get the term description from the storage backend
     $this->definition = NULL;
-    $this->urlprefix = NULL;
+    $this->url = NULL;
 
     // TODO: we need some sort of administrative interface that lets the user
     // switch to the desired vocabulary type. For now, we'll just use the
@@ -28,7 +28,7 @@ class TripalTerm extends Entity {
         $this->details = $term_details;
         if ($term_details and $term_details['definition']) {
           $this->definition = $term_details['definition'];
-          $this->urlprefix = $term_details['urlprefix'];
+          $this->url = $term_details['url'];
         }
       }
     }

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

@@ -1,5 +1,30 @@
 <?php
 
+/**
+ * Retrieves an entity that matches the given table and record id.
+ *
+ * @param $table
+ *   The name of the Chado table.
+ * @param $record_id
+ *   The record's primary key in the table specified by $table.
+ *
+ * @return
+ *   A chado_entity object.
+ */
+function tripal_load_chado_entity($table, $record_id) {
+   $entity_id = db_select('chado_entity', 'ce')
+     ->fields('ce', array('entity_id'))
+     ->condition('ce.record_id', $record_id)
+     ->condition('ce.data_table', $table)
+     ->execute()
+     ->fetchField();
+   if ($entity_id) {
+     $entity = entity_load('TripalEntity', array($entity_id));
+     return reset($entity);
+   }
+   return NULL;
+}
+
 /**
  * Publishes content in Chado as a new TripalEntity entity.
  *

+ 10 - 10
tripal_chado/includes/fields/dbxref_id.inc → tripal_chado/includes/fields/chado_base__dbxref_id.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_dbxref_id_formatter(&$element, $entity_type, $entity, $field,
+function chado_base__dbxref_id_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   foreach ($items as $delta => $item) {
@@ -42,7 +42,7 @@ function tripal_chado_dbxref_id_formatter(&$element, $entity_type, $entity, $fie
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_dbxref_id_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function chado_base__dbxref_id_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
   $field_name = $field['field_name'];
 
   // Get the field defaults.
@@ -86,8 +86,8 @@ function tripal_chado_dbxref_id_widget(&$widget, $form, $form_state, $field, $in
   $schema = chado_get_schema('dbxref');
   $options = tripal_get_db_select_options();
 
-  $widget['#element_validate'] = array('tripal_chado_dbxref_id_widget_validate');
-  $widget['#theme'] = 'tripal_chado_dbxref_id_widget';
+  $widget['#element_validate'] = array('chado_base__dbxref_id_widget_validate');
+  $widget['#theme'] = 'chado_base__dbxref_id_widget';
   $widget['#prefix'] =  "<span id='$field_name-dbxref--db-id'>";
   $widget['#suffix'] =  "</span>";
 
@@ -96,7 +96,7 @@ function tripal_chado_dbxref_id_widget(&$widget, $form, $form_state, $field, $in
     '#title' => $element['#title'],
     '#description' =>  $element['#description'],
     '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
-    '#theme' => 'tripal_chado_dbxref_id_widget',
+    '#theme' => 'chado_base__dbxref_id_widget',
     //'#collapsible' => TRUE,
     //'#collapsed' => $collapsed,
   );
@@ -113,7 +113,7 @@ function tripal_chado_dbxref_id_widget(&$widget, $form, $form_state, $field, $in
     '#required' => $element['#required'],
     '#default_value' => $db_id,
     '#ajax' => array(
-      'callback' => "tripal_chado_dbxref_id_widget_form_ajax_callback",
+      'callback' => "chado_base__dbxref_id_widget_form_ajax_callback",
       'wrapper' => "$field_name-dbxref--db-id",
       'effect' => 'fade',
       'method' => 'replace'
@@ -158,7 +158,7 @@ function tripal_chado_dbxref_id_widget(&$widget, $form, $form_state, $field, $in
 /**
  * An Ajax callback for the tripal_chado_admin_publish_form..
  */
-function tripal_chado_dbxref_id_widget_form_ajax_callback($form, $form_state) {
+function chado_base__dbxref_id_widget_form_ajax_callback($form, $form_state) {
   $field_name = $form_state['triggering_element']['#parents'][0];
   $db_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'dbxref__db_id');
   $accession = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'dbxref__accession');
@@ -180,7 +180,7 @@ function tripal_chado_dbxref_id_widget_form_ajax_callback($form, $form_state) {
 /**
  * Callback function for validating the tripal_chado_organism_select_widget.
  */
-function tripal_chado_dbxref_id_widget_validate($element, &$form_state) {
+function chado_base__dbxref_id_widget_validate($element, &$form_state) {
   $field_name = $element['#parents'][0];
 
   // If the form ID is field_ui_field_edit_form, then the user is editing the
@@ -234,7 +234,7 @@ function tripal_chado_dbxref_id_widget_validate($element, &$form_state) {
  *
  * @param $variables
  */
-function theme_tripal_chado_dbxref_id_widget($variables) {
+function theme_chado_base__dbxref_id_widget($variables) {
   $element = $variables['element'];
 
   $layout = "
@@ -285,7 +285,7 @@ function theme_tripal_chado_dbxref_id_widget($variables) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_dbxref_id_field_load($field, $entity, $base_table, $record) {
+function chado_base__dbxref_id_field_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 199 - 0
tripal_chado/includes/fields/chado_base__organism_id.inc

@@ -0,0 +1,199 @@
+<?php
+
+/**
+ *
+ */
+function chado_base__organism_id_formatter(&$element, $entity_type, $entity, $field,
+    $instance, $langcode, $items, $display) {
+
+  foreach ($items as $delta => $item) {
+    $organism = chado_select_record('organism', array('genus', 'species'), array('organism_id' => $item['value']));
+    $content = '<i>' . $organism[0]->genus .' ' . $organism[0]->species . '</i>';
+    $element[$delta] = array(
+      '#type' => 'markup',
+      '#markup' => $content,
+    );
+  }
+}
+
+/**
+ * Loads the field values with appropriate data.
+ *
+ * This function is called by the tripal_chado_field_storage_load() for
+ * each property managed by the field_chado_storage storage type.  This is
+ * an optional hook function that is only needed if the field has
+ * multiple form elements.
+ *
+ * @param $field
+ * @param $entity
+ * @param $base_table
+ * @param $record
+ */
+function chado_base__organism_id_load($field, $entity, $base_table, $record) {
+  $field_name = $field['field_name'];
+
+  // The organism_id is already set as the value. We need to get it and
+  // see if there is a published entity for this organism. If there is
+  // then we want to add the organism object generated using chado_generate_var
+  // as well as the entity to the record.
+  $organism_id = $entity->{$field_name}['und'][0]['value'];
+  $organism = chado_generate_var('organism', array('organism_id' => $organism_id));
+  $entity->{$field_name}['und'][0]['organism'] = $organism;
+  $org_entity = tripal_load_chado_entity('organism', $organism_id);
+  $entity->{$field_name}['und'][0]['entity'] = $org_entity;
+}
+/**
+ * Implements hook_ws_formatter().
+ */
+function chado_base__organism_id_ws_formatter(&$element, $entity_type, $entity,
+    $field, $instance, $items) {
+
+  foreach ($items as $delta => $item) {
+    $organism = $item['organism'];
+    $entity = $item['entity'];
+    if ($entity) {
+      $element[$delta]['#entity'] = $entity;
+    }
+    $element[$delta]['genus'] = $organism->genus;
+    $element[$delta]['species'] = $organism->species;
+    $element[$delta]['common_name'] = $organism->common_name;
+  }
+}
+
+/**
+ *  Implements hook_widget().
+ */
+function chado_base__organism_id_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $options = tripal_get_organism_select_options(FALSE);
+  $widget['value'] = array(
+    '#type' => 'select',
+    '#title' => $element['#title'],
+    '#description' => $element['#description'],
+    '#options' => $options,
+    '#default_value' => count($items) > 0 ? $items[0]['value'] : 0,
+    '#required' => $element['#required'],
+    '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
+    '#delta' => $delta,
+    '#element_validate' => array('chado_base__organism_id_widget_validate'),
+  );
+  $widget['add_organism'] = array(
+    '#type' => 'item',
+    '#markup' => l('Add a new species', 'admin/content/bio_data/add/species', array('attributes' => array('target' => '_blank'))),
+  );
+}
+/**
+ * Callback function for validating the chado_base__organism_id_widget.
+ */
+function chado_base__organism_id_widget_validate($element, &$form_state) {
+  $field_name = $element['#parents'][0];
+
+  // If the form ID is field_ui_field_edit_form, then the user is editing the
+  // field's values in the manage fields form of Drupal.  We don't want
+  // to validate it as if it were being used in a data entry form.
+  if ($form_state['build_info']['form_id'] =='field_ui_field_edit_form') {
+    return;
+  }
+  $organism_id = tripal_chado_get_field_form_values($field_name, $form_state);
+
+  if (!$organism_id) {
+    form_error($element, t("Please specify an organism."));
+  }
+}
+
+/**
+ * Provides a settings form for the formatter.
+ *
+ * This function is equiavlent to the hook_field_formatter_settings_form()
+ * hook.
+ *
+ * @param $field
+ *   The field structure being configured.
+ * @param $instance
+ *   The instance structure being configured.
+ * @param $view_mode
+ *   The view mode being configured.
+ * @param $form
+ *   The (entire) configuration form array, which will usually have no use here.
+ * @param $form_state
+ *   The form state of the (entire) configuration form.
+ */
+function chado_base__organism_id_formatter_form($field, $instance,
+    $view_mode, $form, &$form_state) {
+
+  $display = $instance['display'][$view_mode];
+  $settings = $display['settings'];
+  $element = array();
+  $term = NULL;
+  $bundle = NULL;
+
+  // Check to see if the organism bundle exists
+  $term = tripal_load_term_entity(array(
+    'namespace' => $field['settings']['semantic_web']['ns'],
+    'accession' => $field['settings']['semantic_web']['type']
+  ));
+  if ($term) {
+    $bundle = tripal_load_bundle_entity(array('term_id' => $term->id));
+  }
+
+  $element['instructions'] = array(
+    '#type' => 'item',
+    '#markup' => 'Please provide the format for viewing the organism. You
+      can specify the format using tokens that correspond to each field'
+  );
+
+  $element['field_display'] = array(
+    '#type' => 'textfield',
+    '#title' => 'Display Format',
+    '#description' => t('Provide a mixture of text and/or tokens for the format.
+        For example: [organism__genus] [organism__species].  When displayed,
+        the tokens will be replaced with the actual value.'),
+    '#default_value' => '[organism__genus] [organism__species]',
+  );
+
+  $element['field_display_entity'] = array(
+    '#type' => 'checkbox',
+    '#title' => 'Display teaser if available',
+    '#description' => t('If the organism that this field is associated with is
+        a published page then display the teaser rather use the display
+        format above.'),
+  );
+
+  $element['tokens'] = array(
+    '#type' => 'fieldset',
+    '#collapsed' => TRUE,
+    '#collapsible' => TRUE,
+    '#title' => 'Available Tokens'
+  );
+  $headers = array('Token', 'Description');
+  $rows = array();
+  $tokens = tripal_get_tokens($bundle);
+  foreach ($tokens as $token) {
+    $rows[] = array(
+      $token['token'],
+      $token['description'],
+    );
+  }
+
+
+  $table_vars = array(
+    'header'     => $headers,
+    'rows'       => $rows,
+    'attributes' => array(),
+    'sticky'     => FALSE,
+    'caption'    => '',
+    'colgroups'  => array(),
+    'empty'      => 'There are no tokens',
+  );
+  $project_details = theme('table', $table_vars);
+
+  $element['tokens']['list'] = array(
+    '#type' => 'item',
+    '#markup' => theme_table($table_vars),
+  );
+
+  return $element;
+
+}
+
+
+

+ 6 - 6
tripal_chado/includes/fields/md5checksum.inc → tripal_chado/includes/fields/chado_feature__md5checksum.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_md5checksum_checkbox_formatter(&$element, $entity_type, $entity, $field,
+function chado_feature__md5checksum_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
   foreach ($items as $delta => $item) {
     $content = key_exists('value', $item) ? $item['value'] : '';
@@ -34,7 +34,7 @@ function tripal_chado_md5checksum_checkbox_formatter(&$element, $entity_type, $e
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_md5checksum_checkbox_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function chado_feature__md5checksum_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
 
   $widget['value'] = array(
     '#type' => 'checkbox',
@@ -44,13 +44,13 @@ function tripal_chado_md5checksum_checkbox_widget(&$widget, $form, $form_state,
     '#default_value' => 1,
     '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
     '#delta' => $delta,
-    '#element_validate' => array('tripal_chado_md5checksum_checkbox_widget_validate'),
+    '#element_validate' => array('chado_feature__md5checksum_widget_validate'),
   );
 }
 /**
- * Callback function for validating the tripal_chado_md5checksum_checkbox_widget.
+ * Callback function for validating the chado_feature__md5checksum_widget.
  */
-function tripal_chado_md5checksum_checkbox_widget_validate($element, &$form_state) {
+function chado_feature__md5checksum_widget_validate($element, &$form_state) {
   $field_name = $element['#parents'][0];
 
   // Calculate the md5 checksum for the sequence only if md5 box is checked and
@@ -76,7 +76,7 @@ function tripal_chado_md5checksum_checkbox_widget_validate($element, &$form_stat
  * @param $entity
  * @param $record
  */
-function tripal_chado_md5checksum_field_load($field, $entity, $record) {
+function chado_feature__md5checksum_field_load($field, $entity, $record) {
   $field_name = $field['field_name'];
   if ($record and property_exists($record, 'md5checksum')) {
     $entity->{$field_name}['und'][] = array('value' => $record->md5checksum);

+ 5 - 5
tripal_chado/includes/fields/residues.inc → tripal_chado/includes/fields/chado_feature__residues.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_residues_textarea_formatter(&$element, $entity_type, $entity, $field,
+function chado_feature__residues_formatter(&$element, $entity_type, $entity, $field,
   $instance, $langcode, $items, $display) {
 
   foreach ($items as $delta => $item) {
@@ -37,7 +37,7 @@ function tripal_chado_residues_textarea_formatter(&$element, $entity_type, $enti
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_residues_textarea_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function chado_feature__residues_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
 
   $widget['value'] = array(
     '#type' => 'textarea',
@@ -46,14 +46,14 @@ function tripal_chado_residues_textarea_widget(&$widget, $form, $form_state, $fi
     '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
     '#default_value' => count($items) > 0 ? $items[0]['value'] : '',
     '#delta' => $delta,
-    '#element_validate' => array('tripal_chado_residues_textarea_widget_validate'),
+    '#element_validate' => array('chado_feature__residues_widget_validate'),
     '#cols' => 30,
   );
 }
 /**
- * Callback function for validating the tripal_chado_residues_textarea_widget.
+ * Callback function for validating the chado_feature__residues_widget.
  */
-function tripal_chado_residues_textarea_widget_validate($element, &$form_state) {
+function chado_feature__residues_widget_validate($element, &$form_state) {
   $field_name = $element['#parents'][0];
 
   // Remove any white spaces.

+ 5 - 5
tripal_chado/includes/fields/seqlen.inc → tripal_chado/includes/fields/chado_feature__seqlen.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_seqlen_hidden_formatter(&$element, $entity_type, $entity, $field,
+function chado_feature__seqlen_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   foreach ($items as $delta => $item) {
@@ -36,7 +36,7 @@ function tripal_chado_seqlen_hidden_formatter(&$element, $entity_type, $entity,
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_seqlen_hidden_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function chado_feature__seqlen_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
 
   $widget['value'] =  array(
     '#type' => 'hidden',
@@ -44,13 +44,13 @@ function tripal_chado_seqlen_hidden_widget(&$widget, $form, $form_state, $field,
     '#description' => $element['#description'],
     '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
     '#delta' => $delta,
-    '#element_validate' => array('tripal_chado_seqlen_hidden_widget_validate'),
+    '#element_validate' => array('chado_feature__seqlen_widget_validate'),
   );
 }
 /**
- * Callback function for validating the tripal_chado_seqlen_hidden_widget.
+ * Callback function for validating the chado_feature__seqlen_widget.
  */
-function tripal_chado_seqlen_hidden_widget_validate($element, &$form_state) {
+function chado_feature__seqlen_widget_validate($element, &$form_state) {
   $field_name = $element['#parents'][0];
 
   // Get the residues so we can calculate teh length.

+ 9 - 9
tripal_chado/includes/fields/cvterm.inc → tripal_chado/includes/fields/chado_linker__cvterm.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_cvterm_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__cvterm_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   $headers = array('Term', 'Definition', 'Is Not', 'Reference');
@@ -79,7 +79,7 @@ function tripal_chado_cvterm_formatter(&$element, $entity_type, $entity, $field,
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_cvterm_widget(&$widget, $form, $form_state, $field,
+function chado_linker__cvterm_widget(&$widget, $form, $form_state, $field,
     $instance, $langcode, $items, $delta, $element) {
 
   $entity = $form['#entity'];
@@ -138,8 +138,8 @@ function tripal_chado_cvterm_widget(&$widget, $form, $form_state, $field,
 
   $widget['#table_name'] = $chado_table;
   $widget['#fkey_field'] = $fkey;
-  $widget['#element_validate'] = array('tripal_chado_cvterm_widget_validate');
-  $widget['#theme'] = 'tripal_chado_cvterm_widget';
+  $widget['#element_validate'] = array('chado_linker__cvterm_widget_validate');
+  $widget['#theme'] = 'chado_linker__cvterm_widget';
   $widget['#prefix'] =  "<span id='$table_name-$delta'>";
   $widget['#suffix'] =  "</span>";
 
@@ -168,7 +168,7 @@ function tripal_chado_cvterm_widget(&$widget, $form, $form_state, $field,
     '#maxlength' => array_key_exists('length', $schema['fields']['name']) ? $schema['fields']['name']['length'] : 255,
     '#autocomplete_path' => 'admin/tripal/storage/chado/auto_name/cvterm/' . $cv_id,
     '#ajax' => array(
-      'callback' => "tripal_chado_cvterm_widget_form_ajax_callback",
+      'callback' => "chado_linker__cvterm_widget_form_ajax_callback",
       'wrapper' => "$table_name-$delta",
       'effect' => 'fade',
       'method' => 'replace'
@@ -191,7 +191,7 @@ function tripal_chado_cvterm_widget(&$widget, $form, $form_state, $field,
 /**
  * An Ajax callback for the dbxref widget.
  */
-function tripal_chado_cvterm_widget_form_ajax_callback($form, $form_state) {
+function chado_linker__cvterm_widget_form_ajax_callback($form, $form_state) {
 
   $field_name = $form_state['triggering_element']['#parents'][0];
   $delta = $form_state['triggering_element']['#parents'][2];
@@ -203,7 +203,7 @@ function tripal_chado_cvterm_widget_form_ajax_callback($form, $form_state) {
 /**
  * Callback function for validating the tripal_chado_organism_select_widget.
  */
-function tripal_chado_cvterm_widget_validate($element, &$form_state) {
+function chado_linker__cvterm_widget_validate($element, &$form_state) {
 
   $field_name = $element['#field_name'];
   $delta = $element['#delta'];
@@ -254,7 +254,7 @@ function tripal_chado_cvterm_widget_validate($element, &$form_state) {
  *
  * @param $variables
  */
-function theme_tripal_chado_cvterm_widget($variables) {
+function theme_chado_linker__cvterm_widget($variables) {
   $element = $variables['element'];
 
   // These two fields were added to the widget to help identify the fields
@@ -292,7 +292,7 @@ function theme_tripal_chado_cvterm_widget($variables) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_cvterm_field_load($field, $entity, $base_table, $record) {
+function chado_linker__cvterm_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 8 - 8
tripal_chado/includes/fields/cvterm_class_adder.inc → tripal_chado/includes/fields/chado_linker__cvterm_adder.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_cvterm_class_adder_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__cvterm_adder_formatter(&$element, $entity_type, $entity, $field,
   $instance, $langcode, $items, $display) {
 
   foreach ($items as $delta => $item) {
@@ -20,12 +20,12 @@ function tripal_chado_cvterm_class_adder_formatter(&$element, $entity_type, $ent
 /**
  *
  */
-function tripal_chado_cvterm_class_adder_widget(&$widget, &$form, &$form_state,
+function chado_linker__cvterm_adder_widget(&$widget, &$form, &$form_state,
     $field, $instance, $langcode, $items, $delta, $element) {
 
   // This field has no value field.  Just a fieldset for adding new annotation types.
 
-  $widget['#element_validate'] = array('tripal_chado_cvterm_class_adder_widget_validate');
+  $widget['#element_validate'] = array('chado_linker__cvterm_adder_widget_validate');
 
   $widget['#type'] = 'fieldset';
   $widget['#title'] = $element['#title'];
@@ -62,14 +62,14 @@ function tripal_chado_cvterm_class_adder_widget(&$widget, &$form, &$form_state,
     '#value' => t('Add Annotation Type'),
     '#type' => 'submit',
     '#name' => 'cvterm_class_adder_button',
-    '#submit' => array('tripal_chado_cvterm_class_adder_widget_submit'),
+    '#submit' => array('chado_linker__cvterm_adder_widget_submit'),
     '#limit_validation_errors' => array(array($field['field_name'])),
   );
 }
 /**
- * Callback function for validating the tripal_chado_cvterm_class_adder_widget.
+ * Callback function for validating the chado_linker__cvterm_adder_widget.
  */
-function tripal_chado_cvterm_class_adder_widget_validate($element, &$form_state) {
+function chado_linker__cvterm_adder_widget_validate($element, &$form_state) {
 
    // Add the new field to the entity
    if (array_key_exists('triggering_element', $form_state) and
@@ -121,8 +121,8 @@ function tripal_chado_cvterm_class_adder_widget_validate($element, &$form_state)
    }
 }
 /**
- * Callback function for submitting the tripal_chado_cvterm_class_adder_widget.
+ * Callback function for submitting the chado_linker__cvterm_adder_widget.
  */
-function tripal_chado_cvterm_class_adder_widget_submit($element, &$form_state) {
+function chado_linker__cvterm_adder_widget_submit($element, &$form_state) {
 
 }

+ 10 - 10
tripal_chado/includes/fields/dbxref.inc → tripal_chado/includes/fields/chado_linker__dbxref.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_dbxref_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__dbxref_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   $chado_table = $field['settings']['chado_table'];
@@ -42,7 +42,7 @@ function tripal_chado_dbxref_formatter(&$element, $entity_type, $entity, $field,
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field,
+function chado_linker__dbxref_widget(&$widget, $form, $form_state, $field,
     $instance, $langcode, $items, $delta, $element) {
 
   $field_name = $field['field_name'];
@@ -92,8 +92,8 @@ function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field,
 
   $widget['#table_name'] = $chado_table;
   $widget['#fkey_field'] = $fkey;
-  $widget['#element_validate'] = array('tripal_chado_dbxref_widget_validate');
-  $widget['#theme'] = 'tripal_chado_dbxref_widget';
+  $widget['#element_validate'] = array('chado_linker__dbxref_widget_validate');
+  $widget['#theme'] = 'chado_linker__dbxref_widget';
   $widget['#prefix'] =  "<span id='$field_name-dbxref--db-id-$delta'>";
   $widget['#suffix'] =  "</span>";
 
@@ -120,7 +120,7 @@ function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field,
     '#required' => $element['#required'],
     '#default_value' => $db_id,
     '#ajax' => array(
-      'callback' => "tripal_chado_dbxref_widget_form_ajax_callback",
+      'callback' => "chado_linker__dbxref_widget_form_ajax_callback",
       'wrapper' => "$field_name-dbxref--db-id-$delta",
       'effect' => 'fade',
       'method' => 'replace'
@@ -135,7 +135,7 @@ function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field,
     '#size' => 15,
     '#autocomplete_path' => 'admin/tripal/storage/chado/auto_name/dbxref/' . $db_id,
     '#ajax' => array(
-      'callback' => "tripal_chado_dbxref_widget_form_ajax_callback",
+      'callback' => "chado_linker__dbxref_widget_form_ajax_callback",
       'wrapper' => "$field_name-dbxref--db-id-$delta",
       'effect' => 'fade',
       'method' => 'replace'
@@ -167,7 +167,7 @@ function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field,
 /**
  * An Ajax callback for the dbxref widget.
  */
-function tripal_chado_dbxref_widget_form_ajax_callback($form, $form_state) {
+function chado_linker__dbxref_widget_form_ajax_callback($form, $form_state) {
 
   $field_name = $form_state['triggering_element']['#parents'][0];
   $delta = $form_state['triggering_element']['#parents'][2];
@@ -194,7 +194,7 @@ function tripal_chado_dbxref_widget_form_ajax_callback($form, $form_state) {
 /**
  * Callback function for validating the tripal_chado_organism_select_widget.
  */
-function tripal_chado_dbxref_widget_validate($element, &$form_state) {
+function chado_linker__dbxref_widget_validate($element, &$form_state) {
 
   $field_name = $element['#field_name'];
   $delta = $element['#delta'];
@@ -256,7 +256,7 @@ function tripal_chado_dbxref_widget_validate($element, &$form_state) {
  *
  * @param $variables
  */
-function theme_tripal_chado_dbxref_widget($variables) {
+function theme_chado_linker__dbxref_widget($variables) {
   $element = $variables['element'];
 
   // These two fields were added to the widget to help identify the fields
@@ -298,7 +298,7 @@ function theme_tripal_chado_dbxref_widget($variables) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_dbxref_field_load($field, $entity, $base_table, $record) {
+function chado_linker__dbxref_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 9 - 9
tripal_chado/includes/fields/kvproperty.inc → tripal_chado/includes/fields/chado_linker__prop.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_kvproperty_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__prop_formatter(&$element, $entity_type, $entity, $field,
   $instance, $langcode, $items, $display) {
 
   $field_name = $field['field_name'];
@@ -39,7 +39,7 @@ function tripal_chado_kvproperty_formatter(&$element, $entity_type, $entity, $fi
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_kvproperty_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+function chado_linker__prop_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
   $entity = $form['#entity'];
   $field_name = $field['field_name'];
 
@@ -84,7 +84,7 @@ function tripal_chado_kvproperty_widget(&$widget, $form, $form_state, $field, $i
   $widget['#field_name'] = $element['#field_name'];
   $widget['#language'] = $element['#language'];
   $widget['#weight'] = isset($element['#weight']) ? $element['#weight'] : 0;
-  $widget['#element_validate'] = array('tripal_chado_kvproperty_widget_validate');
+  $widget['#element_validate'] = array('chado_linker__prop_widget_validate');
   $widget['#cardinality'] = 1;
 
   $widget['value'] = array(
@@ -115,14 +115,14 @@ function tripal_chado_kvproperty_widget(&$widget, $form, $form_state, $field, $i
  * @param unknown $form
  * @param unknown $form_state
  */
-function tripal_chado_kvproperty_widget_form_ajax_callback($form, $form_state) {
+function chado_linker__prop_widget_form_ajax_callback($form, $form_state) {
   $field_name = $form_state['triggering_element']['#parents'][0];
   return $form[$field_name];
 }
 /**
- * Callback function for validating the tripal_chado_kvproperty_widget.
+ * Callback function for validating the chado_linker__prop_widget.
  */
-function tripal_chado_kvproperty_widget_validate($element, &$form_state) {
+function chado_linker__prop_widget_validate($element, &$form_state) {
   $field_name = $element['#field_name'];
   $delta = $element['#delta'];
   $entity = $element['#entity'];
@@ -172,9 +172,9 @@ function tripal_chado_kvproperty_widget_validate($element, &$form_state) {
   }
 }
 /**
- * Callback function for submitting the tripal_chado_kvproperty_widget.
+ * Callback function for submitting the chado_linker__prop_widget.
  */
-function tripal_chado_kvproperty_widget_submit($element, &$form_state) {
+function chado_linker__prop_widget_submit($element, &$form_state) {
 }
 
 /**
@@ -190,7 +190,7 @@ function tripal_chado_kvproperty_widget_submit($element, &$form_state) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_kvproperty_field_load($field, $entity, $base_table, $record) {
+function chado_linker__prop_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 8 - 8
tripal_chado/includes/fields/kvproperty_adder.inc → tripal_chado/includes/fields/chado_linker__prop_adder.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_kvproperty_adder_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__prop_adder_formatter(&$element, $entity_type, $entity, $field,
   $instance, $langcode, $items, $display) {
 
   foreach ($items as $delta => $item) {
@@ -20,10 +20,10 @@ function tripal_chado_kvproperty_adder_formatter(&$element, $entity_type, $entit
 /**
  *
  */
-function tripal_chado_kvproperty_adder_widget(&$widget, $form, $form_state,
+function chado_linker__prop_adder_widget(&$widget, $form, $form_state,
     $field, $instance, $langcode, $items, $delta, $element) {
   // This field has no value field.  Just a fieldset for adding new properties.
-  $widget['#element_validate'] = array('tripal_chado_kvproperty_adder_widget_validate');
+  $widget['#element_validate'] = array('chado_linker__prop_adder_widget_validate');
 
   $widget['#type'] = 'fieldset';
   $widget['#title'] = $element['#title'];
@@ -61,14 +61,14 @@ function tripal_chado_kvproperty_adder_widget(&$widget, $form, $form_state,
     '#value' => t('Add Property'),
     '#type' => 'submit',
     '#name' => 'kvproperty_adder_button',
-    '#submit' => array('tripal_chado_kvproperty_adder_widget_submit'),
+    '#submit' => array('chado_linker__prop_adder_widget_submit'),
     '#limit_validation_errors' => array(array($field['field_name'])),
   );
 }
 /**
- * Callback function for validating the tripal_chado_kvproperty_adder_widget.
+ * Callback function for validating the chado_linker__prop_adder_widget.
  */
-function tripal_chado_kvproperty_adder_widget_validate($element, &$form_state) {
+function chado_linker__prop_adder_widget_validate($element, &$form_state) {
 
    // Add the new field to the entity
    if (array_key_exists('triggering_element', $form_state) and
@@ -122,7 +122,7 @@ function tripal_chado_kvproperty_adder_widget_validate($element, &$form_state) {
    }
 }
 /**
- * Callback function for submitting the tripal_chado_kvproperty_adder_widget.
+ * Callback function for submitting the chado_linker__prop_adder_widget.
  */
-function tripal_chado_kvproperty_adder_widget_submit($element, &$form_state) {
+function chado_linker__prop_adder_widget_submit($element, &$form_state) {
 }

+ 9 - 9
tripal_chado/includes/fields/pub.inc → tripal_chado/includes/fields/chado_linker__pub.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_pub_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__pub_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   $list_items = array();
@@ -48,7 +48,7 @@ function tripal_chado_pub_formatter(&$element, $entity_type, $entity, $field,
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_pub_widget(&$widget, $form, $form_state, $field,
+function chado_linker__pub_widget(&$widget, $form, $form_state, $field,
     $instance, $langcode, $items, $delta, $element) {
 
   $entity = $form['#entity'];
@@ -89,8 +89,8 @@ function tripal_chado_pub_widget(&$widget, $form, $form_state, $field,
 
   $widget['#table_name'] = $table_name;
   $widget['#fkey_field'] = $fkey;
-  $widget['#element_validate'] = array('tripal_chado_pub_widget_validate');
-  $widget['#theme'] = 'tripal_chado_pub_widget';
+  $widget['#element_validate'] = array('chado_linker__pub_widget_validate');
+  $widget['#theme'] = 'chado_linker__pub_widget';
   $widget['#prefix'] =  "<span id='$table_name-$delta'>";
   $widget['#suffix'] =  "</span>";
 
@@ -113,7 +113,7 @@ function tripal_chado_pub_widget(&$widget, $form, $form_state, $field,
     '#default_value' => $title,
     '#autocomplete_path' => 'admin/tripal/storage/chado/auto_name/pub',
     '#ajax' => array(
-      'callback' => "tripal_chado_pub_widget_form_ajax_callback",
+      'callback' => "chado_linker__pub_widget_form_ajax_callback",
       'wrapper' => "$table_name-$delta",
       'effect' => 'fade',
       'method' => 'replace'
@@ -124,7 +124,7 @@ function tripal_chado_pub_widget(&$widget, $form, $form_state, $field,
 /**
  * An Ajax callback for the pub widget.
  */
-function tripal_chado_pub_widget_form_ajax_callback($form, $form_state) {
+function chado_linker__pub_widget_form_ajax_callback($form, $form_state) {
 
   $field_name = $form_state['triggering_element']['#parents'][0];
   $delta = $form_state['triggering_element']['#parents'][2];
@@ -135,7 +135,7 @@ function tripal_chado_pub_widget_form_ajax_callback($form, $form_state) {
 /**
  * Callback function for validating the tripal_chado_organism_select_widget.
  */
-function tripal_chado_pub_widget_validate($element, &$form_state) {
+function chado_linker__pub_widget_validate($element, &$form_state) {
 
   $field_name = $element['#field_name'];
   $delta = $element['#delta'];
@@ -181,7 +181,7 @@ function tripal_chado_pub_widget_validate($element, &$form_state) {
  *
  * @param $variables
  */
-function theme_tripal_chado_pub_widget($variables) {
+function theme_chado_linker__pub_widget($variables) {
   $element = $variables['element'];
 
   // These two fields were added to the widget to help identify the fields
@@ -213,7 +213,7 @@ function theme_tripal_chado_pub_widget($variables) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_pub_field_load($field, $entity, $base_table, $record) {
+function chado_linker__pub_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 8 - 8
tripal_chado/includes/fields/synonym.inc → tripal_chado/includes/fields/chado_linker__synonym.inc

@@ -10,7 +10,7 @@
  * @param unknown $items
  * @param unknown $display
  */
-function tripal_chado_synonym_formatter(&$element, $entity_type, $entity, $field,
+function chado_linker__synonym_formatter(&$element, $entity_type, $entity, $field,
     $instance, $langcode, $items, $display) {
 
   $chado_table = $field['settings']['chado_table'];
@@ -41,7 +41,7 @@ function tripal_chado_synonym_formatter(&$element, $entity_type, $entity, $field
  * @param unknown $delta
  * @param unknown $element
  */
-function tripal_chado_synonym_widget(&$widget, $form, $form_state, $field,
+function chado_linker__synonym_widget(&$widget, $form, $form_state, $field,
     $instance, $langcode, $items, $delta, $element) {
 
   $entity = $form['#entity'];
@@ -100,8 +100,8 @@ function tripal_chado_synonym_widget(&$widget, $form, $form_state, $field,
 
   $widget['#table_name'] = $table_name;
   $widget['#fkey_field'] = $fkey;
-  $widget['#element_validate'] = array('tripal_chado_synonym_widget_validate');
-  $widget['#theme'] = 'tripal_chado_synonym_widget';
+  $widget['#element_validate'] = array('chado_linker__synonym_widget_validate');
+  $widget['#theme'] = 'chado_linker__synonym_widget';
   $widget['#prefix'] =  "<span id='$table_name-$delta'>";
   $widget['#suffix'] =  "</span>";
 
@@ -145,7 +145,7 @@ function tripal_chado_synonym_widget(&$widget, $form, $form_state, $field,
 /**
  * An Ajax callback for the synonym widget.
  */
-function tripal_chado_synonym_widget_form_ajax_callback($form, $form_state) {
+function chado_linker__synonym_widget_form_ajax_callback($form, $form_state) {
 
   $field_name = $form_state['triggering_element']['#parents'][0];
   $delta = $form_state['triggering_element']['#parents'][2];
@@ -156,7 +156,7 @@ function tripal_chado_synonym_widget_form_ajax_callback($form, $form_state) {
 /**
  * Callback function for validating the tripal_chado_organism_select_widget.
  */
-function tripal_chado_synonym_widget_validate($element, &$form_state) {
+function chado_linker__synonym_widget_validate($element, &$form_state) {
 
   $field_name = $element['#field_name'];
   $delta = $element['#delta'];
@@ -229,7 +229,7 @@ function tripal_chado_synonym_widget_validate($element, &$form_state) {
  *
  * @param $variables
  */
-function theme_tripal_chado_synonym_widget($variables) {
+function theme_chado_linker__synonym_widget($variables) {
   $element = $variables['element'];
 
   // These two fields were added to the widget to help identify the fields
@@ -270,7 +270,7 @@ function theme_tripal_chado_synonym_widget($variables) {
  * @param $base_table
  * @param $record
  */
-function tripal_chado_synonym_field_load($field, $entity, $base_table, $record) {
+function chado_linker__synonym_load($field, $entity, $base_table, $record) {
 
   $field_name = $field['field_name'];
   $field_type = $field['type'];

+ 0 - 117
tripal_chado/includes/fields/organism_id.inc

@@ -1,117 +0,0 @@
-<?php
-
-/**
- *
- * @param $entity_type
- * @param $entity
- * @param $field
- * @param $instance
- * @param $langcode
- * @param $items
- * @param $display
- * @return string
- */
-function tripal_chado_organism_select_formatter(&$element, $entity_type, $entity, $field,
-    $instance, $langcode, $items, $display) {
-
-  foreach ($items as $delta => $item) {
-    $organism = chado_select_record('organism', array('genus', 'species'), array('organism_id' => $item['value']));
-    $content = '<i>' . $organism[0]->genus .' ' . $organism[0]->species . '</i>';
-    $element[$delta] = array(
-      '#type' => 'markup',
-      '#markup' => $content,
-    );
-  }
-}
-/**
- *
- * @param $field_name
- * @param $widget
- * @param $form
- * @param $form_state
- * @param $field
- * @param $instance
- * @param $langcode
- * @param $items
- * @param $delta
- * @param $element
- */
-function tripal_chado_organism_select_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
-  $options = tripal_get_organism_select_options(FALSE);
-  $widget['value'] = array(
-    '#type' => 'select',
-    '#title' => $element['#title'],
-    '#description' => $element['#description'],
-    '#options' => $options,
-    '#default_value' => count($items) > 0 ? $items[0]['value'] : 0,
-    '#required' => $element['#required'],
-    '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
-    '#delta' => $delta,
-    '#element_validate' => array('tripal_chado_organism_select_widget_validate'),
-  );
-  $widget['add_organism'] = array(
-    '#type' => 'item',
-    '#markup' => l('Add a new species', 'admin/content/bio_data/add/species', array('attributes' => array('target' => '_blank'))),
-  );
-}
-/**
- * Callback function for validating the tripal_chado_organism_select_widget.
- */
-function tripal_chado_organism_select_widget_validate($element, &$form_state) {
-  $field_name = $element['#parents'][0];
-
-  // If the form ID is field_ui_field_edit_form, then the user is editing the
-  // field's values in the manage fields form of Drupal.  We don't want
-  // to validate it as if it were being used in a data entry form.
-  if ($form_state['build_info']['form_id'] =='field_ui_field_edit_form') {
-    return;
-  }
-  $organism_id = tripal_chado_get_field_form_values($field_name, $form_state);
-
-  if (!$organism_id) {
-    form_error($element, t("Please specify an organism."));
-  }
-}
-
-function organism_id_field_formatter_settings_summary($field, $instance, $view_mode) {
-  $display = $instance['display'][$view_mode];
-  $settings = $display['settings'];
-
-  $summary = '';
-
-  if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') {
-    $summary = t('Length: @chars chars', array('@chars' => $settings['trim_length']));
-  }
-
-  return $summary;
-}
-/**
- * Implements hook_field_formatter_settings_form()
- *
- * @param $field
- * @param $instance
- * @param $view_mode
- * @param $form
- * @param $form_state
- */
-function organism_id_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
-  $display = $instance['display'][$view_mode];
-  $settings = $display['settings'];
-  $element = array();
-
-  if ($display['type'] == 'text_trimmed' || $display['type'] == 'text_summary_or_trimmed') {
-    $element['trim_length'] = array(
-      '#title' => t('Length'),
-      '#type' => 'textfield',
-      '#size' => 20,
-      '#default_value' => $settings['trim_length'],
-      '#element_validate' => array('element_validate_integer_positive'),
-      '#required' => TRUE,
-    );
-  }
-
-  return $element;
-
-}
-
-

+ 2 - 2
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -325,7 +325,7 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
         // Allow the creating module to alter the value if desired.  The
         // module should do this if the field has any other form elements
         // that need populationg besides the default value.
-        $load_function = $field_module . '_' . $field_type . '_field_load';
+        $load_function = $field_type . '_load';
         module_load_include('inc', $field_module, 'includes/fields/' . $field_type);
         if (function_exists($load_function)) {
           $load_function($field, $entity, $base_table, $record);
@@ -338,7 +338,7 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
       if ($field_table != $base_table) {
         // Set an empty value by default, and let the hook function update it.
         $entity->{$field_name}['und'][0]['value'] = '';
-        $load_function = $field_module . '_' . $field_type . '_field_load';
+        $load_function = $field_type . '_load';
         module_load_include('inc', $field_module, 'includes/fields/' . $field_type);
         if (function_exists($load_function)) {
           $load_function($field, $entity, $base_table, $record);

+ 1217 - 0
tripal_chado/includes/tripal_chado.fields.inc

@@ -0,0 +1,1217 @@
+<?php
+
+/**
+ * Implements hook_field_info().
+ */
+function tripal_chado_field_info() {
+  $fields = array(
+    /*
+     * Generic fields that support multiple base tables.
+     */
+    'chado_base__organism_id' => array(
+      'label' => t('Organism'),
+      'description' => t('A field for specifying an organism.'),
+      'default_widget' => 'chado_base__organism_id_widget',
+      'default_formatter' => 'chado_base__organism_id_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+
+    'chado_base__dbxref_id' => array(
+      'label' => t('Cross reference'),
+      'description' => t('This record can be cross referenced with a record in
+          another online database. This field is intended for the most prominent
+          reference.  At a minimum, the database and accession must be provided.'),
+      'default_widget' => 'chado_base__dbxref_id_widget',
+      'default_formatter' => 'chado_base__dbxref_id_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+
+    /*
+     * Fields that support linker tables.
+     */
+    'chado_linker__cvterm' => array(
+      'label' => t('Annotations'),
+      'description' => t('This record can be annotated with terms
+          from other vocabularies.'),
+      'default_widget' => 'chado_linker__cvterm_widget',
+      'default_formatter' => 'chado_linker__cvterm_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_linker__synonym' => array(
+      'label' => t('Synonyms'),
+      'description' => t('Adds an alternative name (synonym or alias) to this record.'),
+      'default_widget' => 'chado_linker__synonym_widget',
+      'default_formatter' => 'chado_linker__synonym_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_linker__prop' => array(
+      'label' => t('Add a Property'),
+      'description' => t('Add details about this property.'),
+      'default_widget' => 'chado_linker__prop_widget',
+      'default_formatter' => 'chado_linker__prop_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_linker__dbxref' => array(
+      'label' => t('Cross references'),
+      'description' => t('This record can be cross referenced with a record in
+        another online database. This field is intended for one or more
+        references.  At a minimum, the database and accession must be provided.'),
+      'default_widget' => 'chado_linker__dbxref_widget',
+      'default_formatter' => 'chado_linker__dbxref_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_linker__pub' => array(
+      'label' => t('Publications'),
+      'description' => t('Associates a publication (e.g. journal article,
+          conference proceedings, book chapter, etc.) with this record.'),
+      'default_widget' => 'chado_linker__pub_widget',
+      'default_formatter' => 'chado_linker__pub_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    /*
+     * Fields that add new fields. These fields are not shown on
+     * pages.  They are avaiable to site curators when adding/updating
+     * a record and allow the user to add new linker table fields.
+     */
+    'chado_linker__prop_adder' => array(
+      'label' => t('Add a Property Type'),
+      'description' => t('This record may have any number of properties. Use
+          this field to first add the type.'),
+      'default_widget' => 'chado_linker__prop_adder_widget',
+      'default_formatter' => 'hidden',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+
+    // The field provides a widget for adding new vocabularies for cvterm
+    // linker tables. This will allow cvterms to be grouped by vocabulary
+    // ('category').
+    'chado_linker__cvterm_adder' => array(
+      'label' => t('Add an Annotation Type'),
+      'description' => t('This record may have any number of types of
+          annotations. Use this field to first add the type.'),
+      'default_widget' => 'chado_linker__cvterm_adder_widget',
+      'default_formatter' => 'hidden',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    /*
+     * Field specific to the feature table of Chado.
+     */
+    'chado_feature__residues' => array(
+      'label' => t('Residues'),
+      'description' => t('A field for managing nucleotide and protein residues.'),
+      'default_widget' => 'chado_feature__residues_widget',
+      'default_formatter' => 'chado_feature__residues_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_feature__md5checksum' => array(
+      'label' => t('MD5 checksum'),
+      'description' => t('A field for generating MD5 checksum for a sequence.'),
+      'default_widget' => 'chado_feature__md5checksum_widget',
+      'default_formatter' => 'chado_feature__md5checksum_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+    'chado_feature__seqlen' => array(
+      'label' => t('Sequence length'),
+      'description' => t('A field for calculating the length of a sequence.'),
+      'default_widget' => 'chado_feature__seqlen_widget',
+      'default_formatter' => 'chado_feature__seqlen_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
+
+  );
+  return $fields;
+}
+/**
+ * Implements hook_field_widget_info().
+ */
+function tripal_chado_field_widget_info() {
+  return array(
+    /*
+     * Generic fields that support multiple base tables.
+  */
+    'chado_base__organism_id_widget' => array(
+      'label' => t('Organism Select'),
+      'field types' => array('chado_base__organism_id')
+    ),
+    'chado_base__dbxref_id_widget' => array(
+      'label' => t('Cross reference'),
+      'field types' => array('chado_base__dbxref_id'),
+      'description' => t('This record can be cross referenced with a record in
+        another online database. This field is intended for the most
+        prominent reference.  At a minimum, the database and accession
+        must be provided.'),
+    ),
+
+    /*
+     * Fields that support linker tables.
+     */
+    'chado_linker__pub_widget' => array(
+      'label' => t('Publications'),
+      'field types' => array('chado_linker__pub'),
+    ),
+    'chado_linker__dbxref_widget' => array(
+      'label' => t('Cross references'),
+      'field types' => array('chado_linker__dbxref'),
+      'description' => t('This record can be cross referenced with a record
+        in another online database. This field is intended for the most
+        prominent reference.  At a minimum, the database and accession
+        must be provided.'),
+    ),
+
+    'chado_linker__cvterm_widget' => array(
+      'label' => t('Annotations'),
+      'field types' => array('chado_linker__cvterm'),
+      'description' => t('This record can be annotated with terms
+          from other vocabularies.'),
+    ),
+    'chado_linker__prop_widget' => array(
+      'label' => t('Property'),
+      'field types' => array('chado_linker__prop'),
+    ),
+    'chado_linker__synonym_widget' => array(
+      'label' => t('Synonyms'),
+      'field types' => array('chado_linker__synonym'),
+    ),
+    /*
+     * Fields that add new fields. These fields are not shown on
+     * pages.  They are avaiable to site curators when adding/updating
+     * a record and allow the user to add new linker table fields.
+     */
+    'chado_linker__prop_adder_widget' => array(
+      'label' => t('Add a Property'),
+      'field types' => array('chado_linker__prop_adder'),
+    ),
+    'chado_linker__cvterm_adder_widget' => array(
+      'label' => t('Add an Annotation'),
+      'field types' => array('chado_linker__cvterm_adder'),
+    ),
+    /*
+     * Field specific to the feature table of Chado.
+     */
+    'chado_feature__md5checksum_widget' => array(
+      'label' => t('MD5 Checksum Checkbox'),
+      'field types' => array('chado_feature__md5checksum'),
+    ),
+    'chado_feature__residues_widget' => array(
+      'label' => t('Residues'),
+      'field types' => array('chado_feature__residues'),
+    ),
+    'chado_feature__seqlen_widget' => array(
+      'label' => t('Sequence Length'),
+      'field types' => array('chado_feature__seqlen'),
+    ),
+  );
+}
+/**
+ * Implements hook_field_formatter_info().
+ */
+function tripal_chado_field_formatter_info() {
+  return array(
+    'chado_base__organism_id_formatter' => array(
+      'label' => t('Organism'),
+      'field types' => array('chado_base__organism_id')
+    ),
+    'chado_base__dbxref_id_formatter' => array(
+      'label' => t('Cross reference'),
+      'field types' => array('chado_base__dbxref_id')
+    ),
+    'chado_linker__pub_formatter' => array(
+      'label' => t('Publications'),
+      'field types' => array('chado_linker__pub')
+    ),
+    'chado_linker__dbxref_formatter' => array(
+      'label' => t('Cross references'),
+      'field types' => array('chado_linker__dbxref')
+    ),
+    'chado_linker__cvterm_formatter' => array(
+      'label' => t('Annotations'),
+      'field types' => array('chado_linker__cvterm')
+    ),
+    'chado_linker__prop_formatter' => array(
+      'label' => t('Property'),
+      'field types' => array('chado_linker__prop')
+    ),
+    'chado_linker__synonym_formatter' => array(
+      'label' => t('Synonyms'),
+      'field types' => array('chado_linker__synonym')
+    ),
+    'chado_linker__cvterm_adder_formatter' => array(
+      'label' => t('Add an Annotation'),
+      'field types' => array('chado_linker__cvterm_adder')
+    ),
+    'chado_linker__prop_adder_formatter' => array(
+      'label' => t('Add a Property'),
+      'field types' => array('chado_linker__prop_adder')
+    ),
+    'chado_feature__md5checksum_formatter' => array(
+      'label' => t('MD5 checksum'),
+      'field types' => array('chado_feature__md5checksum')
+    ),
+    'chado_feature__residues_formatter' => array(
+      'label' => t('Residues'),
+      'field types' => array('chado_feature__residues')
+    ),
+    'chado_feature__seqlen_formatter' => array(
+      'label' => t('Sequence length'),
+      'field types' => array('chado_feature__seqlen')
+    ),
+  );
+}
+/**
+ * Implements hook_field_formatter_settings_summary.
+ */
+function tripal_chado_field_formatter_settings_summary($field, $instance, $view_mode) {
+
+  $summary = '';
+  switch ($field['type']) {
+    case 'chado_base__organism_id':
+      $summary = 'Organisms can be displayed in vaious ways.';
+      break;
+    default:
+      $summary = '';
+  }
+
+  return $summary;
+}
+
+/**
+ * Implements hook_field_formatter_settings_form.
+ */
+function tripal_chado_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
+  $element = array();
+
+  $field_type = $field['type'];
+  form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/' . $field_type);
+  module_load_include('inc', 'tripal_chado', 'includes/fields/' . $field_type);
+  $function = $field_type . '_formatter_form';
+  if (function_exists($function)) {
+    $element = $function($field, $instance, $view_mode, $form, $form_state);
+  }
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_formatter_view().
+ */
+function tripal_chado_field_formatter_view($entity_type, $entity, $field,
+    $instance, $langcode, $items, $display) {
+
+  $element = array();
+
+  $field_type = $display['type'];
+  module_load_include('inc', 'tripal_chado', 'includes/fields/' . $field_type);
+  $function = $field_type;
+  if (function_exists($function)) {
+    $function($element, $entity_type, $entity, $field, $instance, $langcode, $items, $display);
+  }
+
+  return $element;
+}
+
+/**
+ * Implements hook_field_widget_form().
+ */
+function tripal_chado_field_widget_form(&$form, &$form_state, $field,
+    $instance, $langcode, $items, $delta, $element) {
+
+  $widget = $element;
+
+  $field_type = $instance['widget']['type'];
+  form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/' . $field_type);
+  module_load_include('inc', 'tripal_chado', 'includes/fields/' . $field_type);
+  $function = $field_type;
+  if (function_exists($function)) {
+    $function($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
+  }
+
+  return $widget;
+}
+
+/**
+ * Returns the values of the field from the $form_state.
+ */
+function tripal_chado_get_field_form_values($field_name, $form_state, $delta = 0, $child = NULL) {
+  $value = NULL;
+  // The form_state must have the 'values' key. If not then just return.
+  if (!array_key_exists('values', $form_state)) {
+    return $value;
+  }
+
+  // If the field name is not in the form_state['values'] then return.
+  if (!array_key_exists($field_name, $form_state['values'])) {
+    return $value;
+  }
+
+  // Iterate through the values looking for the field_name provided.
+  foreach ($form_state['values'][$field_name] as $langcode => $items) {
+    if (!array_key_exists($delta, $items)) {
+      continue;
+    }
+    $item = $items[$delta];
+    if ($child){
+      if(array_key_exists($child, $item) and $item[$child] != '') {
+        $value = $item[$child];
+      }
+    }
+    else {
+      $value = $item['value'];
+    }
+  }
+  return $value;
+}
+
+/**
+ * Sets the values of the field from the $form_state.
+ */
+function tripal_chado_set_field_form_values($field_name, &$form_state, $newvalue, $delta = 0, $child = NULL) {
+  // The form_state must have the 'values' key. If not then just return.
+  if (!array_key_exists('values', $form_state)) {
+    return FALSE;
+  }
+
+  // If the field name is not in the form_state['values'] then reutrn.
+  if (!array_key_exists($field_name, $form_state['values'])) {
+    return FALSE;
+  }
+
+  foreach ($form_state['values'][$field_name] as $langcode => $items) {
+    if ($child) {
+      $form_state['values'][$field_name][$langcode][$delta][$child] = $newvalue;
+    }
+    else {
+      $form_state['values'][$field_name][$langcode][$delta]['value'] = $newvalue;
+    }
+  }
+  return TRUE;
+}
+
+/**
+ * Implements hook_field_widget_form_alter().
+ */
+function tripal_chado_field_widget_form_alter(&$element, &$form_state, $context) {
+
+  if (array_key_exists('#field_name', $element)) {
+    $field_name = $element['#field_name'];
+    $matches = array();
+
+    if (preg_match('/(.+?)__(.+?)$/', $field_name, $matches)) {
+      $tablename = $matches[1];
+      $colname = $matches[2];
+      $schema = chado_get_schema($tablename);
+
+      // The timelastmodified field exists in many Chado tables.  We want
+      // the form element to update to the most recent time rather than the time
+      // in the database.
+      if ($colname == 'timelastmodified' and $schema['fields'][$colname]['type'] == 'datetime') {
+        // We want the default value for the field to be the current time.
+        $element['#default_value']['value'] = format_date(time(), 'custom', "Y-m-d H:i:s", 'UTC');
+        $element['#date_items']['value'] = $element['#default_value']['value'];
+      }
+      // We want the date combo fieldset to be collaspible so we will
+      // add our own theme_wrapper to replace the one added by the date
+      // module.
+      if (array_key_exists($colname, $schema['fields']) and $schema['fields'][$colname]['type'] == 'datetime') {
+        $element['#theme_wrappers'] = array('tripal_chado_date_combo');
+      }
+    }
+  }
+}
+
+
+
+/**
+ * Returns a $field_info array for a field based on a database column.
+ *
+ */
+function tripal_chado_add_bundle_fields_base__fields_defaults($table_name, $schema, $column_name) {
+  $details = $schema['fields'][$column_name];
+
+  // Create an array with information about this field.
+  $field = array(
+    'field_type' => '',
+    'widget_type' => '',
+    'description' => '',
+    'label' => ucwords(preg_replace('/_/', ' ', $column_name)),
+    'is_required' => 0,
+    'storage' => 'field_chado_storage',
+    'widget_settings' => array(
+      'display_label' => 1
+    ),
+    'field_settings' => array(
+      // The table in Chado where this field maps to.
+      'chado_table' => $table_name,
+      // The column in the Chado table that this field maps to.
+      'chado_column' => $column_name,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  // Alter the field info array depending on the column details.
+  switch($details['type']) {
+    case 'char':
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textfield';
+      $field['field_settings']['max_length'] = $details['length'];
+      break;
+    case 'varchar':
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textfield';
+      $field['field_settings']['max_length'] = $details['length'];
+      break;
+    case 'text':
+      $field['field_type'] = 'text';
+      $field['widget_type'] = 'text_textarea';
+      $field['field_settings']['max_length'] = 17179869184;
+      $field['field_settings']['text_processing'] = 1;
+      $field['format'] = filter_default_format();
+      break;
+    case 'blob':
+      // not sure how to support a blob field.
+      continue;
+      break;
+    case 'int':
+      $field['field_type'] = 'number_integer';
+      $field['widget_type'] = 'number';
+      break;
+    case 'float':
+      $field['field_type'] = 'number_float';
+      $field['widget_type'] = 'number';
+      $field['field_settings']['precision'] = 10;
+      $field['field_settings']['scale'] = 2;
+      $field['field_settings']['decimal_separator'] = '.';
+      break;
+    case 'numeric':
+      $field['field_type'] = 'number_decimal';
+      $field['widget_type'] = 'number';
+      break;
+    case 'serial':
+      // Serial fields are most likely not needed as a field.
+      break;
+    case 'boolean':
+      $field['field_type'] = 'list_boolean';
+      $field['widget_type'] = 'options_onoff';
+      $field['field_settings']['allowed_values'] = array(0 => "No", 1 => "Yes");
+      break;
+    case 'datetime':
+      // Use the Drupal Date and Date API to create the field/widget
+      $field['field_type'] = 'datetime';
+      $field['widget_type'] = 'date_select';
+      $field['widget_settings']['increment'] = 1;
+      $field['widget_settings']['tz_handling'] = 'none';
+      $field['widget_settings']['collapsible'] = TRUE;
+
+      // TODO: Add settings so that the minutes increment by 1.
+      // And turn off the timezone, as the Chado field doesn't support it.
+      break;
+  }
+
+  // Set some default semantic web information
+  if ($column_name == 'name') {
+    $field['field_settings']['semantic_web']['type'] = 'name';
+    $field['field_settings']['semantic_web']['ns'] = 'foaf';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
+  }
+  if ($column_name == 'description' or $column_name == 'definition' or
+      $column_name == 'comment') {
+    $field['field_settings']['semantic_web']['type'] = 'description';
+    $field['field_settings']['semantic_web']['ns'] = 'hydra';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://www.w3.org/ns/hydra/core#';
+  }
+
+  //
+  // GENERIC COLUMNS
+  //
+  if ($field['field_settings']['chado_column'] == 'organism_id') {
+    $field['field_type'] = 'chado_base__organism_id';
+    $field['widget_type'] = 'chado_base__organism_id_widget';
+    $field['label'] = 'Organism';
+    $field['description'] = 'Select an organism.';
+    $field['field_settings']['semantic_web']['type'] = 'organism';
+    $field['field_settings']['semantic_web']['ns'] = 'local';
+    $field['field_settings']['semantic_web']['nsurl'] = '';
+
+  }
+  elseif ($field['field_settings']['chado_column'] =='dbxref_id') {
+    $field['field_type'] = 'chado_base__dbxref_id';
+    $field['widget_type'] = 'chado_base_dbxref_id_widget';
+    $field['label'] = 'Cross Reference';
+    $field['description'] = 'This record can be cross referenced with a ' .
+        'record in another online database. The primary reference is for the ' .
+        'most prominent reference.  At a minimum, the database and accession ' .
+        'must be provided.  To remove a set reference, change the database ' .
+        'field to "Select a Database".';
+  }
+  elseif ($field['label'] == 'Timeaccessioned') {
+    $field['label'] = 'Time Accessioned';
+    $field['description'] = 'Please enter the time that this record was first added to the database.';
+  }
+  elseif ($field['label'] == 'Timelastmodified') {
+    $field['label'] = 'Time Last Modified';
+    $field['description'] = 'Please enter the time that this record was last modified. The default is the current time.';
+  }
+  //
+  // ORGANISM TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'organism' and $field['field_settings']['chado_column'] == 'comment') {
+    $field['label'] = 'Description';
+  }
+  //
+  // FEATURE TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'uniquename') {
+    $field['field_type'] = 'text';
+    $field['widget_type'] = 'text_textfield';
+    $field['field_settings']['text_processing'] = 0;
+    $field['field_settings']['semantic_web']['type'] = 'name';
+    $field['field_settings']['semantic_web']['ns'] = 'foaf';
+    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'md5checksum') {
+    $field['field_type'] = 'chado_feature__md5checksum';
+    $field['widget_type'] = 'chado_feature__md5checksum_widget';
+    $field['label'] = 'MD5 Checksum';
+    $field['description'] = 'Generate an MD5 checksum for the sequence.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'seqlen') {
+    $field['field_type'] = 'chado_feature__seqlen';
+    $field['widget_type'] = 'chado_feature__seqlen_widget';
+    $field['label'] = 'Seqlen';
+    $field['description'] = 'The length of the residues.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'residues') {
+    $field['field_type'] = 'chado_feature__residues';
+    $field['widget_type'] = 'chado_feature__residues_widget';
+    $field['label'] = 'Residues';
+    $field['description'] = 'Please provide an IUPAC compatible residues for this feature. Spaces and new lines are allowed.';
+  }
+  //
+  // ANALYSIS TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'program') {
+    $field['field_settings']['semantic_web']['type'] = 'SoftwareApplication';
+    $field['field_settings']['semantic_web']['ns'] = 'schema';
+    $field['field_settings']['semantic_web']['nsurl'] = 'https://schema.org/';
+    $field['description'] = 'The program name (e.g. blastx, blastp, sim4, genscan. If the analysis was not derived from a software package then provide a very brief description of the pipeline, workflow or method.';
+    $field['label'] = 'Program, Pipeline, Workflow or Method Name.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourceuri') {
+    $field['field_type'] = 'text';
+    $field['widget_type'] = 'text_textfield';
+    $field['field_settings']['text_processing'] = 0;
+    $field['label'] = 'Source URL';
+    $field['description'] = 'The URL where the original source data was derived.  Ideally, this should link to the page where more information about the source data can be found.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourcename') {
+    $field['label'] = 'Source Name';
+    $field['description'] = 'The name of the source data. This could be a file name, data set or a small description for how the data was collected. For long descriptions use the larger description field.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourceversion') {
+    $field['label'] = 'Source Version';
+    $field['description'] = 'If hte source data set has a version include it here.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'algorithm') {
+    $field['label'] = 'Source Version';
+    $field['description'] = 'The name of the algorithm used to produce the dataset if different from the program.';
+  }
+  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'programversion') {
+    $field['label'] = 'Program Version';
+    $field['description'] = 'The version of the program used to perform this analysis. (e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]. Enter "n/a" if no version is available or applicable.';
+  }
+  //
+  // PROJECT TABLE
+  //
+  elseif ($field['field_settings']['chado_table'] == 'project' and $field['field_settings']['chado_column'] == 'description') {
+    $field['label'] = 'Short Description';
+  }
+  return $field;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * The field_ui_display_overview_form is used for formatting the display
+ * or layout of fields attached to an entity and shown on the entity view page.
+ *
+ * This function removes the cvterm class and property adder field as those are
+ * really not meant for users to show or manage.
+ */
+function tripal_chado_form_field_ui_display_overview_form_alter(&$form, &$form_state, $form_id) {
+  // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
+  // on the add/edit form of an entity for adding new property fields.
+  $fields_names = element_children($form['fields']);
+  foreach ($fields_names as $field_name) {
+    $field_info = field_info_field($field_name);
+    if ($field_info['type'] == 'kvproperty_adder') {
+      unset($form['fields'][$field_name]);
+    }
+    if ($field_info['type'] == 'cvterm_class_adder') {
+      unset($form['fields'][$field_name]);
+    }
+  }
+}
+/**
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * The field_ui_field_overview_form is used for ordering and configuring the
+ * fields attached to an entity.
+ *
+ * This function removes the property adder field as that is really not meant
+ * for users to show or manage.
+ */
+function tripal_chado_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
+  // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
+  // on the add/edit form of an entity for adding new property fields.
+  $fields_names = element_children($form['fields']);
+  foreach ($fields_names as $field_name) {
+    $field_info = field_info_field($field_name);
+    if ($field_info['type'] == 'kvproperty_adder') {
+      unset($form['fields'][$field_name]);
+    }
+    if ($field_info['type'] == 'cvterm_class_adder') {
+      unset($form['fields'][$field_name]);
+    }
+  }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function tripal_chado_field_is_empty($item, $field) {
+
+  // If there is no value field then the field is empty.
+  if (!array_key_exists('value', $item)) {
+    return TRUE;
+  }
+
+  // Iterate through all of the fields and if at least one has a value
+  // the field is not empty.
+  foreach ($item as $form_field_name => $value) {
+    if (isset($value) and $value != NULL and $value != '') {
+      return FALSE;
+    }
+  }
+
+  // Otherwise, the field is empty.
+  return TRUE;
+}
+
+
+
+/**
+ * Implements hook_add_bundle_fields().
+ */
+function tripal_chado_add_bundle_fields($entity_type, $bundle, $term) {
+
+  $bundle_name = $bundle->name;
+
+  // This array will hold details that map the bundle to tables in Chado.
+  $bundle_data = array();
+
+  // Get the cvterm that corresponds to this TripalTerm object.
+  $vocab = entity_load('TripalVocab', array($term->vocab_id));
+  $vocab = reset($vocab);
+  $match = array(
+    'dbxref_id' => array(
+      'db_id' => array(
+        'name' => $vocab->namespace,
+      ),
+      'accession' => $term->accession
+    ),
+  );
+  $cvterm = chado_generate_var('cvterm', $match);
+
+  // The organism table does not have a type_id so we won't ever find
+  // a record for it in the tripal_cv_defaults table.
+  if ($cvterm->name == 'organism') {
+    $bundle_data = array(
+      'cv_id' => $cvterm->cv_id->cv_id,
+      'cvterm_id' => $cvterm->cvterm_id,
+      'data_table' => 'organism',
+      'type_table' => 'organism',
+      'field' =>  '',
+    );
+  }
+  // The analysis table does not have a type_id so we won't ever find
+  // a record for it in the tripalcv_defaults table.
+  else if ($cvterm->name == 'analysis') {
+    $bundle_data = array(
+      'cv_id' => $cvterm->cv_id->cv_id,
+      'cvterm_id' => $cvterm->cvterm_id,
+      'data_table' => 'analysis',
+      'type_table' => 'analysis',
+      'field' =>  '',
+    );
+  }
+  else if ($cvterm->name == 'project') {
+    $bundle_data = array(
+      'cv_id' => $cvterm->cv_id->cv_id,
+      'cvterm_id' => $cvterm->cvterm_id,
+      'data_table' => 'project',
+      'type_table' => 'project',
+      'field' =>  '',
+    );
+  }
+  else {
+    // TODO: WHAT TO DO IF A VOCABULARY IS USED AS A DEFAULT FOR MULTIPLE
+    // TABLES.
+    // Look to see if this vocabulary is used as a default for any table.
+    $default = db_select('tripal_cv_defaults', 't')
+      ->fields('t')
+      ->condition('cv_id', $cvterm->cv_id->cv_id)
+      ->execute()
+      ->fetchObject();
+    if ($default) {
+      $bundle_data = array(
+        'cv_id' => $cvterm->cv_id->cv_id,
+        'cvterm_id' => $cvterm->cvterm_id,
+        'data_table' => $default->table_name,
+        'type_table' => $default->table_name,
+        'field' =>  $default->field_name,
+      );
+    }
+  }
+
+  // Save the mapping information so that we can reuse it when we need to
+  // look things up for later for an entity
+  tripal_set_bundle_variable('chado_cvterm_id', $bundle->id, $bundle_data['cvterm_id']);
+  tripal_set_bundle_variable('chado_table', $bundle->id, $bundle_data['data_table']);
+  tripal_set_bundle_variable('chado_column', $bundle->id, $bundle_data['field']);
+
+  //////////////////////////////////////////////////////////////////////////////
+  // ADD FIELDS TO BUNDLE
+  ////////////////////////////////////////////////////////////////////////////
+
+  ////
+  //
+  // Base table fields.
+  //
+  // Adds the fields for the base table to the entity.  Adds fields
+  // for all columns including FK fields.  Excludes primary key and the
+  // type_id field (that's inherent in the bundle).
+  tripal_chado_add_bundle_fields_base__fields($entity_type, $bundle_name, $bundle_data);
+
+  ////
+  //
+  // Property table fields.
+  //
+  // Check to see if there are any property tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $prop_table = $bundle_data['data_table'] . 'prop';
+  if (chado_table_exists($prop_table)) {
+    tripal_chado_add_bundle_fields_linker__prop_adder_field($entity_type, $bundle_name, $prop_table, $bundle_data['data_table']);
+  }
+
+  ////
+  //
+  // Dbxref table fields.
+  //
+  // Check to see if there are any dbxref tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $dbxref_table = $bundle_data['data_table'] . '_dbxref';
+  if (chado_table_exists($dbxref_table)) {
+    tripal_chado_add_bundle_fields_linker__dbxref_field($entity_type, $bundle_name, $dbxref_table, $bundle_data['data_table']);
+  }
+
+  ////
+  //
+  // Cvterm table fields.
+  //
+  // Check to see if there are any cvterm tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $cvterm_table = $bundle_data['data_table'] . '_cvterm';
+  if (chado_table_exists($cvterm_table)) {
+    tripal_chado_add_bundle_fields_linker__cvterm_adder_field($entity_type, $bundle_name, $cvterm_table, $bundle_data['data_table']);
+  }
+
+
+  ////
+  //
+  // Synonym table fields.
+  //
+  // Check to see if there are any synonym tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $syn_table = $bundle_data['data_table'] . '_synonym';
+  if (chado_table_exists($syn_table)) {
+    tripal_chado_add_bundle_fields_linker__synonym_field($entity_type, $bundle_name, $syn_table, $bundle_data['data_table']);
+  }
+
+  ////
+  //
+  // Pub table fields.
+  //
+  // Check to see if there are any pub tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $pub_table = $bundle_data['data_table'] . '_pub';
+  if (chado_table_exists($pub_table)) {
+    tripal_chado_add_bundle_fields_linker__pub_field($entity_type, $bundle_name, $pub_table, $bundle_data['data_table']);
+  }
+}
+
+/**
+ * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
+ *
+ * @param $entity_type
+ * @param $bundle_name
+ * @param $base_table
+ * @param $dbxref_table
+ */
+function tripal_chado_add_bundle_fields_linker__dbxref_field($entity_type_name, $bundle_name, $dbxref_table, $base_table) {
+  // We already have a dbxref_id field.
+  $field_name = $dbxref_table;
+  $schema = chado_get_schema($dbxref_table);
+  $pkey = $schema['primary key'][0];
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'chado_base__dbxref_id',
+    'widget_type' => 'chado_base__dbxref_id_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Cross References',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'storage' => 'field_chado_storage',
+    'field_settings' => array(
+      // The Chado table that this field maps to.
+      'chado_table' => $dbxref_table,
+      // The column in the chado table that this field maps to.
+      'chado_column' => $pkey,
+      // The base table that this field is connected to.
+      'base_table' => $base_table,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  // If the base table has a 'dbxref_id' then change the label to
+  // indicate these are secondary cross references.
+  $schema = chado_get_schema($base_table);
+  if (array_key_exists('dbxref_id', $schema['fields'])) {
+    $field_info['label'] = 'Secondary Cross References';
+  }
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
+ *
+ * @param $entity_type
+ * @param $bundle_name
+ * @param $base_table
+ * @param $dbxref_table
+ */
+function tripal_chado_add_bundle_fields_linker__synonym_field($entity_type_name, $bundle_name, $syn_table, $base_table) {
+  // We already have a dbxref_id field.
+  $field_name = $syn_table;
+  $schema = chado_get_schema($syn_table);
+  $pkey = $schema['primary key'][0];
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'chado_linker__synonym',
+    'widget_type' => 'chado_linker__synonym_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Synonyms',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'storage' => 'field_chado_storage',
+    'field_settings' => array(
+      // The Chado table that this field maps to.
+      'chado_table' => $syn_table,
+      // The column in the chado table that this field maps to.
+      'chado_column' => $pkey,
+      // The base table that this field is connected to.
+      'base_table' => $base_table,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
+ *
+ * @param $entity_type
+ * @param $bundle_name
+ * @param $base_table
+ * @param $dbxref_table
+ */
+function tripal_chado_add_bundle_fields_linker__pub_field($entity_type_name, $bundle_name, $pub_table, $base_table) {
+  // We already have a dbxref_id field.
+  $field_name = $pub_table;
+  $schema = chado_get_schema($pub_table);
+  $pkey = $schema['primary key'][0];
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'chado_linker__pub',
+    'widget_type' => 'chado_linker__pub_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Publications',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'storage' => 'field_chado_storage',
+    'field_settings' => array(
+      // The Chado table that this field maps to.
+      'chado_table' => $pub_table,
+      // The column in the chado table that this field maps to.
+      'chado_column' => $pkey,
+      // The base table that this field is connected to.
+      'base_table' => $base_table,
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for managing properties that are stored in a prop table.
+ *
+ * @param $entity_type_name
+ * @param $bundle_name
+ * @param $kv_table
+ */
+function tripal_chado_add_bundle_fields_linker__prop_adder_field($entity_type_name, $bundle_name, $kv_table, $base_table) {
+  $field_name = $kv_table;
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'chado_linker__prop_adder',
+    'widget_type' => 'chado_linker__prop_adder_widget',
+    'field_settings' => array(
+      'base_table' => $base_table,
+    ),
+    'storage' => 'field_chado_storage',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Additional Properties',
+    'is_required' => 0,
+  );
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for managing properties that are stored in a prop table.
+ *
+ * @param $entity_type_name
+ * @param $bundle_name
+ * @param $kv_table
+ */
+function tripal_chado_add_bundle_fields_linker__cvterm_adder_field($entity_type_name, $bundle_name, $cvterm_table, $base_table) {
+  // First add a generic property field so that users can add new property types.
+  $field_name = $cvterm_table;
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'chado_linker__cvterm_adder',
+    'widget_type' => 'chado_linker__cvterm_adder_widget',
+    'field_settings' => array(
+      'base_table' => $base_table,
+    ),
+    'storage' => 'field_chado_storage',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Additional Annotation Types',
+    'is_required' => 0,
+  );
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for the base table to the entity.
+ */
+function tripal_chado_add_bundle_fields_base__fields($entity_type_name, $bundle_name, $bundle_data) {
+
+  $table_name = $bundle_data['data_table'];
+  $type_table = $bundle_data['type_table'];
+  $type_field = $bundle_data['field'];
+
+  // Iterate through the columns of the table and see if fields have been
+  // created for each one. If not, then create them.
+  $schema = chado_get_schema($table_name);
+  $columns = $schema['fields'];
+  foreach ($columns as $column_name => $details) {
+    $field_name = $table_name . '__' . $column_name;
+
+    // Skip the primary key field.
+    if ($column_name == $schema['primary key'][0]) {
+      continue;
+    }
+
+    // Skip the type field.
+    if ($table_name == $type_table and $column_name == $type_field) {
+      continue;
+    }
+
+    // Get the field defaults for this column.
+    $field_info = tripal_chado_add_bundle_fields_base__fields_defaults($table_name, $schema, $column_name);
+
+
+    // TODO: add in a call to drupal_alter to allow other modules to change
+    // the field settings.
+
+    // Determine if the field is required.
+    if (array_key_exists('not null', $details) and $details['not null'] === TRUE) {
+      $field_info['is_required'] = array_key_exists('default', $details) ? 0 : 1;
+    }
+
+    // If we don't have a field type then we don't need to create a field.
+    if (!$field_info['field_type']) {
+      // If we don't have a field type but it is required and doesn't have
+      // a default value then we are in trouble.
+      if ($field_info['is_required'] and !array_key_exists('default', $details)) {
+        throw new Exception(t('The %table.%field type, %type, is not yet supported for Entity fields, but it is required,',
+            array('%table' => $table_name, '%field' => $column_name, '%type' => $details['type'])));
+      }
+      continue;
+    }
+
+    // If this field is a foreign key field then we will have a custom field.
+    $is_fk = FALSE;
+    if (array_key_exists('foreign keys', $schema)) {
+      foreach ($schema['foreign keys'] as $remote_table => $fk_details) {
+        if (array_key_exists($column_name, $fk_details['columns'])) {
+          $is_fk = TRUE;
+        }
+      }
+    }
+
+    // Add the field to the bundle.
+    tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+  }
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * This function is used to validate any field. Fields with custom
+ * widgets will most likely have their own validate function but for all
+ * others we need a way to validate them.
+ *
+ */
+function tripal_chado_field_validate($entity_type, $entity, $field, $instance,
+    $langcode, $items, &$errors) {
+
+  //dpm($entity_type);
+
+}
+
+
+/**
+ * Implements hook_field_attach_validate().
+ *
+ * This function is used to validate entity-wide validation any of the fields
+ * attached to an entity.
+ */
+function tripal_chado_field_attach_validate($entity_type, $entity, &$errors) {
+  $bundle_name = $entity->bundle;
+  $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
+  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+
+  // Provide some higher-level validation for the organism type.
+  if ($term->name == 'organism') {
+
+  }
+}
+
+
+
+
+
+
+
+
+
+
+

+ 100 - 89
tripal_chado/includes/tripal_chado.term_storage.inc

@@ -23,7 +23,7 @@ function tripal_chado_vocab_get_term($namespace, $accession) {
     'accession'  => $accession,
     'name'       => '',
     'definition' => 'Term is undefined.',
-    'urlprefix'  => '',
+    'url'  => '',
     // The following are not required for the returned array but we'll
     // add these for convenience later when we look at the TripalTerm
     // objects and these will be there.
@@ -50,17 +50,22 @@ function tripal_chado_vocab_get_term($namespace, $accession) {
     return $empty_term;
   }
   $cvterm = chado_expand_var($cvterm, 'field', 'cvterm.definition');
-  return array(
+  $term = array(
     'namespace'  => $cvterm->dbxref_id->db_id->name,
     'accession'  => $cvterm->dbxref_id->accession,
     'name'       => $cvterm->name,
+    'url'        => '',
     'definition' => (isset($cvterm->definition)) ? $cvterm->definition : '',
-    'urlprefix'  => $cvterm->dbxref_id->db_id->urlprefix,
     // The following are not required for the returned array but we'll
     // add these for convenience later when we look at the TripalTerm
     // objects and these will be there.
     'cvterm'     => $cvterm,
   );
+  if ($cvterm->dbxref_id->db_id->urlprefix) {
+    $term['url'] = $cvterm->dbxref_id->db_id->urlprefix .
+      $cvterm->dbxref_id->db_id->name . ':' . $cvterm->dbxref_id->accession;
+  }
+  return $term;
 }
 
 /**
@@ -88,60 +93,81 @@ function tripal_chado_vocab_import_form_submit($form, &$form_state) {
  * Implements hook_vocab_select_term_form().
  */
 function tripal_chado_vocab_select_term_form($form, &$form_state) {
-  $term_name = '';
-  $num_terms = 0;
-  $cv_id = '';
-  $terms = array();
-
-  // Set defaults using the form state.
-  if (array_key_exists('storage', $form_state)) {
-    if (array_key_exists('terms', $form_state['storage'])) {
-      $terms = $form_state['storage']['terms'];
-    }
-  }
-  $num_terms = count($terms);
+  $term_name = array_key_exists('values', $form_state) ? $form_state['values']['term_name'] : '';
 
   // If no term has been selected yet then provide the auto complete field.
-  if ($num_terms == 0) {
-    $form['term_name'] = array(
-      '#title'       => t('Content Type'),
-      '#type'        => 'textfield',
-      '#description' => t("The content type must be the name of a term in
-          a controlled vocabulary and the controlled vocabulary should
-          already be loaded into Tripal.  For example, to create a content
-          type for storing 'genes', use the 'gene' term from the
-          Sequence Ontology (SO)."),
-      '#required'    => TRUE,
-      '#default_value' => $term_name,
-      '#autocomplete_path' => "admin/tripal/storage/term/$cv_id",
-    );
-  }
+  $form['term_name'] = array(
+    '#title'       => t('Content Type'),
+    '#type'        => 'textfield',
+    '#description' => t("The content type must be the name of a term in
+        a controlled vocabulary and the controlled vocabulary should
+        already be loaded into Tripal.  For example, to create a content
+        type for storing 'genes', use the 'gene' term from the
+        Sequence Ontology (SO)."),
+    '#required'    => TRUE,
+    '#default_value' => $term_name,
+    '#autocomplete_path' => "admin/tripal/storage/term/",
+  );
+  $form['select_button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Lookup Term'),
+    '#name' => 'select_cvterm',
+    '#ajax' => array(
+      'callback' => "tripal_chado_vocab_select_term_form_ajax_callback",
+      'wrapper' => "tripal-chado-vocab-select-form",
+      'effect' => 'fade',
+      'method' => 'replace'
+    ),
+  );
 
-  // If the term belongs to more than one vocabulary then add additional fields
-  // to let the user select the vocabulary.
-  if ($num_terms > 1) {
-    $form['term_name'] = array(
-      '#type' => 'hidden',
-      '#value' => $term_name,
+  if ($term_name) {
+    $form['terms_list'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Matching Terms'),
+      '#description' => t('Please select the term the best matches the
+          content type you want to create. If the same term exists in
+          multiple vocabularies you will see more than one option below.')
     );
-
-    $cvs = array();
+    $match = array(
+      'name' => $term_name,
+    );
+    $terms = chado_generate_var('cvterm', $match, array('return_array' => TRUE));
+    $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
+    $num_terms = 0;
     foreach ($terms as $term) {
-      $cvs[$term->cv_id->cv_id] = 'Vocabulary: <b>' . $term->cv_id->name . '</b> (' . $term->cv_id->definition . ')<br>' . $term->name . ': ' . $term->definition;
+       $form['terms_list']['term-' . $term->cvterm_id] = array(
+         '#type' => 'checkbox',
+         '#title' =>  $term->name,
+         '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name .
+             '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '.  ' .
+             '<br><b>Definition:</b>  ' . $term->definition,
+       );
+       $num_terms++;
     }
-    $form['cv_id'] = array(
-      '#type' => 'radios',
-      '#title' => t('Select the appropriate vocabulary'),
-      '#options' => $cvs,
+    if ($num_terms == 0) {
+      $form['terms_list']['none'] = array(
+        '#type' => 'item',
+        '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>'
+      );
+    }
+    // Add in the button for the cases of no terms or too many.
+    $form['submit_button'] = array(
+      '#type' => 'submit',
+      '#value' => t('Use this term'),
+      '#name' => 'use_cvterm'
     );
   }
 
-  // Add in the button for the cases of no terms or too many.
-  $form['select_button'] = array(
-    '#type' => 'submit',
-    '#value' => t('Use this term'),
-    '#name' => 'select_cvterm'
-  );
+  $form['#prefix'] = '<div id = "tripal-chado-vocab-select-form">';
+  $form['#suffix'] = '</div>';
+
+  return $form;
+}
+
+/**
+ * Implements an AJAX callback for the tripal_chado_vocab_select_term_form.
+ */
+function tripal_chado_vocab_select_term_form_ajax_callback($form, $form_state) {
   return $form;
 }
 
@@ -149,53 +175,38 @@ function tripal_chado_vocab_select_term_form($form, &$form_state) {
  * Implements hook_vocab_select_term_form_validate().
  */
 function tripal_chado_vocab_select_term_form_validate($form, &$form_state) {
+
   if (array_key_exists('clicked_button', $form_state) and
-      $form_state['clicked_button']['#name'] =='select_cvterm') {
+      $form_state['clicked_button']['#name'] =='use_cvterm') {
 
-    // First, make sure the term is unique. If not then we can't check it.
-    $term_name = NULL;
-    $cv_id = NULL;
-    $cvterm = NULL;
-    if (array_key_exists('term_name', $form_state['values'])) {
-      $term_name = $form_state['input']['term_name'];
-    }
-    if (array_key_exists('cv_id', $form_state['input'])) {
-      $cv_id = $form_state['input']['cv_id'];
-    }
+    $cvterm_id = NULL;
 
-    // If a term and $cv_id are provided then we can look for the term using
-    // both and we should find a unique term. If only ther term is provided
-    // we can still look for a unique term but there must only be one.
-    if ($term_name and !$cv_id) {
-      $match = array(
-        'name' => $term_name,
-      );
+    // Make sure we have a cvterm selected
+    $num_selected = 0;
+    foreach ($form_state['values'] as $key => $value) {
+      $matches = array();
+      if (preg_match("/^term-(\d+)$/", $key, $matches) and
+          $form_state['values']['term-' . $matches[1]]) {
+        $cvterm_id = $matches[1];
+        $term = chado_generate_var('cvterm', array('cvterm_id' => $cvterm_id));
+        $num_selected++;
+      }
     }
-    else {
-      $match = array(
-        'name' => $term_name,
-        'cv_id' => $cv_id,
-      );
+    if ($num_selected == 0) {
+      form_set_error('', 'Please select at least one term.');
     }
-    $terms = chado_generate_var('cvterm', $match, array('return_array' => TRUE));
-    $form_state['storage']['terms'] = $terms;
-
-    // If we do not have any terms then the term provided by the user does not
-    // exists and we need to provide an error message.
-    if (count($terms) == 0) {
-      form_set_error('term_name', t('The term does not exist in this database.'));
-    }
-    // If we have more than one term then we need to set an error so that the
-    // form can provide a list of vocabularies to select from.
-    if (count($terms) > 1) {
-      form_set_error('term_name', t('The term is not unique. A list of vocabularies
-        that contain this term. Please select the most appropriate vocabulary.'));
+    else if ($num_selected > 1) {
+      form_set_error('term-' . $cvterm_id, 'Please select only one term from the list below.');
     }
-    // If we have a unique term then set the namespace, accession and name.
-    if (count($terms) == 1) {
-      $form_state['storage']['namespace'] = $terms[0]->dbxref_id->db_id->name;
-      $form_state['storage']['accession'] = $terms[0]->dbxref_id->accession;
-      $form_state['storage']['term_name'] = $terms[0]->name;
+    else {
+      $form_state['storage']['namespace'] = $term->dbxref_id->db_id->name;
+      $form_state['storage']['accession'] = $term->dbxref_id->accession;
+      $form_state['storage']['term_name'] = $term->name;
     }
   }
+  // For any other button click it's an AJAX call and we just want to reubild
+  // the form.
+  else {
+    $form_state['rebuild'] = TRUE;
+  }
 }

+ 24 - 1236
tripal_chado/tripal_chado.module

@@ -1,7 +1,10 @@
 <?php
 
-// APPLICATION PROGRAMMER INTERFACE -------------
-// Chado API
+//
+// APPLICATION PROGRAMMER INTERFACE
+//
+
+// Generic Chado API functions
 require_once "api/tripal_chado.api.inc";
 require_once 'api/tripal_chado.property.api.inc';
 require_once 'api/tripal_chado.query.api.inc';
@@ -13,6 +16,7 @@ require_once 'api/tripal_chado.schema_v1.3.api.inc';
 require_once 'api/tripal_chado.schema_v1.2.api.inc';
 require_once 'api/tripal_chado.schema_v1.11.api.inc';
 
+// Chado module specific API functions
 require_once 'api/modules/tripal_chado.analysis.api.inc';
 require_once 'api/modules/tripal_chado.contact.api.inc';
 require_once 'api/modules/tripal_chado.cv.api.inc';
@@ -22,10 +26,16 @@ require_once 'api/modules/tripal_chado.organism.api.inc';
 require_once 'api/modules/tripal_chado.pub.api.inc';
 require_once 'api/modules/tripal_chado.stock.api.inc';
 
+//
+// REQUIRED INCLUDE FILES
+//
+// These require files implement hooks and therefore must
+// ways be included when the module is interpreted.
 require_once "includes/tripal_chado.entity.inc";
 require_once "includes/tripal_chado.schema.inc";
 require_once "includes/tripal_chado.term_storage.inc";
 require_once "includes/tripal_chado.field_storage.inc";
+require_once "includes/tripal_chado.fields.inc";
 
 tripal_chado_set_globals();
 
@@ -504,643 +514,35 @@ function tripal_chado_permission() {
   );
 }
 
-/**
- * Implements hook_field_info().
- */
-function tripal_chado_field_info() {
-  $fields = array(
-    'organism_id' => array(
-      'label' => t('Organism'),
-      'description' => t('A field for specifying an organism.'),
-      'default_widget' => 'tripal_chado_organism_select_widget',
-      'default_formatter' => 'tripal_chado_organism_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    'dbxref_id' => array(
-      'label' => t('Cross reference'),
-      'description' => t('This record can be cross referenced with a record in
-          another online database. This field is intended for the most prominent
-          reference.  At a minimum, the database and accession must be provided.'),
-      'default_widget' => 'tripal_chado_dbxref_id_widget',
-      'default_formatter' => 'tripal_chado_dbxref_id_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    'residues' => array(
-      'label' => t('Residues'),
-      'description' => t('A field for managing nucleotide and protein residues.'),
-      'default_widget' => 'tripal_chado_residue_textarea_widget',
-      'default_formatter' => 'tripal_chado_residues_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    'md5checksum' => array(
-      'label' => t('MD5 checksum'),
-      'description' => t('A field for generating MD5 checksum for a sequence.'),
-      'default_widget' => 'tripal_chado_md5checksum_checkbox_widget',
-      'default_formatter' => 'tripal_chado_md5checksum_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    'seqlen' => array(
-      'label' => t('Sequence length'),
-      'description' => t('A field for calculating the length of a sequence.'),
-      'default_widget' => 'tripal_chado_seqlen_hidden_widget',
-      'default_formatter' => 'tripal_chado_seqlen_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    // The field provides a widget for adding new properties
-    // to an entity that is connected to a base table that has a prop table
-    // in Chado.
-    'kvproperty_adder' => array(
-      'label' => t('Add a Property Type'),
-      'description' => t('This record may have any number of properties. Use
-          this field to first add the type.'),
-      'default_widget' => 'tripal_chado_kvproperty_adder_widget',
-      //'default_formatter' => 'tripal_chado_kvproperty_adder_formatter',
-      'default_formatter' => 'hidden',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    // The field provides form elements for adding a property to an entity
-    // that in turn gets stored in a prop table of Chado (e.g. featureprop,
-    // stockprop, etc).
-    'kvproperty' => array(
-      'label' => t('Add a Property'),
-      'description' => t('Add details about this property.'),
-      'default_widget' => 'tripal_chado_kvproperty_widget',
-      'default_formatter' => 'tripal_chado_kvproperty_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-
-    // The field provides form elements for adding multiple dbxrefs to an
-    // entity that in turn get stored in a [base]_dbxref table of Chado
-    // (e.g. organism_dbxref, feature_dbxref).  This is different
-    // from the dbxref_id field as that is specific to a dbxref_id in
-    // a base table.
-    'dbxref' => array(
-      'label' => t('Cross references'),
-      'description' => t('This record can be cross referenced with a record in
-          another online database. This field is intended for one or more
-          references.  At a minimum, the database and accession must be provided.'),
-      'default_widget' => 'tripal_chado_dbxref_widget',
-      'default_formatter' => 'tripal_chado_dbxref_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    // The field provides a widget for adding new vocabularies for cvterm
-    // linker tables. This will allow cvterms to be grouped by vocabulary
-    // ('category').
-    'cvterm_class_adder' => array(
-      'label' => t('Add an Annotation Type'),
-      'description' => t('This record may have any number of types of
-          annotations. Use this field to first add the type.'),
-      'default_widget' => 'tripal_chado_cvterm_class_adder_widget',
-      //'default_formatter' => 'tripal_chado_cvterm_class_adder_formatter',
-      'default_formatter' => 'hidden',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-    // The field provides form elements for adding multiple cvterms to an
-    // entity that in turn get stored in a [base]_cvterm table of Chado
-    // (e.g. feature_cvterm).
-    'cvterm' => array(
-      'label' => t('Annotations'),
-      'description' => t('This record can be annotated with terms
-          from other vocabularies.'),
-      'default_widget' => 'tripal_chado_cvterm_widget',
-      'default_formatter' => 'tripal_chado_cvterm_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-
-    'synonym' => array(
-      'label' => t('Synonyms'),
-      'description' => t('Adds an alternative name (synonym or alias) to this record.'),
-      'default_widget' => 'tripal_chado_synonym_widget',
-      'default_formatter' => 'tripal_chado_synonym_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-
-    'pub' => array(
-      'label' => t('Publications'),
-      'description' => t('Associates a publication (e.g. journal article, conference proceedings, book chapter, etc.) with this record.'),
-      'default_widget' => 'tripal_chado_pub_widget',
-      'default_formatter' => 'tripal_chado_pub_formatter',
-      'settings' => array(),
-      'storage' => array(
-        'type' => 'field_chado_storage',
-        'module' => 'tripal_chado',
-        'active' => TRUE
-      ),
-    ),
-  );
-  return $fields;
-}
-
-/**
- * Implements hook_field_widget_info().
- */
-function tripal_chado_field_widget_info() {
-  return array(
-    'tripal_chado_organism_select_widget' => array(
-      'label' => t('Organism Select'),
-      'field types' => array('organism_id')
-    ),
-    'tripal_chado_dbxref_id_widget' => array(
-      'label' => t('Cross reference'),
-      'field types' => array('dbxref_id'),
-      'description' => t('This record can be cross referenced with a record in
-        another online database. This field is intended for the most
-        prominent reference.  At a minimum, the database and accession
-        must be provided.'),
-    ),
-    'tripal_chado_dbxref_widget' => array(
-      'label' => t('Cross references'),
-      'field types' => array('dbxref'),
-      'description' => t('This record can be cross referenced with a record
-        in another online database. This field is intended for the most
-        prominent reference.  At a minimum, the database and accession
-        must be provided.'),
-    ),
-    'tripal_chado_cvterm_class_adder_widget' => array(
-      'label' => t('Add an Annotation'),
-      'field types' => array('cvterm_class_adder'),
-    ),
-    'tripal_chado_cvterm_widget' => array(
-      'label' => t('Annotations'),
-      'field types' => array('cvterm'),
-      'description' => t('This record can be annotated with terms
-          from other vocabularies.'),
-    ),
-    'tripal_chado_md5checksum_checkbox_widget' => array(
-      'label' => t('MD5 Checksum Checkbox'),
-      'field types' => array('md5checksum'),
-    ),
-    'tripal_chado_residues_textarea_widget' => array(
-      'label' => t('Residues'),
-      'field types' => array('residues'),
-    ),
-    'tripal_chado_seqlen_hidden_widget' => array(
-      'label' => t('Sequence Length'),
-      'field types' => array('seqlen'),
-    ),
-    'tripal_chado_kvproperty_adder_widget' => array(
-      'label' => t('Add a Property'),
-      'field types' => array('kvproperty_adder'),
-    ),
-    'tripal_chado_kvproperty_widget' => array(
-      'label' => t('Property'),
-      'field types' => array('kvproperty'),
-    ),
-    'tripal_chado_synonym_widget' => array(
-      'label' => t('Synonyms'),
-      'field types' => array('synonym'),
-    ),
-    'tripal_chado_pub_widget' => array(
-      'label' => t('Publications'),
-      'field types' => array('pub'),
-    ),
-  );
-}
-/**
- * Implements hook_field_formatter_info().
- */
-function tripal_chado_field_formatter_info() {
-  return array(
-    'tripal_chado_organism_formatter' => array(
-      'label' => t('Organism'),
-      'field types' => array('organism_id')
-    ),
-    'tripal_chado_dbxref_id_formatter' => array(
-      'label' => t('Cross reference'),
-      'field types' => array('dbxref_id')
-    ),
-    'tripal_chado_dbxref_formatter' => array(
-      'label' => t('Cross references'),
-      'field types' => array('dbxref')
-    ),
-    'tripal_chado_cvterm_class_adder_formatter' => array(
-      'label' => t('Add an Annotation'),
-      'field types' => array('cvterm_class_adder')
-    ),
-    'tripal_chado_cvterm_formatter' => array(
-      'label' => t('Annotations'),
-      'field types' => array('cvterm')
-    ),
-    'tripal_chado_md5checksum_formatter' => array(
-      'label' => t('MD5 checksum'),
-      'field types' => array('md5checksum')
-    ),
-    'tripal_chado_residues_formatter' => array(
-      'label' => t('Residues'),
-      'field types' => array('residues')
-    ),
-    'tripal_chado_seqlen_formatter' => array(
-      'label' => t('Sequence length'),
-      'field types' => array('seqlen')
-    ),
-    'tripal_chado_kvproperty_adder_formatter' => array(
-      'label' => t('Add a Property'),
-      'field types' => array('kvproperty_adder')
-    ),
-    'tripal_chado_kvproperty_formatter' => array(
-      'label' => t('Property'),
-      'field types' => array('kvproperty')
-    ),
-    'tripal_chado_synonym_formatter' => array(
-      'label' => t('Synonyms'),
-      'field types' => array('synonym')
-    ),
-    'tripal_chado_pub_formatter' => array(
-      'label' => t('Publications'),
-      'field types' => array('pub')
-    ),
-  );
-}
-
-/**
- * Implements hook_field_widget_form_alter().
- */
-function tripal_chado_field_widget_form_alter(&$element, &$form_state, $context) {
-
-  if (array_key_exists('#field_name', $element)) {
-    $field_name = $element['#field_name'];
-    $matches = array();
-
-    if (preg_match('/(.+?)__(.+?)$/', $field_name, $matches)) {
-      $tablename = $matches[1];
-      $colname = $matches[2];
-      $schema = chado_get_schema($tablename);
-
-      // The timelastmodified field exists in many Chado tables.  We want
-      // the form element to update to the most recent time rather than the time
-      // in the database.
-      if ($colname == 'timelastmodified' and $schema['fields'][$colname]['type'] == 'datetime') {
-        // We want the default value for the field to be the current time.
-        $element['#default_value']['value'] = format_date(time(), 'custom', "Y-m-d H:i:s", 'UTC');
-        $element['#date_items']['value'] = $element['#default_value']['value'];
-      }
-      // We want the date combo fieldset to be collaspible so we will
-      // add our own theme_wrapper to replace the one added by the date
-      // module.
-      if (array_key_exists($colname, $schema['fields']) and $schema['fields'][$colname]['type'] == 'datetime') {
-        $element['#theme_wrappers'] = array('tripal_chado_date_combo');
-      }
-    }
-  }
-}
-
-
-/**
- * Implements hook_field_formatter_view().
- */
-function tripal_chado_field_formatter_view($entity_type, $entity, $field,
-  $instance, $langcode, $items, $display) {
-
-  $element = array();
-  switch ($display['type']) {
-    case 'tripal_chado_organism_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/organism_id');
-      tripal_chado_organism_select_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_dbxref_id_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref_id');
-      tripal_chado_dbxref_id_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_dbxref_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref');
-      tripal_chado_dbxref_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_cvterm_class_adder_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm_class_adder');
-      tripal_chado_cvterm_class_adder_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_cvterm_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm');
-      tripal_chado_cvterm_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_md5checksum_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
-      tripal_chado_md5checksum_checkbox_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_residues_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/residues');
-      tripal_chado_residues_textarea_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_seqlen_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/seqlen');
-      tripal_chado_seqlen_hidden_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_kvproperty_adder_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/kvproperty_adder');
-      tripal_chado_kvproperty_adder_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_kvproperty_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/kvproperty');
-      tripal_chado_kvproperty_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_synonym_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/synonym');
-      tripal_chado_synonym_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-    case 'tripal_chado_pub_formatter':
-      module_load_include('inc', 'tripal_chado', 'includes/fields/pub');
-      tripal_chado_pub_formatter($element, $entity_type, $entity, $field,
-          $instance, $langcode, $items, $display);
-      break;
-  }
-  return $element;
-}
-
-/**
- * Implements hook_field_widget_form().
- */
-function tripal_chado_field_widget_form(&$form, &$form_state, $field,
-    $instance, $langcode, $items, $delta, $element) {
-
-  $widget = $element;
-  switch ($instance['widget']['type']) {
-    case 'tripal_chado_organism_select_widget':
-      // Make sure the include files get parsed now and for the form submits.
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/organism_id');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/organism_id');
-      // Update the widget with the new field.
-      tripal_chado_organism_select_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_dbxref_id_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/dbxref_id');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref_id');
-      tripal_chado_dbxref_id_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_dbxref_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/dbxref');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref');
-      tripal_chado_dbxref_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_cvterm_class_adder_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/cvterm_class_adder');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm_class_adder');
-      tripal_chado_cvterm_class_adder_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_cvterm_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/cvterm');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/cvterm');
-      tripal_chado_cvterm_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_md5checksum_checkbox_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/md5checksum');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
-      tripal_chado_md5checksum_checkbox_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_residues_textarea_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/residues');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/residues');
-      tripal_chado_residues_textarea_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_seqlen_hidden_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/seqlen');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/seqlen');
-      tripal_chado_seqlen_hidden_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_kvproperty_adder_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/kvproperty_adder');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/kvproperty_adder');
-      tripal_chado_kvproperty_adder_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_kvproperty_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/kvproperty');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/kvproperty');
-      tripal_chado_kvproperty_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_synonym_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/synonym');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/synonym');
-      tripal_chado_synonym_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-    case 'tripal_chado_pub_widget':
-      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/pub');
-      module_load_include('inc', 'tripal_chado', 'includes/fields/pub');
-      tripal_chado_pub_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
-      break;
-  }
-  return $widget;
-}
-
-/**
- * Implements hook_form_FORM_ID_alter().
- *
- * The field_ui_display_overview_form is used for formatting the display
- * or layout of fields attached to an entity and shown on the entity view page.
- *
- * This function removes the cvterm class and property adder field as those are
- * really not meant for users to show or manage.
- */
-function tripal_chado_form_field_ui_display_overview_form_alter(&$form, &$form_state, $form_id) {
-  // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
-  // on the add/edit form of an entity for adding new property fields.
-  $fields_names = element_children($form['fields']);
-  foreach ($fields_names as $field_name) {
-    $field_info = field_info_field($field_name);
-    if ($field_info['type'] == 'kvproperty_adder') {
-      unset($form['fields'][$field_name]);
-    }
-    if ($field_info['type'] == 'cvterm_class_adder') {
-      unset($form['fields'][$field_name]);
-    }
-  }
-}
-/**
- * Implements hook_form_FORM_ID_alter().
- *
- * The field_ui_field_overview_form is used for ordering and configuring the
- * fields attached to an entity.
- *
- * This function removes the property adder field as that is really not meant
- * for users to show or manage.
- */
-function tripal_chado_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
-  // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
-  // on the add/edit form of an entity for adding new property fields.
-  $fields_names = element_children($form['fields']);
-  foreach ($fields_names as $field_name) {
-    $field_info = field_info_field($field_name);
-    if ($field_info['type'] == 'kvproperty_adder') {
-      unset($form['fields'][$field_name]);
-    }
-    if ($field_info['type'] == 'cvterm_class_adder') {
-      unset($form['fields'][$field_name]);
-    }
-  }
-}
-/**
- * Implements hook_field_is_empty().
- */
-function tripal_chado_field_is_empty($item, $field) {
-
-  // If there is no value field then the field is empty.
-  if (!array_key_exists('value', $item)) {
-    return TRUE;
-  }
-
-  // Iterate through all of the fields and if at least one has a value
-  // the field is not empty.
-  foreach ($item as $form_field_name => $value) {
-    if (isset($value) and $value != NULL and $value != '') {
-      return FALSE;
-    }
-  }
-
-  // Otherwise, the field is empty.
-  return TRUE;
-}
-
-/**
- * Returns the values of the field from the $form_state.
- */
-function tripal_chado_get_field_form_values($field_name, $form_state, $delta = 0, $child = NULL) {
-  $value = NULL;
-  // The form_state must have the 'values' key. If not then just return.
-  if (!array_key_exists('values', $form_state)) {
-    return $value;
-  }
-
-  // If the field name is not in the form_state['values'] then return.
-  if (!array_key_exists($field_name, $form_state['values'])) {
-    return $value;
-  }
-
-  // Iterate through the values looking for the field_name provided.
-  foreach ($form_state['values'][$field_name] as $langcode => $items) {
-    if (!array_key_exists($delta, $items)) {
-      continue;
-    }
-    $item = $items[$delta];
-    if ($child){
-      if(array_key_exists($child, $item) and $item[$child] != '') {
-        $value = $item[$child];
-      }
-    }
-    else {
-      $value = $item['value'];
-    }
-  }
-  return $value;
-}
-
-/**
- * Sets the values of the field from the $form_state.
- */
-function tripal_chado_set_field_form_values($field_name, &$form_state, $newvalue, $delta = 0, $child = NULL) {
-  // The form_state must have the 'values' key. If not then just return.
-  if (!array_key_exists('values', $form_state)) {
-    return FALSE;
-  }
-
-  // If the field name is not in the form_state['values'] then reutrn.
-  if (!array_key_exists($field_name, $form_state['values'])) {
-    return FALSE;
-  }
-
-  foreach ($form_state['values'][$field_name] as $langcode => $items) {
-    if ($child) {
-      $form_state['values'][$field_name][$langcode][$delta][$child] = $newvalue;
-    }
-    else {
-      $form_state['values'][$field_name][$langcode][$delta]['value'] = $newvalue;
-    }
-  }
-  return TRUE;
-}
-
 /**
  * Implements hook_theme().
  */
 function tripal_chado_theme($existing, $type, $theme, $path) {
   return array(
     // Theme fields.
-    'tripal_chado_dbxref_id_widget' => array(
+    'chado_base__dbxref_id_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/dbxref_id.inc',
+      'file' => 'includes/fields/chado_base__dbxref_id.inc',
     ),
-    'tripal_chado_dbxref_widget' => array(
+    'chado_linker__dbxref_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/dbxref.inc',
+      'file' => 'includes/fields/chado_linker__dbxref.inc',
     ),
-    'tripal_chado_cvterm_widget' => array(
+    'chado_linker__cvterm_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/cvterm.inc',
+      'file' => 'includes/fields/chado_linker__cvterm.inc',
     ),
-    'tripal_chado_synonym_widget' => array(
+    'chado_linker__synonym_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/synonym.inc',
+      'file' => 'includes/fields/chado_linker__synonym.inc',
     ),
-    'tripal_chado_pub_widget' => array(
+    'chado_linker__pub_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/pub.inc',
+      'file' => 'includes/fields/chado_linker__pub.inc',
     ),
-    'tripal_chado_kvproperty_addr_widget' => array(
+    'chado_linker__prop_adder_widget' => array(
       'render element' => 'element',
-      'file' => 'includes/fields/dbxref_id.inc',
+      'file' => 'includes/fields/chado_linker__prop_adder.inc',
     ),
     'tripal_chado_date_combo' => array(
       'render element' => 'element',
@@ -1159,622 +561,8 @@ function tripal_chado_theme($existing, $type, $theme, $path) {
   );
 }
 
-/**
- * Implements hook_add_bundle_fields().
- */
-function tripal_chado_add_bundle_fields($entity_type, $bundle, $term) {
-
-  $bundle_name = $bundle->name;
-
-  // This array will hold details that map the bundle to tables in Chado.
-  $bundle_data = array();
-
-  // Get the cvterm that corresponds to this TripalTerm object.
-  $vocab = entity_load('TripalVocab', array($term->vocab_id));
-  $vocab = reset($vocab);
-  $match = array(
-    'dbxref_id' => array(
-      'db_id' => array(
-        'name' => $vocab->namespace,
-      ),
-      'accession' => $term->accession
-    ),
-  );
-  $cvterm = chado_generate_var('cvterm', $match);
-
-  // The organism table does not have a type_id so we won't ever find
-  // a record for it in the tripal_cv_defaults table.
-  if ($cvterm->name == 'organism') {
-    $bundle_data = array(
-      'cv_id' => $cvterm->cv_id->cv_id,
-      'cvterm_id' => $cvterm->cvterm_id,
-      'data_table' => 'organism',
-      'type_table' => 'organism',
-      'field' =>  '',
-    );
-  }
-  // The analysis table does not have a type_id so we won't ever find
-  // a record for it in the tripalcv_defaults table.
-  else if ($cvterm->name == 'analysis') {
-    $bundle_data = array(
-      'cv_id' => $cvterm->cv_id->cv_id,
-      'cvterm_id' => $cvterm->cvterm_id,
-      'data_table' => 'analysis',
-      'type_table' => 'analysis',
-      'field' =>  '',
-    );
-  }
-  else if ($cvterm->name == 'project') {
-    $bundle_data = array(
-      'cv_id' => $cvterm->cv_id->cv_id,
-      'cvterm_id' => $cvterm->cvterm_id,
-      'data_table' => 'project',
-      'type_table' => 'project',
-      'field' =>  '',
-    );
-  }
-  else {
-    // TODO: WHAT TO DO IF A VOCABULARY IS USED AS A DEFAULT FOR MULTIPLE
-    // TABLES.
-    // Look to see if this vocabulary is used as a default for any table.
-    $default = db_select('tripal_cv_defaults', 't')
-      ->fields('t')
-      ->condition('cv_id', $cvterm->cv_id->cv_id)
-      ->execute()
-      ->fetchObject();
-    if ($default) {
-      $bundle_data = array(
-        'cv_id' => $cvterm->cv_id->cv_id,
-        'cvterm_id' => $cvterm->cvterm_id,
-        'data_table' => $default->table_name,
-        'type_table' => $default->table_name,
-        'field' =>  $default->field_name,
-      );
-    }
-  }
-
-  // Save the mapping information so that we can reuse it when we need to
-  // look things up for later for an entity
-  tripal_set_bundle_variable('chado_cvterm_id', $bundle->id, $bundle_data['cvterm_id']);
-  tripal_set_bundle_variable('chado_table', $bundle->id, $bundle_data['data_table']);
-  tripal_set_bundle_variable('chado_column', $bundle->id, $bundle_data['field']);
-
-  //////////////////////////////////////////////////////////////////////////////
-  // ADD FIELDS TO BUNDLE
-  ////////////////////////////////////////////////////////////////////////////
-
-  ////
-  //
-  // Base table fields.
-  //
-  // Adds the fields for the base table to the entity.  Adds fields
-  // for all columns including FK fields.  Excludes primary key and the
-  // type_id field (that's inherent in the bundle).
-  tripal_chado_add_bundle_base_fields($entity_type, $bundle_name, $bundle_data);
-
-  ////
-  //
-  // Property table fields.
-  //
-  // Check to see if there are any property tables with FKs to this
-  // base table. If so, add the fields for that type of table.
-  $prop_table = $bundle_data['data_table'] . 'prop';
-  if (chado_table_exists($prop_table)) {
-    tripal_chado_add_bundle_kvproperty_adder_field($entity_type, $bundle_name, $prop_table, $bundle_data['data_table']);
-  }
-
-  ////
-  //
-  // Dbxref table fields.
-  //
-  // Check to see if there are any dbxref tables with FKs to this
-  // base table. If so, add the fields for that type of table.
-  $dbxref_table = $bundle_data['data_table'] . '_dbxref';
-  if (chado_table_exists($dbxref_table)) {
-    tripal_chado_add_bundle_dbxref_field($entity_type, $bundle_name, $dbxref_table, $bundle_data['data_table']);
-  }
-
-  ////
-  //
-  // Cvterm table fields.
-  //
-  // Check to see if there are any cvterm tables with FKs to this
-  // base table. If so, add the fields for that type of table.
-  $cvterm_table = $bundle_data['data_table'] . '_cvterm';
-  if (chado_table_exists($cvterm_table)) {
-    tripal_chado_add_bundle_cvterm_class_adder_field($entity_type, $bundle_name, $cvterm_table, $bundle_data['data_table']);
-  }
-
-
-  ////
-  //
-  // Synonym table fields.
-  //
-  // Check to see if there are any synonym tables with FKs to this
-  // base table. If so, add the fields for that type of table.
-  $syn_table = $bundle_data['data_table'] . '_synonym';
-  if (chado_table_exists($syn_table)) {
-    tripal_chado_add_bundle_synonym_field($entity_type, $bundle_name, $syn_table, $bundle_data['data_table']);
-  }
-
-  ////
-  //
-  // Pub table fields.
-  //
-  // Check to see if there are any pub tables with FKs to this
-  // base table. If so, add the fields for that type of table.
-  $pub_table = $bundle_data['data_table'] . '_pub';
-  if (chado_table_exists($pub_table)) {
-    tripal_chado_add_bundle_pub_field($entity_type, $bundle_name, $pub_table, $bundle_data['data_table']);
-  }
-}
-
-/**
- * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
- *
- * @param $entity_type
- * @param $bundle_name
- * @param $base_table
- * @param $dbxref_table
- */
-function tripal_chado_add_bundle_dbxref_field($entity_type_name, $bundle_name, $dbxref_table, $base_table) {
-  // We already have a dbxref_id field.
-  $field_name = $dbxref_table;
-  $schema = chado_get_schema($dbxref_table);
-  $pkey = $schema['primary key'][0];
-
-  // Initialize the field array.
-  $field_info = array(
-    'field_type' => 'dbxref',
-    'widget_type' => 'tripal_fields_dbxfref_widget',
-    'widget_settings' => array('display_label' => 1),
-    'description' => '',
-    'label' => 'Cross References',
-    'is_required' => 0,
-    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
-    'storage' => 'field_chado_storage',
-    'field_settings' => array(
-      // The Chado table that this field maps to.
-      'chado_table' => $dbxref_table,
-      // The column in the chado table that this field maps to.
-      'chado_column' => $pkey,
-      // The base table that this field is connected to.
-      'base_table' => $base_table,
-      'semantic_web' => array(
-        // The type is the term from a vocabulary that desribes this field..
-        'type' => '',
-        // The namepsace for the vocabulary (e.g. 'foaf').
-        'ns' => '',
-        // The URL for the namespace.  It must be that the type can be
-        // appended to the URL.
-        'nsurl' => '',
-      ),
-    ),
-  );
-
-  // If the base table has a 'dbxref_id' then change the label to
-  // indicate these are secondary cross references.
-  $schema = chado_get_schema($base_table);
-  if (array_key_exists('dbxref_id', $schema['fields'])) {
-    $field_info['label'] = 'Secondary Cross References';
-  }
-  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-}
-/**
- * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
- *
- * @param $entity_type
- * @param $bundle_name
- * @param $base_table
- * @param $dbxref_table
- */
-function tripal_chado_add_bundle_synonym_field($entity_type_name, $bundle_name, $syn_table, $base_table) {
-  // We already have a dbxref_id field.
-  $field_name = $syn_table;
-  $schema = chado_get_schema($syn_table);
-  $pkey = $schema['primary key'][0];
-
-  // Initialize the field array.
-  $field_info = array(
-    'field_type' => 'synonym',
-    'widget_type' => 'tripal_fields_synonym_widget',
-    'widget_settings' => array('display_label' => 1),
-    'description' => '',
-    'label' => 'Synonyms',
-    'is_required' => 0,
-    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
-    'storage' => 'field_chado_storage',
-    'field_settings' => array(
-      // The Chado table that this field maps to.
-      'chado_table' => $syn_table,
-      // The column in the chado table that this field maps to.
-      'chado_column' => $pkey,
-      // The base table that this field is connected to.
-      'base_table' => $base_table,
-      'semantic_web' => array(
-        // The type is the term from a vocabulary that desribes this field..
-        'type' => '',
-        // The namepsace for the vocabulary (e.g. 'foaf').
-        'ns' => '',
-        // The URL for the namespace.  It must be that the type can be
-        // appended to the URL.
-        'nsurl' => '',
-      ),
-    ),
-  );
-
-  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-}
-/**
- * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
- *
- * @param $entity_type
- * @param $bundle_name
- * @param $base_table
- * @param $dbxref_table
- */
-function tripal_chado_add_bundle_pub_field($entity_type_name, $bundle_name, $pub_table, $base_table) {
-  // We already have a dbxref_id field.
-  $field_name = $pub_table;
-  $schema = chado_get_schema($pub_table);
-  $pkey = $schema['primary key'][0];
-
-  // Initialize the field array.
-  $field_info = array(
-    'field_type' => 'pub',
-    'widget_type' => 'tripal_fields_pub_widget',
-    'widget_settings' => array('display_label' => 1),
-    'description' => '',
-    'label' => 'Publications',
-    'is_required' => 0,
-    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
-    'storage' => 'field_chado_storage',
-    'field_settings' => array(
-      // The Chado table that this field maps to.
-      'chado_table' => $pub_table,
-      // The column in the chado table that this field maps to.
-      'chado_column' => $pkey,
-      // The base table that this field is connected to.
-      'base_table' => $base_table,
-      'semantic_web' => array(
-        // The type is the term from a vocabulary that desribes this field..
-        'type' => '',
-        // The namepsace for the vocabulary (e.g. 'foaf').
-        'ns' => '',
-        // The URL for the namespace.  It must be that the type can be
-        // appended to the URL.
-        'nsurl' => '',
-      ),
-    ),
-  );
-
-  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-}
-/**
- * Adds the fields for managing properties that are stored in a prop table.
- *
- * @param $entity_type_name
- * @param $bundle_name
- * @param $kv_table
- */
-function tripal_chado_add_bundle_kvproperty_adder_field($entity_type_name, $bundle_name, $kv_table, $base_table) {
-  $field_name = $kv_table;
-
-  // Initialize the field array.
-  $field_info = array(
-    'field_type' => 'kvproperty_adder',
-    'widget_type' => 'tripal_fields_kvproperty_adder_widget',
-    'field_settings' => array(
-      'base_table' => $base_table,
-    ),
-    'storage' => 'field_chado_storage',
-    'widget_settings' => array('display_label' => 1),
-    'description' => '',
-    'label' => 'Additional Properties',
-    'is_required' => 0,
-  );
-  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-}
-/**
- * Adds the fields for managing properties that are stored in a prop table.
- *
- * @param $entity_type_name
- * @param $bundle_name
- * @param $kv_table
- */
-function tripal_chado_add_bundle_cvterm_class_adder_field($entity_type_name, $bundle_name, $cvterm_table, $base_table) {
-  // First add a generic property field so that users can add new property types.
-  $field_name = $cvterm_table;
-
-  // Initialize the field array.
-  $field_info = array(
-    'field_type' => 'cvterm_class_adder',
-    'widget_type' => 'tripal_fields_cvterm_class_adder_widget',
-    'field_settings' => array(
-      'base_table' => $base_table,
-    ),
-    'storage' => 'field_chado_storage',
-    'widget_settings' => array('display_label' => 1),
-    'description' => '',
-    'label' => 'Additional Annotation Types',
-    'is_required' => 0,
-  );
-  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-}
-/**
- * Adds the fields for the base table to the entity.
- */
-function tripal_chado_add_bundle_base_fields($entity_type_name, $bundle_name, $bundle_data) {
-
-  $table_name = $bundle_data['data_table'];
-  $type_table = $bundle_data['type_table'];
-  $type_field = $bundle_data['field'];
-
-  // Iterate through the columns of the table and see if fields have been
-  // created for each one. If not, then create them.
-  $schema = chado_get_schema($table_name);
-  $columns = $schema['fields'];
-  foreach ($columns as $column_name => $details) {
-    $field_name = $table_name . '__' . $column_name;
-
-    // Skip the primary key field.
-    if ($column_name == $schema['primary key'][0]) {
-      continue;
-    }
-
-    // Skip the type field.
-    if ($table_name == $type_table and $column_name == $type_field) {
-      continue;
-    }
-
-    // Get the field defaults for this column.
-    $field_info = tripal_chado_get_table_column_field_default($table_name, $schema, $column_name);
-
-
-    // TODO: add in a call to drupal_alter to allow other modules to change
-    // the field settings.
-
-    // Determine if the field is required.
-    if (array_key_exists('not null', $details) and $details['not null'] === TRUE) {
-      $field_info['is_required'] = array_key_exists('default', $details) ? 0 : 1;
-    }
-
-    // If we don't have a field type then we don't need to create a field.
-    if (!$field_info['field_type']) {
-      // If we don't have a field type but it is required and doesn't have
-      // a default value then we are in trouble.
-      if ($field_info['is_required'] and !array_key_exists('default', $details)) {
-        throw new Exception(t('The %table.%field type, %type, is not yet supported for Entity fields, but it is required,',
-            array('%table' => $table_name, '%field' => $column_name, '%type' => $details['type'])));
-      }
-      continue;
-    }
-
-    // If this field is a foreign key field then we will have a custom field.
-    $is_fk = FALSE;
-    if (array_key_exists('foreign keys', $schema)) {
-      foreach ($schema['foreign keys'] as $remote_table => $fk_details) {
-        if (array_key_exists($column_name, $fk_details['columns'])) {
-          $is_fk = TRUE;
-        }
-      }
-    }
-
-    // Add the field to the bundle.
-    tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
-  }
-}
-
-/**
- * Returns a $field_info array for a field based on a database column.
- *
- */
-function tripal_chado_get_table_column_field_default($table_name, $schema, $column_name) {
-  $details = $schema['fields'][$column_name];
-
-  // Create an array with information about this field.
-  $field = array(
-    'field_type' => '',
-    'widget_type' => '',
-    'description' => '',
-    'label' => ucwords(preg_replace('/_/', ' ', $column_name)),
-    'is_required' => 0,
-    'storage' => 'field_chado_storage',
-    'widget_settings' => array(
-      'display_label' => 1
-    ),
-    'field_settings' => array(
-      // The table in Chado where this field maps to.
-      'chado_table' => $table_name,
-      // The column in the Chado table that this field maps to.
-      'chado_column' => $column_name,
-      'semantic_web' => array(
-        // The type is the term from a vocabulary that desribes this field..
-        'type' => '',
-        // The namepsace for the vocabulary (e.g. 'foaf').
-        'ns' => '',
-        // The URL for the namespace.  It must be that the type can be
-        // appended to the URL.
-        'nsurl' => '',
-      ),
-    ),
-  );
-
-  // Alter the field info array depending on the column details.
-  switch($details['type']) {
-    case 'char':
-      $field['field_type'] = 'text';
-      $field['widget_type'] = 'text_textfield';
-      $field['field_settings']['max_length'] = $details['length'];
-      break;
-    case 'varchar':
-      $field['field_type'] = 'text';
-      $field['widget_type'] = 'text_textfield';
-      $field['field_settings']['max_length'] = $details['length'];
-      break;
-    case 'text':
-      $field['field_type'] = 'text';
-      $field['widget_type'] = 'text_textarea';
-      $field['field_settings']['max_length'] = 17179869184;
-      $field['field_settings']['text_processing'] = 1;
-      $field['format'] = filter_default_format();
-      break;
-    case 'blob':
-      // not sure how to support a blob field.
-      continue;
-      break;
-    case 'int':
-      $field['field_type'] = 'number_integer';
-      $field['widget_type'] = 'number';
-      break;
-    case 'float':
-      $field['field_type'] = 'number_float';
-      $field['widget_type'] = 'number';
-      $field['field_settings']['precision'] = 10;
-      $field['field_settings']['scale'] = 2;
-      $field['field_settings']['decimal_separator'] = '.';
-      break;
-    case 'numeric':
-      $field['field_type'] = 'number_decimal';
-      $field['widget_type'] = 'number';
-      break;
-    case 'serial':
-      // Serial fields are most likely not needed as a field.
-      break;
-    case 'boolean':
-      $field['field_type'] = 'list_boolean';
-      $field['widget_type'] = 'options_onoff';
-      $field['field_settings']['allowed_values'] = array(0 => "No", 1 => "Yes");
-      break;
-    case 'datetime':
-      // Use the Drupal Date and Date API to create the field/widget
-      $field['field_type'] = 'datetime';
-      $field['widget_type'] = 'date_select';
-      $field['widget_settings']['increment'] = 1;
-      $field['widget_settings']['tz_handling'] = 'none';
-      $field['widget_settings']['collapsible'] = TRUE;
-
-      // TODO: Add settings so that the minutes increment by 1.
-      // And turn off the timezone, as the Chado field doesn't support it.
-      break;
-  }
 
-  // Set some default semantic web information
-  if ($column_name == 'name') {
-    $field['field_settings']['semantic_web']['type'] = 'name';
-    $field['field_settings']['semantic_web']['ns'] = 'foaf';
-    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
-  }
-  if ($column_name == 'description' or $column_name == 'definition' or
-      $column_name == 'comment') {
-    $field['field_settings']['semantic_web']['type'] = 'description';
-    $field['field_settings']['semantic_web']['ns'] = 'hydra';
-    $field['field_settings']['semantic_web']['nsurl'] = 'http://www.w3.org/ns/hydra/core#';
-  }
 
-  //
-  // GENERIC COLUMNS
-  //
-  if ($field['field_settings']['chado_column'] =='organism_id') {
-    $field['field_type'] = 'organism_id';
-    $field['widget_type'] = 'tripal_chado_organism_select_widget';
-    $field['label'] = 'Organism';
-    $field['description'] = 'Select an organism.';
-  }
-  elseif ($field['field_settings']['chado_column'] =='dbxref_id') {
-    $field['field_type'] = 'dbxref_id';
-    $field['widget_type'] = 'tripal_chado_primary_dbxref_widget';
-    $field['label'] = 'Cross Reference';
-    $field['description'] = 'This record can be cross referenced with a ' .
-      'record in another online database. The primary reference is for the ' .
-      'most prominent reference.  At a minimum, the database and accession ' .
-      'must be provided.  To remove a set reference, change the database ' .
-      'field to "Select a Database".';
-  }
-  elseif ($field['label'] == 'Timeaccessioned') {
-    $field['label'] = 'Time Accessioned';
-    $field['description'] = 'Please enter the time that this record was first added to the database.';
-  }
-  elseif ($field['label'] == 'Timelastmodified') {
-    $field['label'] = 'Time Last Modified';
-    $field['description'] = 'Please enter the time that this record was last modified. The default is the current time.';
-  }
-  //
-  // ORGANISM TABLE
-  //
-  elseif ($field['field_settings']['chado_table'] == 'organism' and $field['field_settings']['chado_column'] == 'comment') {
-    $field['label'] = 'Description';
-  }
-  //
-  // FEATURE TABLE
-  //
-  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'uniquename') {
-    $field['field_type'] = 'text';
-    $field['widget_type'] = 'text_textfield';
-    $field['field_settings']['text_processing'] = 0;
-    $field['field_settings']['semantic_web']['type'] = 'name';
-    $field['field_settings']['semantic_web']['ns'] = 'foaf';
-    $field['field_settings']['semantic_web']['nsurl'] = 'http://xmlns.com/foaf/0.1/';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'md5checksum') {
-    $field['field_type'] = 'md5checksum';
-    $field['widget_type'] = 'tripal_chado_md5checksum_checkbox_widget';
-    $field['label'] = 'MD5 Checksum';
-    $field['description'] = 'Generate an MD5 checksum for the sequence.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'seqlen') {
-    $field['field_type'] = 'seqlen';
-    $field['widget_type'] = 'tripal_chado_seqlen_hidden_widget';
-    $field['label'] = 'Seqlen';
-    $field['description'] = 'The length of the residues.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'feature' and $field['field_settings']['chado_column'] == 'residues') {
-    $field['field_type'] = 'residues';
-    $field['widget_type'] = 'tripal_chado_residues_textarea_widget';
-    $field['label'] = 'Residues';
-    $field['description'] = 'Please provide an IUPAC compatible residues for this feature. Spaces and new lines are allowed.';
-  }
-  //
-  // ANALYSIS TABLE
-  //
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'program') {
-    $field['field_settings']['semantic_web']['type'] = 'SoftwareApplication';
-    $field['field_settings']['semantic_web']['ns'] = 'schema';
-    $field['field_settings']['semantic_web']['nsurl'] = 'https://schema.org/';
-    $field['description'] = 'The program name (e.g. blastx, blastp, sim4, genscan. If the analysis was not derived from a software package then provide a very brief description of the pipeline, workflow or method.';
-    $field['label'] = 'Program, Pipeline, Workflow or Method Name.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourceuri') {
-    $field['field_type'] = 'text';
-    $field['widget_type'] = 'text_textfield';
-    $field['field_settings']['text_processing'] = 0;
-    $field['label'] = 'Source URL';
-    $field['description'] = 'The URL where the original source data was derived.  Ideally, this should link to the page where more information about the source data can be found.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourcename') {
-    $field['label'] = 'Source Name';
-    $field['description'] = 'The name of the source data. This could be a file name, data set or a small description for how the data was collected. For long descriptions use the larger description field.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'sourceversion') {
-    $field['label'] = 'Source Version';
-    $field['description'] = 'If hte source data set has a version include it here.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'algorithm') {
-    $field['label'] = 'Source Version';
-    $field['description'] = 'The name of the algorithm used to produce the dataset if different from the program.';
-  }
-  elseif ($field['field_settings']['chado_table'] == 'analysis' and $field['field_settings']['chado_column'] == 'programversion') {
-    $field['label'] = 'Program Version';
-    $field['description'] = 'The version of the program used to perform this analysis. (e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]. Enter "n/a" if no version is available or applicable.';
-  }
-  //
-  // PROJECT TABLE
-  //
-  elseif ($field['field_settings']['chado_table'] == 'project' and $field['field_settings']['chado_column'] == 'description') {
-    $field['label'] = 'Short Description';
-  }
-  return $field;
-}
 
 /**
  * Implements hook_exclude_type_by_default()

+ 184 - 22
tripal_ws/includes/tripal_ws.rest.inc

@@ -113,9 +113,11 @@ function tripal_ws_handle_vocab_service($api_url, &$response, $ws_args) {
   }
   // If we don't have a $namespace then show a paged list of terms.
   else if ($namespace and !$accession) {
+    tripal_ws_get_vocab($api_url, $response, $ws_args, $namespace);
   }
   // If we have a content type and an entity ID then show the entity
-  else {
+  else if ($namespace and $accession) {
+    tripal_ws_get_term($api_url, $response, $ws_args, $namespace, $accession);
   }
 }
 
@@ -138,11 +140,22 @@ function tripal_ws_get_vocabs($api_url, &$response) {
   $response['label'] = 'Content Types';
   $response['member'] = array();
 
-  // TODO: determine how to get the list of in-house terms that are used
-  // on the site.  This should really only include terms that are used
-  // as TripalEntity bundle types and that aren't part of another published
-  // vocabulary.
-
+  $vocabs = db_select('tripal_vocab', 'tv')
+    ->fields('tv')
+    ->execute();
+  // Iterate through the vocabularies and add an entry in the collection.
+  $i = 0;
+  while ($vocab = $vocabs->fetchObject()) {
+    $term =
+    // Add the bundle as a content type.
+    $response['member'][] = array(
+      '@id' => $api_url . '/vocab/' . urlencode($vocab->namespace),
+      '@type' => 'vocabulary',
+      'namespace' => $vocab->namespace,
+    );
+    $i++;
+  }
+  $response['totalItems'] = $i;
 
   //$response['totalItems'] = $i;
 
@@ -153,6 +166,100 @@ function tripal_ws_get_vocabs($api_url, &$response) {
   $response['@context']['label'] = 'rdfs:label';
   $response['@context']['description'] = 'hydra:description';
 }
+
+/**
+ *
+ * @param $api_url
+ * @param $response
+ * @param $ws_args
+ */
+function tripal_ws_get_vocab($api_url, &$response, $ws_args, $namespace) {
+
+  // First, add the vocabularies used into the @context section.
+  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
+  $response['@context']['schema'] = 'https://schema.org/';
+
+  // Next add in the ID for tihs resource.
+  $response['@id'] = $api_url . '/vocab/' . $namespace;
+
+  // Get the vocabulary
+  $vocab = tripal_load_vocab_entity(array('namespace' => $namespace));
+
+  // Start the list.
+  $response['@type'] = 'Collection';
+  $response['totalItems'] = 0;
+  $response['label'] = $namespace . " vocabulary collection";
+  $response['comment'] = 'The following list of terms may not be the full ' .
+    'list for the vocabulary.  The terms listed here are only those ' .
+    'that have associated content on this site.';
+
+  // Get the list of terms for this vocab.
+  $query = db_select('tripal_term', 'tt')
+    ->fields('tt', array('id'))
+    ->condition('vocab_id', $vocab->id)
+    ->orderBy('accession', 'DESC');
+
+  // Iterate through the entities and add them to the list.
+  $terms = $query->execute();
+  $i = 0;
+  while($term = $terms->fetchObject()) {
+    $term = tripal_load_term_entity(array('term_id' => $term->id));
+    $response['member'][] = array(
+      '@id' => $api_url . '/vocab/' . urlencode($namespace) . '/' .  urlencode($term->accession),
+      '@type' => 'vocabulary_term',
+      'namespace' => $vocab->namespace,
+      'accession' => $term->accession,
+      'name' => $term->name,
+      'definition' => $term->definition,
+    );
+    $i++;
+  }
+  $response['totalItems'] = $i;
+
+  // Lastly, add in the terms used into the @context section.
+  $response['@context']['Collection'] = 'hydra:Collection';
+  $response['@context']['totalItems'] = 'hydra:totalItems';
+  $response['@context']['member'] = 'hydra:member';
+  $response['@context']['label'] = 'rdfs:label';
+  $response['@context']['comment'] = 'rdfs:comment';
+  $response['@context']['itemPage'] = 'schema:itemPage';
+
+}
+
+/**
+ *
+ * @param $api_url
+ * @param $response
+ * @param $ws_args
+ */
+function tripal_ws_get_term($api_url, &$response, $ws_args, $namespace, $accession) {
+
+  // First, add the vocabularies used into the @context section.
+  $response['@context']['rdfs'] = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
+  $response['@context']['hydra'] = 'http://www.w3.org/ns/hydra/core#';
+  $response['@context']['schema'] = 'https://schema.org/';
+
+  // Get the term.
+  $term = tripal_load_term_entity(array('namespace' => $namespace, 'accession' => $accession));
+
+  // Next add in the ID and Type for this resources.
+  $response['@id'] = $api_url . '/vocab/' . urlencode($namespace) . '/' . urlencode($accession);
+  $response['@type'] = 'vocabulary_term';
+  $response['label'] = $term->name;
+  $response['namespace'] = $namespace;
+  $response['accession'] = $accession;
+  $response['name'] = $term->name;
+  $response['definition'] = $term->definition;
+
+  if ($term->url) {
+    $response['URL'] = $term->url;
+  }
+
+  // Lastly, add in the terms used into the @context section.
+  $response['@context']['label'] = 'rdfs:label';
+  $response['@context']['itemPage'] = 'schema:itemPage';
+}
 /**
  * Provides a collection (list) of all of the content types.
  *
@@ -205,7 +312,7 @@ function tripal_ws_get_content_types($api_url, &$response) {
     }
     // Add the bundle as a content type.
     $response['member'][] = array(
-      '@id' => $api_url . '/content/' . $bundle->label,
+      '@id' => $api_url . '/content/' . urlencode($bundle->label),
       '@type' => $vocab->namespace . ':' . $term->accession,
       'label' => $bundle->label,
       'description' => $description,
@@ -273,7 +380,7 @@ function tripal_ws_get_content_type($api_url, &$response, $ws_args, $ctype) {
     $entities = entity_load('TripalEntity', array_keys($results['TripalEntity']));
     foreach ($entities as $entity) {
       $response['member'][] = array(
-        '@id' => $api_url . '/content/' . $ctype . '/' .  $entity->id,
+        '@id' => $api_url . '/content/' . urlencode($ctype) . '/' .  $entity->id,
         '@type' => $vocab->namespace . ':' . $term->accession,
         'label' => $entity->title,
         'itemPage' => url('/bio-data/' . $entity->id, array('absolute' => TRUE)),
@@ -351,9 +458,21 @@ function tripal_ws_get_content($api_url, &$response, $ws_args, $ctype, $entity_i
   // Iterate throught the fields and add each value to the response.
   //$response['fields'] = $fields;
   foreach ($fields as $field_name => $field) {
-    $field_value = $entity->$field_name;
+    $field_info = field_info_field($field_name);
+    $items = field_get_items('TripalEntity', $entity, $field_name);
 
-    // Get the semantic web settings for this field
+    // By default, the label for the key in the output should be the
+    // term from the vocabulary that the field is assigned. But in the
+    // case that the field is not assigned a term, we must use the field name.
+    $key = $field['field_name'];
+    if (array_key_exists('semantic_web', $field['settings'])) {
+      if (array_key_exists('type', $field['settings']['semantic_web']) and
+          $field['settings']['semantic_web']['type']) {
+        $key = $field['settings']['semantic_web']['type'];
+      }
+    }
+
+    // Get the semantic web settings for this field.
     $field_type = '';
     if (array_key_exists('semantic_web', $field['settings'])) {
       $field_type = $field['settings']['semantic_web']['type'];
@@ -361,26 +480,69 @@ function tripal_ws_get_content($api_url, &$response, $ws_args, $ctype, $entity_i
         $ns = $field['settings']['semantic_web']['ns'];
         $nsurl = $field['settings']['semantic_web']['nsurl'];
         $response['@context'][$ns] = $nsurl;
-        $response['@context'][$field['label']] = $ns . ':' .$field_type;
+        $response['@context'][$key] = $ns . ':' .$field_type;
       }
     }
 
-    // TODO: need a way to hide fields.
+    // Skip hidden fields.
+    if ($field['display']['default']['type'] == 'hidden') {
+      continue;
+    }
 
-    // Get the values based on cardinality
-    $items = field_get_items('TripalEntity', $entity, $field_name);
-    $values = '';
-    if (array_key_exists('und', $field_value) and count($field_value['und']) == 1) {
-      $values = $field_value['und'][0]['value'];
+    // We want to allow the field to format itself for web services if it
+    // wants to.  The file can do so if it implements a '_ws_formatter'
+    // function.
+    $function = $field_info['type'] . '_ws_formatter';
+    module_load_include('inc', $field['display']['default']['module'], 'includes/fields/' . $field_info['type']);
+    if (function_exists($function)) {
+      $values = array();
+      $function($values, $entity->type, $entity, $field_info, $field, $items);
+
+      // Iterate through the key/values returned and handle hashed keys
+      $add_vals = array();
+      for ($i = 0; $i < count($values); $i++) {
+        foreach ($values[$i] as $skey => $svalue) {
+          // If the key is '#entity' then this should like to another
+          // services.
+          if ($skey == '#entity') {
+            $sentity = $svalue;
+            $sbundle = tripal_load_bundle_entity(array('name' => $sentity->bundle));
+            $sterm = tripal_load_term_entity(array('term_id' => $sbundle->term_id));
+            $vocab = $sterm->vocab;
+            $add_vals['@id'] = $api_url . '/content/' . urlencode($sterm->name) . '/' . $sentity->id;
+            $add_vals['@type'] = $vocab->namespace . ':' . $sterm->name;
+            unset($values[$i][$skey]);
+          }
+        }
+        $values[$i] = array_merge($add_vals, $values[$i]);
+      }
+
+
+      if (count($values) == 0) {
+        $response[$key] = '';
+      }
+      if (count($values) == 1) {
+        $response[$key] = $values[0];
+      }
+      else {
+        $response[$key] = $values;
+      }
     }
-    // If cardinality is greater than 1 then the value should be an array
+    // If a function doesn't exist then just show the default value is is.
     else {
-      $values = array();
-      for ($i = 0; $i < count($field_value); $i++) {
-        $values[] = $field_value['und'][$i]['value'];
+      // Get the values based on cardinality
+      if (count($items) == 1) {
+        $values = $items[0]['value'];
+      }
+      // If cardinality is greater than 1 then the value should be an array
+      else {
+        $values = array();
+        for ($i = 0; $i < count($items); $i++) {
+          $values[] = $items[$i]['value'];
+        }
       }
+      $response[$key] = $values;
     }
-    $response[$field['label']] = $values;
   }
 
  //$response['fields'] = $fields;