1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009 |
- <?php
- /**
- * @file
- * API to manage the chado prop table for various Tripal Node Types
- *
- * How To Use:
- * @code
- function chado_example_form($form, $form_state) {
- // Default values for form elements can come in the following ways:
- //
- // 1) as elements of the $node object. This occurs when editing an existing node
- // 2) in the $form_state['values'] array which occurs on a failed validation or
- // ajax callbacks when the ajax call originates from non-submit fields other
- // than button
- // 3) in the $form_state['input'] array which occurs on ajax callbacks from submit
- // form elements (e.g. buttons) and the form is being rebuilt but has not yet
- // been validated
- //
- // The reference elements added by this function do use AJAX calls from buttons,
- // therefore, it is important to check for form values in the $form_state['values']
- // for case #2 above, and in the $form_state['input'] for case #3.
- // See the chado analysis node form for an example.
- // Next, add in all the form array definition particular to your node type
- // To add in the chado properties form elements, you first need to prepare the arguments
- // for the function call.
- $details = array(
- 'property_table' => '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' => "<div id='properties-fieldset'>",
- '#suffix' => '</div>',
- '#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' => '<div id="tripal-generic-edit-properties-table">',
- '#suffix' => '</div>',
- '#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' => '<span class="addtl-properties-add-new-property">',
- '#suffix' => '</span>'
- );
- $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[<type_id>][<rank>] = <value>
- *
- * 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
- );
- }
- }
- }
- }
- }
|