Browse Source

Created a new Tripal API module containing the chado node properties api and function wrappers for the old properties api

Lacey Sanderson 11 years ago
parent
commit
e8c22d49b5

+ 98 - 0
tripal_api/DEPRECATED/tripal-2.0_pre-release.inc

@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Wrappers for functions created for Tripal 2 whose names were changed before release
+ */
+
+/**
+ * This function is a wrapper for adding fields to an existing form for managing properties.
+ * Many of the chado tables have a corresponding 'prop' table (e.g. analysisprop, contactprop,
+ * organismprop, etc) and those prop tables all have the same schema.  Use this function
+ * to add all the necessary components to a form for allowing the user to add/edit properties to
+ * a given record.  To retreive properties in hook_insert or hook_update of a node form use
+ * use the function tripal_core_properties_form_retreive().
+ *
+ * @param $form
+ *   The Drupal form array into which the properties elements will be added
+ * @param $form_state
+ *   The corresponding form_state array for the form
+ * @param $prop_table
+ *   The name of the property table (e.g. anlaysisprop, contactprop)
+ * @param $id_field
+ *   The name of the ID field in the property table (e.g. analysis_id, contact_id)
+ * @param $cv_name
+ *   The name of the controlled vocabulary that these properties are derived from
+ * @param $available_props
+ *   An array of properties to inclde in the properties drop down.  This array should
+ *   have cvterm_id's as the key and the cvterm name as the value
+ * @param $id
+ *   The current base table ID.  For example, if the property table is analysisprop then the
+ *   value should be that of the analysis_id.  If the property table is contactprop then the
+ *   value should be that of the contact_id.  This is the record from which currently assigned
+ *   properties will be retrieved.
+ * @param $exclude
+ *   An optional array of cvterms to exclude when retreiving terms already saved in the database.
+ *   Use this array when properties are present but should be handled elsewhere.
+ *   For example, for contacts, the description field is stored as a property because
+ *   the actual field is only 255 characters. The 'contact_description' therefore should
+ *   not be shown in the list of properties, even if present, because it is handled by
+ *   a different form element.
+ * @param $include
+ *   An optional array of terms to pre-populate in the form.  This argument can be used to
+ *   add a default set of pre-populated properties regardless if they exist in the database
+ *   or not.  The array should be of the following form:
+ *     array(
+ *       array('cvterm' => $obj1, 'value' => $val1),
+ *       array('cvterm' => $obj2, 'value' => $val2),
+ *       ... etc
+ *     );
+ *   The 'cvterm' key should have as a value an object with these properties: 'name', 'cvterm_id', 'definition'.
+ * @param $instructions
+ *   An optional additional set of instructions for the form properties.
+ * @param $fset_title
+ *   A title for the property field set.  The default is 'Additional Details'.
+ * @ingroup tripal_properties_api
+ */
+function tripal_core_properties_form(&$form, &$form_state, $prop_table, $id_field, $cv_name,
+    $available_props, $id = NULL, $exclude = array(), $include = array(), $instructions = '',
+    $fset_title = 'Additional Details') {
+
+    // $available_props is now created by the form based on the cv
+    // $exclude and $include are not yet supported
+
+    $details = array(
+    'property_table' => $prop_table,
+    'base_foreign_key' => $id_field,
+    'base_key_value' => $id,
+    'cv_name' => $cv_name,
+    'fieldset_title' => $fset_title,
+    'additional_instructions' => $instructions
+  );
+  tripal_api_chado_node_properties_form($form, $form_state, $details);
+
+}
+
+/**
+ * This function is used in a hook_insert, hook_update for a node form
+ * when the properties form has been added to the form.  It retrieves all of the properties
+ * and returns them in an array of the format:
+ *
+ *   $properties[<property name>][<rank>] = <value>
+ *
+ * This array can then be used for inserting or updating properties using the API call
+ * tripal_hook_insert_property()
+ *
+ * @param $node
+ * @param $cvname
+ *   The name of the controlled vocabulary that the properties belong to
+ *
+ * @return
+ *   A properties array
+ *
+ * @ingroup tripal_properties_api
+ */
+function tripal_core_properties_form_retreive($node, $cv_name) {
+
+  return tripal_api_chado_node_properties_form_retreive($node);
+}

+ 637 - 0
tripal_api/chado_node_api/tripal_api.chado_node.properties.inc

@@ -0,0 +1,637 @@
+<?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
+    tripal_api_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
+ */
+
+/**
+ * @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
+ */
+function tripal_api_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['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>'
+  );
+
+  // 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' => 'tripal_api_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 CVT.cvterm_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.
+   */
+  foreach ($existing_properties as $property) {
+
+    $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' => "tripal_api_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('tripal_api_chado_node_properties_form_remove_button_validate'),
+      '#submit' => array('tripal_api_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' => 'textfield',
+  );
+
+  // add button
+  $form['properties']['property_table']['new']['property_action'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add'),
+    '#name' => "property-add",
+    '#ajax' => array(
+      'callback' => "tripal_api_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('tripal_api_chado_node_properties_form_add_button_validate'),
+    '#submit' => array('tripal_api_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 tripal_api_chado_node_properties_form
+ */
+function tripal_api_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 tripal_api_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
+ */
+function tripal_api_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'])) {
+    tripal_api_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 tripal_api_chado_node_properties_form
+ */
+function tripal_api_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 tripal_api_chado_node_properties_form
+ */
+function tripal_api_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'])) {
+    tripal_api_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 tripal_api_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],
+ *   ),
+ * );
+ */
+function tripal_api_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['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
+ */
+function theme_tripal_api_chado_node_properties_form_table($variables) {
+  $element = $variables['element'];
+
+  $header = array(
+    'type' => t('Type'),
+    'value' => t('Value'),
+    'rank' => t('Rank'),
+    '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_properties_api
+ */
+function tripal_api_chado_node_properties_form_retreive($node) {
+  $properties = array();
+  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)
+ */
+function tripal_api_chado_node_properties_form_update_properties($node, $details) {
+
+  // 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
+  $properties = tripal_api_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
+        );
+      }
+
+    }
+  }
+}

