'example_dbxref', // the name of the table linking additional dbxrefs to this node
'base_foreign_key' => 'example_id', // key to link to the chado content created by this node
'base_key_value' => $example_id, // the value of the above key
'fieldset_title' => 'Additional References', // the non-translated title for this fieldset
'additional_instructions' => '' // a non-stranslated string providing additional instructions
);
// Finally, and add the additional form elements to the form
chado_add_node_form_dbxrefs($form, $form_state, $details);
return $form;
}
function chado_example_insert($node) {
// if there is an example_id in the $node object then this must be a sync so
// we can skip adding the chado_example as it is already there, although
// we do need to proceed with the rest of the insert
if (!property_exists($node, 'example_id')) {
// Add record to chado example table
// Add to any other tables needed
// Add all additional database references
// This function will create new database references as needed and select existing ones.
// Existing _dbxref links will be cleared and then re-added
chado_update_node_form_dbxrefs(
$node, // the node object passed in via hook_insert()
'example_dbxref', // the name of the _dbxref linking table
'example_id', // key to link to the chado content created by this node
$node->example_id // value of the above key
);
}
// Add record to chado_example linking example_id to new node
}
function chado_example_update($node) {
// Update record in chado example table
// Update any other tables needed
// Update all additional database references
// This function will create new database references as needed and select existing ones.
// Existing _dbxref links will be cleared and then re-added
chado_update_node_form_dbxrefs(
$node, // the node object passed in via hook_insert()
'example_dbxref', // the name of the _dbxref linking table
'example_id', // key to link to the chado content created by this node
$node->example_id // value of the above key
);
// Don't need to update chado_example linking table since niether example_id or nid can be changed in update
}
* @endcode
*
* @ingroup tripal_chado_node_api
*/
/**
* @section
* Additional Database References Form to be added to node forms
*/
/**
* Provides a form for adding to BASE_dbxref and dbxref tables
*
* @param $form
* The Drupal form array into which the dbxref elements will be added
* @param $form_state
* The corresponding form_state array for the form
* @param $details
* An array defining details needed by this form. Required Keys are:
* - linking_table: the name of the dbxref linking table (ie: feature_dbxref)
* - base_foreign_key: the name of the foreign key linking this table to the non-dbxref table (ie: feature_id)
* - base_key_value: the value of the base_foreign_key for the current form (ie: 999 if the feature_id=999)
* Optional keys include:
* - fieldset_title: the non-translated title for this fieldset
* - additional_instructions: a non-translated string providing additional instructions
* - select_options: must be an array where the [key] is a valid db_id and
* the [value] is the human-readable name of the option. This includes all databases
* in the chado db table by default
*
* @ingroup tripal_chado_node_api
*/
function chado_add_node_form_dbxrefs(&$form, &$form_state, $details) {
// Set defaults for optional fields
$details['fieldset_title'] = 'External References';
$details['additional_instructions'] = '';
// Get the list of databases to display in the dropdown.
if (isset($details['select_options'])) {
// the callee has provided a list
$db_options = $details['select_options'];
}
else {
// get the list of databases from the db table
$db_options = array(0 => 'Select a Database');
$options = array('order_by' => array('name' => 'ASC'));
$select = chado_select_record('db', array('db_id','name'), array(), $options);
foreach($select as $db) {
$db_options[$db->db_id] = $db->name;
}
}
// Tell tripal administrators how to add terms to the property types drop down.
$importance = (empty($db_options)) ? TRIPAL_WARNING : TRIPAL_INFO;
$tripal_message = tripal_set_message(
t('To add databases to the drop down list, you need to add
an external database reference.',
array(
'@dblink' => url('admin/tripal/chado/tripal_db/add')
)
),
$importance,
array('return_html' => TRUE)
);
// Group all of the chado node api fieldsets into vertical tabs.
$form['chado_node_api'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'css' => array(
'chado-node-api' => drupal_get_path('module', 'tripal_core') . '/theme/css/chado_node_api.css',
),
),
);
// the fieldset of the dbxref elements
$form['addtl_dbxrefs'] = array(
'#type' => 'fieldset',
'#title' => t($details['fieldset_title']),
'#description' => t('You may add additional database references by
selecting a database reference type from the dropdown and adding text. You may add
as many database references as desired by clicking the add button on the right. To
remove a database reference, click the remove button. ' . $details['additional_instructions']),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'chado_node_api',
'#weight' => 9,
'#attributes' => array('class' => array('chado-node-api','dbxrefs')),
'#attached' => array(
'js' => array(
'chado-node-api-vertical-tabs' => drupal_get_path('module', 'tripal_core') . '/theme/js/chadoNodeApi_updateVerticalTabSummary.js',
),
),
);
$form['addtl_dbxrefs']['admin_message'] = array(
'#type' => 'markup',
'#markup' => $tripal_message
);
// this form element is a tree, so that we don't puke all of the values into then node variable
// it is set as a tree, and keeps them in the $form_state['values']['dbxref_table'] heading.
$form['addtl_dbxrefs']['dbxref_table'] = array(
'#type' => 'markup',
'#tree' => TRUE,
'#prefix' => '
',
'#suffix' => '
',
'#theme' => 'chado_node_additional_dbxrefs_form_table'
);
/* DBxrefs can come to us in two ways:
*
* 1) In the form state in the $form_state['chado_additional_dbxrefs']. Data is in this field
* when an AJAX call updates the form state or a validation error.
*
* 2) Directly from the database if the record already has dbxref's associated. This
* data is only used the first time the form is loaded. On AJAX calls or validation
* errors the fields on the form are populated from the $form_state['chado_additional_dbxrefs']
* entry.
*/
if (isset($form_state['chado_additional_dbxrefs'])) {
$existing_dbxrefs = $form_state['chado_additional_dbxrefs'];
}
else {
$existing_dbxrefs = chado_query(
"SELECT
db.name as db_name,
db.db_id as db_id,
dbxref.dbxref_id,
dbxref.accession as accession,
dbxref.description as description,
dbxref.version
FROM {dbxref} dbxref
LEFT JOIN {db} db ON db.db_id = dbxref.db_id
LEFT JOIN {".$details['linking_table']."} linking_table ON linking_table.dbxref_id = dbxref.dbxref_id
WHERE linking_table.".$details['base_foreign_key']."= :base_key_value
ORDER BY db.name ASC, dbxref.version ASC",
array(':base_key_value' => $details['base_key_value'])
);
}
/* The format of the $existing_dbxref's array is either:
*
* From the chado_additional_dbxrefs array:
* $form_state['chado_additional_dbxrefs'] = array(
* '[db_id]-[version]' => array(
* 'db_id' => [the db.db_id value]
* 'db_name' => [the db.name value]
* 'dbxref_id' => [the dbxref.dbxref_id value, or temporary value if it doesn't yet exists],
* 'version' => [the dbxref.version value],
* 'accession' => [the dbxref.accession value],
* ),
* );
*
* OR
* Populated from the database:
* $existing_dbxref = array(
* 0 => array(
* 'dbxref_id' => [the dbxref.dbxref_id value],
* 'db_name' => [the db.name value],
* 'db_id' => [the db.db_id value],
* 'accession' => [the dbxref.accession value],
* 'description' => [the dbxref.description value],
* 'version' => [the dbxref.versiion value],
* ),
* );
*
* NOTE: The main difference is the key
*
* Loop on the array elements of the $existing_dbxrefs array and add
* an element to the form for each one.
*/
$num_dbxrefs = 0;
foreach ($existing_dbxrefs as $dbxref) {
if (array_key_exists($dbxref->db_id, $db_options)) {
$num_dbxrefs++;
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id]['#type'] = 'markup';
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id]['#value'] = '';
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['#type'] = 'markup';
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['#value'] = '';
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['#attributes'] = array(
'class' => array('dbxref', 'saved')
);
// Determine whether this dbxref is unsaved or not.
// We can tell this by looking at the dbxref_id: if it's not
// saved yet we will have entered a TEMP###.
if (preg_match('/^TEMP/', $dbxref->dbxref_id)) {
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['#attributes'] = array(
'class' => array('dbxref', 'unsaved')
);
}
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['db_id'] = array(
'#type' => 'hidden',
'#value' => $dbxref->db_id
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['accession'] = array(
'#type' => 'hidden',
'#value' => $dbxref->accession
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['version'] = array(
'#type' => 'hidden',
'#value' => $dbxref->version,
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['dbxref_id'] = array(
'#type' => 'hidden',
'#value' => $dbxref->dbxref_id
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['db'] = array(
'#type' => 'markup',
'#markup' => $dbxref->db_name
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['dbxref_version'] = array(
'#type' => 'markup',
'#markup' => $dbxref->version,
);
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['dbxref_accession'] = array(
'#type' => 'markup',
'#markup' => $dbxref->accession
);
// remove button
$form['addtl_dbxrefs']['dbxref_table'][$dbxref->db_id][$dbxref->dbxref_id]['dbxref_action'] = array(
'#type' => 'submit',
'#value' => t('Remove'),
'#name' => "dbxrefs_remove-".$dbxref->db_id.'-'.$dbxref->dbxref_id,
'#ajax' => array(
'callback' => "chado_add_node_form_subtable_ajax_update",
'wrapper' => 'tripal-generic-edit-addtl_dbxrefs-table',
'effect' => 'fade',
'method' => 'replace',
'prevent' => 'click'
),
// When this button is clicked, the form will be validated and submitted.
// Therefore, we set custom submit and validate functions to override the
// default node form submit. In the validate function we validate only the
// additional dbxref fields and in the submit we remove the indicated dbxref
// from the chado_additional_dbxrefs array. In order to keep validate errors
// from the node form validate and Drupal required errors for non-dbxref fields
// preventing the user from removing dbxrefs we set the #limit_validation_errors below
'#validate' => array('chado_add_node_form_subtables_remove_button_validate'),
'#submit' => array('chado_add_node_form_subtables_remove_button_submit'),
// Limit the validation of the form upon clicking this button to the dbxref_table tree
// No other fields will be validated (ie: no fields from the main form or any other api
// added form).
'#limit_validation_errors' => array(
array('dbxref_table') // Validate all fields within $form_state['values']['dbxref_table']
)
);
}
}
// Quickly add a hidden field stating how many dbxrefs are currently added.
$form['addtl_dbxrefs']['num_dbxrefs'] = array(
'#type' => 'hidden',
'#value' => $num_dbxrefs,
'#attributes' => array('class' => 'num-dbxrefs')
);
// Form elements for adding a new dbxref
//---------------------------------------------
$form['addtl_dbxrefs']['dbxref_table']['new'] = array(
'#type' => 'markup',
'#prefix' => '',
'#suffix' => ''
);
// add in the existing databases
$form['addtl_dbxrefs']['dbxref_table']['new']['db'] = array(
'#type' => 'select',
'#options' => $db_options,
);
$form['addtl_dbxrefs']['dbxref_table']['new']['dbxref_accession'] = array(
'#type' => 'textfield',
);
$form['addtl_dbxrefs']['dbxref_table']['new']['dbxref_version'] = array(
'#type' => 'textfield',
'#size' => 10,
);
// add button
$form['addtl_dbxrefs']['dbxref_table']['new']['dbxref_action'] = array(
'#type' => 'submit',
'#value' => t('Add'),
'#name' => "dbxrefs-add",
'#ajax' => array(
'callback' => "chado_add_node_form_subtable_ajax_update",
'wrapper' => 'tripal-generic-edit-addtl_dbxrefs-table',
'effect' => 'fade',
'method' => 'replace',
'prevent' => 'click'
),
// When this button is clicked, the form will be validated and submitted.
// Therefore, we set custom submit and validate functions to override the
// default node form submit. In the validate function we validate only the
// additional dbxref fields and in the submit we add them to the chado_additional_dbxrefs
// array. In order to keep validate errors from the node form validate and Drupal
// required errors for non-dbxref fields preventing the user from adding dbxrefs we
// set the #limit_validation_errors below
'#validate' => array('chado_add_node_form_subtables_add_button_validate'),
'#submit' => array('chado_add_node_form_subtables_add_button_submit'),
// Limit the validation of the form upon clicking this button to the dbxref_table tree
// No other fields will be validated (ie: no fields from the main form or any other api
// added form).
'#limit_validation_errors' => array(
array('dbxref_table') // Validate all fields within $form_state['values']['dbxref_table']
)
);
}
/**
* Validate the user input for creating a new dbxref
* Called by the add button in chado_add_node_form_dbxrefs
*
* @ingroup tripal_core
*/
function chado_add_node_form_dbxrefs_add_button_validate($form, &$form_state) {
// Ensure the db_id is supplied & Valid
$db = chado_select_record(
'db',
array('db_id', 'name'),
array('db_id' => $form_state['values']['dbxref_table']['new']['db'])
);
if (!isset($db[0])) {
form_set_error('dbxref_table][new][db', 'Please select a database before attempting to add a new database reference.');
}
else {
$form_state['values']['dbxref_table']['new']['db_name'] = $db[0]->name;
}
// Ensure accession is supplied
if (empty($form_state['values']['dbxref_table']['new']['dbxref_accession'])) {
form_set_error('dbxref_table][new][dbxref_accession','You must enter the accession before attempting to add a new database reference.');
}
}
/**
* Called by the add button in chado_add_node_form_dbxrefs
*
* Create an array of additional dbxrefs in the form state. This array will then be
* used to rebuild the form in subsequent builds
*
* @ingroup tripal_core
*/
function chado_add_node_form_dbxrefs_add_button_submit($form, &$form_state) {
// if the chado_additional_dbxrefs array is not set then this is the first time modifying the
// dbxref table. this means we need to include all the dbxrefs from the db
if (!isset($form_state['chado_additional_dbxrefs'])) {
chado_add_node_form_dbxrefs_create_dbxref_formstate_array($form, $form_state);
}
// get details for the new dbxref
$dbxref = array(
'db_id' => $form_state['values']['dbxref_table']['new']['db'],
'db_name' => $form_state['values']['dbxref_table']['new']['db_name'],
'dbxref_id' => 'TEMP' . uniqid(),
'version' => $form_state['values']['dbxref_table']['new']['dbxref_version'],
'accession' => $form_state['values']['dbxref_table']['new']['dbxref_accession'],
);
$key = $dbxref['db_id'] . '-' . $dbxref['dbxref_id'];
$form_state['chado_additional_dbxrefs'][$key] = (object) $dbxref;
// we don't want the new element to pick up the values from the previous element so wipe them out
unset($form_state['input']['dbxref_table']['new']['db']);
unset($form_state['input']['dbxref_table']['new']['db_name']);
unset($form_state['input']['dbxref_table']['new']['dbxref_version']);
unset($form_state['input']['dbxref_table']['new']['dbxref_accession']);
}
/**
* Called by the many remove buttons in chado_add_node_form_dbxrefs
*
* @ingroup tripal_core
*/
function chado_add_node_form_dbxrefs_remove_button_validate($form, $form_state) {
// No validation needed.
}
/**
* Remove the correct dbxref from the form
* Called by the many remove buttons in chado_add_node_form_dbxrefs
*
* @ingroup tripal_core
*/
function chado_add_node_form_dbxrefs_remove_button_submit(&$form, &$form_state) {
// if the chado_additional_dbxrefs array is not set then this is the first time modifying the
// dbxref table. this means we need to include all the dbxrefs from the db
if (!isset($form_state['chado_additional_dbxrefs'])) {
chado_add_node_form_dbxrefs_create_dbxref_formstate_array($form, $form_state);
}
// remove the specified dbxref from the form dbxref table
if(preg_match('/dbxrefs_remove-([^-]+-[^-]+)/',$form_state['triggering_element']['#name'],$match)) {
$key = $match[1];
if (array_key_exists($key, $form_state['chado_additional_dbxrefs'])) {
unset($form_state['chado_additional_dbxrefs'][$key]);
}
}
}
/**
* Creates an array in form_state containing the existing addtl_dbxrefs. This array is
* then modified by the add/remove buttons and used as a source for rebuilding the form.
* This function get's called at each button (add and remove) button submits the first
* time one of the button's is clicked to instantiates the $form_state['chado_additional_dbxrefs'] array
*
* $form_state['chado_additional_dbxrefs'] = array(
* '[db_id]-[version]' => array(
* 'db_id' => [the db.db_id value]
* 'db_name' => [the db.name value]
* 'dbxref_id' => [the dbxref.dbxref_id value, or temporary value if it doesn't yet exists],
* 'version' => [the dbxref.version value],
* 'accession' => [the dbxref.accession value],
* ),
* );
*
* @ingroup tripal_core
*/
function chado_add_node_form_dbxrefs_create_dbxref_formstate_array($form, &$form_state) {
$form_state['chado_additional_dbxrefs'] = array();
foreach (element_children($form['addtl_dbxrefs']['dbxref_table']) as $db_id) {
if ($db_id != 'new') {
foreach (element_children($form['addtl_dbxrefs']['dbxref_table'][$db_id]) as $version) {
$element = $form['addtl_dbxrefs']['dbxref_table'][$db_id][$version];
$dbxref = array(
'db_id' => $element['db_id']['#value'],
'db_name' => $element['db']['#markup'],
'dbxref_id' => $element['dbxref_id']['#value'],
'version' => $element['dbxref_version']['#markup'],
'accession' => $element['dbxref_accession']['#markup'],
);
$key = $dbxref['db_id'] . '-' . $dbxref['dbxref_id'];
$form_state['chado_additional_dbxrefs'][$key] = (object) $dbxref;
}
}
}
}
/**
* Function to theme the add/remove dbxrefs form into a table
*
* @ingroup tripal_chado_node_api
*/
function theme_chado_add_node_form_dbxrefs_table($variables) {
$element = $variables['element'];
$header = array(
'db' => t('Database'),
'dbxref_accession' => t('Accession'),
'dbxref_version' => t('Version'),
'dbxref_action' => t('Actions'),
);
$rows = array();
foreach (element_children($element) as $db_id) {
if ($db_id == 'new') {
$row = array();
$row['data'] = array();
foreach ($header as $fieldname => $title) {
$row['data'][] = drupal_render($element[$db_id][$fieldname]);
}
$rows[] = $row;
}
else {
foreach (element_children($element[$db_id]) as $version) {
$row = array();
$row['data'] = array();
$row['class'] = $element[$db_id][$version]['#attributes']['class'];
foreach ($header as $fieldname => $title) {
$row['data'][] = drupal_render($element[$db_id][$version][$fieldname]);
}
$rows[] = $row;
}
}
}
return theme('table', array(
'header' => $header,
'rows' => $rows
));
}
/**
* This function is used in a hook_insert, hook_update for a node form
* when the additional_dbxrefs form has been added to the form. It retrieves all of the dbxrefs
* and returns them in an array of the format:
*
* $dbxefs[][][] =
*
* This array can then be used for inserting or updating dbxrefs using the API call
* tripal_hook_insert_dbxref()
*
* @param $node
*
* @return
* A dbxref array
*
* @ingroup tripal_chado_node_api
*/
function chado_retrieve_node_form_dbxrefs($node) {
$dbxrefs = array();
if (isset($node->dbxref_table)) {
foreach ($node->dbxref_table as $db_id => $elements) {
if ($db_id != 'new') {
foreach ($elements as $dbxref_id => $dbxref) {
$version = (!empty($dbxref['version'])) ? $dbxref['version'] : 'NONE';
$dbxrefs[$db_id][$version][$dbxref_id] = $dbxref['accession'];
}
}
}
}
return $dbxrefs;
}
/**
* This function is used in hook_insert or hook_update and handles inserting of any new
* dbxrefs and creation of links between those dbxrefs and node content
*
* @param $node
* The node passed into hook_insert & hook_update
* @param $details
* - linking_table: the name of the _dbxref linking table (ie: feature_dbxref)
* - foreignkey_name: the name of the foreign key used to link to the node content (ie: feature_id)
* - foreignkey_value: the value of the foreign key (ie: 445, if there exists a feature where feature_id=445)
* @param $retrieved_dbxrefs
* An array of databa references from chado_retrieve_node_form_dbxrefs($node).
* This can be used if you need special handling for some of the database references
*
* @ingroup tripal_chado_node_api
*/
function chado_update_node_form_dbxrefs($node, $details, $retrieved_dbxrefs = FALSE) {
$linking_table = $details['linking_table'];
$foreignkey_name = $details['foreignkey_name'];
$foreignkey_value = $details['foreignkey_value'];
if (isset($node->dbxref_table) AND ($foreignkey_value > 0)) {
// First remove existing dbxref links
chado_delete_record($linking_table, array($foreignkey_name => $foreignkey_value));
// Add back in dbxref links and insert dbxrefs as needed
if ($retrieved_dbxrefs) {
$dbxrefs = $retrieved_dbxrefs;
}
else {
$dbxrefs = chado_retrieve_node_form_dbxrefs($node);
}
foreach ($dbxrefs as $db_id => $versions) {
foreach ($versions as $version => $elements) {
foreach ($elements as $dbxref_id => $accession) {
// If there is no dbxref then we have to create that first
if (preg_match('/^TEMP/',$dbxref_id)) {
$version = ($version == 'NONE') ? '' : $version;
$success = tripal_insert_dbxref(array(
'db_id' => $db_id,
'accession' => $accession,
'version' => $version,
'description' => NULL
));
if ($success) {
$dbxref_id = $success->dbxref_id;
}
else {
$dbxref_id = FALSE;
}
}
// add _dbxref linker
if ($dbxref_id) {
if (preg_match('/(\w+)_dbxref/',$linking_table,$matches)) {
$base_table = $matches[1];
$success_link = tripal_associate_dbxref(
$base_table,
$foreignkey_value,
array('dbxref_id' => $dbxref_id)
);
}
}
}
}
}
}
}