'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 ); } } } } }