+ 7 - 0
tripal_api/tripal_api.info

@@ -0,0 +1,7 @@
+name = Tripal API
+description = Contains general API functionality useful to a number of Tripal modules.
+core = 7.x
+project = tripal_api
+package = Tripal
+version = 7.x-2.0-beta1
+configure = admin/tripal

+ 25 - 0
tripal_api/tripal_api.module

@@ -0,0 +1,25 @@
+<?php
+
+/**
+ *
+ */
+
+require_once 'chado_node_api/tripal_api.chado_node.properties.inc';
+
+// DEPRECATED Function Wrappers
+require_once 'DEPRECATED/tripal-2.0_pre-release.inc';
+
+/**
+ * Implements hook_theme().
+ * Registers template files/functions used by this module.
+ *
+ * @ingroup tripal_core
+ */
+function tripal_api_theme($existing, $type, $theme, $path) {
+  return array(
+    'tripal_api_chado_node_properties_form_table' => array(
+      'function' => 'theme_tripal_api_chado_node_properties_form_table',
+      'render element' => 'element',
+    ),
+  );
+}

+ 516 - 0
tripal_core/api/tripal_core.properties.api.inc

@@ -0,0 +1,516 @@
+<?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 properties 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 properties form elements, you first need to prepare the arguments
+    // for the function call.  One inportant argument is a list of properties that
+    // will be made avaialble for the user to select from.  You should query the
+    // database to retrieve the applicable terms from the cvterm table and store them in an array
+    // of where the cvterm.cvterm_id field is the key and the cvterm.name is the value.
+    // these terms should all be from the same vocabulary.
+    $properties = array();
+    $properties[] = '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 = :ontology_name AND
+        NOT CVT.is_obsolete = 1
+      ORDER BY CVT.name ASC
+    ";
+
+    $ontology_name = 'name_of_proptype_ontology';  //you need to set this variable with the cv.name of the ontology governing your prop tables type_id
+    $prop_types = chado_query($sql, array(':ontology_name' => $ontology_name));
+    while ($prop = $prop_types->fetchObject()) {
+      $properties[$prop->cvterm_id] = $prop->name;
+    }
+
+    // the properties form will add a select dropdown of terms containing the items in the $properties array
+    // constructed above, but it will also pre-populate rows of properties that already are associated
+    // with the object.  If you would like to pre-populated properties regardless if they exist in the database
+    // or not, you can create an $include array which has the following format:
+    //     array(
+    //       array('cvterm' => $obj1, 'value' => $val1),
+    //       array('cvterm' => $obj2, 'value' => $val2),
+    //       ... etc
+    //     );
+    //   The 'cvterm' key should have as a value an object with these properties: 'name', 'cvterm_id', 'definition'.
+    $include = array();
+
+    // sometimes a property exists in the database and is properly associated with the object, but we do
+    // not want it to appear in the list of properties that are pre-populated. It may be handled in some
+    // other way.  For example, for contacts, the description field is stored as a property because
+    // the actual contact.description field is only 255 characters. The 'contact_description' property should
+    // not be shown in the list of properties, even if present, because it is handled by
+    // a different form element.  This array holds the value of the cvterm.name column of the cvterms
+    // to exclude
+    $exclude = array();
+
+    // the instructions argument provides additional instructions to the user beyond the default instructions.
+    $instructions = t('To add additional properties to the drop down. ' . l("Add terms to the $ontology_name vocabulary", "admin/tripal/chado/tripal_cv/cvterm/add") . ".");
+
+    // Finally, and add the properties form elements to the form
+    tripal_core_properties_form(
+      $form, $form_state,     // form and form_state of the current form
+      'exampleprop',          // properties table name
+      'example_id',           // key to link to the chado content created by this node
+      $ontology_name,         // name of ontology governing your prop table type_id column
+      $properties,            // an array of properties to use in the drop-down
+      $example_id,            // the value of the above key
+      $exclude,               // elements from the ontology you don't want to be available as property types
+      $include,               // additional elements not in the ontology that you do what in the drop-down
+      $instructions,          // form specific instructions
+      'Properties'            // name of the fieldset
+    );
+
+    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 each property (exampleprop table). The tripal_core_properties_form_retrieve()
+      // function retrieves all of the properties and returns them in an array of the format:
+      //
+      // $properties[<property name>][<rank>] = <value
+      //
+      // This array can then be used for inserting or updating properties using the API call
+      //   tripal_hook_insert_property()
+      //
+      // example_property = controlled vocab name for exampleprop.type_id
+      $properties = tripal_core_properties_form_retreive($node, 'example_property');
+      foreach ($properties as $property => $elements) {
+        foreach ($elements as $rank => $value) {
+
+          $success = tripal_core_insert_property(
+            'example',        //base table name
+            $example_id,      // key to link to the chado content created by this node
+            $property,        // cvterm.name of the property to be added
+            $ontology_name,   // name of the ontology the cvterm is from
+            $value            // the value o the property
+          );
+
+          if (!$success) {
+            watchdog(
+              'tripal_example',
+              'Example Update: Unable to insert property %cvterm %value.',
+              array('%cvterm' => $property, '%value' => $value),
+              WATCHDOG_ERROR
+            );
+          }
+        }
+      }
+    }
+
+    // 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
+
+      // First delete any existing properties
+      tripal_core_chado_delete(
+        'exampleprop',                        // the name of the prop table
+        array('example_id' => $example_id)    // name of your key and the current value used to determine which to delete
+      );
+
+      // Add each property (exampleprop table)
+      // example_property = controlled vocab name for exampleprop.type_id
+      $properties = tripal_core_properties_form_retreive($node, 'example_property');
+      foreach ($properties as $property => $elements) {
+        foreach ($elements as $rank => $value) {
+
+          $success = tripal_core_insert_property(
+            'example',        //base table name
+            $example_id,      // key to link to the chado content created by this node
+            $property,        // cvterm.name of the property to be added
+            $ontology_name,   // name of the ontology the cvterm is from
+            $value            // the value o the property
+          );
+
+          if (!$success) {
+            watchdog(
+              'tripal_example',
+              'Example Update: Unable to insert property %cvterm %value.',
+              array('%cvterm' => $property, '%value' => $value),
+              WATCHDOG_ERROR
+            );
+          }
+        }
+      }
+    }
+
+    // Don't need to update chado_example linking table since niether example_id or nid can be changed in update
+
+  }
+
+ * @endcode
+ */
+
+/**
+ * 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_properties_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_properties_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_properties_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_properties_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_properties_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_properties_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);
+}

+ 0 - 1153
tripal_core/api/tripal_core_properties.api.inc

@@ -1,1153 +0,0 @@
-<?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 properties 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 properties form elements, you first need to prepare the arguments
-    // for the function call.  One inportant argument is a list of properties that 
-    // will be made avaialble for the user to select from.  You should query the 
-    // database to retrieve the applicable terms from the cvterm table and store them in an array
-    // of where the cvterm.cvterm_id field is the key and the cvterm.name is the value.
-    // these terms should all be from the same vocabulary.
-    $properties = array();
-    $properties[] = '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 = :ontology_name AND
-        NOT CVT.is_obsolete = 1
-      ORDER BY CVT.name ASC
-    ";
-    
-    $ontology_name = 'name_of_proptype_ontology';  //you need to set this variable with the cv.name of the ontology governing your prop tables type_id
-    $prop_types = chado_query($sql, array(':ontology_name' => $ontology_name));
-    while ($prop = $prop_types->fetchObject()) {
-      $properties[$prop->cvterm_id] = $prop->name;
-    }
-
-    // the properties form will add a select dropdown of terms containing the items in the $properties array
-    // constructed above, but it will also pre-populate rows of properties that already are associated
-    // with the object.  If you would like to pre-populated properties regardless if they exist in the database
-    // or not, you can create an $include array which has the following format:
-    //     array(
-    //       array('cvterm' => $obj1, 'value' => $val1),
-    //       array('cvterm' => $obj2, 'value' => $val2),
-    //       ... etc
-    //     );
-    //   The 'cvterm' key should have as a value an object with these properties: 'name', 'cvterm_id', 'definition'.
-    $include = array();
-    
-    // sometimes a property exists in the database and is properly associated with the object, but we do 
-    // not want it to appear in the list of properties that are pre-populated. It may be handled in some
-    // other way.  For example, for contacts, the description field is stored as a property because
-    // the actual contact.description field is only 255 characters. The 'contact_description' property should
-    // not be shown in the list of properties, even if present, because it is handled by
-    // a different form element.  This array holds the value of the cvterm.name column of the cvterms 
-    // to exclude
-    $exclude = array();
-    
-    // the instructions argument provides additional instructions to the user beyond the default instructions.
-    $instructions = t('To add additional properties to the drop down. ' . l("Add terms to the $ontology_name vocabulary", "admin/tripal/chado/tripal_cv/cvterm/add") . ".");
-
-    // Finally, and add the properties form elements to the form
-    tripal_core_properties_form(
-      $form, $form_state,     // form and form_state of the current form
-      'exampleprop',          // properties table name
-      'example_id',           // key to link to the chado content created by this node
-      $ontology_name,         // name of ontology governing your prop table type_id column
-      $properties,            // an array of properties to use in the drop-down
-      $example_id,            // the value of the above key
-      $exclude,               // elements from the ontology you don't want to be available as property types
-      $include,               // additional elements not in the ontology that you do what in the drop-down
-      $instructions,          // form specific instructions
-      'Properties'            // name of the fieldset
-    );
-
-    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 each property (exampleprop table). The tripal_core_properties_form_retrieve()
-      // function retrieves all of the properties and returns them in an array of the format:
-      //
-      // $properties[<property name>][<rank>] = <value
-      //
-      // This array can then be used for inserting or updating properties using the API call
-      //   tripal_hook_insert_property()
-      //
-      // example_property = controlled vocab name for exampleprop.type_id
-      $properties = tripal_core_properties_form_retreive($node, 'example_property');
-      foreach ($properties as $property => $elements) {
-        foreach ($elements as $rank => $value) {
-
-          $success = tripal_core_insert_property(
-            'example',        //base table name
-            $example_id,      // key to link to the chado content created by this node
-            $property,        // cvterm.name of the property to be added
-            $ontology_name,   // name of the ontology the cvterm is from
-            $value            // the value o the property
-          );
-
-          if (!$success) {
-            watchdog(
-              'tripal_example',
-              'Example Update: Unable to insert property %cvterm %value.',
-              array('%cvterm' => $property, '%value' => $value),
-              WATCHDOG_ERROR
-            );
-          }
-        }
-      }
-    }
-
-    // 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
-
-      // First delete any existing properties
-      tripal_core_chado_delete(
-        'exampleprop',                        // the name of the prop table
-        array('example_id' => $example_id)    // name of your key and the current value used to determine which to delete
-      );
-
-      // Add each property (exampleprop table)
-      // example_property = controlled vocab name for exampleprop.type_id
-      $properties = tripal_core_properties_form_retreive($node, 'example_property');
-      foreach ($properties as $property => $elements) {
-        foreach ($elements as $rank => $value) {
-
-          $success = tripal_core_insert_property(
-            'example',        //base table name
-            $example_id,      // key to link to the chado content created by this node
-            $property,        // cvterm.name of the property to be added
-            $ontology_name,   // name of the ontology the cvterm is from
-            $value            // the value o the property
-          );
-
-          if (!$success) {
-            watchdog(
-              'tripal_example',
-              'Example Update: Unable to insert property %cvterm %value.',
-              array('%cvterm' => $property, '%value' => $value),
-              WATCHDOG_ERROR
-            );
-          }
-        }
-      }
-    }
-
-    // Don't need to update chado_example linking table since niether example_id or nid can be changed in update
-
-  }
-
- * @endcode
- */
-
-/**
- * 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_properties_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_properties_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_properties_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_properties_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_properties_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_properties_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);
-}
-
-/**
- * This function is a wrapper for adding fields to an existing form for managing properties.
- * Many of the chado tables have a corresponding 'prop' table (e.g. analysisprop, contactprop,
- * organismprop, etc) and those prop tables all have the same schema.  Use this function
- * to add all the necessary components to a form for allowing the user to add/edit properties to
- * a given record.  To retreive properties in hook_insert or hook_update of a node form use
- * use the function tripal_core_properties_form_retreive().
- *
- * @param $form
- *   The Drupal form array into which the properties elements will be added
- * @param $form_state
- *   The corresponding form_state array for the form
- * @param $prop_table
- *   The name of the property table (e.g. anlaysisprop, contactprop)
- * @param $id_field
- *   The name of the ID field in the property table (e.g. analysis_id, contact_id)
- * @param $cv_name
- *   The name of the controlled vocabulary that these properties are derived from
- * @param $available_props
- *   An array of properties to inclde in the properties drop down.  This array should
- *   have cvterm_id's as the key and the cvterm name as the value
- * @param $id
- *   The current base table ID.  For example, if the property table is analysisprop then the
- *   value should be that of the analysis_id.  If the property table is contactprop then the
- *   value should be that of the contact_id.  This is the record from which currently assigned
- *   properties will be retrieved.
- * @param $exclude
- *   An optional array of cvterms to exclude when retreiving terms already saved in the database.
- *   Use this array when properties are present but should be handled elsewhere.
- *   For example, for contacts, the description field is stored as a property because
- *   the actual field is only 255 characters. The 'contact_description' therefore should
- *   not be shown in the list of properties, even if present, because it is handled by
- *   a different form element.
- * @param $include
- *   An optional array of terms to pre-populate in the form.  This argument can be used to
- *   add a default set of pre-populated properties regardless if they exist in the database
- *   or not.  The array should be of the following form:
- *     array(
- *       array('cvterm' => $obj1, 'value' => $val1),
- *       array('cvterm' => $obj2, 'value' => $val2),
- *       ... etc
- *     );
- *   The 'cvterm' key should have as a value an object with these properties: 'name', 'cvterm_id', 'definition'.
- * @param $instructions
- *   An optional additional set of instructions for the form properties.
- * @param $fset_title
- *   A title for the property field set.  The default is 'Additional Details'.
- * @ingroup tripal_properties_api
- */
-function tripal_core_properties_form(&$form, &$form_state, $prop_table, $id_field, $cv_name,
-    $available_props, $id = NULL, $exclude = array(), $include = array(), $instructions = '',
-    $fset_title = 'Additional Details') {
-
-  $d_removed = array(); // lists removed properties
-
-  // if we are re constructing the form from a failed validation or ajax callback
-  // then use the $form_state['values'] values
-  if (array_key_exists('values', $form_state)) {
-    $d_removed = $form_state['values']['removed'];
-  }
-  // if we are re building the form from after submission (from ajax call) then
-  // the values are in the $form_state['input'] array
-  if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
-    $d_removed = $form_state['input']['removed'];
-  }
-
-  $form['properties'] = array(
-    '#type' => 'fieldset',
-    '#title' => t($fset_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. ' . $instructions),
-  );
-  $form['properties']['table'] = array(
-    '#type' => 'markup',
-    '#value' =>  '',
-    '#prefix' => '<div id="tripal-generic-edit-properties-table">',
-    '#suffix' => '</div>',
-  );
-
-  // this array keeps track of all properties we have and allows the functions
-  // below to select the next rank if a property is dupliated
-  $ranks = array();
-
-  // add in the properties from the Chado prop table (only pertains to existing analyses)
-  if ($id) {
-    tripal_core_properties_form_add_prop_table_props($prop_table, $id_field, $cv_name,
-      $form, $form_state, $id, $ranks, $d_removed, $exclude, $include);
-  }
-
-  // add in any new properties that have been added by the user through an AHAH callback
-  tripal_core_properties_form_add_new_props($form, $form_state, $ranks, $d_removed);
-
-  // add an empty row of field to allow for addition of a new property
-  tripal_core_properties_form_add_new_empty_props($form, $form_state, $available_props);
-
-  $form['properties']['table']['#theme'] = 'tripal_core_properties_form';
-}
-
-/**
- * This function is responsible for adding a blank row to the properties table for
- * adding a new property.
- */
-function  tripal_core_properties_form_add_new_empty_props(&$form, &$form_state, $properties_select) {
-
-  // get the field defaults either from $form_state['values'] or $form_state['input']
-  $description = '';
-  $text = '';
-  $id = 0;
-  if (array_key_exists('values', $form_state)) {
-    $id = $form_state['values']['new_id'];
-    $text = $form_state['values']['new_value'];
-  }
-  // if we have a property ID then get it's definition to display to the user
-  if($id) {
-    $values = array('cvterm_id' => $id);
-    $cvterm = tripal_core_chado_select('cvterm', array('definition'), $values);
-
-    if ($cvterm[0]->definition) {
-      $description = $cvterm[0]->definition;
-    }
-  }
-  $rows = 1;
-
-  // add one more blank set of property fields
-  $form['properties']['table']['new']["new_id"] = array(
-    '#type'          => 'select',
-    '#options'       => $properties_select,
-    '#default_value' => $id,
-    '#ajax' => array(
-      'callback' => "tripal_core_props_property_ajax_get_description",
-      'wrapper'  => 'tripal-properties-new_value',
-      'effect'   => 'fade',
-      'method'   => 'replace',
-    ),
-  );
-  $form['properties']['table']['new']["new_value"] = array(
-    '#type'           => 'textarea',
-    '#default_value'  => $text,
-    '#cols'           => 50,
-    '#rows'           => $rows,
-    '#prefix'         => '<div id="tripal-properties-new_value">',
-    '#description'    => '<span style="white-space: normal">' . $description . '</span>',
-    '#suffix'         => '</div>',
-  );
-
-  $form['properties']['table']['new']["add"] = array(
-    '#type'    => 'button',
-    '#value'   => t('Add'),
-    '#name'    => 'add',
-    '#ajax'      => array(
-      'callback' => "tripal_core_props_property_ajax_update",
-      'wrapper'  => 'tripal-properties-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 form submit.  In the validate function we set the form_state
-    // to rebuild the form so the submit function never actually gets called,
-    // but we need it or Drupal will run the default validate anyway.
-    // we also set #limit_validation_errors to empty so fields that
-    // are required that don't have values won't generate warnings.
-    '#submit'   => array('tripal_core_props_form_props_button_submit'),
-    '#validate' => array('tripal_core_props_form_props_button_validate'),
-    '#limit_validation_errors' => array(array('new_id')),
-  );
-}
-
-/**
- * This function is used to rebuild the form if an ajax call is made vai a button.
- * The button causes the form to be submitted. We don't want this so we override
- * the validate and submit routines on the form button. Therefore, this function
- * only needs to tell Drupal to rebuild the form
- */
-function  tripal_core_props_form_props_button_validate($form, &$form_state){
-  if (array_key_exists('triggering_element', $form_state) and
-  $form_state['triggering_element']['#name'] == 'add' and
-  $form_state['input']['new_id'] == 0 ){
-    form_set_error('new_id', "Please specify a property type");
-    return;
-  }
-  $form_state['rebuild'] = TRUE;
-}
-/**
- * This function is just a dummy to override the default form submit on ajax calls for buttons
- */
-function tripal_core_props_form_props_button_submit($form, &$form_state){
-  // do nothing
-}
-/**
- * This adds
- */
-function tripal_core_properties_form_add_new_props(&$form, &$form_state, &$ranks, &$d_removed) {
-
-  // set some default values
-  $j = 0;
-  $num_properties = 0;
-
-  $values = array();
-  if (array_key_exists('values', $form_state)) {
-    $values = $form_state['values'];
-  }
-  if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
-    $values = $form_state['input'];
-  }
-
-  // first, add in all of the new properties that were added previously via this form
-  foreach ($values as $element_name => $value) {
-    if (preg_match('/new_value-(\d+)-(\d+)/', $element_name, $matches)) {
-      $new_id = $matches[1];
-      $rank = $matches[2];
-
-      // skip any properties that the user requested to delete through a previous
-      // ajax callback or through the current ajax callback
-      if (array_key_exists("$new_id-$rank", $d_removed)) {
-        continue;
-      }
-      if (array_key_exists('triggering_element', $form_state) and
-      $form_state['triggering_element']['#name'] == 'remove-' . $new_id . '-' . $rank) {
-        $d_removed["$new_id-$rank"] = 1;
-        continue;
-      }
-
-      // get this new_id information
-      $args = array('cvterm_id' => $new_id);
-      $cvterm = tripal_core_chado_select('cvterm', array('name', 'definition'), $args);
-
-      // add it to the $ranks array
-      $ranks[$new_id][$rank]['name']  = $cvterm[0]->name;
-      $ranks[$new_id][$rank]['id']    = $new_id;
-      $ranks[$new_id][$rank]['value'] = $value;
-      $ranks[$new_id][$rank]['definition']  = $cvterm[0]->definition;
-      $num_properties++;
-
-      // determine how many rows we need in the textarea
-      $rows = 1;
-      $rows = strlen($value) / 80 + 1;
-      if ($rows > 10) {
-        $rows = 10;
-      }
-
-      // add the new fields
-      $form['properties']['table']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
-        '#markup' => $cvterm[0]->name
-      );
-      $form['properties']['table']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
-        '#type'          => 'textarea',
-        '#default_value' => $value,
-        '#cols'          => 50,
-        '#rows'          => $rows,
-        '#description'   => '<span style="white-space: normal">' . $cvterm[0]->definition . '</span>',
-      );
-
-      $form['properties']['table']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
-        '#type'         => 'button',
-        '#value'        => t('Remove'),
-        '#name'         => "remove-$new_id-$rank",
-        '#ajax' => array(
-          'callback' => "tripal_core_props_property_ajax_update",
-          'wrapper'  => 'tripal-properties-edit-properties-table',
-          'effect'   => 'fade',
-          'event'    => 'mousedown',
-          '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 form submit.  In the validate function we set the form_state
-        // to rebuild the form so the submit function never actually gets called,
-        // but we need it or Drupal will run the default validate anyway.
-        // we also set #limit_validation_errors to empty so fields that
-        // are required that don't have values won't generate warnings.
-        '#submit'   => array('tripal_core_props_form_props_button_submit'),
-        '#validate' => array('tripal_core_props_form_props_button_validate'),
-        '#limit_validation_errors' => array(),
-      );
-    }
-  }
-
-  // second add in any new properties added during this callback
-  if (array_key_exists('triggering_element', $form_state) and
-    $form_state['triggering_element']['#name'] == 'add' and
-    $form_state['input']['new_id'] != 0) {
-    $new_id    = $form_state['input']['new_id'];
-    $new_value = $form_state['input']['new_value'];
-
-    // get the rank by counting the number of entries
-    $rank = count($ranks[$new_id]);
-
-    // get this new_id information
-    $cvterm = tripal_core_chado_select('cvterm', array('name', 'definition'), array('cvterm_id' => $new_id));
-
-    // add it to the $ranks array
-    $ranks[$new_id][$rank]['name']  = $cvterm[0]->name;
-    $ranks[$new_id][$rank]['id']    = $new_id;
-    $ranks[$new_id][$rank]['value'] = $value;
-    $ranks[$new_id][$rank]['definition']  = $cvterm[0]->definition;
-    $num_properties++;
-
-    // determine how many rows we need in the textarea
-    $rows = 1;
-
-    // add the new fields
-    $form['properties']['table']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
-      '#markup' => $cvterm[0]->name
-    );
-    $form['properties']['table']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
-      '#type'          => 'textarea',
-      '#default_value' => $new_value,
-      '#cols'          => 50,
-      '#rows'          => $rows,
-      '#description'   => $cvterm[0]->definition,
-    );
-
-    $form['properties']['table']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
-      '#type'         => 'button',
-      '#value'        => t('Remove'),
-      '#name'         => "remove-$new_id-$rank",
-      '#ajax' => array(
-        'callback' => "tripal_core_props_property_ajax_update",
-        'wrapper'  => 'tripal-properties-edit-properties-table',
-        'effect'   => 'fade',
-        'event'    => 'mousedown',
-        '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 form submit.  In the validate function we set the form_state
-      // to rebuild the form so the submit function never actually gets called,
-      // but we need it or Drupal will run the default validate anyway.
-      // we also set #limit_validation_errors to empty so fields that
-      // are required that don't have values won't generate warnings.
-      '#submit'   => array('tripal_core_props_form_props_button_submit'),
-      '#validate' => array('tripal_core_props_form_props_button_validate'),
-      '#limit_validation_errors' => array(),
-    );
-
-  }
-
-  return $num_properties;
-}
-/**
- * This function queries the proper xxxprop table to look for existing values for the given
- * $id.  It then adds these properties to the form for editing.  It also will incorporate
- * extra properties that were specified manually by the caller.
- *
- */
-function  tripal_core_properties_form_add_prop_table_props($prop_table, $id_field, $cv_name,
-    &$form, $form_state, $id, &$ranks, &$d_removed, $exclude = array(), $include = array()) {
-
-  // get the existing properties
-  $num_properties = 0;
-
-  if (!$id) {
-    return;
-  }
-
-  // create an array of properties so we can merge those in the database with those provided by the caller
-  $all_props = array();
-  foreach ($include as $prop) {
-    $all_props[] = $prop;
-  }
-
-  // now merge in properties saved in the database
-  $sql = "
-    SELECT CVT.cvterm_id, CVT.name, CVT.definition, PP.value, PP.rank
-    FROM {" . $prop_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.$id_field = :id AND
-      CV.name = '$cv_name'
-      ORDER BY CVT.name, PP.rank
-      ";
-  $props = chado_query($sql, array(':id' => $id));
-  while ($prop = $props->fetchObject()) {
-    $all_props[] = array('cvterm' => $prop, 'value' => $prop->value);
-  }
-
-  // iterate through the properties
-  foreach ($all_props as $prop) {
-
-    $type_id    = $prop['cvterm']->cvterm_id;
-    $value      = $prop['value'];
-    $name       = $prop['cvterm']->name;
-    $definition = $prop['cvterm']->definition;
-    $rank       = 0;
-    if(array_key_exists($type_id, $ranks)) {
-      $rank = count($ranks[$type_id]);
-    }
-
-    // skip any properties that the user requested to delete through a previous
-    // AHAH callback or through the current AHAH callback
-    if (array_key_exists("$type_id-$rank", $d_removed)) {
-      continue;
-    }
-    // skip any properties that should be excluded
-    if (count(array_intersect(array($name), $exclude)) == 1) {
-      continue;
-    }
-    if (array_key_exists('triggering_element', $form_state) and
-    $form_state['triggering_element']['#name'] == 'remove-' . $type_id . '-' . $rank) {
-      $d_removed["$type_id-$rank"] = 1;
-      continue;
-    }
-
-    $ranks[$type_id][$rank]['name']  = $name;
-    $ranks[$type_id][$rank]['id']    = $type_id;
-    $ranks[$type_id][$rank]['value'] = $value;
-    $num_properties++;
-    $rows = 1;
-    $rows = strlen($value) / 80 + 1;
-    if ($rows > 10) {
-      $rows = 10;
-    }
-
-
-    $form['properties']['table'][$type_id][$rank]["prop_id-$type_id-$rank"] = array(
-      '#markup'        => $name,
-    );
-    $form['properties']['table'][$type_id][$rank]["prop_value-$type_id-$rank"] = array(
-      '#type'          => 'textarea',
-      '#default_value' => $value,
-      '#cols'          => 50,
-      '#rows'          => $rows,
-      '#description'   => '<span style="white-space: normal">' . $definition . '</span>',
-    );
-
-    $form['properties']['table'][$type_id][$rank]["remove-$type_id-$rank"] = array(
-      '#type'         => 'button',
-      '#value'        => t('Remove'),
-      '#name'         => "remove-$type_id-$rank",
-      '#ajax' => array(
-        'callback' => "tripal_core_props_property_ajax_update",
-        'wrapper'  => 'tripal-properties-edit-properties-table',
-        'effect'   => 'fade',
-        'event'    => 'mousedown',
-        '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 form submit.  In the validate function we set the form_state
-      // to rebuild the form so the submit function never actually gets called,
-      // but we need it or Drupal will run the default validate anyway.
-      // we also set #limit_validation_errors to empty so fields that
-      // are required that don't have values won't generate warnings.
-      '#submit'   => array('tripal_core_props_form_props_button_submit'),
-      '#validate' => array('tripal_core_props_form_props_button_validate'),
-      '#limit_validation_errors' => array(),
-    );
-  }
-  return $num_properties;
-}
-
-/**
- * Form AJAX callback for adding a blank property row
- *
- * We only want to return the properties as that's all we'll replace with this callback
- */
-function tripal_core_props_property_ajax_update($form, $form_state) {
-  $properties_html = tripal_core_props_theme_node_form_properties($form['properties']['table']);
-  $form['properties']['table'] = array(
-    '#markup' => $properties_html,
-    '#prefix' => '<div id="tripal-properties-edit-properties-table">',
-    '#suffix' => '</div>',
-  );
-  return $form['properties']['table'];
-}
-/**
- * Form AJAX callback for updating a property description. This
- * function only gets called when the property drop down is changed
- * on the bottom (empty) row of properties
- */
-function tripal_core_props_property_ajax_get_description($form, $form_state) {
-  return $form['properties']['table']['new']["new_value"];
-}
-
-/**
- * We need to theme the form so that the properties fields look good
- */
-function theme_tripal_core_properties_form($variables) {
-  $form = $variables['form'];
-
-  $properties_table = tripal_core_props_theme_node_form_properties($form);
-  $markup = $properties_table;
-
-  $form['properties']['table'] = array(
-    '#markup' => $markup,
-    '#prefix' => '<div id="tripal-properties-edit-properties-table">',
-    '#suffix' => '</div>',
-  );
-  $form['buttons']['#weight'] = 50;
-  return drupal_render($form['properties']['table']);
-}
-/**
- *
- */
-function tripal_core_props_theme_node_form_properties($form) {
-  $rows = array();
-
-  // first add in the properties derived from the prop table
-  // the array tree for these properties looks like this:
-  // $form['properties']['table'][$type_id][$rank]["prop_id-$type_id-$rank"]
-  foreach ($form as $type_id => $elements) {
-    // there are other fields in the properties array so we only
-    // want the numeric ones those are our type_id
-    if (is_numeric($type_id)) {
-      foreach ($elements as $rank => $element) {
-        if (is_numeric($rank)) {
-          $rows[] = array(
-            drupal_render($element["prop_id-$type_id-$rank"]),
-            drupal_render($element["prop_value-$type_id-$rank"]),
-            drupal_render($element["remove-$type_id-$rank"]),
-          );
-        }
-      }
-    }
-  }
-
-  // second, add in any new properties added by the user through AHAH callbacks
-  // the array tree for these properties looks like this:
-  // $form['properties']['table']['new'][$type_id][$rank]["new_id-$new_id-$rank"]
-  foreach ($form['new'] as $type_id => $elements) {
-    if (is_numeric($type_id)) {
-      foreach ($elements as $rank => $element) {
-        if (is_numeric($rank)) {
-          $rows[] = array(
-            drupal_render($element["new_id-$type_id-$rank"]),
-            drupal_render($element["new_value-$type_id-$rank"]),
-            drupal_render($element["remove-$type_id-$rank"]),
-          );
-        }
-      }
-    }
-  }
-
-  // finally add in a set of blank field for adding a new property
-  $rows[] = array(
-    drupal_render($form['new']['new_id']),
-    array(
-      'data' => drupal_render($form['new']['new_value']),
-      'width' => '60%',
-    ),
-    drupal_render($form['new']['add']),
-  );
-
-  $headers = array('Property Type', 'Value', 'Actions');
-  $table = array(
-    'header' => $headers,
-    'rows' => $rows,
-    'attributes' => array(),
-    'sticky' => TRUE,
-    'caption' => '',
-    'colgroups' => array(),
-    'empty' => '',
-  );
-
-  return theme_table($table);
-}
-
-/**
- * This function is used in a hook_insert, hook_update for a node form
- * when the properties form has been added to the form.  It retrieves all of the properties
- * and returns them in an array of the format:
- *
- *   $properties[<property name>][<rank>] = <value
- *
- * This array can then be used for inserting or updating properties using the API call
- * tripal_hook_insert_property()
- *
- * @param $node
- * @param $cvname
- *   The name of the controlled vocabulary that the properties belong to
- *
- * @return
- *   A properties array
- *
- * @ingroup tripal_properties_api
- */
-function tripal_core_properties_form_retreive($node, $cv_name) {
-
-  // now add the properties
-  $properties = array(); // stores all of the properties we need to add
-
-  // get the list of properties for easy lookup (without doing lots of database queries
-  $properties_list = array();
-  $sql = "
-      SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition
-      FROM  {cvterm} CVT
-        INNER JOIN {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);
-  while ($prop = $prop_types->fetchObject()) {
-    $properties_list[$prop->cvterm_id] = $prop->name;
-  }
-
-  // get the properties that should be added. Properties are in one of two forms:
-  //  1) prop_value-[type id]-[index]
-  //  2) new_value-[type id]-[index]
-  //  3) new_id, new_value
-
-  foreach ($node as $name => $value) {
-    if (preg_match('/^new_value-(\d+)-(\d+)/', $name, $matches)) {
-      $type_id = $matches[1];
-      $index = $matches[2];
-      $name = $properties_list[$type_id];
-      $properties[$name][$index] = trim($value);
-    }
-    if (preg_match('/^prop_value-(\d+)-(\d+)/', $name, $matches)) {
-      $type_id = $matches[1];
-      $index = $matches[2];
-      $name = $properties_list[$type_id];
-      $properties[$name][$index] = trim($value);
-    }
-  }
-  if (property_exists($node, 'new_id') and $node->new_id and property_exists($node, 'new_value') and $node->new_value) {
-    $type_id = $node->new_id;
-    $name = $properties_list[$type_id];
-    $index = 0;
-    if (array_key_exists($name, $properties)) {
-      $index = count($properties[$name]);
-    }
-    $properties[$name][$index] = trim($node->new_value);
-  }
-
-  return $properties;
-}

+ 2 - 1
tripal_core/tripal_core.info

@@ -6,4 +6,5 @@ package = Tripal
 version = 7.x-2.0-beta1
 configure = admin/tripal
 
-dependencies[] = views
+dependencies[] = views
+dependencies[] = tripal_api

+ 1 - 6
tripal_core/tripal_core.module

@@ -42,7 +42,7 @@ require_once "api/tripal_core_chado_nodes.api.inc";
 require_once "api/tripal_core_custom_tables.api.inc";
 require_once "api/tripal_core_jobs.api.inc";
 require_once "api/tripal_core_mviews.api.inc";
-require_once "api/tripal_core_properties.api.inc";
+require_once "api/tripal_core.properties.api.inc";
 require_once "api/tripal_core.database_references.api.inc";
 require_once "api/tripal_core.relationships.api.inc";
 
@@ -415,11 +415,6 @@ function tripal_core_theme($existing, $type, $theme, $path) {
       'path' => "$path/theme"
     ),
 
-    // form theme
-    'tripal_core_properties_form' => array(
-      'render element' => 'form',
-    ),
-
     // additional dbxrefs form theme
     'tripal_core_additional_dbxrefs_form_table' => array(
       'function' => 'theme_tripal_core_additional_dbxrefs_form_table',