Quellcode durchsuchen

Fixed merge conflict

Stephen Ficklin vor 8 Jahren
Ursprung
Commit
6990ac893c

+ 38 - 37
tripal/api/tripal.entities.api.inc

@@ -165,29 +165,38 @@ function tripal_load_bundle_entity($values) {
  *
  * @param $bundle
  *   The newly created TripalBundle object.
+ * @param $storage_args
+ *   Arguments passed to the storage backend for this bundle.  These arguments
+ *   typically provide details for how to associate this bundle with records
+ *   in the storage system.  Each storage system will have its own set of
+ *   arguments that it will expect.
  */
-function hook_bundle_create(&$bundle) {
+function hook_bundle_create(&$bundle, $storage_args) {
 
 }
 /**
  * Creates a new Tripal Entity type (i.e. bundle).
  *
- * @param $vocabulary
- *   The abbreviated vocabulary for the vocabulary (e.g. RO, SO, PATO).
- * @param $accession
- *   The unique term ID in the vocabulary $vocabulary (i.e. an accession).
- * @param $term_name
- *   A human-readable name for this term.  This will became the name that
- *   appears for the content type.  In practice, this should be the name
- *   of the term. (E.g. the name for SO:0000704 is gene).
+ * @param $args
+ *   An array of arguments that must include the following keys:
+ *     - vocabulary:  The abbreviated vocabulary for the vocabulary
+ *       (e.g. RO, SO, PATO).
+ *     - accession:  The unique term ID in the vocabulary $vocabulary
+ *       (i.e. an accession).
+ *     - term_name: A human-readable name for this term.  This will became
+ *       the name that appears for the content type.  In practice, this
+ *       should be the name of the term. (E.g. the name for SO:0000704 is gene).
  * @param $error
  *  A string, passed by reference, that is filled with the error message
  *  if the function fails.
- *
  * @return
- *  TRUE if the entity type (bundle) was succesfully created.  FALSE otherwise.
+ *   The bundle object or FALSE if failure.
  */
-function tripal_create_bundle($vocabulary, $accession, $term_name, &$error = '') {
+function tripal_create_bundle($args, &$error = '') {
+  $vocabulary = $args['vocabulary'];
+  $accession = $args['accession'];
+  $term_name = $args['term_name'];
+  $storage_args = $args['storage_args'];
 
   $transaction = db_transaction();
   try {
@@ -204,8 +213,8 @@ function tripal_create_bundle($vocabulary, $accession, $term_name, &$error = '')
       'accession' => $accession
     ));
     if (!$term) {
-      $args = array('vocab_id' => $vocab->id, 'accession' => $accession, 'name' => $term_name);
-      $term = entity_get_controller('TripalTerm')->create($args);
+      $targs = array('vocab_id' => $vocab->id, 'accession' => $accession, 'name' => $term_name);
+      $term = entity_get_controller('TripalTerm')->create($targs);
       $term = $term->save();
     }
 
@@ -229,7 +238,7 @@ function tripal_create_bundle($vocabulary, $accession, $term_name, &$error = '')
     $modules = module_implements('bundle_create');
     foreach ($modules as $module) {
       $function = $module . '_bundle_create';
-      $function($bundle);
+      $function($bundle, $storage_args);
     }
 
     // Clear the entity cache so that Drupal will read our
@@ -251,7 +260,7 @@ function tripal_create_bundle($vocabulary, $accession, $term_name, &$error = '')
     return FALSE;
   }
 
-  return TRUE;
+  return $bundle;
 }
 
 /**
@@ -480,19 +489,19 @@ function tripal_set_bundle_variable($variable_name, $bundle_id, $value) {
 /**
  * Get Page Title Format for a given Tripal Entity Type.
  *
- * @param TripalBundle $entity
+ * @param TripalBundle $bundle
  *   The Entity object for the Tripal Bundle the title format is for.
  */
-function tripal_get_title_format($entity) {
+function tripal_get_title_format($bundle) {
 
   // Get the existing title format if it exists.
-  $title_format = tripal_get_bundle_variable('title_format', $entity->id);
+  $title_format = tripal_get_bundle_variable('title_format', $bundle->id);
 
   // If there isn't yet a title format for this bundle/type then we should
   // determine the default.
   if (!$title_format) {
-    $title_format = tripal_get_default_title_format($entity);
-    tripal_save_title_format($entity, $title_format);
+    $title_format = tripal_get_default_title_format($bundle);
+    tripal_save_title_format($bundle, $title_format);
   }
 
   return $title_format;
@@ -514,22 +523,22 @@ function tripal_save_title_format($entity, $format) {
 /**
  * Determine the default title format to use for an entity.
  *
- * @param TripalBundle $entity
+ * @param TripalBundle $bundle
  *   The Entity object for the Tripal Bundle that the title format is for.
  *
  * @return string
  *   A default title format.
  */
-function tripal_get_default_title_format($entity) {
+function tripal_get_default_title_format($bundle) {
   $format = '';
 
   // Retrieve all available tokens.
-  $tokens = tripal_get_entity_tokens($entity);
+  $tokens = tripal_get_entity_tokens($bundle);
 
   // A) Check to see if more informed modules have suggested a title for this
   //    type. Invoke hook_tripal_default_title_format() to get all suggestions
   //    from other modules.
-  $suggestions = module_invoke_all('tripal_default_title_format', $entity, $tokens);
+  $suggestions = module_invoke_all('tripal_default_title_format', $bundle, $tokens);
   if ($suggestions) {
     // Use the suggestion with the lightest weight.
     $lightest_key = NULL;
@@ -541,15 +550,7 @@ function tripal_get_default_title_format($entity) {
     return $format;
   }
 
-  // B) Check to see if any fields contain "name" in the machine name and if
-  //    so, use them.
-  $name_fields = preg_grep('/name/', array_keys($tokens));
-  if ($name_fields AND !$format) {
-    $format = implode(', ', $name_fields);
-    return $format;
-  }
-
-  // C) Generate our own ugly title by simply comma-separating all the
+  // B) Generate our own ugly title by simply comma-separating all the
   //    required fields.
   if (!$format) {
     $tmp = array();
@@ -570,7 +571,7 @@ function tripal_get_default_title_format($entity) {
 /**
  * Implement this hook to define default formats for Tripal Content Types.
  *
- * @param TripalBundle $entity
+ * @param TripalBundle $bundle
  *   A tripal content type entity with information to be used for determining the default title format.
  * @param array $available_tokens
  *   An array of available tokens for this particular tripal content type.
@@ -587,13 +588,13 @@ function tripal_get_default_title_format($entity) {
  *    - format: a string including approved tokens used to determine the title
  *        on Tripal content pages.
  */
-function hook_tripal_default_title_format($entity, $available_tokens) {
+function hook_tripal_default_title_format($bundle, $available_tokens) {
   $format = array();
 
   // If you want to suggest a default format for a particular vocabulary term:
   //---------------------------------------------------------------------------
   // Load the term associated with this Tripal Content type.
-  $term = entity_load('TripalTerm', array('id' => $entity->term_id));
+  $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
   $term = reset($term);
 
   // If it's the term you are interested in then suggest a format.

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

@@ -29,7 +29,7 @@
 function hook_vocab_storage_info() {
   return array(
     'term_chado_storage' => array(
-      'label' => t('Chado storage'),
+      'label' => t('Chado'),
       'description' => t('Integrates terms stored in the local Chado database with Tripal entities.'),
       'settings' => array(),
     ),

+ 14 - 11
tripal/includes/TripalBundleController.inc

@@ -32,7 +32,7 @@ class TripalBundleController extends EntityAPIControllerExportable {
     $modules = module_implements('bundle_create');
     foreach ($modules as $module) {
       $function = $module . '_bundle_create';
-      $function($bundle);
+      $function($bundle, array());
     }
 
     return $bundle;
@@ -45,19 +45,19 @@ class TripalBundleController extends EntityAPIControllerExportable {
    * @param DatabaseTransaction $transaction
    */
   public function delete($ids, DatabaseTransaction $transaction = NULL) {
-    $entities = $ids ? $this->load($ids) : FALSE;
+    $bundles = $ids ? $this->load($ids) : FALSE;
 
     if (!$transaction) {
       $transaction = db_transaction();
     }
 
-    if ($entities) {
+    if ($bundles) {
 
-      foreach ($entities as $id => $entity) {
+      foreach ($bundles as $id => $bundle) {
 
         // Find any TripalEntity fields that are attached to this bundle and
         // remove them.
-        $instances = field_info_instances('TripalEntity', $entity->name);
+        $instances = field_info_instances('TripalEntity', $bundle->name);
         foreach ($instances as $instance) {
           // Mark the instance as deleted and purge it.
           $field = field_info_field($instance['field_name']);
@@ -67,18 +67,18 @@ class TripalBundleController extends EntityAPIControllerExportable {
           // If the field has no more instances then purge it too.
           if (count($field['bundles']) == 1 and
               count($field['bundles']['TripalEntity']) == 1 and
-              in_array($entity->name, $field['bundles']['TripalEntity'])) {
+              in_array($bundle->name, $field['bundles']['TripalEntity'])) {
             field_purge_field($field);
           }
         }
 
         // TODO: this Chado sepcific code needs to be moved out of here!
 
-        // Remove the entries in the chado_entity and tripal_entity t
+        // Remove the entries in the chado_entity and tripal_entity
         $query = db_select('chado_entity', 'ce');
         $query->join('tripal_entity', 'te', 'te.id = ce.entity_id');
         $records = $query->fields('ce', array('chado_entity_id', 'data_table', 'record_id'))
-          ->condition('te.bundle', $entity->name)
+          ->condition('te.bundle', $bundle->name)
           ->execute();
         $num_removed = 0;
         while ($record = $records->fetchObject()) {
@@ -90,13 +90,16 @@ class TripalBundleController extends EntityAPIControllerExportable {
             ->execute();
           $num_removed++;
         }
+        db_delete('chado_bundle')
+          ->condition('bundle_id', $bundle->id)
+          ->execute();
         if ($num_removed > 0) {
           drupal_set_message(t('Removed %num records', array('%num' => $num_removed)));
         }
 
         // Remove the terms for the bundles that are to be deleted.
         db_delete('tripal_term')
-          ->condition('id', $entity->term_id)
+          ->condition('id', $bundle->term_id)
           ->execute();
       }
 
@@ -106,8 +109,8 @@ class TripalBundleController extends EntityAPIControllerExportable {
       // Not sure what this does, but copied from the
       // EntityAPIControllerExportable->delete() function which this one
       // overrides.
-      foreach ($entities as $id => $entity) {
-        if (entity_has_status($this->entityType, $entity, ENTITY_IN_CODE)) {
+      foreach ($bundles as $id => $bundle) {
+        if (entity_has_status($this->entityType, $bundle, ENTITY_IN_CODE)) {
           entity_defaults_rebuild(array($this->entityType));
           break;
         }

+ 510 - 42
tripal/includes/TripalBundleUIController.inc

@@ -33,8 +33,6 @@ class TripalBundleUIController extends EntityDefaultUIController {
       'page callback' => 'drupal_get_form',
       'page arguments' => array('tripal_admin_add_type_form'),
       'access arguments' => array('administer tripal data types'),
-      'file' =>  'includes/tripal.admin.inc',
-      'file path' => drupal_get_path('module', 'tripal'),
       'type' => MENU_LOCAL_ACTION,
       'weight' => 2
     );
@@ -73,10 +71,10 @@ class TripalBundleUIController extends EntityDefaultUIController {
  */
 function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
 
-  $entity_type = $form_state['build_info']['args'][0];
+  $bundle = $form_state['build_info']['args'][0];
   $term = NULL;
   $vocab = NULL;
-  if (preg_match('/bio_data_(\d+)/', $entity_type->name, $matches)) {
+  if (preg_match('/bio_data_(\d+)/', $bundle->name, $matches)) {
     $term = entity_load('TripalTerm', array('id' => $matches[1]));
     $term = reset($term);
     $vocab = entity_load('TripalVocab', array('id' => $term->vocab_id));
@@ -86,6 +84,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
   // Add a validate and submit handler to save the data in this form.
   $form['#validate'] = array('tripal_tripal_bundle_form_validate');
   $form['#submit'] = array('tripal_tripal_bundle_form_submit');
+  $form['#bundle'] = $bundle;
 
   // @TODO: Move this into a css file.
   $form['#attached']['css'] = array(
@@ -149,7 +148,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
       displayed as part of the list on the <em>Add new content page</em>. It is recommended that
       this name begin with a capital letter and contain only letters, numbers, and spaces.
       This name must be unique.'),
-    '#default_value' => $entity_type->label,
+    '#default_value' => $bundle->label,
   );
 
   $form['description'] = array(
@@ -172,7 +171,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
 
   // Set Title Format.
   //-------------------------
-  $title_format = tripal_get_title_format($entity_type);
+  $title_format = tripal_get_title_format($bundle);
 
   $form['set_titles'] = array(
     '#type' => 'fieldset',
@@ -192,7 +191,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
       <p>Keep in mind that it might be confusing to users if more than
       one page has the same title. We recommend you <strong>choose a combination of tokens that
       will uniquely identify your content</strong>.</p>',
-      array('%type' => $entity_type->label)),
+      array('%type' => $bundle->label)),
   );
 
   $form['set_titles']['title_format'] = array(
@@ -214,7 +213,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
     '#collapsed' => TRUE
   );
 
-  $tokens = tripal_get_entity_tokens($entity_type);
+  $tokens = tripal_get_entity_tokens($bundle);
   $form['set_titles']['tokens'] = array(
     '#type' => 'hidden',
     '#value' => serialize($tokens)
@@ -227,7 +226,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
 
   // Set URL Alias Pattern.
   //-------------------------
-  $url_pattern = tripal_get_bundle_variable('url_format', $entity_type->id, '');
+  $url_pattern = tripal_get_bundle_variable('url_format', $bundle->id, '');
   if (!$url_pattern) $url_pattern = str_replace(' ', '', $term->name) . '/[TripalEntity__entity_id]';
 
   $form['url'] = array(
@@ -245,7 +244,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
     This allows you to present more friendly, informative URLs to your user.</p>
     <p><strong>You must choose a combination of tokens that results in a unique path for
     each page!</strong></p>',
-    array('%type' => $entity_type->label)),
+    array('%type' => $bundle->label)),
   );
 
   $form['url']['url_pattern'] = array(
@@ -259,7 +258,7 @@ function tripal_tripal_bundle_form($form, &$form_state, $entityDataType) {
     '#rows' => 1
   );
 
-  $tokens = tripal_get_entity_tokens($entity_type, array('required only' => TRUE));
+  $tokens = tripal_get_entity_tokens($bundle, array('required only' => TRUE));
   $form['url']['tokens'] = array(
     '#type' => 'hidden',
     '#value' => serialize($tokens)
@@ -405,24 +404,429 @@ function tripal_bundle_access($op, $type = NULL, $account = NULL) {
  */
 function tripal_admin_add_type_form($form, &$form_state) {
 
-  // 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.
+  // TODO: the Chado specific function calls should not be here.  They need
+  // to be genericized.
   $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_select_term_form';
-    if (function_exists($function)) {
-      $form = $function($form, $form_state);
-    }
-  }
-  else {
+  if (!is_array($stores) or count($stores) == 0) {
     tripal_set_message('A storage backend is not enabled for managing
           the vocabulary terms used to create content.  Please enable
           a module that supports storage of vocabualary terms (e.g. tripal_chado)
           and return to create new Tripal content types.', TRIPAL_NOTICE);
+    return;
+  }
+  $keys = array_keys($stores);
+  $module = $stores[$keys[0]]['module'];
+  $function = $module . '_vocab_select_term_form';
+  if (function_exists($function)) {
+    $form = $function($form, $form_state);
   }
+
+  $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.
+  $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/chado/auto_name/cvterm/",
+  );
+  $form['select_button'] = array(
+    '#type' => 'submit',
+    '#value' => t('Lookup Term'),
+    '#name' => 'select_cvterm',
+    '#ajax' => array(
+      'callback' => "tripal_admin_add_type_form_ajax_callback",
+      'wrapper' => "tripal-vocab-select-form",
+      'effect' => 'fade',
+      'method' => 'replace'
+    ),
+  );
+
+  if ($term_name) {
+    $submit_disabled = TRUE;
+    $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.')
+    );
+    $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;
+    $selected_term_id = '';
+    foreach ($terms as $term) {
+      // Save the user a click by setting the default value as 1 if there's
+      // only one matching term.
+      $default = FALSE;
+      $attrs = array();
+      if ($num_terms == 0 and count($terms) == 1) {
+        $default = TRUE;
+        $attrs = array('checked' => 'checked');
+      }
+      $term_element_name = 'term-' . $term->cvterm_id;
+      $form['terms_list'][$term_element_name] = array(
+        '#type' => 'checkbox',
+        '#title' =>  $term->name,
+        '#default_value' => $default,
+        '#attributes' => $attrs,
+        '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
+        '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '.  ' .
+        '<br><b>Definition:</b>  ' . $term->definition,
+        '#ajax' => array(
+          'callback' => "tripal_admin_add_type_form_ajax_callback",
+          'wrapper' => "tripal-vocab-select-form",
+          'effect' => 'fade',
+          'method' => 'replace'
+        ),
+      );
+
+      if (array_key_exists('values', $form_state) and array_key_exists($term_element_name, $form_state['values']) and
+          $form_state['values'][$term_element_name] == 1) {
+        $selected_term_id = $term->cvterm_id;
+      }
+      $num_terms++;
+    }
+    if ($num_terms == 0) {
+      $form['terms_list']['none'] = array(
+        '#type' => 'item',
+        '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>'
+      );
+    }
+    $form['storage'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Storage Settings'),
+      '#description' => t('The primary record for each content of this type
+          must be stored in a single storage backend. Please select the
+          storage method and settings for this content type.')
+    );
+
+    // TODO: we should have a default store setting so that user's have
+    // fewer clicks.
+
+    // TODO: there should be a way for each storage backend to determine if
+    // it can handle the content type.  Maybe certain content types aren't
+    // yet supported by every sotrage backend.
+    $default_store = 'term_chado_storage';
+    $store_options = array(0 => '-- Select --');
+    foreach ($stores as $store_type => $store) {
+      $store_options[$store_type] = $store['label'];
+    }
+    if (array_key_exists('values', $form_state) and
+        array_key_exists('store_select', $form_state['values'])) {
+      $default_store = $form_state['values']['store_select'];
+    }
+    $form['storage']['store_select'] = array(
+      '#type' => 'select',
+      '#title' => 'Storage backend',
+      '#options' => $store_options,
+      '#default_value' => $default_store,
+      '#ajax' => array(
+        'callback' => "tripal_admin_add_type_form_ajax_callback",
+        'wrapper' => "tripal-vocab-select-form",
+        'effect' => 'fade',
+        'method' => 'replace'
+      ),
+      '#description' => 'Select a storage background for this content type.'
+    );
+
+    if ($default_store) {
+      if ($store_type == 'term_chado_storage') {
+        // TODO: Move this Chado-specific code out of there.
+        $default_table = '';
+        if (array_key_exists('base_chado_table', $form_state['values'])
+            and $form_state['values']['base_chado_table']) {
+          $default_table = $form_state['values']['base_chado_table'];
+        }
+        else {
+          $mapped_table = chado_get_cvterm_mapping(array('cvterm_id' => $selected_term_id));
+          if ($mapped_table) {
+            $default_table = $mapped_table->chado_table;
+          }
+        }
+
+        $form['storage'][$store_type]['selected_cvterm_id'] = array(
+          '#type' => 'value',
+          '#value' => $selected_term_id,
+        );
+
+        $base_tables = chado_get_base_tables();
+        $options = array(0 => '-- Select table --');
+        foreach ($base_tables AS $tablename) {
+          $options[$tablename] = $tablename;
+        }
+        $form['storage'][$store_type]['base_chado_table'] = array(
+          '#type' => 'select',
+          '#title' => 'Chado table',
+          '#options' => $options,
+          '#description' => 'Select the Chado table into which the primary records for this content type will be stored.',
+          '#default_value' => $default_table,
+          '#ajax' => array(
+            'callback' => "tripal_admin_add_type_form_ajax_callback",
+            'wrapper' => "tripal-vocab-select-form",
+            'effect' => 'fade',
+            'method' => 'replace'
+          ),
+        );
+        $form_state['input']['base_chado_table'] = $default_table;
+
+        if ($default_table) {
+
+          // There are a few places in form below where the type columns
+          // can be used to identify a record. So, we'll create the
+          // select options here so it can be used multiple places.  We
+          // only want to include in our column options those fields that
+          // are foreign keys to the cvterm table
+          $schema = chado_get_schema($default_table);
+          $column_options = array('0' => '--Select column--');
+          $cvt_fkeys = array_keys($schema['foreign keys']['cvterm']['columns']);
+          foreach ($schema['fields'] as $column_name => $column_details) {
+            if (in_array($column_name, $cvt_fkeys)) {
+              $column_options[$column_name] = $column_name;
+            }
+          }
+
+          $default_has_all = 'No';
+          if (array_key_exists('chado_table_has_all', $form_state['values'])
+              and $form_state['values']['chado_table_has_all']) {
+            $default_has_all = $form_state['values']['chado_table_has_all'];
+          }
+          $form['storage'][$store_type]['chado_table_has_all'] = array(
+            '#type' => 'radios',
+            '#options' => array(
+              'Yes' => 'Yes',
+              'No' => 'No'
+            ),
+            '#title' => 'Are all records in the "' . $default_table .
+              '" table of type "'. $term_name . '"?',
+            '#description' => 'Select "Yes" if the "' .
+              $default_table . '" table houses more that just data of type "' .
+              $term_name . '".',
+            '#default_value' => $default_has_all,
+            '#ajax' => array(
+              'callback' => "tripal_admin_add_type_form_ajax_callback",
+              'wrapper' => "tripal-vocab-select-form",
+              'effect' => 'fade',
+              'method' => 'replace'
+            ),
+          );
+
+          // If the table does not contain just one single type of record.
+          if ($default_has_all == 'No') {
+
+            $default_type_column = '';
+            if (array_key_exists('type_id', $schema['fields'])) {
+              $default_type_column = 'type_id';
+            }
+
+            // If the table doesn't have an obvious type_id column then
+            // let the user specify how record types are identified.
+            if ($default_type_column) {
+
+              $default_has_type = 'Yes';
+              if (array_key_exists('chado_table_has_type', $form_state['values'])
+                  and $form_state['values']['chado_table_has_type']) {
+                $default_has_type = $form_state['values']['chado_table_has_type'];
+              }
+              $form['storage'][$store_type]['chado_table_has_type'] = array(
+                '#type' => 'radios',
+                '#title' => 'Do you want to use the "' . $default_table . '.' .
+                   $default_type_column . '" to distinguish between content types?',
+                '#options' => array(
+                  'Yes' => 'Yes',
+                  'No' => 'No'
+                ),
+                '#description' => t('Many tables in Chado use a "type_id" column
+                    to identify specific data types. For example, the "feature.type_id"
+                    column is used to distinguish between different types of
+                    genomic features that are all housed in the feature table.
+                    But there is sometimes a cvterm linker table where additional
+                    attributes for the record can be set.  Here you can specify if you
+                    want to use the type_id column or select another way
+                    to identify records in the "' . $default_table . '" table that
+                    are of this type'),
+                '#default_value' => $default_has_type,
+                '#ajax' => array(
+                  'callback' => "tripal_admin_add_type_form_ajax_callback",
+                  'wrapper' => "tripal-vocab-select-form",
+                  'effect' => 'fade',
+                  'method' => 'replace'
+                ),
+              );
+
+              // If the type_id column is not wanted to differentiate between
+              // different records of this type then find out if the user wants
+              // to use a linker cvterm table, but only if one exists
+              $linker_table = $default_table . '_cvterm';
+              if ($default_has_type == "Yes") {
+                $submit_disabled = FALSE;
+              }
+              else if ($default_has_type == 'No' and chado_table_exists($linker_table)) {
+                $default_use_linker = 'Yes';
+                if (array_key_exists('chado_type_use_linker', $form_state['values'])
+                    and $form_state['values']['chado_type_use_linker']) {
+                  $default_use_linker = $form_state['values']['chado_type_use_linker'];
+                }
+                $form['storage'][$store_type]['chado_type_use_linker'] = array(
+                  '#type' => 'radios',
+                  '#title' => 'Do you want to use the "' . $linker_table . '" table
+                     to distinguish between content types?',
+                  '#options' => array(
+                    'Yes' => 'Yes',
+                    'No' => 'No'
+                  ),
+                  '#description' => t('Sometimes records can be distringuish
+                      using a linker table, especially if there is no column
+                      in the specified Chado table to identify the
+                      record types. In these cases the linker table is
+                      sometimes used.'),
+                  '#default_value' => $default_use_linker,
+                  '#ajax' => array(
+                    'callback' => "tripal_admin_add_type_form_ajax_callback",
+                    'wrapper' => "tripal-vocab-select-form",
+                    'effect' => 'fade',
+                    'method' => 'replace'
+                  ),
+                );
+
+                if($default_use_linker == 'No') {
+                  if (count(array_keys($column_options)) == 1) {
+                    $form['storage'][$store_type]['type_column'] = array(
+                      '#type' => 'item',
+                      '#title' => 'Type Column',
+                      '#description' => 'The  "' . $default_table .
+                        '" table does not have a column
+                        that stores data types. You cannot map the "' .
+                        $term_name . '" type to this table.',
+                    );
+                  }
+                  else {
+                    $form['storage'][$store_type]['type_column'] = array(
+                      '#type' => 'select',
+                      '#title' => 'Type Column',
+                      '#options' => $column_options,
+                      '#description' => 'Please select the column in the "' .
+                        $default_table . '" table that will identify a record
+                        as being of type "' . $term_name . '".',
+                      '#default_value' => $default_type_column,
+                      '#ajax' => array(
+                        'callback' => "tripal_admin_add_type_form_ajax_callback",
+                        'wrapper' => "tripal-vocab-select-form",
+                        'effect' => 'fade',
+                        'method' => 'replace'
+                      ),
+                    );
+                    $submit_disabled = FALSE;
+                  }
+                } // end if(!$default_use_linker) {
+                else {
+                  $submit_disabled = FALSE;
+                }
+              } // end if ($default_has_type == 'No' and ...
+            } // end else if ($default_type_column) {
+            else {
+              // If the type_id column is not wanted to differentiate between
+              // different records of this type then find out if the user wants
+              // to use a linker table, but only if one exists
+              $linker_table = $default_table . '_cvterm';
+              if (chado_table_exists($linker_table)) {
+                $default_use_linker = 'Yes';
+                if (array_key_exists('chado_type_use_linker', $form_state['values'])
+                    and $form_state['values']['chado_type_use_linker']) {
+                  $default_use_linker = $form_state['values']['chado_type_use_linker'];
+                }
+                $form['storage'][$store_type]['chado_type_use_linker'] = array(
+                  '#type' => 'radios',
+                  '#title' => 'Do you want to use the "' . $linker_table . '" table
+                     to distinguish between content types?',
+                  '#options' => array(
+                    'Yes' => 'Yes',
+                    'No' => 'No'
+                  ),
+                  '#description' => t('Sometimes records can be distringuish
+                      using a linker, especially if there is no column
+                      in the specified Chado table to identify the
+                      record types. In these cases the linker table is
+                      sometimes used.'),
+                  '#default_value' => $default_use_linker,
+                  '#ajax' => array(
+                    'callback' => "tripal_admin_add_type_form_ajax_callback",
+                    'wrapper' => "tripal-vocab-select-form",
+                    'effect' => 'fade',
+                    'method' => 'replace'
+                  ),
+                );
+
+                if($default_use_linker == 'No') {
+                  if (count(array_keys($column_options)) == 1) {
+                    $form['storage'][$store_type]['type_column'] = array(
+                      '#type' => 'item',
+                      '#title' => 'Type Column',
+                      '#description' => 'The  "' . $default_table .
+                        '" table does not have a column
+                        that stores data types. You cannot map the "' .
+                        $term_name . '" type to this table.',
+                    );
+                  }
+                  else {
+                    $form['storage'][$store_type]['type_column'] = array(
+                      '#type' => 'select',
+                      '#title' => 'Type Column',
+                      '#options' => $column_options,
+                      '#description' => 'Please select the column in the "' .
+                        $default_table . '" table that will identify a record
+                        as being of type "' . $term_name . '".',
+                      '#default_value' => $default_type_column,
+                      '#ajax' => array(
+                        'callback' => "tripal_admin_add_type_form_ajax_callback",
+                        'wrapper' => "tripal-vocab-select-form",
+                        'effect' => 'fade',
+                        'method' => 'replace'
+                      ),
+                    );
+                    $submit_disabled = FALSE;
+                  }
+                } // end if(!$default_use_linker) {
+                else {
+                  $submit_disabled = FALSE;
+                }
+              } // end if ($default_has_type == 'No' and ...
+            } // end else
+          } //  end if ($default_has_all == 'No') {
+          else {
+            $submit_disabled = FALSE;
+          }
+        } // end if ($default_table) {
+      } // end if ($default_store) {
+      // Add in the button for the cases of no terms or too many.
+      $form['submit_button'] = array(
+        '#type' => 'submit',
+        '#value' => t('Create content type'),
+        '#name' => 'use_cvterm',
+        '#disabled' => $submit_disabled,
+      );
+    }
+  }
+
+  $form['#prefix'] = '<div id = "tripal-vocab-select-form">';
+  $form['#suffix'] = '</div>';
+
+
+  return $form;
+}
+/**
+ * Implements an AJAX callback for the tripal_chado_vocab_select_term_form.
+ */
+function tripal_admin_add_type_form_ajax_callback($form, $form_state) {
   return $form;
 }
 /**
@@ -430,41 +834,101 @@ function tripal_admin_add_type_form($form, &$form_state) {
  *
  */
 function tripal_admin_add_type_form_validate($form, &$form_state) {
-  // 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_select_term_form_validate';
-    if (function_exists($function)) {
-      $function($form, $form_state);
+  $keys = array_keys($stores);
+  $module = $stores[$keys[0]]['module'];
+  $function = $module . '_vocab_select_term_form_validate';
+  if (function_exists($function)) {
+    $function($form, $form_state);
+  }
+
+  if (array_key_exists('clicked_button', $form_state) and
+      $form_state['clicked_button']['#name'] =='use_cvterm') {
+
+    $cvterm_id = NULL;
+
+    // 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++;
+      }
+    }
+    if ($num_selected == 0) {
+      form_set_error('', 'Please select at least one term.');
+    }
+    else if ($num_selected > 1) {
+      form_set_error('term-' . $cvterm_id, 'Please select only one term from the list below.');
+    }
+    else {
+      $form_state['term']['vocabulary'] = $term->dbxref_id->db_id->name;
+      $form_state['term']['accession'] = $term->dbxref_id->accession;
+      $form_state['term']['term_name'] = $term->name;
+
+      // Make sure a default table is selected
+      // TODO: move this Chado specific code out.
+      $default_table = $form_state['values']['base_chado_table'];
+      if (!$default_table) {
+        form_set_error('base_chado_table', 'Please select a default table.');
+      }
     }
   }
+  // For any other button click it's an AJAX call and we just want to reubild
+  // the form.
+  else {
+    $form_state['rebuild'] = TRUE;
+  }
 }
 /**
  * Implements hook_submit() for the tripal_admin_add_type_form.
- *
- * The storage backend must set the
- *
  */
 function tripal_admin_add_type_form_submit($form, &$form_state) {
   $vocabulary = '';
   $accession = '';
-  if (array_key_exists('storage', $form_state)) {
-    $storage = $form_state['storage'];
-    $vocabulary = array_key_exists('vocabulary', $storage) ? $storage['vocabulary'] : '';
-    $accession = array_key_exists('accession', $storage) ? $storage['accession'] : '';
-    $term_name = array_key_exists('term_name', $storage) ? $storage['term_name'] : '';
+  if (array_key_exists('term', $form_state)) {
+    $selected_term = $form_state['term'];
+    $vocabulary = array_key_exists('vocabulary', $selected_term) ? $selected_term['vocabulary'] : '';
+    $accession = array_key_exists('accession', $selected_term) ? $selected_term['accession'] : '';
+    $term_name = array_key_exists('term_name', $selected_term) ? $selected_term['term_name'] : '';
 
     // Before we try to add this type, check to see if it already exists
     // as a bundle.
     $term = tripal_load_term_entity(array('vocabulary' => $vocabulary, 'accession' => $accession));
     if (!$term) {
       $error = '';
-      $success = tripal_create_bundle($vocabulary, $accession, $term_name, $error);
-      if (!$success) {
+      // TODO: the storage args are Chado specific. This should be made
+      // more generic to support other back-ends.
+      $storage_args = array();
+      if (array_key_exists('base_chado_table', $form_state['values'])) {
+        $storage_args['data_table'] = $form_state['values']['base_chado_table'];
+      }
+      if (array_key_exists('chado_type_use_linker', $form_state['values']) and
+          $form_state['values']['chado_type_use_linker'] == 'Yes') {
+        $storage_args['type_linker_table'] = $form_state['values']['base_chado_table'] . '_cvterm';
+        $storage_args['type_column'] = 'cvterm_id';
+      }
+      if (array_key_exists('chado_table_has_type', $form_state['values']) and
+          $form_state['values']['chado_table_has_type'] == 'Yes') {
+        $storage_args['type_column'] = 'type_id';
+      }
+      if (array_key_exists('type_column', $form_state['values']) and
+          $form_state['values']['type_column']) {
+        $storage_args['type_column'] = $form_state['values']['type_column'];
+      }
+
+      $storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
+      $args = array(
+        'vocabulary' => $vocabulary,
+        'accession' => $accession,
+        'term_name' => $term_name,
+        'storage_args' => $storage_args,
+      );
+      $bundle = tripal_create_bundle($args, $error);
+      if (!$bundle) {
         drupal_set_message($error, 'error');
         $form_state['redirect'] = "admin/structure/bio_data";
       }
@@ -472,6 +936,10 @@ function tripal_admin_add_type_form_submit($form, &$form_state) {
         drupal_set_message('New data type created.');
         $form_state['redirect'] = "admin/structure/bio_data";
       }
+
+      // TODO: move these Chado specific settings out.
+
+
     }
     else {
       drupal_set_message("The term '$accession' already exists as a content type.", 'warning');

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

@@ -86,7 +86,7 @@ function tripal_field_formatter_info_alter(&$info) {
  * This is a Triapl defined hook and is called in the TripalBundle::create()
  * function to allow modules to perform tasks when a bundle is created.
  */
-function tripal_bundle_create($bundle) {
+function tripal_bundle_create($bundle, $args) {
   $field_type = 'rdfs__type';
   $field_name = 'rdfs__type';
 

+ 1 - 1
tripal/tripal.module

@@ -576,4 +576,4 @@ function tripal_form_alter(&$form, $form_state, $form_id) {
   if ($form_id == 'field_ui_field_edit_form' and $form['#instance']['entity_type'] == 'TripalEntity') {
     tripal_field_instance_settings_form_alter($form, $form_state);
   }
-}
+}

+ 13 - 0
tripal_chado/api/modules/tripal_chado.cv.api.inc

@@ -147,6 +147,8 @@ function tripal_get_cv_select_options() {
  *   An array apropriate for use with the chado_generate_var for uniquely
  *   identifying a cvterm record. Alternativley, there are also some specially
  *   handled keys. They are:
+ *    - id: an ID for the term of the for [dbname]:[accession], where [dbname]
+ *      is the short name of the vocabulary and accession is the unique ID.
  *    - cv_id:  an integer indicating the cv_id or an array with 'name' => the
  *      name of the cv.
  *    - synonym: an array with 'name' => the name of the synonym of the cvterm
@@ -234,6 +236,17 @@ function tripal_get_cvterm($identifiers, $options = array()) {
       $options
     );
   }
+  if (isset($identifiers['id'])) {
+    list($db_name, $accession) = preg_split('/:/', $identifiers['id']);
+    $cvterm = chado_generate_var('cvterm',array(
+      'dbxref_id' => array(
+        'db_id' => array(
+          'name' => $db_name,
+        ),
+        'accession' => $accession,
+      )
+    ));
+  }
 
   // Else we have a simple case and we can just use chado_generate_var to get the cvterm
   else {

+ 1 - 1
tripal_chado/api/modules/tripal_chado.feature.api.inc

@@ -1125,7 +1125,7 @@ function chado_get_feature_relationships($feature_id, $side = 'as_subject') {
   $esql = "
     SELECT entity_id
     FROM {chado_entity}
-    WHERE data_table = 'feature' AND record_id = :feature_id";
+    WHERE record_id = :feature_id";
   $relationships = array();
   while ($rel = $results->fetchObject()) {
     $entity = db_query($esql, array(':feature_id' => $rel->subject_id))->fetchObject();

+ 47 - 23
tripal_chado/api/tripal_chado.api.inc

@@ -68,51 +68,73 @@ function tripal_chado_publish_records($values, $job_id = NULL) {
   $bundle_name = $values['bundle_name'];
   $sync_node = array_key_exists('sync_node', $values) ? $values['sync_node'] : '';
 
-
   // Load the bundle entity so we can get information about which Chado
   // table/field this entity belongs to.
   $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
-  $bundle_id = $bundle->id;
-  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-  $vocab = $term->vocab;
-  $params = array(
-    'vocabulary' => $vocab->vocabulary,
-    'accession' => $term->accession,
-  );
-  $mapped_table = chado_get_cvterm_mapping($params);
-  $table = $mapped_table->chado_table;
-  $column = $mapped_table->chado_field;
-  $cvterm_id = $mapped_table->cvterm->cvterm_id;
+  if (!$bundle) {
+    tripal_report_error('tripal_chado', TRIPAL_ERROR,
+        "Unknown bundle. Could not publish record: @error",
+        array('@error' => 'The bundle name must be provided'));
+    return FALSE;
+  }
+
+  $table = $bundle->data_table;
+  $type_column = $bundle->type_column;
+  $type_linker_table = $bundle->type_linker_table;
+  $cvterm_id  = $bundle->type_id;
 
   // Get the table information for the Chado table.
   $table_schema = chado_get_schema($table);
   $pkey_field = $table_schema['primary key'][0];
 
   // Construct the SQL for identifying which records should be published.
+  $args = array();
   $select = "SELECT $pkey_field as record_id ";
   $from = "
     FROM {" . $table . "} T
       LEFT JOIN public.chado_entity CE on CE.record_id = T.$pkey_field
-    AND CE.data_table = '$table'
+         AND CE.data_table = :table
   ";
 
   // For migration of Tripal v2 nodes to entities we want to include the
   // coresponding chado linker table.
   if ($sync_node && db_table_exists('chado_' . $table)) {
     $select = "SELECT T.$pkey_field as record_id, CT.nid ";
-    $from .= "INNER JOIN public.chado_$table CT ON CT.$pkey_field = T.$pkey_field";
+    $from .= " INNER JOIN public.chado_$table CT ON CT.$pkey_field = T.$pkey_field";
   }
   $where = " WHERE CE.record_id IS NULL ";
-  // TODO: here we have hard-coded the analysis and organism tables, but
-  // there may be more that don't hve a type field.  I think if the $column
-  // value is null then that check is good enough, but it needs to be looked at.
-  if ($table != 'analysis' and $table != 'organism') {
-    $where .= "AND $column = $cvterm_id";
+  $args[':table'] = $table;
+
+  // Handle bundles that use a linker property table for identifying the type
+  // of record to publish.
+  if ($type_linker_table and $type_column) {
+    $propschema = chado_get_schema($type_linker_table);
+    $fkeys = $propschema['foreign keys'][$table]['columns'];
+    foreach ($fkeys as $leftkey => $rightkey) {
+      if ($rightkey == $pkey_field) {
+        $from .= " INNER JOIN {" . $type_linker_table . "} LT ON T.$pkey_field = LT.$leftkey ";
+      }
+    }
+    $where .= "AND LT.$type_column = :cvterm_id";
+    $args[':cvterm_id'] = $cvterm_id;
+  }
+
+  // If the type column is in the base table then add in the SQL for that.
+  if (!type_linker_table and $type_column) {
+    $where .= "AND T.$type_column = :cvterm_id";
+    $args[':cvterm_id'] = $cvterm_id;
+  }
+  // If no type column is specified then we have a problem.
+  if ($type_linker_table and !$type_column) {
+    tripal_report_error('tripal_chado', TRIPAL_ERROR,
+        "Could not publish record: @error",
+        array('@error' => 'The bundle does not properly map to Chado.'));
+    return FALSE;
   }
 
   // First get the count
   $sql = "SELECT count(*) as num_records " . $from . $where;
-    $result = chado_query($sql);
+  $result = chado_query($sql, $args);
   $count = $result->fetchField();
 
   // calculate the interval for updates
@@ -123,9 +145,13 @@ function tripal_chado_publish_records($values, $job_id = NULL) {
 
   // Perform the query.
   $sql = $select . $from . $where;
-  $records = chado_query($sql);
+  $records = chado_query($sql, $args);
   $transaction  = db_transaction();
 
+  print "\nNOTE: publishing records is performed using a database transaction. \n" .
+      "If the load fails or is terminated prematurely then the entire set of \n" .
+      "is rolled back with no changes to the database\n\n";
+
   printf("%d of %d records. (%0.2f%%) Memory: %s bytes\r", $i, $count, 0, number_format(memory_get_usage()));
   try {
     $i = 0;
@@ -157,8 +183,6 @@ function tripal_chado_publish_records($values, $job_id = NULL) {
         'entity_id' => $entity->id,
         'record_id' => $record_id,
         'data_table' => $table,
-        'type_table' => $table,
-        'field' => $column,
       );
 
       // For the Tv2 to Tv3 migration we want to add the nid to the

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

@@ -647,7 +647,7 @@ function chado_get_cvterm_mapping($params) {
     $cvterm = chado_generate_var('cvterm', $match);
   }
 
-  $result = db_select('tripal_cvterm_mapping', 'tcm')
+  $result = db_select('chado_cvterm_mapping', 'tcm')
     ->fields('tcm')
     ->condition('cvterm_id', $cvterm->cvterm_id)
     ->execute();

+ 3 - 2
tripal_chado/api/tripal_chado.variables.api.inc

@@ -151,9 +151,10 @@ function chado_generate_var($table, $values, $base_options = array()) {
   $table_desc = chado_get_schema($table);
   if (!$table_desc or count($table_desc) == 0) {
     tripal_report_error('tripal_chado', TRIPAL_ERROR,
-      "chado_generate_var: The table '%table' has not been defined. " .
+      "chado_generate_var: The table '%table' has not been defined " .
       "and cannot be expanded. If this is a custom table, please add it using the Tripal " .
-      "custom table interface.", array('%table' => $table));
+      "custom table interface. Values: %values",
+        array('%table' => $table, '%values' => print_r($values, TRUE)));
     if ($return_array) {
       return array();
     }

+ 75 - 0
tripal_chado/includes/tripal_chado.bundle.inc

@@ -0,0 +1,75 @@
+<?php
+/**
+ * Implements hook_chado_bundle_create().
+ *
+ * This is a Tripal hook. It allows any module to perform tasks after
+ * a bundle has been created.
+ *
+ * @param $bundle
+ *  The TripalBundle object.
+ */
+
+function tripal_chado_bundle_create($bundle, $storage_args) {
+  $entity_type = $bundle->type;
+
+  if (!array_key_exists('data_table', $storage_args)) {
+    throw new Exception('Cannot create content type. Missing the "data_table" for this bundle.');
+  }
+
+  $type_id = '';
+  if (array_key_exists('type_id', $storage_args)) {
+    $type_id = $storage_args['type_id'];
+  }
+  else {
+    $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+    $vocab = tripal_load_vocab_entity(array('vocab_id' => $term->vocab_id));
+    $cvterm = chado_generate_var('cvterm', array(
+      'dbxref_id' => array(
+        'db_id' => array(
+          'name' => $vocab->vocabulary,
+        ),
+        'accession' => $term->accession
+      ),
+    ));
+    $type_id = $cvterm->cvterm_id;
+  }
+
+  // Before adding fields to this bundle, let's make sure we associate
+  // what table in Chado this bundle is mapped to
+  $chado_bundle = db_select('chado_bundle', 'cb')
+    ->fields('cb', array('chado_bundle_id'))
+    ->condition('bundle_id', $bundle->id)
+    ->execute()
+    ->fetchField();
+  if (!$chado_bundle) {
+    $record = array(
+      'bundle_id' => $bundle->id,
+      'data_table' => $storage_args['data_table'],
+      'type_id' => $type_id,
+    );
+    if (array_key_exists('type_linker_table', $storage_args)) {
+      $record['type_linker_table'] = $storage_args['type_linker_table'];
+    }
+    if (array_key_exists('type_column', $storage_args)) {
+      $record['type_column'] = $storage_args['type_column'];
+    }
+    $success = drupal_write_record('chado_bundle', $record);
+    if (!$success) {
+      throw new Exception('Cannot create content type. Problem associating type with Chado.');
+    }
+  }
+
+
+  // Before adding fields to the bundle we need to story the Chado table that
+  // this bundle maps to.  That information is in the $args['form_values']
+  // array.  The $args['form_values'] array contains values that indicate the
+  // Chado table.
+  // TODO: we need to store the chado table and type field used for this
+  // bundle.
+
+  // Create/Add the new fields for this bundle.
+  tripal_chado_bundle_create_fields($entity_type, $bundle, $record);
+
+  // Create/Add the new field instances for this bundle.
+  tripal_chado_bundle_create_instances($entity_type, $bundle, $record);
+}

+ 41 - 57
tripal_chado/includes/tripal_chado.entity.inc

@@ -5,7 +5,8 @@
  * Implements hook_entity_create().
  *
  * This hook is called when brand new entities are created, but
- * they are not loaded so the hook_entity_load() is not yet called.
+ * they are not loaded so the hook_entity_load() is not yet called. We
+ * can use this hook to add properties to the entity before saving.
  */
 function tripal_chado_entity_create(&$entity, $type) {
   if ($type == 'TripalEntity') {
@@ -17,18 +18,9 @@ function tripal_chado_entity_create(&$entity, $type) {
 
       // Add in the Chado table information for this entity type.
       $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
-      $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-      $vocab = $term->vocab;
-      $params = array(
-        'vocabulary' => $vocab->vocabulary,
-        'accession' => $term->accession,
-      );
-      $mapped_table = chado_get_cvterm_mapping($params);
-      $chado_table = $mapped_table->chado_table;
-      $chado_column = $mapped_table->chado_field;
-      if ($chado_table) {
-        $entity->chado_table = $chado_table;
-        $entity->chado_column = $chado_column;
+      if ($bundle->data_table) {
+        $entity->chado_table = $bundle->data_table;
+        $entity->chado_column = $bundle->type_column;
       }
     }
     if (!property_exists($entity, 'chado_record')) {
@@ -56,12 +48,27 @@ function tripal_chado_entity_postsave($entity, $type) {
  */
 function tripal_chado_entity_load($entities, $type) {
 
+  if ($type == 'TripalBundle') {
+    foreach ($entities as $bundle) {
+      // We want to add in the record ID to the entity.
+      if (property_exists($bundle, 'id')) {
+        $chado_bundle = db_select('chado_bundle', 'cb')
+          ->fields('cb')
+          ->condition('cb.bundle_id', $bundle->id)
+          ->execute()
+          ->fetchObject();
+        $bundle->data_table = $chado_bundle->data_table;
+        $bundle->type_linker_table = $chado_bundle->type_linker_table;
+        $bundle->type_column = $chado_bundle->type_column;
+        $bundle->type_id = $chado_bundle->type_id;
+      }
+    }
+  }
   if ($type == 'TripalEntity') {
     foreach ($entities as $entity) {
 
       // We want to add in the record ID to the entity.
       if (property_exists($entity, 'id')) {
-
         // Set some defaults on vars needed by this module.
         $entity->chado_table = NULL;
         $entity->chado_column = NULL;
@@ -73,19 +80,10 @@ function tripal_chado_entity_load($entities, $type) {
         if (!$bundle) {
           continue;
         }
-        $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-        $vocab = $term->vocab;
-        $params = array(
-          'vocabulary' => $vocab->vocabulary,
-          'accession' => $term->accession,
-        );
-        $mapped_table = chado_get_cvterm_mapping($params);
-        $chado_table = $mapped_table->chado_table;
-        $chado_column = $mapped_table->chado_field;
-        if ($chado_table) {
-          $entity->chado_table = $chado_table;
-          $entity->chado_column = $chado_column;
-        }
+        // TODO: this may need fixing. The chado_column may not always
+        // be the type_id of the base table. Is it expected to be so here?
+        $entity->chado_table = $bundle->data_table;
+        $entity->chado_column = $bundle->type_column;
 
         $chado_entity = db_select('chado_entity' ,'ce')
           ->fields('ce')
@@ -93,8 +91,8 @@ function tripal_chado_entity_load($entities, $type) {
           ->execute()
           ->fetchObject();
         if ($chado_entity) {
-          $schema = chado_get_schema($chado_table);
-          $record = chado_generate_var($chado_table, array($schema['primary key'][0] => $chado_entity->record_id));
+          $schema = chado_get_schema($entity->chado_table);
+          $record = chado_generate_var($entity->chado_table, array($schema['primary key'][0] => $chado_entity->record_id));
           $entity->chado_record = $record;
           $entity->chado_record_id = $chado_entity->record_id;
         }
@@ -160,27 +158,14 @@ function tripal_chado_entity_access($op, $entity = NULL, $account = NULL) {
 
 /**
  * Implements hook_tripal_default_title_format().
+ *
+ * Overrides the default titles.
  */
-function tripal_chado_tripal_default_title_format($entity, $available_tokens) {
+function tripal_chado_tripal_default_title_format($bundle, $available_tokens) {
   $format = array();
 
-  // Load the bundle
-  $bundle = tripal_load_bundle_entity(array('term_id' => $entity->term_id));
-  $bundle_id = $bundle->id;
-  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-  $vocab = $term->vocab;
-  $params = array(
-    'vocabulary' => $vocab->vocabulary,
-    'accession' => $term->accession,
-  );
-  $mapped_table = chado_get_cvterm_mapping($params);
-  $table = $mapped_table->chado_table;
-  $column = $mapped_table->chado_field;
-  $cvterm_id = $mapped_table->cvterm->cvterm_id;
-
-  // TODO: the tables should not be hardcoded, but a the unique constraint should
-  // be used to create default titles
-  // For organism titles  we want the genus and species with no comma separation.
+  $table = $bundle->data_table;
+
   if ($table == 'organism') {
     $format[] = array(
       'format' => '[taxrank__genus] [taxrank__species]',
@@ -189,7 +174,7 @@ function tripal_chado_tripal_default_title_format($entity, $available_tokens) {
   }
   if ($table == 'analysis') {
     $format[] = array(
-      'format' => '[analysis__name]',
+      'format' => '[schema__name]',
       'weight' => -5
     );
   }
@@ -205,6 +190,12 @@ function tripal_chado_tripal_default_title_format($entity, $available_tokens) {
       'weight' => -5
     );
   }
+  if ($table == 'pub') {
+    $format[] = array(
+      'format' => '[tpub__title]',
+      'weight' => -5,
+    );
+  }
   return $format;
 }
 
@@ -288,15 +279,8 @@ function tripal_chado_entity_view($entity, $type, $view_mode, $langcode) {
 
       // Get the Chado table for this data type.
       $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
-      $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-      $vocab = $term->vocab;
-      $params = array(
-        'vocabulary' => $vocab->vocabulary,
-        'accession' => $term->accession,
-      );
-      $mapped_table = chado_get_cvterm_mapping($params);
-      $chado_table = $mapped_table->chado_table;
-      $chado_field = $mapped_table->chado_field;
+      $chado_table = $bundle->data_table;
+      $chado_field = $bundle->type_column;
 
       // Get the list of templates that should be used for entities and generatte
       // the key in the array for this entity type (using the chado table the

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

@@ -63,8 +63,6 @@ function tripal_chado_field_storage_write($entity_type, $entity, $op, $fields) {
       'entity_id' => $entity->id,
       'record_id' => $base_record_id,
       'data_table' => $base_table,
-      'type_table' => $base_table,
-      'field' => $type_field,
     );
     $success = drupal_write_record('chado_entity', $record);
     if (!$success) {
@@ -191,13 +189,18 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
   $langcode = $language->language;
 
   foreach ($entities as $id => $entity) {
+
     if (property_exists($entity, 'chado_table')) {
       // Get the base table and record id for the fields of this entity.
       $base_table = $entity->chado_table;
       $type_field = $entity->chado_column;
       $record_id  = $entity->chado_record_id;
-    }
+   }
     else {
+      $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
+      $base_table = $bundle->data_table;
+      $type_field = $bundle->type_column;
+
       // Get the base table and record id for the fields of this entity.
       $details = db_select('chado_entity', 'ce')
         ->fields('ce')
@@ -207,9 +210,6 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
       if (!$details) {
         // TODO: what to do if record is missing!
       }
-      // Get some values needed for loading the values from Chado.
-      $base_table = isset($details->data_table) ? $details->data_table : '';
-      $type_field = isset($details->field) ? $details->field : '';
       $record_id  = isset($details->record_id) ? $details->record_id : '';
     }
 

+ 75 - 70
tripal_chado/includes/tripal_chado.fields.inc

@@ -1,51 +1,19 @@
 <?php
 
-/**
- * Implements hook_chado_bundle_create().
- *
- * This is a Tripal hook. It allows any module to perform tasks after
- * a bundle has been created.
- *
- * @param $bundle
- *  The TripalBundle object.
- */
-
-function tripal_chado_bundle_create($bundle) {
-  $entity_type = $bundle->type;
-
-  // Create/Add the new fields for this bundle.
-  tripal_chado_bundle_create_fields($entity_type, $bundle);
-
-  // Create/Add the new field instances for this bundle.
-  tripal_chado_bundle_create_instances($entity_type, $bundle);
-}
-
-
-
 /**
  * Implements hook_field_create_info().
  *
  * This is a Tripal defined hook that supports integration with the
  * TripalEntity field.
  */
-function tripal_chado_bundle_create_fields($entity_type, $bundle) {
-
-  // Get the table this bundle is mapped to.
-  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-  $vocab = $term->vocab;
-  $params = array(
-    'vocabulary' => $vocab->vocabulary,
-    'accession' => $term->accession,
-  );
-  $mapped_table = chado_get_cvterm_mapping($params);
+function tripal_chado_bundle_create_fields($entity_type, $bundle, $chado_bundle) {
 
   // Get the details about the mapping of this bundle to the Chado table:
   $details = array(
-    'chado_cv_id' => $mapped_table->cvterm->cv_id->cv_id,
-    'chado_cvterm_id' => $mapped_table->cvterm->cvterm_id,
-    'chado_table' => $mapped_table->chado_table,
-    'chado_type_table' => $mapped_table->chado_table,
-    'chado_type_column' => $mapped_table->chado_field,
+    'chado_cvterm_id' => $chado_bundle['type_id'],
+    'chado_table' => $chado_bundle['data_table'],
+    'chado_type_table' => $chado_bundle['type_linker_table'],
+    'chado_type_column' => $chado_bundle['type_column'],
   );
 
   $info = array();
@@ -85,7 +53,6 @@ function tripal_chado_bundle_create_fields_base(&$info, $details, $entity_type,
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
 
   // Iterate through the columns of the table and see if fields have been
@@ -108,9 +75,9 @@ function tripal_chado_bundle_create_fields_base(&$info, $details, $entity_type,
     $cvterm = tripal_get_chado_semweb_term($table_name, $column_name, array('return_object' => TRUE));
     if (!$cvterm) {
       tripal_report_error('tripal', TRIPAL_ERROR,
-        'Cannot create term for "%table_name.%column_name". Missing an appropriate vocabulary term',
+        'Cannot create field for "%table_name.%column_name". Missing an appropriate vocabulary term',
          array('%table_name' => $table_name, '%column_name' => $column_name));
-      drupal_set_message(t('Cannot create term for "%table_name.%column_name". Missing an appropriate vocabulary term',
+      drupal_set_message(t('Cannot create field for "%table_name.%column_name". Missing an appropriate vocabulary term',
         array('%table_name' => $table_name, '%column_name' => $column_name)), 'error');
       continue;
     }
@@ -213,13 +180,13 @@ function tripal_chado_bundle_create_fields_custom(&$info, $details, $entity_type
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
-  $schema = chado_get_schema($table_name);
 
+  $schema = chado_get_schema($table_name);
 
   // BASE ORGANISM_ID
-  if ($table_name != 'organism' and array_key_exists('organism_id', $schema['fields'])) {
+  if ($table_name != 'organism' and
+      array_key_exists('organism_id', $schema['fields'])) {
     $field_name = 'obi__organism';
     $field_type = 'obi__organism';
     $info[$field_name] = array(
@@ -336,7 +303,6 @@ function tripal_chado_bundle_create_fields_linker(&$info, $details, $entity_type
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
 
   // CONTACTS
@@ -531,23 +497,13 @@ function tripal_chado_bundle_create_fields_linker(&$info, $details, $entity_type
  * This is a Tripal defined hook that supports integration with the
  * TripalEntity field.
  */
-function tripal_chado_bundle_create_instances($entity_type, $bundle) {
-
-  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
-  $vocab = $term->vocab;
-  $params = array(
-    'vocabulary' => $vocab->vocabulary,
-    'accession' => $term->accession,
-  );
-  $mapped_table = chado_get_cvterm_mapping($params);
+function tripal_chado_bundle_create_instances($entity_type, $bundle, $chado_bundle) {
 
-  // Get the details about the mapping of this bundle to the Chado table:
   $details = array(
-    'chado_cv_id' => $mapped_table->cvterm->cv_id->cv_id,
-    'chado_cvterm_id' => $mapped_table->cvterm->cvterm_id,
-    'chado_table' => $mapped_table->chado_table,
-    'chado_type_table' => $mapped_table->chado_table,
-    'chado_type_column' => $mapped_table->chado_field,
+    'chado_cvterm_id' => $chado_bundle['type_id'],
+    'chado_table' => $chado_bundle['data_table'],
+    'chado_type_table' => $chado_bundle['type_linker_table'],
+    'chado_type_column' => $chado_bundle['type_column'],
   );
 
   tripal_chado_bundle_create_instances_base($info, $entity_type, $bundle, $details);
@@ -583,7 +539,6 @@ function tripal_chado_bundle_create_instances_base(&$info, $entity_type, $bundle
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
 
   // Iterate through the columns of the table and see if fields have been
@@ -775,7 +730,6 @@ function tripal_chado_bundle_create_instances_custom(&$info, $entity_type, $bund
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
   $schema = chado_get_schema($table_name);
 
@@ -1030,7 +984,6 @@ function tripal_chado_bundle_create_instances_linker(&$info, $entity_type, $bund
   $table_name = $details['chado_table'];
   $type_table = $details['chado_type_table'];
   $type_field = $details['chado_type_column'];
-  $cv_id      = $details['chado_cv_id'];
   $cvterm_id  = $details['chado_cvterm_id'];
 
   // CONTACTS
@@ -1611,12 +1564,6 @@ function tripal_chado_field_instance_settings_form_alter(&$form, $form_state) {
   $field = $form['#field'];
   $instance = $form['#instance'];
 
-  // Only show the Chado mapping information for field instances that map
-  // to Chado.
-  if (!array_key_exists('chado_table',$instance['settings'])) {
-    return;
-  }
-
   // Construct a table for the vocabulary information.
   $headers = array();
   $rows = array();
@@ -1659,8 +1606,6 @@ function tripal_chado_field_instance_settings_form_alter(&$form, $form_state) {
     '#type' => 'fieldset',
     '#title' => 'Chado Mapping',
     '#description' => t('This field maps to data in Chado to the following table:'),
-    '#prefix' => '<div id = "tripal-field-term-fieldset">',
-    '#suffix' => '</div>',
   );
   $form['chado_mapping']['details'] = array(
     '#type' => 'item',
@@ -1668,3 +1613,63 @@ function tripal_chado_field_instance_settings_form_alter(&$form, $form_state) {
   );
 
 }
+
+/**
+ * Implements hook_form_FROM_ID_alter()
+ */
+function tripal_chado_form_tripalbundle_form_alter(&$form, $form_state) {
+  global $language;
+  $bundle = $form['#bundle'];
+
+  $term = entity_load('TripalTerm', array('id' => $bundle->term_id));
+  $term = reset($term);
+  $vocab = $term->vocab;
+  $params = array(
+    'vocabulary' => $vocab->vocabulary,
+    'accession' => $term->accession,
+  );
+  $mapped_table = chado_get_cvterm_mapping($params);
+  $chado_table = $mapped_table->chado_table;
+  $chado_column = $mapped_table->chado_field;
+
+  // Construct a table for the vocabulary information.
+  $headers = array();
+  $rows = array();
+  $rows[] = array(
+    array(
+      'data' => 'Chado Table',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $chado_table
+  );
+  $rows[] = array(
+    array(
+      'data' => 'Type Column',
+      'header' => TRUE,
+      'width' => '20%',
+    ),
+    $chado_column
+  );
+  $table = array(
+    'header' => $headers,
+    'rows' => $rows,
+    'attributes' => array(
+    ),
+    'sticky' => FALSE,
+    'caption' => '',
+    'colgroups' => array(),
+    'empty' => '',
+  );
+
+  $form['chado_mapping'] = array(
+    '#type' => 'item',
+    '#title' => 'Chado Mapping',
+    '#markup' => theme_table($table),
+    '#description' => t('This content type maps to the table in Chado
+        listed above.  Chado allows multiple data types to be housed
+        in a single table. Therefore, the column that is used to
+        differentiate between data types is also listed above.'),
+    '#weight' => 0,
+  );
+}

+ 14 - 14
tripal_chado/includes/tripal_chado.mapping.inc

@@ -10,7 +10,7 @@ function tripal_chado_map_cvterms() {
 
   // Perform this action in a transaction
   $transaction = db_transaction();
-  print "\nNOTE: Populating of tripal_cvterm_mapping table is performed using a database transaction. \n" .
+  print "\nNOTE: Populating of chado_cvterm_mapping table is performed using a database transaction. \n" .
       "If the load fails or is terminated prematurely then the entire set of \n" .
       "insertions/updates is rolled back and will not be found in the database\n\n";
   try {
@@ -67,10 +67,10 @@ function tripal_chado_map_cvterms() {
             AND name = :name";
         $cvterm_id = chado_query($cvterm_sql, array(':dbxref_id' => $dbxref_id, ':name' => $name))->fetchField();
         if ($cvterm_id) {
-         // Check if this term is already mapped in the tripal_cvterm_mapping table
+         // Check if this term is already mapped in the chado_cvterm_mapping table
          $check_sql =
            "SELECT mapping_id
-             FROM tripal_cvterm_mapping
+             FROM chado_cvterm_mapping
              WHERE cvterm_id = :cvterm_id";
          $mapped = db_query($check_sql, array(':cvterm_id' => $cvterm_id))->fetchField();
          // If mapping does not exist and a table name matches the term name, add it
@@ -94,31 +94,31 @@ function tripal_chado_map_cvterms() {
 /*
  * Add a cvterm mapping record
  *
- * Check if the cvterm mapping record exists. If not, add it to the tripal_cvterm_mapping
+ * Check if the cvterm mapping record exists. If not, add it to the chado_cvterm_mapping
  * table
  */
 function tripal_chado_add_cvterm_mapping($cvterm_id, $tablename, $chado_field) {
   // check if the record exists
-  $record = db_select('tripal_cvterm_mapping', 'tcm')
-  ->fields('tcm', array('mapping_id'))
-  ->condition('cvterm_id', $cvterm_id)
-  ->execute()
-  ->fetchField();
-  // insert records into the tripal_cvterm_mapping table.
+  $record = db_select('chado_cvterm_mapping', 'tcm')
+    ->fields('tcm', array('mapping_id'))
+    ->condition('cvterm_id', $cvterm_id)
+    ->execute()
+    ->fetchField();
+  // insert records into the chado_cvterm_mapping table.
   if (!$record) {
-    db_insert('tripal_cvterm_mapping')
+    db_insert('chado_cvterm_mapping')
     ->fields(
         array(
           'cvterm_id' => $cvterm_id,
           'chado_table' => $tablename,
           'chado_field' => $chado_field
         )
-        )
-        ->execute();
+      )
+      ->execute();
   }
   // if the record exists, update the term mapping
   else {
-    db_update('tripal_cvterm_mapping')
+    db_update('chado_cvterm_mapping')
     ->fields(
         array(
           'chado_table' => $tablename,

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

@@ -607,7 +607,7 @@ function tripal_chado_migrate_map_types($tv2_content_types) {
  */
 function tripal_chado_migrate_selected_types($tv3_content_types) {
 
-  // Initialize the population of the tripal_cvterm_mapping table before migration.
+  // Initialize the population of the chado_cvterm_mapping table before migration.
   tripal_chado_map_cvterms();
 
   foreach ($tv3_content_types AS $tv3_content_type) {

+ 79 - 0
tripal_chado/includes/tripal_chado.semweb.inc

@@ -30,6 +30,8 @@ function tripal_chado_populate_chado_semweb_table() {
   tripal_chado_populate_vocab_SCHEMA();
   tripal_chado_populate_vocab_SWO();
   tripal_chado_populate_vocab_TAXRANK();
+  tripal_chado_populate_vocab_TPUB();
+  tripal_chado_populate_vocab_UO();
 }
 
 /**
@@ -182,6 +184,30 @@ function tripal_chado_populate_vocab_EDAM() {
     'definition' => 'Biological or biomedical data has been rendered into an image, typically for display on screen.',
   ));
   tripal_associate_chado_semweb_term(NULL, 'eimage_id', $term);
+
+  $term = tripal_insert_cvterm(array(
+    'id' => 'data:1274',
+    'name' => 'Map',
+    'cv_name' => 'EDAM',
+    'definition' => 'A map of (typically one) DNA sequence annotated with positional or non-positional features.',
+  ));
+  tripal_associate_chado_semweb_term(NULL, 'eimage_id', $term);
+
+  $term = tripal_insert_cvterm(array(
+    'id' => 'data:1278',
+    'name' => 'Genetic map',
+    'cv_name' => 'EDAM',
+    'definition' => 'A map showing the relative positions of genetic markers in a nucleic acid sequence, based on estimation of non-physical distance such as recombination frequencies.',
+  ));
+  tripal_associate_chado_semweb_term('featuremap', 'featuremap_id', $term);
+
+  $term = tripal_insert_cvterm(array(
+    'id' => 'data:1280',
+    'name' => 'Physical map',
+    'cv_name' => 'EDAM',
+    'definition' => 'A map of DNA (linear or circular) annotated with physical features or landmarks such as restriction sites, cloned DNA fragments, genes or genetic markers, along with the physical distances between them. Distance in a physical map is measured in base pairs. A physical map might be ordered relative to a reference map (typically a genetic map) in the process of genome sequencing.',
+  ));
+  tripal_associate_chado_semweb_term('featuremap', 'featuremap_id', $term);
 }
 
 /**
@@ -391,6 +417,59 @@ function tripal_chado_populate_vocab_SWO() {
 
 }
 
+/**
+ * Adds the pub table mapping.
+ */
+function tripal_chado_populate_vocab_TPUB() {
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000039'));
+  tripal_associate_chado_semweb_term('pub', 'title', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000243'));
+  tripal_associate_chado_semweb_term('pub', 'volumetitle', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000042'));
+  tripal_associate_chado_semweb_term('pub', 'volume', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000256'));
+  tripal_associate_chado_semweb_term('pub', 'series_name', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000043'));
+  tripal_associate_chado_semweb_term('pub', 'issue', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000059'));
+  tripal_associate_chado_semweb_term('pub', 'pyear', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000044'));
+  tripal_associate_chado_semweb_term('pub', 'pages', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000244'));
+  tripal_associate_chado_semweb_term('pub', 'publisher', $term);
+
+  $term = tripal_get_cvterm(array('id' => 'TPUB:0000245'));
+  tripal_associate_chado_semweb_term('pub', 'pubplace', $term);
+}
+
+/**
+ * Adds the Uni Ontology database, terms and mappings.
+ */
+function tripal_chado_populate_vocab_UO() {
+  tripal_insert_db(array(
+    'name' => 'UO',
+    'description' => 'Units of Measurement Ontology',
+    'url' => 'http://purl.obolibrary.org/obo/uo',
+    'urlprefix' => 'http://purl.obolibrary.org/obo/TAXRANK_',
+  ));
+  tripal_insert_cv('uo','Units of Measurement Ontology');
+
+  $term = tripal_insert_cvterm(array(
+    'id' => 'UO:0000000',
+    'name' => 'unit',
+    'cv_name' => 'uo',
+    'description' => 'A unit of measurement is a standardized quantity of a physical quality.'
+  ));
+  tripal_associate_chado_semweb_term('featuremap', 'unittype_id', $term);
+}
+
 /**
  * Adds the Taxonomic Rank Ontology database and terms.
  */

+ 56 - 4
tripal_chado/includes/tripal_chado.setup.inc

@@ -409,23 +409,75 @@ function tripal_chado_prepare_chado() {
 
     // Create the 'Organism' entity type. This uses the obi:organism term.
     $error = '';
-    if (!tripal_create_bundle('OBI', '0100026', 'organism', $error)) {
+    $args = array(
+     'vocabulary' => 'OBI',
+     'accession' => '0100026',
+     'term_name' => 'organism',
+     'storage_args' => array(
+       'data_table' => 'organism',
+     )
+    );
+    if (!tripal_create_bundle($args, $error)) {
       throw new Exception($error['!message']);
     }
 
     // Create the 'Analysis' entity type. This uses the local:analysis term.
     $error = '';
-    if (!tripal_create_bundle('local', 'analysis', 'analysis', $error)) {
+    $args = array(
+      'vocabulary' => 'local',
+      'accession' => 'analysis',
+      'term_name' => 'analysis',
+      'storage_args' => array(
+        'data_table' => 'analysis',
+      )
+    );
+    if (!tripal_create_bundle($args, $error)) {
       throw new Exception($error['!message']);
     }
 
     // Create the 'Project' entity type. This uses the local:project term.
     $error = '';
-    if (!tripal_create_bundle('local', 'project', 'project', $error)) {
+    $args = array(
+      'vocabulary' => 'local',
+      'accession' => 'project',
+      'term_name' => 'project',
+      'storage_args' => array(
+        'data_table' => 'project',
+      )
+    );
+    if (!tripal_create_bundle($args, $error)) {
+      throw new Exception($error['!message']);
+    }
+
+    // Create the 'Genetic Map' entity type. This uses the local:project term.
+    $error = '';
+    $args = array(
+      'vocabulary' => 'data',
+      'accession' => '1274',
+      'term_name' => 'Map',
+      'storage_args' => array(
+        'data_table' => 'featuremap',
+      )
+    );
+    if (!tripal_create_bundle($args, $error)) {
+      throw new Exception($error['!message']);
+    }
+
+    // Create the 'Publication' entity type.
+    $error = '';
+    $args = array(
+      'vocabulary' => 'TPUB',
+      'accession' => '0000002',
+      'term_name' => 'Publication',
+      'storage_args' => array(
+        'data_table' => 'pub',
+      )
+    );
+    if (!tripal_create_bundle($args, $error)) {
       throw new Exception($error['!message']);
     }
 
-    // Initialize the population of the tripal_cvterm_mapping table.
+    // Initialize the population of the chado_cvterm_mapping table.
     tripal_chado_map_cvterms();
 
     // Set a variable to indicate the site is prepared.

+ 1 - 167
tripal_chado/includes/tripal_chado.vocab_storage.inc

@@ -7,7 +7,7 @@
 function tripal_chado_vocab_storage_info() {
   return array(
     'term_chado_storage' => array(
-      'label' => t('Chado storage'),
+      'label' => t('Chado'),
       'module' => 'tripal_chado',
       'description' => t('Integrates terms stored in the local Chado database
           with Tripal entities.'),
@@ -119,170 +119,4 @@ function tripal_chado_vocab_import_form_validate($form, &$form_state) {
 function tripal_chado_vocab_import_form_submit($form, &$form_state) {
   module_load_include('inc', 'tripal_chado', 'includes/loaders/tripal_chado.obo_loader');
   return tripal_cv_obo_form_submit($form, $form_state);
-}
-/**
- * Implements hook_vocab_select_term_form().
- *
- * TODO: can't this be moved over to the tripal module and remove the
- *  Chado specific parts???
- */
-function tripal_chado_vocab_select_term_form($form, &$form_state) {
-  $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.
-  $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/chado/auto_name/cvterm/",
-  );
-  $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 ($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.')
-    );
-    $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) {
-      // Save the user a click by setting the default value as 1 if there's
-      // only one matching term.
-      $default = FALSE;
-      $attrs = array();
-      if ($num_terms == 0 and count($terms) == 1) {
-        $default = TRUE;
-        $attrs = array('checked' => 'checked');
-      }
-       $form['terms_list']['term-' . $term->cvterm_id] = array(
-         '#type' => 'checkbox',
-         '#title' =>  $term->name,
-         '#default_value' => $default,
-         '#attributes' => $attrs,
-         '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
-             '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '.  ' .
-             '<br><b>Definition:</b>  ' . $term->definition,
-       );
-       $base_tables = chado_get_base_tables();
-       $default_table = 0;
-       $options = array(0 => '-- Select table --');
-       foreach ($base_tables AS $tablename) {
-         $options[$tablename] = $tablename;
-       }
-       $mapped_table = chado_get_cvterm_mapping(array('cvterm_id' => $term->cvterm_id));
-       if ($mapped_table) {
-         $default_table = $mapped_table->chado_table;
-       }
-       $form['terms_list']['default_table-' . $term->cvterm_id] = array(
-         '#type' => 'select',
-         '#title' => 'Chado table',
-         '#options' => $options,
-         '#description' => 'Select the table in Chado where data of this type
-           will be stored.',
-         '#default_value' => $default_table
-       );
-       $num_terms++;
-    }
-    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'
-    );
-  }
-
-  $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;
-}
-
-/**
- * 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'] =='use_cvterm') {
-
-    $cvterm_id = NULL;
-
-    // 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++;
-      }
-    }
-    if ($num_selected == 0) {
-      form_set_error('', 'Please select at least one term.');
-    }
-    else if ($num_selected > 1) {
-      form_set_error('term-' . $cvterm_id, 'Please select only one term from the list below.');
-    }
-    else {
-      $form_state['storage']['vocabulary'] = $term->dbxref_id->db_id->name;
-      $form_state['storage']['accession'] = $term->dbxref_id->accession;
-      $form_state['storage']['term_name'] = $term->name;
-      // Make sure a default table is selected
-      $default_table = $form_state['values']['default_table-' . $cvterm_id];
-      if (!$default_table) {
-        form_set_error('default_table-' . $cvterm_id, 'Please select a default table.');
-      }
-      else {
-        $chado_field = NULL;
-        $schema = chado_get_schema($default_table);
-        if (isset($schema['foreign keys']['cvterm']['columns'])) {
-          $fkey = array_keys($schema['foreign keys']['cvterm']['columns']);
-          $chado_field = $fkey[0];
-        }
-        tripal_chado_add_cvterm_mapping($cvterm_id, $default_table, $chado_field);
-      }
-    }
-  }
-  // For any other button click it's an AJAX call and we just want to reubild
-  // the form.
-  else {
-    $form_state['rebuild'] = TRUE;
-  }
 }

+ 69 - 20
tripal_chado/tripal_chado.install

@@ -156,6 +156,7 @@ function tripal_chado_schema() {
 
   // Links TripalEntity entities to the chado record.
   $schema['chado_entity'] = tripal_chado_chado_entity_schema();
+  $schema['chado_bundle'] = tripal_chado_chado_bundle_schema();
   $schema['chado_semweb'] = tripal_chado_chado_semweb_schema();
 
   $schema['tripal_mviews'] = tripal_chado_tripal_mviews_schema();
@@ -180,7 +181,7 @@ function tripal_chado_schema() {
   }
 
   // Map cvterm usage to chado tables
-  $schema['tripal_cvterm_mapping'] = tripal_chado_tripal_cvterm_mapping_schema();
+  $schema['chado_cvterm_mapping'] = tripal_chado_chado_cvterm_mapping_schema();
 
   return $schema;
 }
@@ -584,48 +585,96 @@ function tripal_chado_chado_entity_schema() {
         'type' => 'int',
         'not null' => TRUE,
       ),
+      'data_table' => array(
+        'description' => 'The table in that this record belongs to',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
+      ),
       'record_id' => array(
         'description' => 'The unique numerical identifier for the record that this entity is associated with (e.g. feature_id, stock_id, library_id, etc.).',
         'type' => 'int',
         'not null' => TRUE,
       ),
+      'nid' => array(
+        'description' => 'Optional. For linking nid to the entity when migrating Tripal v2 content',
+        'type' => 'int',
+      )
+    ),
+    'indexes' => array(
+      'record_id' => array('record_id'),
+      'entity_id' => array('entity_id'),
+      'data_table' => array('data_table'),
+      'nid' => array('nid'),
+    ),
+    'unique keys' => array(
+      'table_record' => array('data_table', 'record_id'),
+      'entity_id' => array('entity_id'),
+    ),
+    'primary key' => array('chado_entity_id'),
+  );
+  return $schema;
+}
+
+/**
+ * Links Biological Data Entities to the chado "base" table the data is stored in.
+ * This is where we would specify that a particular gene maps to the record in the
+ * chado.feature table with a feature_id=2432;
+ */
+function tripal_chado_chado_bundle_schema() {
+
+  $schema = array(
+    'description' => 'Describes how a bundle maps data to Chado',
+    'fields' => array(
+      'chado_bundle_id' => array(
+        'description' => 'The primary identifier for this table.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+      ),
+      'bundle_id' => array(
+        'description' => 'The unique entity id.',
+        'type' => 'int',
+        'not null' => TRUE,
+      ),
       'data_table' => array(
-        'description' => 'Indicates the table in Chado that this term services (e.g. feature, stock, library, etc.)',
+        'description' => 'The table in Chado that this term services (e.g. feature, stock, library, etc.)',
         'type' => 'varchar',
         'length' => 128,
         'not null' => TRUE,
         'default' => '',
       ),
-      'type_table' => array(
-        'description' => 'Sometimes the record in the data table doesn’t have a field that specifies  the record type.  For example, an analysis type is stored in the analysisprop table.  If the data_table does have a type field then this value will be the same as the data_table.',
+      'type_linker_table' => array(
+        'description' => 'The table in Chado that identifies the data type. This column should be used if the data type is not specified by a "type_id" (or similar) field in the data table. The linker table must have a foreign key to the data table.',
         'type' => 'varchar',
         'length' => 128,
-        'not null' => TRUE,
+        'not null' => FALSE,
         'default' => '',
       ),
-      'field' => array(
-        'description' => 'The name of the field in the typetable that contains the cvterm record.',
+      'type_column' => array(
+        'description' => 'The column in the data table that distinguishes the data types. This must be in a foreign key relationship to the cvterm table.',
         'type' => 'varchar',
         'length' => 128,
         'not null' => FALSE,
-        'default' => ''
+        'default' => '',
+      ),
+      'type_id' => array(
+        'description' => 'If a type_column is set then this is the cvterm_id of the data type that this bundle maps to.',
+        'type' => 'varchar',
+        'length' => 128,
+        'not null' => TRUE,
+        'default' => '',
       ),
-      'nid' => array(
-        'description' => 'Optional. For linking nid to the entity when migrating Tripal v2 content',
-        'type' => 'int',
-      )
     ),
     'indexes' => array(
-      'record_id' => array('record_id'),
-      'entity_id' => array('entity_id'),
+      'bundle_id' => array('bundle_id'),
       'data_table' => array('data_table'),
-      'nid' => array('nid'),
     ),
     'unique keys' => array(
-      'record' => array('data_table', 'record_id'),
-      'entity_id' => array('entity_id'),
+      'record' => array('bundle_id'),
     ),
-    'primary key' => array('chado_entity_id'),
+    'primary key' => array('chado_bundle_id'),
   );
   return $schema;
 }
@@ -634,10 +683,10 @@ function tripal_chado_chado_entity_schema() {
  * Tripal cvterm mapping schema
  * Map cvterms to chado tables that use them
  */
-function tripal_chado_tripal_cvterm_mapping_schema() {
+function tripal_chado_chado_cvterm_mapping_schema() {
 
   $schema = array (
-    'table' => 'tripal_cvterm_mapping',
+    'table' => 'chado_cvterm_mapping',
     'fields' => array (
       'mapping_id' => array(
         'type' => 'serial',

+ 1 - 0
tripal_chado/tripal_chado.module

@@ -37,6 +37,7 @@ require_once "includes/tripal_chado.entity.inc";
 require_once "includes/tripal_chado.schema.inc";
 require_once "includes/tripal_chado.vocab_storage.inc";
 require_once "includes/tripal_chado.field_storage.inc";
+require_once "includes/tripal_chado.bundle.inc";
 require_once "includes/tripal_chado.fields.inc";
 require_once "includes/tripal_chado.mapping.inc";
 

+ 1 - 1
tripal_chado/views_handlers/chado_views_handler_filter.inc

@@ -421,7 +421,7 @@ function _chado_views_add_table_joins(&$handler) {
   $alias = $handler->query->add_relationship('chado_entity', $join, $handler->table_alias, $handler->relationship);
 
   // Restrict the chado_entity join to only return the table this field is from.
-  $handler->query->add_where(0, $alias .'.data_table', $handler->definition['chado_table'], '=');
+  $handler->query->add_where(0, $alias . '.data_table', $handler->definition['chado_table'], '=');
 
   // Now, we need to join from chado_entity to the chado table needed by this field.
   // This only works if the field is from the base table.

+ 0 - 1
tripal_panes/tripal_panes.install

@@ -180,7 +180,6 @@ function tripal_panes_update_dependencies() {
   // site upgrade when the tripal_core module is disabled.
   //module_load_include('module', 'tripal', 'tripal');
   //tripal_import_api();
-print "HI!!!\n";
   module_load_include('module', 'tripal_ws', 'tripal_ws');
 
   $dependencies = array();