function chado_add_node_form_properties
2.x tripal_core.chado_nodes.properties.api.inc | chado_add_node_form_properties(&$form, &$form_state, $details) |
3.x tripal_core.chado_nodes.properties.api.inc | chado_add_node_form_properties(&$form, &$form_state, $details) |
11 calls to chado_add_node_form_properties()
- chado_analysis_form in tripal_analysis/
includes/ tripal_analysis.chado_node.inc - Implements hook_form(). When editing or creating a new node of type 'chado_analysis' we need a form. This function creates the form that will be used for this.
- chado_contact_form in tripal_contact/
includes/ tripal_contact.chado_node.inc - Implementation of hook_form().
- chado_example_form in tripal_example/
includes/ tripal_example.chado_node.inc - Implementation of hook_form()
- chado_featuremap_form in tripal_featuremap/
includes/ tripal_featuremap.chado_node.inc - When editing or creating a new node of type 'chado_featuremap' we need a form. This function creates the form that will be used for this.
- chado_feature_form in tripal_feature/
includes/ tripal_feature.chado_node.inc - Implementation of hook_form().
1 string reference to 'chado_add_node_form_properties'
- chado_node_properties_form in tripal_core/
api/ tripal_core.DEPRECATED.api.inc
File
- tripal_core/
api/ tripal_core.chado_nodes.properties.api.inc, line 140 - API to manage the chado prop table for various Tripal Node Types
Code
function chado_add_node_form_properties(&$form, &$form_state, $details) {
// Set defaults for optional fields
if (!array_key_exists('fieldset_title', $details)) {
$details['fieldset_title'] = 'Properties';
}
if (!array_key_exists('additional_instructions', $details)) {
$details['additional_instructions'] = '';
}
if (!array_key_exists('default_properties', $details)) {
$details['default_properties'] = array();
}
if (!is_array($details['default_properties'])) {
drupal_set_message("The 'default_properties' option must be an array", "error");
tripal_report_error('tcprops_form', TRIPAL_ERROR,
"The 'default_properties' option must be an array",
array());
return;
}
// make sure the property table exists before proceeding.
if (!chado_table_exists($details['property_table'])) {
drupal_set_message("Cannot add property elements to the form. The property table, '" .
$details['property_table'] . "', does not exists", "error");
tripal_report_error('tcprops_form', TRIPAL_ERROR,
"Cannot add property elements to the form. The property table, '%name', cannot be found.",
array('%name' => $details['property_table']));
return;
}
// if the chado_id_field is not specified then set it using the
// typical chado naming scheme
if (!array_key_exists('chado_id_field', $details)) {
$chado_id_table = preg_replace('/prop$/', '', $details['property_table']);
$chado_id_field = $chado_id_table . '_id';
$details['nodetype'] = $chado_id_table;
$details['chado_id_field'] = $chado_id_field;
}
else {
$details['nodetype'] = str_replace('_id', '', $details['chado_id_field']);
}
// make sure the specified cv exists
if (isset($details['cv_name'])) {
// make sure the cv_name is real
$result = chado_select_record('cv', array('cv_id'), array('name' => $details['cv_name']));
if (count($result) == 0) {
drupal_set_message("Cannot add property elements to the form. The CV name, '" .
$details['cv_name'] . "', does not exists", "error");
tripal_report_error('tcprops_form', TRIPAL_ERROR,
"Cannot add property elements to the form. The CV named, '%name', cannot be found.",
array('%name' => $details['cv_name']));
return;
}
// add the cv_id option to the details array
$details['cv_id'] = $result[0]->cv_id;
}
elseif (isset($details['cv_id'])) {
// make sure the cv_id is real
$result = chado_select_record('cv', array('name'), array('cv_id' => $details['cv_id']));
if (count($result) == 0) {
drupal_set_message("Cannot add property elements to the form. The CV ID, '" .
$details['cv_id'] . "', does not exist", "error");
tripal_report_error('tcprops_form', TRIPAL_ERROR,
"Cannot add property elements to the form. The CV ID, '%id', cannot be found.",
array('%id' => $details['cv_id']));
return;
}
// add the cv_name option to the details array
$details['cv_name'] = $result[0]->name;
}
else {
// If we didn't get given a cv identifier, then try retrieving the default one
// using the new cv defaults api
$default_cv = tripal_get_default_cv($details['property_table'], 'type_id');
if (!empty($default_cv)) {
$details['cv_id'] = $default_cv->cv_id;
$details['cv_name'] = $default_cv->name;
}
else {
$default_form_link = l('vocabulary defaults configuration page',
'admin/tripal/chado/tripal_cv/defaults',
array('attributes' => array('target' => '_blank')));
$message = "There is not a default vocabulary set for Property Types. Please set one using the $default_form_link.";
if (preg_match('/(\w+)_id/', $details['chado_id_field'], $matches)) {
$table = $matches[1];
$table = ucwords(str_replace('_', ' ', $table));
$message = "There is not a default vocabulary set for $table Property Types. Please set one using the $default_form_link.";
}
tripal_set_message($message, TRIPAL_WARNING);
tripal_report_error('tcprops_form', TRIPAL_ERROR,
"Please provide either a 'cv_name' or 'cv_id' as an option for adding properties to the form",
array());
}
return;
}
// Get property types for the select list. If the user has provided a set
// then use those, otherwise get them from the cvterm table for specified cv.
if (array_key_exists('select_options', $details) and
is_array($details['select_options'])) {
$property_options = $details['select_options'];
}
// if the select options are not provided then try to get them on our own
else {
// if the vocabulary name is provided in the details then use that to
// get the terms
if (isset($details['cv_name'])) {
$property_options = array();
$property_options[] = 'Select a Property';
$sql = "
SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition, CV.cv_id as cv_id
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;
}
}
// if the cv_id is set in the $details array then use that to get the terms
elseif (isset($details['cv_id'])) {
$property_options = array();
$property_options[] = 'Select a Property';
$sql = "
SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition, CV.name as cv_name
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;
}
}
}
// Tell tripal administrators how to add terms to the property types drop down.
if (empty($property_options)) {
$tripal_message = tripal_set_message(
t('There are currently no property types! To add properties to the drop
down list, you need to <a href="@cvtermlink">add a controlled vocabulary term</a>
to the %cv_name controlled vocabulary.',
array(
'%cv_name' => $details['cv_name'],
'@cvtermlink' => url('admin/tripal/chado/tripal_cv/cv/' . $details['cv_id'] . '/cvterm/add')
)
),
TRIPAL_NOTICE,
array('return_html' => TRUE)
);
}
else {
$tripal_message = tripal_set_message(
t('To add additional properties to the drop down list, you need to <a href="@cvtermlink">add
a controlled vocabulary term</a> to the %cv_name controlled vocabulary.',
array(
'%cv_name' => $details['cv_name'],
'@cvtermlink' => url('admin/tripal/chado/tripal_cv/cv/' . $details['cv_id'] . '/cvterm/add')
)
),
TRIPAL_INFO,
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 property elements
$instructions = 'To add properties of the current %nodetype, select the type of
information from the drop-down below and enter the information in the text box before
clicking "Add". To remove incorrect information, click the "Remove" button.
Note: you cannot edit previously added information but instead need to
remove and re-add it.';
$form['properties'] = array(
'#type' => 'fieldset',
'#title' => t($details['fieldset_title']),
'#description' => t('<p><strong>Additional information about a
%nodetype.</strong></p><p>' . $instructions . $details['additional_instructions'] . '</p>', array('%nodetype' => $details['nodetype'])),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'chado_node_api',
'#weight' => 8,
'#attributes' => array('class' => array('chado-node-api', 'properties')),
'#attached' => array(
'js' => array(
'chado-node-api-vertical-tabs' => drupal_get_path('module', 'tripal_core') . '/theme/js/chadoNodeApi_updateVerticalTabSummary.js',
),
),
);
// 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' => '<div id="tripal-generic-edit-properties-table">',
'#suffix' => '</div>',
'#theme' => 'chado_node_properties_form_table'
);
// We need to provide feedback to the user that changes made
// are not saved until the node is saved.
$form['properties']['property_table']['save_warning'] = array(
'#type' => 'markup',
'#prefix' => '<div id="property-save-warning" class="messages warning" style="display:none;">',
'#suffix' => '</div>',
'#markup' => '* The changes to these properties will not be saved until the
"Save" button at the bottom of this form is clicked. <span class="specific-changes"></span>',
'#attached' => array(
'js' => array(
'chado-node-api-unsaved' => drupal_get_path('module', 'tripal_core') . '/theme/js/chadoNodeApi_unsavedNotify.js',
),
),
);
// 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) As entries in the $details['default_properties'] option
*
* 2) 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.
*
* 3) 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 {
// build the SQL for extracting properties already assigned to this record
$sql_args = array();
$sql_args[':chado_id'] = $details['chado_id'];
if (array_key_exists('cv_name', $details)) {
$cv_where = "CV.name = :cvname";
$sql_args[':cvname'] = $details['cv_name'];
}
elseif (array_key_exists('cv_id', $details)) {
$cv_where = "CV.cv_id = :cvid";
$sql_args[':cvid'] = $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['chado_id_field'] . " = :chado_id AND
$cv_where
ORDER BY CVT.name, PP.rank", $sql_args)->fetchAll();
// next add in any default properties
if (array_key_exists('default_properties', $details)) {
// next iterate through each of the default properties and create a new
// stdClass array that contains the fields needed.
foreach ($details['default_properties'] as $property) {
$new_prop = new stdClass();
$new_prop->type_id = $property['cvterm']->cvterm_id;
$new_prop->type_name = $property['cvterm']->name;
$new_prop->definition = $property['cvterm']->definition;
$new_prop->value = $property['value'];
$new_prop->property_id = 'TEMP' . uniqid();
$new_prop->rank = 'TEMP' . uniqid();
$existing_properties[] = $new_prop;
}
}
}
/* 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 temporary value if it doesn't yet exist],
* 'value' => [the BASEprop.value value],
* 'rank' => [the BASEprop.rank value or NULL if not saved yet],
* ),
* );
*
* OR
* Populated from the database:
* $existing_property = array(
* 0 => array(
* 'property_id' => [the property.property_id value],
* '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.
*/
$num_properties = 0;
foreach ($existing_properties as $property) {
if (array_key_exists($property->type_id, $property_options)) {
$num_properties++;
$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->property_id]['#type'] = 'markup';
$form['properties']['property_table'][$property->type_id][$property->property_id]['#value'] = '';
$form['properties']['property_table'][$property->type_id][$property->property_id]['#attributes'] = array(
'class' => array('property', 'saved')
);
// Determine whether this property is unsaved or not.
// We can tell this by looking at the property_id: if it's not
// saved yet we will have entered a TEMP###.
if (preg_match('/^TEMP/', $property->property_id)) {
$form['properties']['property_table'][$property->type_id][$property->property_id]['#attributes'] = array(
'class' => array('property', 'unsaved')
);
}
$form['properties']['property_table'][$property->type_id][$property->property_id]['prop_type_id'] = array(
'#type' => 'hidden',
'#value' => $property->type_id
);
$form['properties']['property_table'][$property->type_id][$property->property_id]['prop_value'] = array(
'#type' => 'hidden',
'#value' => $property->value
);
$form['properties']['property_table'][$property->type_id][$property->property_id]['prop_rank'] = array(
'#type' => 'hidden',
'#value' => $property->rank
);
$form['properties']['property_table'][$property->type_id][$property->property_id]['property_id'] = array(
'#type' => 'hidden',
'#value' => $property->property_id
);
$form['properties']['property_table'][$property->type_id][$property->property_id]['type'] = array(
'#type' => 'markup',
'#markup' => $property->type_name,
'#prefix' => '<span class="row-unsaved-warning"></span>'
);
// If a definition is available we want to add that to the type column
// to make it easier for users to determine what an added property means.
if (isset($property->definition)) {
$form['properties']['property_table'][$property->type_id][$property->property_id]['type']['#markup'] = $property->type_name . '<br><i>' . $property->definition . '</i>';
}
$form['properties']['property_table'][$property->type_id][$property->property_id]['value'] = array(
'#type' => 'markup',
'#markup' => $property->value,
);
$form['properties']['property_table'][$property->type_id][$property->property_id]['rank'] = array(
'#type' => 'markup',
'#markup' => $property->rank
);
// remove button
$form['properties']['property_table'][$property->type_id][$property->property_id]['property_action'] = array(
'#type' => 'submit',
'#value' => t('Remove'),
'#name' => "properties_remove-" . $property->type_id . '-' . $property->property_id,
'#ajax' => array(
'callback' => "chado_add_node_form_subtable_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_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 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']
),
);
}
}
// Quickly add a hidden field stating how many properties are currently added.
$form['properties']['num_properties'] = array(
'#type' => 'hidden',
'#value' => $num_properties,
'#attributes' => array('class' => 'num-properties')
);
// Form elements for adding a new property
//---------------------------------------------
$form['properties']['property_table']['new'] = array(
'#type' => 'markup',
'#prefix' => '<span class="addtl-properties-add-new-property">',
'#suffix' => '</span>'
);
// get the value selected (only works on AJAX call) and print the
// description
$type_desc = '';
if (isset($form_state['input']['property_table']['new']['type'])) {
$new_type_id = $form_state['input']['property_table']['new']['type'];
$new_term = tripal_get_cvterm(array('cvterm_id' => $new_type_id));
if ($new_term) {
$type_desc = $new_term->definition;
}
}
$form['properties']['property_table']['new']['type'] = array(
'#type' => 'select',
'#options' => $property_options, // Set at top of form
'#prefix' => '<span id="tripal-generic-edit-properties-new-desc">',
'#suffix' => '<i>' . $type_desc . '</i></span>',
'#ajax' => array(
'callback' => "chado_add_node_form_properties_ajax_desc",
'wrapper' => 'tripal-generic-edit-properties-new-desc',
'effect' => 'fade',
'method' => 'replace',
),
);
$form['properties']['property_table']['new']['value'] = array(
'#type' => 'textarea',
'#rows' => 2,
);
// add button
$form['properties']['property_table']['new']['property_action'] = array(
'#type' => 'submit',
'#value' => t('Add'),
'#name' => "properties-add",
'#ajax' => array(
'callback' => "chado_add_node_form_subtable_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_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 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['properties']['admin_message'] = array(
'#type' => 'markup',
'#markup' => $tripal_message
);
}