array(
'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'),
),
);
}
/**
* Implements hook_field_storage_write().
*/
function tripal_chado_field_storage_write($entity_type, $entity, $op, $fields) {
// Get the bundle and the term for this entity.
$bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
$term = entity_load('TripalTerm', array('id' => $entity->term_id));
$term = reset($term);
// Convert the Tripal term entity into the appropriate record in Chado.
$dbxref = tripal_get_dbxref(array('accession' => $term->accession, 'db_id' => array('name' => $term->vocab->vocabulary)));
$cvterm = tripal_get_cvterm(array('dbxref_id' => $dbxref->dbxref_id));
// Get the base table, type field and record_id from the entity.
$base_table = $entity->chado_table;
$type_field = $entity->chado_column;
$record = $entity->chado_record;
$record_id = $entity->chado_record_id;
$base_schema = chado_get_schema($base_table);
$base_pkey = $base_schema['primary key'][0];
// Convert the fields into a key/value list of fields and their values.
$field_vals = tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity);
// First, write the record for the base table. If we have a record id then
// this is an update and we need to set the primary key. If not, then this
// is an insert and we need to set the type_id if the table supports it.
$values = $field_vals[$base_table];
if ($record_id) {
$values[$base_pkey] = $record_id;
}
elseif ($type_field) {
$values[$type_field] = $cvterm->cvterm_id;
}
$base_record_id = tripal_chado_field_storage_write_table($base_table, $values);
// If this is an insert then add the chado_entity record.
if ($op == FIELD_STORAGE_INSERT) {
// Add the record to the proper chado entity table
$chado_entity_table = tripal_chado_get_bundle_entity_table($bundle);
$record = array(
'entity_id' => $entity->id,
'record_id' => $base_record_id,
);
$success = drupal_write_record($chado_entity_table, $record);
if (!$success) {
drupal_set_message('Unable to insert new Chado entity.', 'error');
}
}
// Now that we have handled the base table, we need to handle linking tables.
foreach ($field_vals as $table_name => $details) {
// Skip the base table as we've already dealt with it.
if ($table_name == $base_table) {
continue;
}
foreach ($details as $delta => $values) {
$record_id = tripal_chado_field_storage_write_table($table_name, $values);
}
}
}
/**
* Write (inserts/updates/deletes) values for a Chado table.
*
* The $values array is of the same format used by chado_insert_record() and
* chado_update_record(). However, both of those methods will use any nested
* arrays (i.e. representing foreign keys) to select an appropriate record ID
* that can be substituted as the value. Here, the nested arrays are
* either inserted or updated as well, but the choice is determined if the
* primary key value is present. If present an update occurs, if not present
* then an insert occurs.
*
* This function is recursive and nested arrays from the lowest point of the
* "tree" are dealt with first.
*
* @param $table_name
* The name of the table on which the insertion/update is performed.
* @param $values
* The values array for the insertion.
*
* @throws Exception
*
* @return
* The unique record ID.
*/
function tripal_chado_field_storage_write_table($table_name, $values) {
$schema = chado_get_schema($table_name);
$fkeys = $schema['foreign keys'];
$pkey = $schema['primary key'][0];
// Fields with a cardinality greater than 1 will often submit an
// empty form. We want to remove these empty submissions. We can detect
// them if all of the fields are empty.
$num_empty = 0;
foreach ($values as $column => $value) {
if (!$value) {
$num_empty++;
}
}
if ($num_empty == count(array_keys($values))) {
return '';
}
// If the primary key column has a value but all other values are empty then
// this is a delete.
if (array_key_exists($pkey, $values) and $values[$pkey]) {
$num_vals = 0;
foreach ($values as $value) {
if ($value) {
$num_vals++;
}
}
if ($num_vals == 1) {
$new_vals[$pkey] = $values[$pkey];
if (!chado_delete_record($table_name, $new_vals)) {
throw new Exception('Could not delete record from table: "' . $table_name . '".');
}
return '';
}
}
// If the primary key column does not have a value then this is an insert.
if (!array_key_exists($pkey, $values) or !$values[$pkey] or !isset($values[$pkey])) {
// Before inserting, we want to make sure the record does not
// already exist. Using the unique constraint check for a matching record.
$options = array('is_duplicate' => TRUE);
$is_duplicate = chado_select_record($table_name, array('*'), $values, $options);
if($is_duplicate) {
$record = chado_select_record($table_name, array('*'), $values);
return $record[0]->$pkey;
}
// Insert the values array as a new record in the table but remove the
// pkey as it should be set.
$new_vals = $values;
unset($new_vals[$pkey]);
$record = chado_insert_record($table_name, $new_vals);
if ($record === FALSE) {
throw new Exception('Could not insert Chado record into table: "' . $table_name . '".');
}
return $record[$pkey];
}
// If we've made it to this point then this is an update.
// TODO: what if the unique constraint matches another record? That is
// not being tested for here.
$match[$pkey] = $values[$pkey];
if (!chado_update_record($table_name, $match, $values)) {
drupal_set_message("Could not update Chado record in table: $table_name.", 'error');
}
return $values[$pkey];
}
/**
* Implements hook_field_storage_pre_load().
*
* Adds a 'chado_record' object containing the base record for the
* entity.
*/
function tripal_chado_field_storage_pre_load($entity_type, $entities, $age,
$fields, $options) {
// Itereate through the entities and add in the Chado record.
foreach ($entities as $id => $entity) {
// Only deal with Tripal Entities.
if ($entity_type != 'TripalEntity') {
continue;
}
$record_id = NULL;
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 record id for the fields of this entity.
$chado_entity_table = tripal_chado_get_bundle_entity_table($bundle);
$details = db_select($chado_entity_table, 'ce')
->fields('ce')
->condition('entity_id', $entity->id)
->execute()
->fetchObject();
if ($details) {
$record_id = isset($details->record_id) ? $details->record_id : '';
}
$entity->chado_table = $base_table;
$entity->chado_record_id = $record_id;
$entity->chado_column = $type_field;
}
if ($record_id) {
// Get this table's schema.
$schema = chado_get_schema($base_table);
$pkey_field = $schema['primary key'][0];
// Get the base record if one exists
$columns = array('*');
$match = array($pkey_field => $record_id);
$record = chado_generate_var($base_table, $match);
$entity->chado_record = $record;
}
}
}
/**
* Implements hook_field_storage_load().
*
* Responsible for loading the fields from the Chado database and adding
* their values to the entity.
*/
function tripal_chado_field_storage_load($entity_type, $entities, $age,
$fields, $options) {
$load_current = $age == FIELD_LOAD_CURRENT;
global $language;
$langcode = $language->language;
foreach ($entities as $id => $entity) {
$base_table = $entity->chado_table;
$type_field = $entity->chado_column;
$record_id = $entity->chado_record_id;
$record = $entity->chado_record;
$schema = chado_get_schema($base_table);
// Iterate through the entity's fields so we can get the column names
// that need to be selected from each of the tables represented.
$tables = array();
foreach ($fields as $field_id => $ids) {
// By the time this hook runs, the relevant field definitions have been
// populated and cached in FieldInfo, so calling field_info_field_by_id()
// on each field individually is more efficient than loading all fields in
// memory upfront with field_info_field_by_ids().
$field = field_info_field_by_id($field_id);
$field_name = $field['field_name'];
$field_type = $field['type'];
$field_module = $field['module'];
// Get the instance for this field. If no instance exists then skip
// loading of this field. This can happen when a field is deleted from
// a bundle using the user UI form.
// TODO: how to deal with deleted fields?
$instance = field_info_instance($entity_type, $field_name, $entity->bundle);
if (!$instance) {
continue;
}
// Skip fields that don't map to a Chado table.
if (!array_key_exists('settings', $instance) or
!array_key_exists('chado_table', $instance['settings'])) {
continue;
}
// Get the Chado table and column for this field.
$field_table = $instance['settings']['chado_table'];
$field_column = $instance['settings']['chado_column'];
// There are only two types of fields: 1) fields that represent a single
// column of the base table, or 2) fields that represent a linked record
// in a many-to-one relationship with the base table.
// Type 1: fields from base tables.
if ($field_table == $base_table) {
// Set an empty value by default, and if there is a record, then update.
$entity->{$field_name}['und'][0]['value'] = '';
if ($record and property_exists($record, $field_column)) {
// If the field column is an object then it's a FK to another table.
// and because $record object is created by the chado_generate_var()
// function we must go one more level deeper to get the value
if (is_object($record->$field_column)) {
$fkey_column = $field_column;
foreach($schema['foreign keys'] as $table => $fk_details) {
foreach($fk_details['columns'] as $lfkey => $rfkey) {
if ($lfkey == $field_column) {
$fkey_column = $rfkey;
}
}
}
$entity->{$field_name}['und'][0]['chado-' . $field_table . '__' . $field_column] = $record->$field_column->$fkey_column;
}
else {
// For non FK fields we'll make the field value be the same
// as the column value.
$entity->{$field_name}['und'][0]['value'] = $record->$field_column;
$entity->{$field_name}['und'][0]['chado-' . $field_table . '__' . $field_column] = $record->$field_column;
}
}
// Allow the creating module to alter the value if desired. The
// module should do this if the field has any other form elements
// that need populationg besides the value which was set above.
tripal_load_include_field_class($field_type);
if (class_exists($field_type) and is_subclass_of($field_type, 'TripalField')) {
$tfield = new $field_type($field, $instance);
$tfield->load($entity, array('record' => $record));
}
// For text fields that were not handled by a TripalField class we
// want to automatically expand those fields.
else {
if ($schema['fields'][$field_column]['type'] == 'text') {
$record = chado_expand_var($record, 'field', "$field_table.$field_column");
$entity->{$field_name}['und'][0]['value'] = $record->$field_column;
// Text fields that have a text_processing == 1 setting need a
// special 'format' element too:
if (array(key_exists('text_processing', $instance['settings']) and
$instance['settings']['text_processing'] == 1)) {
// TODO: we need a way to write the format back to the
// instance settings if the user changes it when using the form.
$entity->{$field_name}['und'][0]['format'] = array_key_exists('format', $instance['settings']) ? $instance['settings']['format'] : 'full_html';
}
}
}
}
// Type 2: fields for linked records. These fields will have any number
// of form elements that might need populating so we'll offload the
// loading of these fields to the field itself.
if ($field_table != $base_table) {
// Set an empty value by default, and let the hook function update it.
$entity->{$field_name}['und'][0]['value'] = '';
tripal_load_include_field_class($field_type);
if (class_exists($field_type) && method_exists($field_type, 'load')) {
$tfield = new $field_type($field, $instance);
$tfield->load($entity, array('record' => $record));
}
}
} // end: foreach ($fields as $field_id => $ids) {
} // end: foreach ($entities as $id => $entity) {
}
/**
* Merges the values of all fields into a single array keyed by table name.
*/
function tripal_chado_field_storage_write_merge_fields($fields, $entity_type, $entity) {
$all_fields = array();
$base_fields = array();
// Iterate through all of the fields and organize them into a
// new fields array keyed by the table name
foreach ($fields as $field_id => $ids) {
// Get the field name and information about it.
$field = field_info_field_by_id($field_id);
$field_name = $field['field_name'];
$instance = field_info_instance('TripalEntity', $field['field_name'], $entity->bundle);
// Some fields (e.g. chado_linker_cvterm_adder) don't add data to
// Chado so they don't have a table, but they are still attached to the
// entity. Just skip these.
if (!array_key_exists('chado_table', $instance['settings'])) {
continue;
}
$chado_table = $instance['settings']['chado_table'];
$chado_column = $instance['settings']['chado_column'];
$base_table = $instance['settings']['base_table'];
// Iterate through the field's items. Fields with cardinality ($delta) > 1
// are multi-valued.
$items = field_get_items($entity_type, $entity, $field_name);
$temp = array();
foreach ($items as $delta => $item) {
// A field may have multiple items. The field can use items
// indexed with "chado-" to represent values that should map directly
// to chado tables and fields.
$value_set = FALSE;
foreach ($item as $item_name => $value) {
$matches = array();
if (preg_match('/^chado-(.*?)__(.*?)$/', $item_name, $matches)) {
$table_name = $matches[1];
$column_name = $matches[2];
// If this field belongs to the base table then we just add
// those values in... there's no delta.
if ($table_name == $base_table) {
$base_fields[$table_name][$column_name] = $value;
}
else {
$temp[$table_name][$delta][$column_name] = $value;
}
$value_set = TRUE;
}
}
// If there is no value set for the field using the
// chado-[table_name]__[field name] naming schema then check if a 'value'
// item is present and if so use that for the table column value.
if (!$value_set and array_key_exists('value', $items[$delta]) and
!is_array($items[$delta]['value'])) {
// If this field belongs to the base table then we just add
// those values in... there's no delta.
if ($base_table == $chado_table) {
$base_fields[$chado_table][$chado_column] = $item['value'];
}
else {
$temp[$chado_table][$delta][$chado_column] = $item['value'];
}
}
}
// Now merge the records for this field with the $new_fields array
foreach ($temp as $table_name => $details) {
foreach ($details as $delta => $list) {
$all_fields[$table_name][] = $list;
}
}
}
$all_fields = array_merge($base_fields, $all_fields);
return $all_fields;
}
/**
* Implements hook_field_storage_query().
*/
function tripal_chado_field_storage_query($query) {
// Initialize the result array.
$result = array(
'TripalEntity' => array()
);
// There must always be an entity filter that includes the bundle. Otherwise
// it would be too overwhelming to search every table of every content type
// for a matching field.
if (!array_key_exists('bundle', $query->entityConditions)) {
return $result;
}
$bundle = tripal_load_bundle_entity(array('name' => $query->entityConditions['bundle']));
if (!$bundle) {
return;
}
$data_table = $bundle->data_table;
$type_column = $bundle->type_column;
$type_id = $bundle->type_id;
$schema = chado_get_schema($data_table);
$pkey = $schema['primary key'][0];
// Initialize the Query object.
$cquery = chado_db_select($data_table, 'base');
$cquery->fields('base', array($pkey));
// Iterate through all the conditions and add to the filters array
// a chado_select_record compatible set of filters.
foreach ($query->fieldConditions as $index => $condition) {
$field = $condition['field'];
$field_name = $field['field_name'];
$field_type = $field['type'];
// Skip conditions that don't belong to this storage type.
if ($field['storage']['type'] != 'field_chado_storage') {
continue;
}
// The Chado settings for a field are part of the instance and each bundle
// can have the same field but with different Chado mappings. Therefore,
// we need to iterate through the bundles to get the field instances.
foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
// If there is a bundle filter for the entity and if the field is not
// associated with the bundle then skip it.
if (array_key_exists('bundle', $query->entityConditions)) {
if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and
!array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
continue;
}
else if ($query->entityConditions['bundle']['value'] != $bundle_name) {
continue;
}
}
// Allow the field to update the query object.
$instance = field_info_instance('TripalEntity', $field['field_name'], $bundle_name);
if (tripal_load_include_field_class($field_type)) {
$field_obj = new $field_type($field, $instance);
$field_obj->query($cquery, $condition);
}
// If there is no ChadoField class for this field then add the
// condition as is.
else {
$alias = $field['field_name'];
$chado_table = $instance['settings']['chado_table'];
$base_table = $instance['settings']['base_table'];
$bschema = chado_get_schema($base_table);
$bpkey = $bschema['primary key'][0];
if ($chado_table == $base_table) {
// Get the base table column that is associated with the term
// passed as $condition['column'].
$base_field = tripal_get_chado_semweb_column($chado_table, $condition['column']);
$cquery->condition('base.' . $base_field , $condition['value'], $condition['operator']);
}
if ($chado_table != $base_table) {
// TODO: I don't think we'll get here because linker fields will
// always have a custom field that should implement a query()
// function. But just in case here's a note to handle it.
}
}
}
} // end foreach ($query->fieldConditions as $index => $condition) {
// Now join with the chado entity table to get published records only.
$chado_entity_table = tripal_chado_get_bundle_entity_table($bundle);
$cquery->join($chado_entity_table, 'CE', "CE.record_id = base.$pkey");
$cquery->join('tripal_entity', 'TE', "CE.entity_id = TE.id");
$cquery->fields('CE', array('entity_id'));
$cquery->fields('TE', array('bundle'));
if (array_key_exists('start', $query->range)) {
$cquery->range($query->range['start'], $query->range['length']);
}
// Make sure we only get records of the correct entity type
$cquery->condition('TE.bundle', $query->entityConditions['bundle']['value']);
// Now set any ordering.
foreach ($query->order as $index => $sort) {
// Add in property ordering.
if ($order['type'] == 'property') {
}
// Add in filter ordering
if ($sort['type'] == 'field') {
$field = $sort['specifier']['field'];
$field_type = $field['type'];
$field_name = $field['field_name'];
// Skip sorts that don't belong to this storage type.
if ($field['storage']['type'] != 'field_chado_storage') {
continue;
}
$column = $sort['specifier']['column'];
$direction = $sort['direction'];
// The Chado settings for a field are part of the instance and each bundle
// can have the same field but with different Chado mappings. Therefore,
// we need to iterate through the bundles to get the field instances.
foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
// If there is a bundle filter for the entity and if the field is not
// associated with the bundle then skip it.
if (array_key_exists('bundle', $query->entityConditions)) {
if (strtolower($query->entityConditions['bundle']['operator']) == 'in' and
!array_key_exists($bundle_name, $query->entityConditions['bundle']['value'])) {
continue;
}
else if ($query->entityConditions['bundle']['value'] != $bundle_name) {
continue;
}
}
// See if there is a ChadoField class for this instance. If not then do
// our best to order the field.
$instance = field_info_instance('TripalEntity', $field_name, $bundle_name);
if (tripal_load_include_field_class($field_type)) {
$field_obj = new $field_type($field, $instance);
$field_obj->queryOrder($cquery, array('column' => $column, 'direction' => $direction));
}
// There is no class so do our best to order the data by this field
else {
$base_table = $instance['settings']['base_table'];
$chado_table = $instance['settings']['chado_table'];
$table_column = tripal_get_chado_semweb_column($chado_table, $column);
if ($table_column) {
if ($chado_table == $base_table) {
$cquery->orderBy('base.' . $table_column, $direction);
}
else {
// TODO: how do we handle a field that doesn't map to the base table.
// We would expect that all of these would be custom field and
// the ChadoField::queryOrder() function would be implemented.
}
}
else {
// TODO: handle when the name can't be matched to a table column.
}
}
} // end foreach ($field['bundles']['TripalEntity'] as $bundle_name) {
} // end if ($sort['type'] == 'field') {
} // end foreach ($query->order as $index => $sort) {
//print_r($cquery->__toString());
//print_r($cquery->getArguments());
$records = $cquery->execute();
$result = array();
while ($record = $records->fetchObject()) {
$ids = array($record->entity_id, 0, $record->bundle);
$result['TripalEntity'][$record->entity_id] = entity_create_stub_entity('TripalEntity', $ids);
}
return $result;
}
/**
* Implements hook_field_storage_bundle_mapping_form().
*
* This is a Tripal added hook to the field storage API.
*/
function tripal_chado_field_storage_bundle_mapping_form($form, &$form_state,
$term, &$submit_disabled) {
$selected_term_id = $term->cvterm_id;
// Initialize the form.
$form = array();
// Get the form default values.
$default = array(
'table' => '',
'has_all' => 'No',
'use_linker' => 'Yes',
'use_prop' => 'Yes',
'type_column' => '',
'prop_term_name' => '',
'prop_term_value' => '',
);
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;
}
}
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'];
}
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'];
}
if (array_key_exists('chado_type_use_prop', $form_state['values'])
and $form_state['values']['chado_type_use_prop']) {
$default['use_prop'] = $form_state['values']['chado_type_use_prop'];
}
if (array_key_exists('type_column', $form_state['values'])
and $form_state['values']['type_column']) {
$default['type_column'] = $form_state['values']['type_column'];
}
if (array_key_exists('prop_term_name', $form_state['values'])
and $form_state['values']['prop_term_name']) {
$default['prop_term_name'] = $form_state['values']['prop_term_name'];
}
if (array_key_exists('prop_term_value', $form_state['values'])
and $form_state['values']['prop_term_value']) {
$default['prop_term_value'] = $form_state['values']['prop_term_value'];
}
$form['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['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'];
// Return now if the user hasn't provided a table.
if (!$default['table']) {
return $form;
}
// Get the schema for this table.
$schema = chado_get_schema($default['table']);
// Ask the user if all of the records in the default table are of one type.
tripal_chado_field_storage_bundle_mapping_form_add_allrecs($form,
$form_state, $term, $default);
// If all the records in the table are of this type then we're done.
if ($default['has_all'] == 'Yes') {
$submit_disabled = FALSE;
return $form;
}
// If this table has a field that maps to a cvterm record then we want
// to ask if the user wants to use that field.
tripal_chado_field_storage_bundle_mapping_form_add_type($form,
$form_state, $term, $default);
// If the type_column is set then we're done! We know the deafult table
// and column to map this data type.
if (!empty($default['type_column'])) {
$submit_disabled = FALSE;
return $form;
}
// Let's set the names of the linker and prop table for use below.
$linker_table = $default['table'] . '_cvterm';
$prop_table = $default['table']. 'prop';
$linker_exists = chado_table_exists($linker_table);
$prop_exists = chado_table_exists($prop_table);
// Ask the user if they want to use a linker table if it exists.
if($prop_exists) {
tripal_chado_field_storage_bundle_mapping_form_add_prop($form,
$form_state, $term, $prop_table, $default);
// If the user wants to use the property table then we're done!
if ($default['use_prop'] == 'Yes') {
$submit_disabled = FALSE;
return $form;
}
// Ask if the user wants to use the linker table if it exists.
if ($linker_exists) {
tripal_chado_field_storage_bundle_mapping_form_add_linker($form,
$form_state, $term, $linker_table, $default);
// If the user wants to use the linker table then we're done!
if ($default['use_linker'] == 'Yes') {
$submit_disabled = FALSE;
return $form;
}
}
}
// A prop table doesn't exist then ask the user if they want
// to use the linker table to associate records.
else if ($linker_exists) {
tripal_chado_field_storage_bundle_mapping_form_add_prop($form,
$form_state, $term, $prop_table, $default);
// If the user wants to use the linker table then we're done!
if ($default['use_linker'] == 'Yes') {
$submit_disabled = FALSE;
return $form;
}
}
$form['notice'] = array(
'#type' => 'item',
'#markup' => 'Unfortunately, the options selected above do not allow for mapping of this content type to records in Chado.'
);
return $form;
}
function tripal_chado_field_storage_bundle_mapping_form_add_type(&$form,
&$form_state, $term, &$default){
$term_name = $term->name;
// Get the list of columns in the default table.
$schema = chado_get_schema($default['table']);
$column_options = array('none' => '--None--');
$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;
}
}
// If this table has no types then just return.
if (count($column_options) == 1) {
$default['type_column'] = '';
return;
}
$default_column = !empty($default['type_column']) ? $default['type_column'] : 'type_id';
$form['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 . '". If you select "--None--" then you will be asked
if you the type can be identified using a property or linker table.
Only fields that have a foreign key to the cvterm table will be listed.',
'#default_value' => $default_column,
'#ajax' => array(
'callback' => "tripal_admin_add_type_form_ajax_callback",
'wrapper' => "tripal-vocab-select-form",
'effect' => 'fade',
'method' => 'replace'
),
);
// Set the default value so we can short circuit the form.
$default['type_column'] = $default_column;
if ($default['type_column'] == 'none') {
$default['type_column'] = '';
}
}
function tripal_chado_field_storage_bundle_mapping_form_add_allrecs(&$form,
&$form_state, $term, $default) {
$term_name = $term->name;
// Form elements to determine if all records in base table are of this type.
$form['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 "No" 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'
),
);
}
function tripal_chado_field_storage_bundle_mapping_form_add_linker(&$form,
&$form_state, $term, $linker_table, $default){
$form['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 distringuished
using a linker table, especially if there is no column
in the specified Chado table to identify the
record type. In these cases the linker table can be 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'
),
);
}
function tripal_chado_field_storage_bundle_mapping_form_add_prop(&$form,
&$form_state, $term, $prop_table, $default){
$form['chado_type_use_prop'] = array(
'#type' => 'radios',
'#title' => 'Do you want to use the "' . $prop_table . '" table
to distinguish between content types?',
'#options' => array(
'Yes' => 'Yes',
'No' => 'No'
),
'#description' => t('Sometimes records can be distringuished
using a value in property table, especially if there is no column
in the specified Chado table to identify the
record type. In these cases the property table can be used.'),
'#default_value' => $default['use_prop'],
'#ajax' => array(
'callback' => "tripal_admin_add_type_form_ajax_callback",
'wrapper' => "tripal-vocab-select-form",
'effect' => 'fade',
'method' => 'replace'
),
);
if ($default['use_prop'] == 'Yes') {
$prop_term_name = $default['prop_term_name'];
$prop_term_value = $default['prop_term_value'];
$form['prop_term_name'] = array(
'#title' => t('Property Type'),
'#type' => 'textfield',
'#description' => t('The content type "' . $term->name . '" must be
associated with a property type. The property type must be the
name of a term in a controlled vocabulary and the controlled
vocabulary should already be loaded into Tripal. Please select
the property type that will be used to identify records of this
type'),
'#required' => TRUE,
'#default_value' => $prop_term_name,
'#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
);
$form['prop_term_select_button'] = array(
'#type' => 'submit',
'#value' => t('Lookup Term'),
'#name' => 'select_prop_cvterm',
'#ajax' => array(
'callback' => "tripal_admin_add_type_form_ajax_callback",
'wrapper' => "tripal-vocab-select-form",
'effect' => 'fade',
'method' => 'replace'
),
);
// If the term has been provided by the user then we want to search for
// matching terms in the database and let them select among any matches.
if ($prop_term_name) {
$form['prop_terms_list'] = array(
'#type' => 'fieldset',
'#title' => t('Matching Terms'),
'#description' => t('Please select the term the best matches the
property type.')
);
$match = array(
'name' => $prop_term_name,
);
$pterms = chado_generate_var('cvterm', $match, array('return_array' => TRUE));
$pterms = chado_expand_var($pterms, 'field', 'cvterm.definition');
$num_terms = 0;
// Let the user select from any matching terms. Sometimes there may be
// more than one that match.
foreach ($pterms as $pterm) {
// 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($pterms) == 1) {
$default = TRUE;
$attrs = array('checked' => 'checked');
}
$term_element_name = 'prop_term-' . $pterm->cvterm_id;
$form['prop_terms_list'][$term_element_name] = array(
'#type' => 'checkbox',
'#title' => $pterm->name,
'#default_value' => $default,
'#attributes' => $attrs,
'#description' => 'Vocabulary: ' . $pterm->cv_id->name . ' (' . $pterm->dbxref_id->db_id->name . ') ' . $pterm->cv_id->definition .
'
Term: ' . $pterm->dbxref_id->db_id->name . ':' . $pterm->dbxref_id->accession . '. ' .
'
Definition: ' . $pterm->definition,
'#ajax' => array(
'callback' => "tripal_admin_add_type_form_ajax_callback",
'wrapper' => "tripal-vocab-select-form",
'effect' => 'fade',
'method' => 'replace'
),
);
$num_terms++;
}
$form['prop_term_value'] = array(
'#type' => 'textfield',
'#title' => 'Property Value',
'#description' => t('All properties have a type and a value. Above you
indicated the type of property. Now you must specify the value
that this properly must have in order to be considered of
type "' . $term->name . '".'),
'#default_value' => $prop_term_value,
'#required' => TRUE,
);
}
}
}
/**
* Implements hook_field_stoage_bundle_mapping_form_validate().
*/
function tripal_chado_field_storage_bundle_mapping_form_validate($form, &$form_state) {
// Get the form values
$default = array(
'table' => '',
'has_all' => 'No',
'use_linker' => 'Yes',
'use_prop' => 'Yes',
'type_column' => '',
'prop_term_name' => '',
'prop_term_value' => '',
);
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'];
}
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'];
}
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'];
}
if (array_key_exists('chado_type_use_prop', $form_state['values'])
and $form_state['values']['chado_type_use_prop']) {
$default['use_prop'] = $form_state['values']['chado_type_use_prop'];
}
if (array_key_exists('type_column', $form_state['values'])
and $form_state['values']['type_column']) {
$default['type_column'] = $form_state['values']['type_column'];
}
if (array_key_exists('prop_term_name', $form_state['values'])
and $form_state['values']['prop_term_name']) {
$default['prop_term_name'] = $form_state['values']['prop_term_name'];
// Make sure we have only one property cvterm selected
$num_selected = 0;
foreach ($form_state['values'] as $key => $value) {
$matches = array();
if (preg_match("/^prop_term-(\d+)$/", $key, $matches) and
$form_state['values']['prop_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 property term.');
}
else if ($num_selected > 1) {
form_set_error('prop_term-' . $cvterm_id, 'Please select only one property term from the list below.');
}
$form_state['prop_term'] = $term;
}
if (array_key_exists('prop_term_value', $form_state['values'])
and $form_state['values']['prop_term_value']) {
$default['prop_term_value'] = $form_state['values']['prop_term_value'];
}
// Make sure a default table is selected
if (!$default['table']) {
form_set_error('base_chado_table', 'Please select a default table.');
}
}
/**
* Implements hook_field_stoage_bundle_mapping_form_submit().
*
* Here we do no action on submit other than set the storage_args
* variable that is passed by reference.
*/
function tripal_chado_field_storage_bundle_mapping_form_submit($form,
&$form_state, $term, &$storage_args) {
// Get the form values
$default = array(
'table' => '',
'has_all' => 'No',
'use_linker' => 'Yes',
'use_prop' => 'Yes',
'type_column' => '',
'prop_term_name' => '',
'prop_term_value' => '',
);
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'];
}
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'];
}
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'];
}
if (array_key_exists('chado_type_use_prop', $form_state['values'])
and $form_state['values']['chado_type_use_prop']) {
$default['use_prop'] = $form_state['values']['chado_type_use_prop'];
}
if (array_key_exists('type_column', $form_state['values'])
and $form_state['values']['type_column']) {
$default['type_column'] = $form_state['values']['type_column'];
}
if (array_key_exists('prop_term_name', $form_state['values'])
and $form_state['values']['prop_term_name']) {
$default['prop_term_name'] = $form_state['values']['prop_term_name'];
}
if (array_key_exists('prop_term_value', $form_state['values'])
and $form_state['values']['prop_term_value']) {
$default['prop_term_value'] = $form_state['values']['prop_term_value'];
}
// If we have a type_column then we know this type uses a column in the
// base table, so we can set the storage args and return.
if ($default['type_column']) {
$storage_args['data_table'] = $default['table'];
$storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
$storage_args['type_column'] = $default['type_column'];
return;
}
// If the user indicated they wanted to use the prop table then we'll
// set the storage args and return.
if ($default['use_prop'] == 'Yes') {
$storage_args['data_table'] = $default['table'];
$storage_args['type_linker_table'] = $default['table'] . 'prop';
$storage_args['type_column'] = 'type_id';
$storage_args['type_id'] = $form_state['prop_term']->cvterm_id;
$storage_args['type_value'] = $default['prop_term_value'];
return;
}
// If the user indicated they wanted to use the linker tables then we'll
// set the storage args and return.
if ($default['use_linker'] == 'Yes') {
$storage_args['data_table'] = $default['table'];
$storage_args['type_id'] = $form_state['values']['selected_cvterm_id'];
$storage_args['type_linker_table'] = $default['table'] . '_cvterm';
$storage_args['type_column'] = 'cvterm_id';
return;
}
}