Browse Source

Working on adding a field for dbxref linking tables

Stephen Ficklin 9 years ago
parent
commit
dc243b3696

+ 68 - 26
tripal/api/tripal.entities.api.inc

@@ -2,22 +2,37 @@
 /**
  * Retrieves a TripalTerm entity that matches the given arguments.
  *
- * @param $namespace
- *   The namespace for the vocabulary
- * @param $accession
- *   The ID (accession) of the term in the vocabulary.
+ * @param $values
+ *   An associative array used to match a term.  Valid keys may be 'namespace',
+ *   'accession, or 'term_id'.  The keys 'namespace' and 'accession' must
+ *   always be used together to uniquely identify a term.  The key 'term_id'
+ *   can be used alone to uniquely identify a term.
  *
  * @return
  *   A TripalTerm entity object or NULL if not found.
  */
-function tripal_load_term_entity($namespace, $accession) {
-  $query = db_select('tripal_term', 'tt');
-  $query->join('tripal_vocab' ,'tv', 'tv.id = tt.vocab_id');
-  $query->fields('tt', array('id', 'accession'))
-    ->fields('tv', array('namespace'))
-    ->condition('tv.namespace', $namespace)
-    ->condition('tt.accession', $accession);
-  $term = $query->execute()->fetchObject();
+function tripal_load_term_entity($values) {
+  $namespace = array_key_exists('namespace', $values) ? $values['namespace'] : '';
+  $accession = array_key_exists('accession', $values) ? $values['accession'] : '';
+  $term_id = array_key_exists('term_id', $values) ? $values['term_id'] : '';
+
+  $term = NULL;
+
+  if ($namespace and $accession) {
+    $query = db_select('tripal_term', 'tt');
+    $query->join('tripal_vocab' ,'tv', 'tv.id = tt.vocab_id');
+    $query->fields('tt', array('id'))
+      ->fields('tv', array('namespace'))
+      ->condition('tv.namespace', $namespace)
+      ->condition('tt.accession', $accession);
+    $term = $query->execute()->fetchObject();
+  }
+  else if ($term_id) {
+    $query = db_select('tripal_term', 'tt');
+    $query->fields('tt', array('id'))
+      ->condition('tt.id', $term_id);
+    $term = $query->execute()->fetchObject();
+  }
 
   if ($term) {
     $entity = entity_load('TripalTerm', array($term->id));
@@ -29,17 +44,24 @@ function tripal_load_term_entity($namespace, $accession) {
 /**
  * Retrieves a TripalVocab entity that maches the given arguments.
  *
- * @param $namespace
+ * @param $values
+ *   An associative array used to match a term.  Currently, the only valid keys
+ *   is 'namespace'.
  *
  * @return
  * A TripalVocab entity object or NULL if not found.
  */
-function tripal_load_vocab_entity($namespace) {
-  $vocab = db_select('tripal_vocab', 'tv')
-    ->fields('tv')
-    ->condition('tv.namespace', $namespace)
-    ->execute()
-    ->fetchObject();
+function tripal_load_vocab_entity($values) {
+  $namespace = array_key_exists('namespace', $values) ? $values['namespace'] : '';
+  $vocab = NULL;
+
+  if ($namespace) {
+    $vocab = db_select('tripal_vocab', 'tv')
+      ->fields('tv')
+      ->condition('tv.namespace', $namespace)
+      ->execute()
+      ->fetchObject();
+  }
 
   if ($vocab) {
     $entity = entity_load('TripalVocab', array($vocab->id));
@@ -97,14 +119,14 @@ function tripal_load_bundle_entity($values) {
 function tripal_create_bundle($namespace, $accession, $term_name, &$error = '') {
 
   // First create the TripalVocab if it doesn't already exist.
-  $vocab = tripal_load_vocab_entity($namespace);
+  $vocab = tripal_load_vocab_entity(array('namespace' => $namespace));
   if (!$vocab) {
     $vocab = entity_get_controller('TripalVocab')->create(array('namespace' => $namespace));
     $vocab->save();
   }
 
   // Next create the TripalTerm if it doesn't already exist.
-  $term = tripal_load_term_entity($namespace, $accession);
+  $term = tripal_load_term_entity(array('namespace' => $namespace, 'accession' => $accession));
   if (!$term) {
     $args = array('vocab_id' => $vocab->id, 'accession' => $accession, 'name' => $term_name);
     $term = entity_get_controller('TripalTerm')->create($args);
@@ -136,11 +158,31 @@ function tripal_create_bundle($namespace, $accession, $term_name, &$error = '')
   // Get the bundle object.
   $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
 
-  // Allow modules to now add fields to the bundle
+  // Allow modules now add fields to the bundle
   module_invoke_all('add_bundle_fields', 'TripalEntity', $bundle, $term);
 
   return TRUE;
 }
+
+/**
+ * Refreshes the bundle such that new fields added by modules will be found.
+ *
+ * @param $bundle_name
+ *   The name of the bundle to refresh (e.g. bio-data_4).
+ */
+function tripal_refresh_bundle_fields($bundle_name) {
+  // Get the bundle object.
+  $bundle = tripal_load_bundle_entity(array('name' => $bundle_name));
+  if (!$bundle) {
+    tripal_report_error('tripal', TRIPAL_ERROR, "Unrecognized bundle name '%bundle'.",
+        array('%bundle' => $bundle_name));
+    return FALSE;
+  }
+  $term = tripal_load_term_entity(array('term_id' => $bundle->term_id));
+
+  // Allow modules now add fields to the bundle
+  module_invoke_all('add_bundle_fields', 'TripalEntity', $bundle, $term);
+}
 /**
  * Adds a field to an Entity bundle.
  *
@@ -248,7 +290,7 @@ function hook_entity_create(&$entity, $entity_type) {
  * @param $bundle
  *   A TripalBundle object.
  * @param $term
- *   An instance of a TripalTerm object.
+ *   A TripalTerm object.
  *
  * @return
  *   TRUE on success, FALSE on failure.
@@ -701,13 +743,13 @@ function hook_vocab_select_term_form_validate($form, &$form_state) {
  * @param $form_state
  *
  */
-function hook__vocab_import_form($form, &$form_state) {
+function hook_vocab_import_form($form, &$form_state) {
   return $form;
 }
-function hook__vocab_import_form_validate($form, &$form_state) {
+function hook_vocab_import_form_validate($form, &$form_state) {
 
 }
-function hook__vocab_import_form_submit($form, &$form_state) {
+function hook_vocab_import_form_submit($form, &$form_state) {
 
 }
 

+ 1 - 1
tripal/includes/TripalBundleUIController.inc

@@ -429,7 +429,7 @@ function tripal_admin_add_type_form_submit($form, &$form_state) {
 
     // Before we try to add this type, check to see if it already exists
     // as a bundle.
-    $term = tripal_load_term_entity($namespace, $accession);
+    $term = tripal_load_term_entity(array('namespace' => $namespace, 'accession' => $accession));
     if (!$term) {
       $error = '';
       $success = tripal_create_bundle($namespace, $accession, $term_name, $error);

BIN
tripal/theme/images/250px-ChadoLogo.png


+ 58 - 1
tripal/tripal.module

@@ -715,7 +715,7 @@ function TripalBundle_load($bundle_type, $reset = FALSE) {
  * Make sure that the wildcard you choose in the tripal_entity entity
  * definition fits the function name here.
  *
- * This function is not meant to be used as an API function. It is only mean
+ * This function is not meant to be used as an API function. It is only meant
  * for use in the menu to resolve the %tripal_entity wildcard.
  *
  * @param $id
@@ -738,6 +738,9 @@ function TripalEntity_load($id, $reset = FALSE) {
  *
  * The field_ui_field_edit_form is used for customizing the settings of
  * a field attached to an entity.
+ *
+ * This alter function disables some of the form widgets when the storage
+ * backend indicates they are not appropriate.
  */
 function tripal_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_id) {
 
@@ -755,6 +758,60 @@ function tripal_form_field_ui_field_edit_form_alter(&$form, &$form_state, $form_
   // TODO: don't the the maximum length be larger than the field size.
 }
 
+
+/**
+ *
+ * Implements hook_form_FORM_ID_alter().
+ *
+ * The field_ui_field_overview_form_ is used for adding and reordering the
+ * fields attached to a bundle.  It also includes edit and delete links and
+ * links for editing field types and widgets.
+ *
+ * This alter function is used to add a new 'Supported By' column to
+ * the table to let the user know where fields are storing their data.
+ */
+function tripal_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
+  // Add the 'Storage Location' to the table header.
+  $form['fields']['#header'][] = 'Supported By * ';
+
+  // Add the storage location as the final column for each field.
+  $storage_info = module_invoke_all('field_storage_info');
+  foreach (element_children($form['fields']) as $field_name) {
+    $field = field_info_field($field_name);
+    // For rows in the tables that aren't fields, just add an empty value
+    // for the storage column.
+    if (!$field) {
+      $form['fields'][$field_name][] = array(
+        '#markup' => '',
+      );
+      continue;
+    }
+    $storage_type = $field['storage']['type'];
+    $storage_label = array_key_exists('label', $storage_info[$storage_type]) ? $storage_info[$storage_type]['label'] : '';
+    if ($storage_type == 'field_sql_storage') {
+      $storage_label = 'Drupal';
+    }
+    if (array_key_exists('logo_url', $storage_info[$storage_type])) {
+      $logo_url = $storage_info[$storage_type]['logo_url'];
+      $form['fields'][$field_name][] = array(
+        '#markup' => '<img class="form-field-ui-field-overview-storage-logo" src="' . $logo_url . '">',
+      );
+    }
+    else {
+      $form['fields'][$field_name][] = array(
+        '#markup' => $storage_label,
+      );
+    }
+  }
+  $form['note'] = array(
+    '#markup' =>  '* Fields attached to this content type can use various
+      storage backends. Please be sure when you add new fields that the
+      storage backend is appropriate. For example, if you use Chado, and you
+      want all biological content to be stored in Chado, be sure that the
+      respective fields are "supported by" Chado.',
+  );
+}
+
 /**
  * Implements hook_menu_alter().
  */

+ 316 - 0
tripal_chado/includes/fields/dbxref.inc

@@ -0,0 +1,316 @@
+<?php
+
+/**
+ *
+ * @param unknown $entity_type
+ * @param unknown $entity
+ * @param unknown $field
+ * @param unknown $instance
+ * @param unknown $langcode
+ * @param unknown $items
+ * @param unknown $display
+ */
+function tripal_chado_dbxref_formatter(&$element, $entity_type, $entity, $field,
+    $instance, $langcode, $items, $display) {
+
+  foreach ($items as $delta => $item) {
+    $accession = '';
+    if ($item['value']) {
+      $dbxref = chado_generate_var('dbxref', array('dbxref_id' => $item['value']));
+      $accession = $dbxref->db_id->name . ':' . $dbxref->accession;
+      if ($dbxref->db_id->urlprefix) {
+        $accession = l($accession, $dbxref->db_id->urlprefix . '/' . $dbxref->accession, array('attributes' => array('target' => '_blank')));
+      }
+    }
+    $element[$delta] = array(
+      '#type' => 'markup',
+      '#markup' => $accession,
+    );
+  }
+}
+/**
+ *
+ * @param unknown $field_name
+ * @param unknown $widget
+ * @param unknown $form
+ * @param unknown $form_state
+ * @param unknown $field
+ * @param unknown $instance
+ * @param unknown $langcode
+ * @param unknown $items
+ * @param unknown $delta
+ * @param unknown $element
+ */
+function tripal_chado_dbxref_widget(&$widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element) {
+  $field_name = $field['field_name'];
+
+  // Get the field defaults.
+  $dbxref_id = '';
+  $db_id = '';
+  $accession = '';
+  $version = '';
+  $description = '';
+
+  // If the field already has a value then it will come through the $items
+  // array.  This happens when editing an existing record.
+  if (array_key_exists($delta, $items)) {
+    $dbxref_id = $items[$delta]['value'];
+    $db_id = $items[$delta]['dbxref__db_id'];
+    $accession = $items[$delta]['dbxref__accession'];
+    $version = $items[$delta]['dbxref__version'];
+    $description = $items[$delta]['dbxref__description'];
+  }
+
+  // Check $form_state['values'] to see if an AJAX call set the values.
+  if (array_key_exists('values', $form_state)) {
+    $dbxref_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, $field_name);
+    $db_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__db_id");
+    $accession = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__accession");
+    $version = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__version");
+    $description = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__description");
+  }
+
+  // If we are here because our parent was triggered in a form submit
+  // then that means an ajax call was made and we don't want the fieldset to
+  // be closed when it returns from the ajax call.
+  $collapsed = TRUE;
+  if (array_key_exists('triggering_element', $form_state) and
+      $form_state['triggering_element']['#parents'][0] == $field_name) {
+    $collapsed = FALSE;
+  }
+  if ($dbxref_id) {
+    $collapsed = FALSE;
+  }
+
+  $schema = chado_get_schema('dbxref');
+  $options = tripal_get_db_select_options();
+
+  $widget['#element_validate'] = array('tripal_chado_dbxref_id_widget_validate');
+  $widget['#theme'] = 'tripal_chado_dbxref_id_widget';
+  $widget['#prefix'] =  "<span id='$field_name-dbxref--db-id'>";
+  $widget['#suffix'] =  "</span>";
+
+  // A temporary element used for theming the fieldset.
+  $widget['#theme_settings'] = array(
+    '#title' => $element['#title'],
+    '#description' =>  $element['#description'],
+    '#weight' => isset($element['#weight']) ? $element['#weight'] : 0,
+    '#theme' => 'tripal_chado_dbxref_id_widget',
+    '#collapsible' => TRUE,
+    '#collapsed' => $collapsed,
+  );
+
+  $widget['value'] = array(
+    '#type' => 'hidden',
+    '#default_value' => $dbxref_id,
+  );
+
+  $widget['dbxref__db_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Database'),
+    '#options' => $options,
+    '#required' => $element['#required'],
+    '#default_value' => $db_id,
+    '#ajax' => array(
+      'callback' => "tripal_chado_dbxref_id_widget_form_ajax_callback",
+      'wrapper' => "$field_name-dbxref--db-id",
+      'effect' => 'fade',
+      'method' => 'replace'
+    ),
+  );
+  $widget['dbxref__accession'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Accession'),
+    '#default_value' => $accession,
+    '#required' => $element['#required'],
+    '#maxlength' => array_key_exists('length', $schema['fields']['accession']) ? $schema['fields']['accession']['length'] : 255,
+    '#size' => 15,
+    '#autocomplete_path' => "admin/tripal/chado/tripal_db/dbxref/auto_name/$db_id",
+    '#ajax' => array(
+      'callback' => "tripal_chado_dbxref_id_widget_form_ajax_callback",
+      'wrapper' => "$field_name-dbxref--db-id",
+      'effect' => 'fade',
+      'method' => 'replace'
+    )
+  );
+  $widget['dbxref__version'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Version'),
+    '#default_value' => $version,
+    '#maxlength' => array_key_exists('length', $schema['fields']['version']) ? $schema['fields']['version']['length'] : 255,
+    '#size' => 5,
+  );
+  $widget['dbxref__description'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Description'),
+    '#default_value' => $description,
+    '#size' => 20,
+  );
+  $widget['links'] = array(
+    '#type' => 'item',
+    '#markup' => l('Add a new database', 'admin/tripal/chado/tripal_db/add', array('attributes' => array('target' => '_blank')))
+  );
+  dpm($widget);
+}
+/**
+ * An Ajax callback for the tripal_chado_admin_publish_form..
+ */
+function tripal_chado_dbxref_widget_form_ajax_callback($form, $form_state) {
+  $field_name = $form_state['triggering_element']['#parents'][0];
+  $db_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'dbxref__db_id');
+  $accession = tripal_chado_get_field_form_values($field_name, $form_state, 0, 'dbxref__accession');
+  if ($db_id and $accession) {
+    $values = array(
+      'db_id' => $db_id,
+      'accession' => $accession,
+    );
+    $options = array('is_duplicate' => TRUE);
+    $has_duplicate = chado_select_record('dbxref', array('*'), $values, $options);
+    if (!$has_duplicate) {
+      drupal_set_message('The selected cross reference is new and will be added for future auto completions.');
+    }
+  }
+
+  return $form[$field_name];
+}
+
+/**
+ * Callback function for validating the tripal_chado_organism_select_widget.
+ */
+function tripal_chado_dbxref_widget_validate($element, &$form_state) {
+  $field_name = $element['#parents'][0];
+
+  // If the form ID is field_ui_field_edit_form, then the user is editing the
+  // field's values in the manage fields form of Drupal.  We don't want
+  // to validate it as if it were being used in a data entry form.
+  if ($form_state['build_info']['form_id'] =='field_ui_field_edit_form') {
+    return;
+  }
+
+  // Get the field values.
+  $dbxref_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, $field_name);
+  $db_id = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__db_id");
+  $accession = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__accession");
+  $version = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__version");
+  $description = tripal_chado_get_field_form_values($field_name, $form_state, 0, "dbxref__description");
+
+  // Make sure that if a database ID is provided that an accession is also
+  // provided.  Here we use the form_set_error function rather than the
+  // form_error function because the form_error will add a red_highlight
+  // around all of the fields in the fieldset which is confusing as it's not
+  // clear to the user what field is required and which isn't. Therefore,
+  // we borrow the code from the 'form_error' function and append the field
+  // so that the proper field is highlighted on error.
+  if (!$db_id and $accession) {
+    form_set_error(implode('][', $element ['#parents']) . '][dbxref__db_id', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+  if ($db_id and !$accession) {
+    form_set_error(implode('][', $element ['#parents']) . '][dbxref__accession', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+  if (!$db_id and !$accession and ($version or $description)) {
+    form_set_error(implode('][', $element ['#parents']) . '][dbxref__db_id', t("A database and the accession must both be provided for the primary cross reference."));
+  }
+
+  // If user did not select a database, we want to remove dbxref_id from the
+  // field.
+  if (!$db_id) {
+    tripal_chado_set_field_form_values($field_name, $form_state, '__NULL__');
+  }
+  // If the dbxref_id does not match the db_id + accession then the user
+  // has selected a new dbxref record and we need to update the hidden
+  // value accordingly.
+  if ($db_id and $accession) {
+    $dbxref = chado_generate_var('dbxref', array('db_id' => $db_id, 'accession' => $accession));
+    if ($dbxref and $dbxref->dbxref_id != $dbxref_id) {
+      tripal_chado_set_field_form_values($field_name, $form_state, $dbxref->dbxref_id);
+    }
+  }
+}
+/**
+ * Theme function for the dbxref_id_widget.
+ *
+ * @param $variables
+ */
+function theme_tripal_chado_dbxref_widget($variables) {
+  $element = $variables['element'];
+
+  $layout = "
+    <div class=\"primary-dbxref-widget\">
+      <div class=\"primary-dbxref-widget-item\">" .
+        drupal_render($element['dbxref__db_id']) . "
+      </div>
+      <div class=\"primary-dbxref-widget-item\">" .
+        drupal_render($element['dbxref__accession']) . "
+      </div>
+      <div class=\"primary-dbxref-widget-item\">" .
+        drupal_render($element['dbxref__version']) . "
+      </div>
+      <div class=\"primary-dbxref-widget-item\">" .
+        drupal_render($element['dbxref__description']) . "
+      </div>
+      <div class=\"primary-dbxref-widget-links\">" . drupal_render($element['links']) . "</div>
+    </div>
+  ";
+
+  $classes = array();
+  $classes[] = 'collapsible';
+  $theme_settings = $element['#theme_settings'];
+  if ($theme_settings['#collapsed'] == TRUE) {
+    $classes[] = 'collapsed';
+  }
+  $fieldset = array(
+    '#title' => $element['#title'],
+    '#value' => '',
+    '#description' => $element['#description'],
+    '#children' => $layout,
+    '#attributes' => array('class' => $classes),
+  );
+
+  return theme('fieldset', array('element' => $fieldset));
+}
+
+/**
+ * Loads the field values with appropriate data.
+ *
+ * This function is called by the tripal_chado_field_storage_load() for
+ * each property managed by the field_chado_storage storage type.  This is
+ * an optional hook function that is only needed if the field has
+ * multiple form elements.
+ *
+ * @param $field
+ * @param $entity
+ * @param $base_table
+ * @param $record
+ */
+function tripal_chado_dbxref_field_load($field, $entity, $base_table, $record) {
+
+  $field_name = $field['field_name'];
+  $field_type = $field['type'];
+  $field_table = $field['settings']['chado_table'];
+
+  // Set some defaults for the empty record.
+  $entity->{$field_name}['und'][0] = array(
+    'value' => '',
+    'dbxref__db_id' => '',
+    'dbxref__accession' => '',
+    'dbxref__version' => '',
+    'dbxref__description' => '',
+  );
+
+  $linker_table = $base_table . '_dbxref';
+  $options = array('return_array' => 1);
+  $record = chado_expand_var($record, 'table', $linker_table, $options);
+  $i = 0;
+  foreach ($record->$linker_table as $index => $linker) {
+    $dbxref = $linker->dbxref_id;
+    $entity->{$field_name}['und'][$i] = array(
+      'value' => $dbxref->dbxref_id,
+      'dbxref__db_id' => $dbxref->db_id->db_id,
+      'dbxref__accession' => $dbxref->accession,
+      'dbxref__version' => $dbxref->version,
+      'dbxref__description' => $dbxref->description,
+    );
+    $i++;
+  }
+}
+

+ 1 - 1
tripal_chado/includes/loaders/tripal_chado.fasta_loader.inc

@@ -135,7 +135,7 @@ function tripal_feature_fasta_load_form() {
        this symbol in your regular expression.')
   );
 
-  // Advanced database cross-reference optoins
+  // Advanced database cross reference options.
   $form['advanced']['db'] = array('#type' => 'fieldset',
     '#title' => t('External Database Reference'),'#weight' => 6,'#collapsed' => TRUE
   );

+ 16 - 3
tripal_chado/includes/tripal_chado.field_storage.inc

@@ -6,9 +6,14 @@
 function tripal_chado_field_storage_info() {
   return array(
     'field_chado_storage' => array(
-      'label' => t('Chado storage'),
+      'label' => t('Chado'),
       'description' => t('Stores fields in the local Chado database.'),
       'settings' => array(),
+      // The logo_url key is supported by Tripal. It's not a Drupal key. It's
+      // used for adding a logo or picture for the data store to help make it
+      // more easily recognized on the  field_ui_field_overview_form. Ideally
+      // the URL should point to a relative path on the local Drupal site.
+      'logo_url' => url(drupal_get_path('module', 'tripal') . '/theme/images/250px-ChadoLogo.png'),
     ),
   );
 }
@@ -263,8 +268,16 @@ function tripal_chado_field_storage_load($entity_type, $entities, $age,
     // Get the base record if one exists
     $columns = array('*');
     $match = array($pkey_field => $record_id);
-    $record = chado_select_record($base_table, $columns, $match);
-    $record = $record[0];
+    $record = chado_generate_var($base_table, $match);
+
+    // For now, expand all 'text' fields.
+    // TODO: we want to be a bit smarter and allow the user to configure this
+    // for now we'll expand.
+    foreach ($schema['fields'] as $field_name => $details) {
+      if ($schema['fields'][$field_name]['type'] == 'text') {
+        $record = chado_expand_var($record, 'field', $base_table . '.' . $field_name);
+      }
+    }
 
     // Iterate through the entity's fields so we can get the column names
     // that need to be selected from each of the tables represented.

+ 4 - 0
tripal_chado/theme/css/tripal_chado.css

@@ -21,4 +21,8 @@
    overflow: scroll;
    white-space: normal;
    background-color: white;
+}
+
+.form-field-ui-field-overview-storage-logo {
+  height: 10px;
 }

+ 163 - 40
tripal_chado/tripal_chado.module

@@ -395,9 +395,10 @@ function tripal_chado_field_info() {
         'active' => TRUE
       ),
     ),
+
     'dbxref_id' => array(
-      'label' => t('Cross-reference'),
-      'description' => t('This record can be cross-referenced with a record in
+      'label' => t('Cross reference'),
+      'description' => t('This record can be cross referenced with a record in
           another online database. This field is intended for the most prominent
           reference.  At a minimum, the database and accession must be provided.'),
       'default_widget' => 'tripal_chado_dbxref_id_widget',
@@ -477,6 +478,26 @@ function tripal_chado_field_info() {
         'active' => TRUE
       ),
     ),
+
+    // The field provides form elements for adding multiple dbxrefs to an
+    // entity that in turn get stored in a [base]_dbxref table of Chado
+    // (e.g. organism_dbxref, feature_dbxref).  This is different
+    // from the dbxref_id field as that is specific to a dbxref_id in
+    // a base table.
+    'dbxref' => array(
+      'label' => t('Cross references'),
+      'description' => t('This record can be cross referenced with a record in
+          another online database. This field is intended for one or more
+          references.  At a minimum, the database and accession must be provided.'),
+      'default_widget' => 'tripal_chado_dbxref_widget',
+      'default_formatter' => 'tripal_chado_dbxref_formatter',
+      'settings' => array(),
+      'storage' => array(
+        'type' => 'field_chado_storage',
+        'module' => 'tripal_chado',
+        'active' => TRUE
+      ),
+    ),
   );
   return $fields;
 }
@@ -491,9 +512,20 @@ function tripal_chado_field_widget_info() {
       'field types' => array('organism_id')
     ),
     'tripal_chado_dbxref_id_widget' => array(
-      'label' => t('Cross-reference'),
+      'label' => t('Cross reference'),
       'field types' => array('dbxref_id'),
-      'description' => t('This record can be cross-referenced with a record in another online database. This field is intended for the most prominent reference.  At a minimum, the database and accession must be provided.'),
+      'description' => t('This record can be cross referenced with a record in
+        another online database. This field is intended for the most
+        prominent reference.  At a minimum, the database and accession
+        must be provided.'),
+    ),
+    'tripal_chado_dbxref_widget' => array(
+      'label' => t('Cross references'),
+      'field types' => array('dbxref'),
+      'description' => t('This record can be cross referenced with a record
+        in another online database. This field is intended for the most
+        prominent reference.  At a minimum, the database and accession
+        must be provided.'),
     ),
     'tripal_chado_md5checksum_checkbox_widget' => array(
       'label' => t('MD5 Checksum Checkbox'),
@@ -527,9 +559,13 @@ function tripal_chado_field_formatter_info() {
       'field types' => array('organism_id')
     ),
     'tripal_chado_dbxref_id_formatter' => array(
-      'label' => t('Cross-reference'),
+      'label' => t('Cross reference'),
       'field types' => array('dbxref_id')
     ),
+    'tripal_chado_dbxref_formatter' => array(
+      'label' => t('Cross references'),
+      'field types' => array('dbxref')
+    ),
     'tripal_chado_md5checksum_formatter' => array(
       'label' => t('MD5 checksum'),
       'field types' => array('md5checksum')
@@ -634,6 +670,11 @@ function tripal_chado_field_formatter_view($entity_type, $entity, $field,
           tripal_chado_dbxref_id_formatter($element, $entity_type, $entity, $field,
               $instance, $langcode, $items, $display);
           break;
+        case 'tripal_chado_dbxref_formatter':
+          module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref');
+          tripal_chado_dbxref_formatter($element, $entity_type, $entity, $field,
+              $instance, $langcode, $items, $display);
+          break;
         case 'tripal_chado_md5checksum_formatter':
           module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
           tripal_chado_md5checksum_checkbox_formatter($element, $entity_type, $entity, $field,
@@ -683,6 +724,11 @@ function tripal_chado_field_widget_form(&$form, &$form_state, $field,
       module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref_id');
       tripal_chado_dbxref_id_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
       break;
+    case 'tripal_chado_dbxref_widget':
+      form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/dbxref');
+      module_load_include('inc', 'tripal_chado', 'includes/fields/dbxref');
+      tripal_chado_dbxref_widget($widget, $form, $form_state, $field, $instance, $langcode, $items, $delta, $element);
+      break;
     case 'tripal_chado_md5checksum_checkbox_widget':
       form_load_include($form_state, 'inc', 'tripal_chado', 'includes/fields/md5checksum');
       module_load_include('inc', 'tripal_chado', 'includes/fields/md5checksum');
@@ -717,6 +763,9 @@ function tripal_chado_field_widget_form(&$form, &$form_state, $field,
  *
  * The field_ui_display_overview_form is used for formatting the display
  * or layout of fields attached to an entity and shown on the entity view page.
+ *
+ * This function removes the property adder field as that is really not meant
+ * for users to show or manage.
  */
 function tripal_chado_form_field_ui_display_overview_form_alter(&$form, &$form_state, $form_id) {
   // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
@@ -734,6 +783,9 @@ function tripal_chado_form_field_ui_display_overview_form_alter(&$form, &$form_s
  *
  * The field_ui_field_overview_form is used for ordering and configuring the
  * fields attached to an entity.
+ *
+ * This function removes the property adder field as that is really not meant
+ * for users to show or manage.
  */
 function tripal_chado_form_field_ui_field_overview_form_alter(&$form, &$form_state, $form_id) {
   // Remove the kvproperty_addr field as it isn't ever displayed. It's just used
@@ -917,32 +969,101 @@ function tripal_chado_add_bundle_fields($entity_type, $bundle, $term) {
     }
   }
 
-  // Adds the fields for the base table to the entity.
-  tripal_chado_add_bundle_base_fields($entity_type, $bundle_name, $bundle_data);
-
   // Save the mapping information so that we can reuse it when we need to
   // look things up for later for an entity
   tripal_set_bundle_variable('chado_cvterm_id', $bundle->id, $bundle_data['cvterm_id']);
   tripal_set_bundle_variable('chado_table', $bundle->id, $bundle_data['data_table']);
   tripal_set_bundle_variable('chado_column', $bundle->id, $bundle_data['field']);
 
-  // Check to see if there are any kv-property tables associated to this
+  //////////////////////////////////////////////////////////////////////////////
+  // ADD FIELDS TO BUNDLE
+  ////////////////////////////////////////////////////////////////////////////
+
+  ////
+  //
+  // Base table fields.
+  //
+  // Adds the fields for the base table to the entity.  Adds fields
+  // for all columns including FK fields.  Excludes primary key and the
+  // type_id field (that's inherent in the bundle).
+  tripal_chado_add_bundle_base_fields($entity_type, $bundle_name, $bundle_data);
+
+  ////
+  //
+  // Property table fields.
+  //
+  // Check to see if there are any property tables with FKs to this
   // base table. If so, add the fields for that type of table.
-  $proptable = $bundle_data['data_table'] . 'prop';
-  if (chado_table_exists($proptable)) {
-    tripal_chado_add_bundle_kvproperty_adder_field($entity_type, $bundle_name, $proptable);
+  $prop_table = $bundle_data['data_table'] . 'prop';
+  if (chado_table_exists($prop_table)) {
+    tripal_chado_add_bundle_kvproperty_adder_field($entity_type, $bundle_name, $prop_table);
+  }
+
+  ////
+  //
+  // Dbxref table fields.
+  //
+  // Check to see if there are any dbxref tables with FKs to this
+  // base table. If so, add the fields for that type of table.
+  $dbxref_table = $bundle_data['data_table'] . '_dbxref';
+  if (chado_table_exists($dbxref_table)) {
+    tripal_chado_add_bundle_dbxref_field($entity_type, $bundle_name, $bundle_data['data_table'], $dbxref_table);
   }
 }
 
 /**
- * Adds the fields for a kv-property table fields
+ * Adds the fields for managing xrefs that are stored in a [base]_dbxref table.
+ *
+ * @param $entity_type
+ * @param $bundle_name
+ * @param $base_table
+ * @param $dbxref_table
+ */
+function tripal_chado_add_bundle_dbxref_field($entity_type_name, $bundle_name, $base_table, $dbxref_table) {
+  // We already have a dbxref_id field.
+  $field_name = $dbxref_table;
+
+  // Initialize the field array.
+  $field_info = array(
+    'field_type' => 'dbxref',
+    'widget_type' => 'tripal_fields_kvproperty_adder_widget',
+    'widget_settings' => array('display_label' => 1),
+    'description' => '',
+    'label' => 'Cross References',
+    'is_required' => 0,
+    'cardinality' => FIELD_CARDINALITY_UNLIMITED,
+    'field_settings' => array(
+      'chado_table' => $dbxref_table,
+      'chado_column' => '',
+      'semantic_web' => array(
+        // The type is the term from a vocabulary that desribes this field..
+        'type' => '',
+        // The namepsace for the vocabulary (e.g. 'foaf').
+        'ns' => '',
+        // The URL for the namespace.  It must be that the type can be
+        // appended to the URL.
+        'nsurl' => '',
+      ),
+    ),
+  );
+
+  // If the base table has a 'dbxref_id' then change the label to
+  // indicate these are secondary cross references.
+  $schema = chado_get_schema($base_table);
+  if (array_key_exists('dbxref_id', $schema['fields'])) {
+    $field_info['label'] = 'Secondary Cross References';
+  }
+  tripal_add_bundle_field($field_name, $field_info, $entity_type_name, $bundle_name);
+}
+/**
+ * Adds the fields for managing properties that are stored in a prop table.
  *
  * @param $entity_type_name
  * @param $bundle_name
  * @param $kv_table
  */
 function tripal_chado_add_bundle_kvproperty_adder_field($entity_type_name, $bundle_name, $kv_table) {
-  // First add a generic property field so that users can add new proeprty types.
+  // First add a generic property field so that users can add new property types.
   $field_name = $kv_table;
 
   // Initialize the field array.
@@ -988,6 +1109,9 @@ function tripal_chado_add_bundle_base_fields($entity_type_name, $bundle_name, $b
     $field_info = tripal_chado_get_table_column_field_default($table_name, $schema, $column_name);
 
 
+    // TODO: add in a call to drupal_alter to allow other modules to change
+    // the field settings.
+
     // Determine if the field is required.
     if (array_key_exists('not null', $details) and $details['not null'] === TRUE) {
       $field_info['is_required'] = array_key_exists('default', $details) ? 0 : 1;
@@ -1004,8 +1128,7 @@ function tripal_chado_add_bundle_base_fields($entity_type_name, $bundle_name, $b
       continue;
     }
 
-    // If this field is a foreign key field then we will have a special custom
-    // field provided by Tripal.
+    // If this field is a foreign key field then we will have a custom field.
     $is_fk = FALSE;
     if (array_key_exists('foreign keys', $schema)) {
       foreach ($schema['foreign keys'] as $remote_table => $fk_details) {
@@ -1031,6 +1154,12 @@ function tripal_chado_get_table_column_field_default($table_name, $schema, $colu
   $field = array(
     'field_type' => '',
     'widget_type' => '',
+    'description' => '',
+    'label' => ucwords(preg_replace('/_/', ' ', $column_name)),
+    'is_required' => 0,
+    'widget_settings' => array(
+      'display_label' => 1
+    ),
     'field_settings' => array(
       'chado_table' => $table_name,
       'chado_column' => $column_name,
@@ -1044,12 +1173,6 @@ function tripal_chado_get_table_column_field_default($table_name, $schema, $colu
         'nsurl' => '',
       ),
     ),
-    'widget_settings' => array(
-      'display_label' => 1
-    ),
-    'description' => '',
-    'label' => ucwords(preg_replace('/_/', ' ', $column_name)),
-    'is_required' => 0,
   );
 
   // Alter the field info array depending on the column details.
@@ -1136,8 +1259,8 @@ function tripal_chado_get_table_column_field_default($table_name, $schema, $colu
   elseif ($field['field_settings']['chado_column'] =='dbxref_id') {
     $field['field_type'] = 'dbxref_id';
     $field['widget_type'] = 'tripal_chado_primary_dbxref_widget';
-    $field['label'] = 'Primary Cross Reference';;
-    $field['description'] = 'This record can be cross-referenced with a ' .
+    $field['label'] = 'Cross Reference';
+    $field['description'] = 'This record can be cross referenced with a ' .
       'record in another online database. The primary reference is for the ' .
       'most prominent reference.  At a minimum, the database and accession ' .
       'must be provided.  To remove a set reference, change the database ' .
@@ -1332,22 +1455,22 @@ function tripal_chado_entity_property_info_alter(&$info) {
  *   The rendered value of the field specified by $field_name.
  */
 function tripal_chado_entity_property_get_value($entity, $options, $field_name, $entity_type) {
-	
-	$display = array(
-		'type' => '',
-		'label' => 'hidden',
-	);
-
-	$langcode = LANGUAGE_NONE;
-	$items = field_get_items($entity_type, $entity, $field_name);
-	if (count($items) == 1) {
-		$render_array = field_view_value($entity_type, $entity, $field_name, $items[0], $display, $langcode);
-	}
-	// @todo: handle fields with multiple values.
-	else {
-		$render_array = field_view_value($entity_type, $entity, $field_name, $items[0], $display, $langcode);
-		drupal_set_message('Tripal Chado currently only supports views integration for single value fields. The first value has been shown.', 'warning');
-	}
+
+  $display = array(
+    'type' => '',
+    'label' => 'hidden',
+  );
+
+  $langcode = LANGUAGE_NONE;
+  $items = field_get_items($entity_type, $entity, $field_name);
+  if (count($items) == 1) {
+    $render_array = field_view_value($entity_type, $entity, $field_name, $items[0], $display, $langcode);
+  }
+  // @todo: handle fields with multiple values.
+  else {
+    $render_array = field_view_value($entity_type, $entity, $field_name, $items[0], $display, $langcode);
+    drupal_set_message('Tripal Chado currently only supports views integration for single value fields. The first value has been shown.', 'warning');
+  }
 
   return drupal_render($render_array);
 }

+ 16 - 2
tripal_panes/theme/templates/tripal_panes_generic.tpl.php

@@ -127,20 +127,34 @@ function tripal_panes_generic_render_table($fields) {
   if (count($fields) == 0) {
     return '';
   }
+
   // Create the rows for the table.
   $header = array();
   $rows = array();
   foreach ($fields as $field) {
+    // We may have multiple values for the field, so we need to iterate
+    // through those values first and add each one.
+    $value = '';
+    foreach (element_children($field) as $index) {
+      $eo = 'odd';
+      if ($index % 2 == 0) {
+        $eo = 'even';
+      }
+      $value .= "<div class=\"field-item $eo\">" . $field[$index]['#markup'] . '</div>';
+    }
+
+    // Add the new row.
     $rows[] = array(
       array(
-        'data' => $field['#title'],
+        'data' => '<div class="field-label">' . $field['#title'] . '</div>',
         'header' => TRUE,
         'width' => '20%',
         'nowrap' => 'nowrap'
       ),
-      '<span class="field field-name-' . preg_replace('/_/', '-', $field['#field_name']) . '">' . $field[0]['#markup'] . '</span>'
+      $value,
     );
   }
+
   // Theme the table.
   return theme_table(array(
     'header' => $header,