'example_property', // the name of the table linking additional properties 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
'cv_name' => 'example_prop_cv', // the name of the cv governing the _prop.type_id
'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_node_properties_form($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 properties
// Existing _property links will be cleared and then re-added
tripal_api_chado_node_properties_form_update_properties(
$node, // the node object passed in via hook_insert()
'example_property', // the name of the _property linking table
'example', // the name of the base chado table for the node
'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 properties
// Existing _property links will be cleared and then re-added
tripal_api_chado_node_properties_form_update_properties(
$node, // the node object passed in via hook_insert()
'example_property', // the name of the _property linking table
'example', // the name of the base chado table for the node
'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
*/
/**
* Retrieve a property for a given base table record
*
* @param $basetable
* The base table for which the property should be retrieved. Thus to retrieve a property
* for a feature the basetable=feature and property is retrieved from featureprop
* @param $record_id
* The foriegn key field of the base table. This should be in integer.
* @param $property
* The cvterm name describing the type of properties to be retrieved
* @param $cv_name
* The name of the cv that the above cvterm is part of
*
* @return
* An array in the same format as that generated by the function
* tripal_core_generate_chado_var(). If only one record is returned it
* is a single object. If more than one record is returned then it is an array
* of objects
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_get_property($basetable, $record_id, $property, $cv_name) {
// get the foreign key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
// construct the array of values to be selected
$values = array(
$fkcol => $record_id,
'type_id' => array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
'is_obsolete' => 0
),
);
$results = tripal_core_generate_chado_var($basetable . 'prop', $values);
if ($results) {
$results = tripal_core_expand_chado_vars($results, 'field', $basetable . 'prop.value');
}
return $results;
}
/**
* Insert a property for a given base table. By default if the property already
* exists a new property is added with the next available rank. If
* $update_if_present argument is specified then the record will be updated if it
* exists rather than adding a new property.
*
* @param $basetable
* The base table for which the property should be inserted. Thus to insert a property
* for a feature the basetable=feature and property is inserted into featureprop
* @param $record_id
* The foriegn key value of the base table. This should be in integer.
* @param $property
* The cvterm name describing the type of properties to be inserted
* @param $cv_name
* The name of the cv that the above cvterm is part of
* @param $value
* The value of the property to be inserted (can be empty)
* @param $update_if_present
* A boolean indicating whether an existing record should be updated. If the
* property already exists and this value is not specified or is zero then
* a new property will be added with the next largest rank.
*
* @return
* Return True on Insert/Update and False otherwise
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_insert_property($basetable, $record_id, $property,
$cv_name, $value, $update_if_present = 0) {
// first see if the property already exists, if the user want's to update
// then we can do that, but otherwise we want to increment the rank and
// insert
$props = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
if (!is_array($props) and $props) {
$props = array($props);
}
$rank = 0;
if (count($props) > 0) {
if ($update_if_present) {
return tripal_core_update_property($basetable, $record_id, $property, $cv_name, $value);
}
else {
// iterate through the properties returned and check to see if the
// property with this value already exists if not, get the largest rank
// and insert the same property but with this new value
foreach ($props as $p) {
if ($p->rank > $rank) {
$rank = $p->rank;
}
if (strcmp($p->value, $value) == 0) {
return TRUE;
}
}
// now add 1 to the rank
$rank++;
}
}
// make sure the cvterm exists. Otherwise we'll get an error with
// prepared statements not matching
$values = array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
);
$options = array();
$term = tripal_core_chado_select('cvterm', array('cvterm_id'), $values, $options);
if (!$term or count($term) == 0) {
watchdog('tripal_core', "Cannot find property '%prop_name' in vocabulary '%cvname'.",
array('%prop_name' => $property, '%cvname' => $cv_name), WATCHDOG_ERROR);
return FALSE;
}
// get the foreign key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
// construct the array of values to be inserted
$values = array(
$fkcol => $record_id,
'type_id' => array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
),
'value' => $value,
'rank' => $rank,
);
$options = array();
$result = tripal_core_chado_insert($basetable . 'prop', $values, $options);
return $result;
}
/**
* Update a property for a given base table record and property name. This
* function should be used only if one record of the property will be present.
* If the property name can have multiple entries (with increasing rank) then
* use the function named tripal_core_update_property_by_id
*
* @param $basetable
* The base table for which the property should be updated. The property table
* is constructed using a combination of the base table name and the suffix
* 'prop' (e.g. basetable = feature then property tabie is featureprop).
* @param $record_id
* The foreign key of the basetable to update a property for. This should be in integer.
* For example, if the basetable is 'feature' then the $record_id should be the feature_id
* @param $property
* The cvterm name of property to be updated
* @param $cv_name
* The name of the cv that the above cvterm is part of
* @param $value
* The value of the property to be inserted (can be empty)
* @param $insert_if_missing
* A boolean indicating whether a record should be inserted if one doesn't exist to update
*
* Note: The property to be updated is select via the unique combination of $record_id and
* $property and then it is updated with the supplied value
*
* @return
* Return True on Update/Insert and False otherwise
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_update_property($basetable, $record_id, $property,
$cv_name, $value, $insert_if_missing = 0) {
// first see if the property is missing (we can't update a missing property
$prop = tripal_core_get_property($basetable, $record_id, $property, $cv_name);
if (count($prop)==0) {
if ($insert_if_missing) {
return tripal_core_insert_property($basetable, $record_id, $property, $cv_name, $value);
}
else {
return FALSE;
}
}
// get the foreign key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
// construct the array that will match the exact record to update
$match = array(
$fkcol => $record_id,
'type_id' => array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
),
);
// construct the array of values to be updated
$values = array(
'value' => $value,
);
return tripal_core_chado_update($basetable . 'prop', $match, $values);
}
/**
* Update a property for a given base table record. This function should be
* used if multiple records of the same property will be present. Also, use this
* function to change the property name of an existing property.
*
* @param $basetable
* The base table for which the property should be updated. The property table
* is constructed using a combination of the base table name and the suffix
* 'prop' (e.g. basetable = feature then property tabie is featureprop).
* @param $record_id
* The primary key of the base table. This should be in integer.
* For example, if the basetable is 'feature' then the $record_id should be the featureprop_id
* @param $property
* The cvterm name of property to be updated
* @param $cv_name
* The name of the cv that the above cvterm is part of
* @param $value
* The value of the property to be inserted (can be empty)
*
* @return
* Return True on Update/Insert and False otherwise
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_update_property_by_id($basetable, $record_id, $property,
$cv_name, $value) {
// get the primary key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$pkcol = $table_desc['primary key'][0];
// construct the array that will match the exact record to update
$match = array(
$pkcol => $record_id,
);
// construct the array of values to be updated
$values = array(
'type_id' => array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
),
'value' => $value,
);
return tripal_core_chado_update($basetable . 'prop', $match, $values);
}
/**
* Deletes a property for a given base table record using the property name
*
* @param $basetable
* The base table for which the property should be deleted. Thus to deleted a property
* for a feature the basetable=feature and property is deleted from featureprop
* @param $record_id
* The primary key of the basetable to delete a property for. This should be in integer.
* @param $property
* The cvterm name describing the type of property to be deleted
* @param $cv_name
* The name of the cv that the above cvterm is part of
*
* Note: The property to be deleted is select via the unique combination of $record_id and $property
*
* @return
* Return True on Delete and False otherwise
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_delete_property($basetable, $record_id, $property, $cv_name) {
// get the foreign key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$fkcol = key($table_desc['foreign keys'][$basetable]['columns']);
// construct the array that will match the exact record to update
$match = array(
$fkcol => $record_id,
'type_id' => array(
'cv_id' => array(
'name' => $cv_name,
),
'name' => $property,
),
);
return tripal_core_chado_delete($basetable . 'prop', $match);
}
/**
* Deletes a property using the property ID
*
* @param $basetable
* The base table for which the property should be deleted. Thus to deleted a property
* for a feature the basetable=feature and property is deleted from featureprop
* @param $record_id
* The primary key of the basetable to delete a property for. This should be in integer.
*
* @return
* Return True on Delete and False otherwise
*
* @ingroup tripal_chado_node_api
*/
function tripal_core_delete_property_by_id($basetable, $record_id) {
// get the foreign key for this property table
$table_desc = tripal_core_get_chado_table_schema($basetable . 'prop');
$pkcol = $table_desc['primary key'][0];
// construct the array that will match the exact record to update
$match = array(
$pkcol => $record_id,
);
return tripal_core_chado_delete($basetable . 'prop', $match);
}
/**
* @section
* Properties Form to be added to node forms
*/
/**
* Provides a form for adding to BASEprop table
*
* @param $form
* The Drupal form array into which the property form 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:
* - property_table: the name of the property linking table (ie: featureprop)
* - base_foreign_key: the name of the foreign key linking this table to the non-property 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)
* Require ONE of the following:
* The controlled vocabulary governing the property types
* -cv_id: the unique key from the cv table
* -cv_name: the cv.name field uniquely identifying the controlled vocab
* 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 cvterm_id and
* the [value] is the human-readable name of the option. This is generated from the cv_name/id by default
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form(&$form, &$form_state, $details) {
// Set Defaults for optional fields
$details['fieldset_title'] = 'Properties';
$details['additional_instructions'] = '';
// Get Property Types for the Select List
if (isset($details['select_options'])) {
$property_options = $details['select_options'];
}
else {
if (isset($details['cv_name'])) {
$property_options = array();
$property_options[] = 'Select a Property';
$sql = "
SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition
FROM {cvterm} CVT
INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
WHERE
CV.name = :cv_name AND
NOT CVT.is_obsolete = 1
ORDER BY CVT.name ASC
";
$prop_types = chado_query($sql, array(':cv_name' => $details['cv_name']));
while ($prop = $prop_types->fetchObject()) {
$property_options[$prop->cvterm_id] = $prop->name;
}
} elseif (isset($details['cv_id'])) {
$property_options = array();
$property_options[] = 'Select a Property';
$sql = "
SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition
FROM {cvterm} CVT
INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
WHERE
CV.cv_id = :cv_id AND
NOT CVT.is_obsolete = 1
ORDER BY CVT.name ASC
";
$prop_types = chado_query($sql, array(':cv_id' => $details['cv_id']));
while ($prop = $prop_types->fetchObject()) {
$property_options[$prop->cvterm_id] = $prop->name;
}
}
}
// the fieldset of the property elements
$form['properties'] = array(
'#type' => 'fieldset',
'#title' => t($details['fieldset_title']),
'#description' => t('You may add additional properties by selecting a property type
from the dropdown and adding text. You may add as many properties as desired by
clicking the add button on the right. To remove a property, click the remove button.
To add additional properties to the drop down. ' . $details['additional_instructions']),
'#prefix' => "
",
'#suffix' => '
',
'#weight' => 8
);
// 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']['property_table'] heading.
$form['properties']['property_table'] = array(
'#type' => 'markup',
'#tree' => TRUE,
'#prefix' => '',
'#suffix' => '
',
'#theme' => 'chado_node_properties_form_table'
);
// Add defaults into form_state to be used elsewhere
$form['properties']['property_table']['details'] = array(
'#type' => 'hidden',
'#value' => serialize($details)
);
/* Properties can come to us in two ways:
*
* 1) In the form state in the $form_state['chado_properties']. 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 properties 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_properties']
* entry.
*/
if (isset($form_state['chado_properties'])) {
$existing_properties = $form_state['chado_properties'];
}
else {
if (isset($details['cv_name'])) {
$existing_properties = chado_query(
"SELECT PP.".$details['property_table']."_id property_id, CVT.cvterm_id as type_id, CVT.name as type_name, CVT.definition, PP.value, PP.rank
FROM {" . $details['property_table'] . "} PP
INNER JOIN {cvterm} CVT ON CVT.cvterm_id = PP.type_id
INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
WHERE
PP." . $details['base_foreign_key'] . " = :base_key_value AND
CV.name = '" .$details['cv_name']. "'
ORDER BY CVT.name, PP.rank",
array(':base_key_value' => $details['base_key_value'])
);
} elseif (isset($details['cv_id'])) {
$existing_properties = chado_query(
"SELECT PP.".$details['property_table']."_id property_id, CVT.cvterm_id as type_id, CVT.name as type_name, CVT.definition, PP.value, PP.rank
FROM {" . $details['property_table'] . "} PP
INNER JOIN {cvterm} CVT ON CVT.cvterm_id = PP.type_id
INNER JOIN {cv} CV ON CVT.cv_id = CV.cv_id
WHERE
PP." . $details['base_foreign_key'] . " = :base_key_value AND
CV.cv_id = '" .$details['cv_id']. "'
ORDER BY CVT.name, PP.rank",
array(':base_key_value' => $details['base_key_value'])
);
}
}
/* The format of the $existing_properties array is either:
*
* From the chado_properties array:
* $form_state['chado_properties'] = array(
* '[type_id]-[rank]' => array(
* 'type_id' => [the cvterm.cvterm_id value]
* 'type_name' => [the cvterm.name value]
* 'property_id' => [the property.property_id value, or NULL if it doesn't yet exist],
* 'value' => [the BASEprop.value value],
* 'rank' => [the BASEprop.rank value],
* ),
* );
*
* OR
* Populated from the database:
* $existing_property = array(
* 0 => array(
* 'property_id' => [the property.property_id value, or NULL if it doesn't yet exist],
* 'type_id' => [the cvterm.cvterm_id value]
* 'type_name' => [the cvterm.name value]
* 'value' => [the BASEprop.value value],
* 'rank' => [the BASEprop.rank value],
* ),
* );
*
* NOTE: The main difference is the key
*
* Loop on the array elements of the $existing_properties array and add
* an element to the form for each one as long as it's also in the
* $properties_options array.
*/
foreach ($existing_properties as $property) {
if (array_key_exists($property->type_id, $property_options)) {
$form['properties']['property_table'][$property->type_id]['#type'] = 'markup';
$form['properties']['property_table'][$property->type_id]['#value'] = '';
$form['properties']['property_table'][$property->type_id][$property->rank]['#type'] = 'markup';
$form['properties']['property_table'][$property->type_id][$property->rank]['#value'] = '';
$form['properties']['property_table'][$property->type_id][$property->rank]['prop_type_id'] = array(
'#type' => 'hidden',
'#value' => $property->type_id
);
$form['properties']['property_table'][$property->type_id][$property->rank]['prop_value'] = array(
'#type' => 'hidden',
'#value' => $property->value
);
$form['properties']['property_table'][$property->type_id][$property->rank]['property_id'] = array(
'#type' => 'hidden',
'#value' => $property->property_id
);
$form['properties']['property_table'][$property->type_id][$property->rank]['type'] = array(
'#type' => 'markup',
'#markup' => $property->type_name
);
$form['properties']['property_table'][$property->type_id][$property->rank]['value'] = array(
'#type' => 'markup',
'#markup' => $property->value
);
$form['properties']['property_table'][$property->type_id][$property->rank]['rank'] = array(
'#type' => 'markup',
'#markup' => $property->rank
);
// remove button
$form['properties']['property_table'][$property->type_id][$property->rank]['property_action'] = array(
'#type' => 'submit',
'#value' => t('Remove'),
'#name' => "property_remove-".$property->type_id.'-'.$property->rank,
'#ajax' => array(
'callback' => "chado_node_properties_form_ajax_update",
'wrapper' => 'tripal-generic-edit-properties-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
// property fields and in the submit we remove the indicated property
// from the chado_properties array. In order to keep validate errors
// from the node form validate and Drupal required errors for non-property fields
// preventing the user from removing properties we set the #limit_validation_errors below
'#validate' => array('chado_node_properties_form_remove_button_validate'),
'#submit' => array('chado_node_properties_form_remove_button_submit'),
// Limit the validation of the form upon clicking this button to the property_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('property_table') // Validate all fields within $form_state['values']['property_table']
)
);
}
}
// Form elements for adding a new property
//---------------------------------------------
$form['properties']['property_table']['new'] = array(
'#type' => 'markup',
'#prefix' => '',
'#suffix' => ''
);
$form['properties']['property_table']['new']['type'] = array(
'#type' => 'select',
'#options' => $property_options, // Set at top of form
);
$form['properties']['property_table']['new']['value'] = array(
'#type' => 'textarea',
'#rows' => 1,
);
// add button
$form['properties']['property_table']['new']['property_action'] = array(
'#type' => 'submit',
'#value' => t('Add'),
'#name' => "property-add",
'#ajax' => array(
'callback' => "chado_node_properties_form_ajax_update",
'wrapper' => 'tripal-generic-edit-properties-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 property fields and in the submit we add them to the chado_properties
// array. In order to keep validate errors from the node form validate and Drupal
// required errors for non-property fields preventing the user from adding properties we
// set the #limit_validation_errors below
'#validate' => array('chado_node_properties_form_add_button_validate'),
'#submit' => array('chado_node_properties_form_add_button_submit'),
// Limit the validation of the form upon clicking this button to the property_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('property_table') // Validate all fields within $form_state['values']['property_table']
)
);
}
/**
* Validate the user input for creating a new property
* Called by the add button in chado_node_properties_form
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_add_button_validate($form, &$form_state) {
// Ensure the type_id is supplied & Valid
$cvterm = tripal_core_chado_select(
'cvterm',
array('cvterm_id', 'name'),
array('cvterm_id' => $form_state['values']['property_table']['new']['type'])
);
if (!isset($cvterm[0])) {
form_set_error('property_table][new][cvterm', 'Please select a property type before attempting to add a new property.');
}
else {
$form_state['values']['property_table']['new']['type_name'] = $cvterm[0]->name;
}
// Ensure value is supplied
if (empty($form_state['values']['property_table']['new']['value'])) {
form_set_error('property_table][new][value','You must enter the property value before attempting to add a new property.');
}
}
/**
* Called by the add button in chado_node_properties_form
*
* Create an array of properties in the form state. This array will then be
* used to rebuild the form in subsequent builds
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_add_button_submit(&$form, &$form_state) {
$details = unserialize($form_state['values']['property_table']['details']);
// if the chado_additional_properties array is not set then this is the first time modifying the
// property table. this means we need to include all the properties from the db
if (!isset($form_state['chado_properties'])) {
chado_node_properties_form_create_property_formstate_array($form, $form_state);
}
// get details for the new property
$property = array(
'type_id' => $form_state['values']['property_table']['new']['type'],
'type_name' => $form_state['values']['property_table']['new']['type_name'],
'property_id' => NULL,
'value' => $form_state['values']['property_table']['new']['value'],
'rank' => '0',
);
// get max rank
$rank = tripal_core_get_max_chado_rank(
$details['property_table'],
array(
$details['base_foreign_key'] => $details['base_key_value'],
'type_id' => $property['type_id']
)
);
$property['rank'] = strval($rank + 1);
$key = $property['type_id'] . '-' . $property['rank'];
$form_state['chado_properties'][$key] = (object) $property;
$form_state['rebuild'] = TRUE;
}
/**
* There is no user input for the remove buttons so there is no need to validate
* However, both a submit & validate need to be specified so this is just a placeholder
*
* Called by the many remove buttons in chado_node_properties_form
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_remove_button_validate($form, $form_state) {
// No Validation needed for remove
}
/**
* Remove the correct property from the form
* Called by the many remove buttons in chado_node_properties_form
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_remove_button_submit(&$form, &$form_state) {
// if the chado_properties array is not set then this is the first time modifying the
// property table. this means we need to include all the properties from the db
if (!isset($form_state['chado_properties'])) {
chado_node_properties_form_create_property_formstate_array($form, $form_state);
}
// remove the specified property from the form property table
if(preg_match('/property_remove-([^-]+-[^-]+)/',$form_state['triggering_element']['#name'],$match)) {
$key = $match[1];
if (array_key_exists($key, $form_state['chado_properties'])) {
unset($form_state['chado_properties'][$key]);
}
}
$form_state['rebuild'] = TRUE;
}
/**
* Ajax function which returns the section of the form to be re-rendered
*/
function chado_node_properties_form_ajax_update($form, $form_state) {
return $form['properties']['property_table'];
}
/**
* Creates an array in form_state containing the existing properties. 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_properties'] array
*
* $form_state['chado_properties'] = array(
* '[type_id]-[rank]' => array(
* 'type_id' => [the cvterm.cvterm_id value]
* 'type_name' => [the cvterm.name value]
* 'property_id' => [the property.property_id value, or NULL if it doesn't yet exist],
* 'value' => [the BASEprop.value value],
* 'rank' => [the BASEprop.rank value],
* ),
* );
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_create_property_formstate_array($form, &$form_state) {
$form_state['chado_properties'] = array();
foreach (element_children($form['properties']['property_table']) as $type_id) {
if ($type_id != 'new') {
foreach (element_children($form['properties']['property_table'][$type_id]) as $rank) {
$element = $form['properties']['property_table'][$type_id][$rank];
$property = array(
'type_id' => $element['prop_type_id']['#value'],
'type_name' => $element['type']['#markup'],
'property_id' => $element['property_id']['#value'],
'value' => $element['value']['#markup'],
'rank' => $element['rank']['#markup']
);
$key = $property['type_id'] . '-' . $property['rank'];
$form_state['chado_properties'][$key] = (object) $property;
}
}
}
}
/**
* Function to theme the add/remove properties form into a table
*
* @ingroup tripal_chado_node_api
*/
function theme_chado_node_properties_form_table($variables) {
$element = $variables['element'];
$header = array(
'type' => t('Type'),
'value' => t('Value'),
'property_action' => t('Actions'),
);
$rows = array();
foreach (element_children($element) as $type_id) {
if ($type_id == 'new') {
$row = array();
$row['data'] = array();
foreach ($header as $fieldname => $title) {
$row['data'][] = drupal_render($element[$type_id][$fieldname]);
}
$rows[] = $row;
}
else {
foreach (element_children($element[$type_id]) as $version) {
$row = array();
$row['data'] = array();
foreach ($header as $fieldname => $title) {
$row['data'][] = drupal_render($element[$type_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 chado node properties form has been added to the form. It retrieves all of the properties
* and returns them in an array of the format:
*
* $dbxefs[][] =
*
* This array can then be used for inserting or updating properties
*
* @param $node
*
* @return
* A property array
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_retreive($node) {
$properties = array();
if (isset($node->property_table)) {
foreach ($node->property_table as $type_id => $elements) {
if ($type_id != 'new' AND $type_id != 'details') {
foreach ($elements as $rank => $element) {
$properties[$type_id][$rank] = $element['prop_value'];
}
}
}
}
return $properties;
}
/**
* This function is used in hook_insert or hook_update and handles inserting of any new
* properties
*
* @param $node
* The node passed into hook_insert & hook_update
* @param $details
* - property_table: the name of the _property linking table (ie: feature_property)
* - base_table: the name of the base table (ie: feature)
* - 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_properties
* An array of properties from chado_node_properties_form_retreive($node). This can be used if you need
* special handling for some of the properties (See FeatureMap chado_featuremap_insert for an example)
*
* @ingroup tripal_chado_node_api
*/
function chado_node_properties_form_update_properties($node, $details, $retrieved_properties = FALSE) {
$details['foreignkey_value'] = (isset($details['foreignkey_value'])) ? $details['foreignkey_value'] : 0;
if (isset($node->property_table) AND ($details['foreignkey_value'] > 0)) {
// First remove existing property links
tripal_core_chado_delete($details['property_table'], array($details['foreignkey_name'] => $details['foreignkey_value']));
// Add back in property links and insert properties as needed
if ($retrieved_properties) {
$properties = $retrieved_properties;
}
else {
$properties = chado_node_properties_form_retreive($node);
}
foreach ($properties as $type_id => $ranks) {
foreach ($ranks as $rank => $value) {
$success = tripal_core_chado_insert(
$details['property_table'],
array(
$details['foreignkey_name'] => $details['foreignkey_value'],
'type_id' => $type_id,
'value' => $value,
'rank' => $rank
)
);
if (!$success) {
watchdog(
'tripal_' . $details['base_table'],
$details['base_table'] . ' Insert: Unable to insert property type_id %cvterm with value %value.',
array('%cvterm' => $type_id, '%value' => $value),
WATCHDOG_ERROR
);
}
}
}
}
}