Browse Source

Started branch for adding properties to the analysis module

spficklin 12 years ago
parent
commit
0c7d49356f

+ 0 - 333
tripal_analysis/includes/tripal_analysis-properties.inc

@@ -1,333 +0,0 @@
-<?php
-/**
- * @file
- * @todo Add file header description
- */
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_edit_ALL_properties_page($node) {
-  $output = '';
-
-  // get the list of properties for this analysis
-  $values  = array('analysis_id' => $node->analysis->analysis_id);
-  $options = array('order_by' => array('type_id' => 'ASC', 'rank' => 'ASC'));
-  $properties = tripal_core_generate_chado_var('analysisprop', $values, $options);
-  $properties = tripal_core_expand_chado_vars($properties, 'field', 'analysisprop.value');
-
-  $expand_add = (sizeof($properties)) ? FALSE : TRUE;
-
-  // add the appopriate form sections
-  $output .= drupal_get_form('tripal_analysis_add_ONE_property_form', $node, $expand_add);
-  $output .= drupal_get_form('tripal_analysis_edit_ALL_properties_form', $node, $properties);
-  $output .= drupal_get_form('tripal_analysis_implement_back_to_analysis_button', $node->nid);
-
-  return $output;
-}
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_add_ONE_property_form($form_state, $node, $expand) {
-  $form = array();
-  $analysis_id = $node->analysis->analysis_id;
-
-  $form['add_properties'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Add Property'),
-    '#collapsible' => TRUE,
-    '#collapsed' => ($expand) ? FALSE : TRUE,
-  );
-
-  $form['prop_nid'] = array(
-    '#type' => 'hidden',
-    '#value' => $node->nid
-  );
-
-  $form['add_properties']['analysis_id'] = array(
-    '#type' => 'value',
-    '#value' => $analysis_id,
-    '#required' => TRUE
-  );
-
-  // right now this defaults to the 'analysis_property' CV
-  // but in the future it should be more flexible
-  $form['cv_name'] = array(
-    '#type' => 'hidden',
-    '#value' => 'analysis_property'
-  );
-
-  // get the list of property types
-  $prop_type_options = array();
-  $columns = array('cvterm_id', 'name');
-  $values = array(
-    'cv_id' => array(
-      'name' => $form['cv_name']['#value'],
-    )
-  );
-  $results = tripal_core_chado_select('cvterm', $columns, $values);
-  foreach ($results as $r) {
-    $prop_type_options[$r->name] = $r->name;
-  }
-
-  $form['add_properties']['property'] = array(
-    '#type' => 'select',
-    '#title' => t('Type of Property'),
-    '#options' => $prop_type_options,
-  );
-
-  $form['add_properties']['prop_value'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Value'),
-  );
-
-  $form['add_properties']['submit-add'] = array(
-    '#type' => 'submit',
-    '#value' => t('Add Property')
-  );
-
-  return $form;
-}
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_add_ONE_property_form_validate($form, &$form_state) {
-
-  // Only Require if Adding Property
-  if ($form_state['clicked_button']['#value'] == t('Add Property') ) {
-
-    // Check that there is a analysis
-    if ( $form_state['values']['analysis_id'] <= 0 ) {
-      form_set_error('analysis_id', 'There is no associated analysis.');
-    }
-
-    // Check that Selected a type
-    if ( !$form_state['values']['property']) {
-      form_set_error('property', 'Please select a type of property.');
-    }
-  }
-}
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_add_ONE_property_form_submit($form, &$form_state) {
-  $analysis_id = $form_state['values']['analysis_id'];
-  $property = $form_state['values']['property'];
-  $value = $form_state['values']['prop_value'];
-  $cv_name = $form_state['values']['cv_name'];
-
-  $succes = tripal_analysis_insert_property($analysis_id, $property, $value, 0, $cv_name);
-  if ($succes) {
-    drupal_set_message(t("Successfully Added Property: %property => %value", array('%property' => $property), array('%value' => $value)));
-  }
-  else {
-    drupal_set_message(t("Failed to Add Property: %property => %value", array('%property' => $property), array('%value' => $value)));
-  }
-}
-
-/**
- * Implements Hook_form()
- * Handles adding of Properties for analysiss
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_edit_ALL_properties_form($form_state, $node, $properties) {
-  $form = array();
-  $analysis_id = $node->analysis->analysis_id;
-
-  $form['nid'] = array(
-    '#type' => 'hidden',
-    '#value' => $node->nid
-  );
-
-  $form['add_properties']['analysis_id'] = array(
-    '#type' => 'value',
-    '#value' => $analysis_id,
-    '#required' => TRUE
-  );
-
-  // right now this defaults to the 'analysis_property' CV
-  // but in the future it should be more flexible
-  $form['cv_name'] = array(
-    '#type' => 'hidden',
-    '#value' => 'tripal_analysis'
-  );
-
-  if (sizeof($properties)) {
-
-    // build the select box options for the property name
-    $prop_type_options = array();
-    $columns = array('cvterm_id', 'name');
-    $values = array(
-      'cv_id' => array(
-        'name' => $form['cv_name']['#value']
-      )
-    );
-    $results = tripal_core_chado_select('cvterm', $columns, $values);
-    foreach ($results as $r) {
-      $prop_type_options[$r->name] = $r->name;
-    }
-
-    // iterate through all of the properties and create a set of form elements
-    foreach ($properties as $i => $property) {
-      
-      // only show the tripal_analysis properties all others properties 
-      // should be handled separately by whatever module added them
-      if($property->type_id->cv_id->name == 'tripal_analysis') {
-
-        $form["num-$i"] = array(
-          '#type' => 'fieldset',
-          '#value' => "Property $i"
-        );
-        $form["num-$i"]["id-$i"] = array(
-          '#type' => 'hidden',
-          '#value' => $property->analysisprop_id
-        );
-        $default = array_search($property->type, $prop_type_options);
-        $form["num-$i"]["type-$i"] = array(
-          '#type' => 'select',
-          '#options' => $prop_type_options,
-          '#default_value' => $property->type_id->name
-        );
-        $form["num-$i"]["value-$i"] = array(
-          '#type' => 'textfield',
-          '#default_value' => $property->value
-        );
-        $form["num-$i"]["delete-$i"] = array(
-          '#type' => 'submit',
-          '#value' => t("Delete"),
-          '#name' => "delete-$i",
-        );
-      }
-    } //end of foreach property
-
-    $form['num_properties'] = array(
-      '#type' => 'hidden',
-      '#value' => $i
-    );
-
-    $form["submit-edits"] = array(
-      '#type' => 'submit',
-      '#value' => t('Update All Properties')
-    );
-  }
-
-  return $form;
-}
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_edit_ALL_properties_form_submit($form, &$form_state) {
-
-  $cv_name = $form_state['values']['cv_name'];
-  $analysis_id = $form_state['values']["analysis_id"];
-  $all_good = 1;
-
-  // if the update button was clicked then do the update
-  if ($form_state['clicked_button']['#value'] == t('Update All Properties') ) {
-    // iterate through each of the properties and set each one
-    for ($i=1; $i<=$form_state['values']['num_properties']; $i++) {
-      $analysisprop_id = $form_state['values']["id-$i"];
-      $property = $form_state['values']["type-$i"];
-      $value = $form_state['values']["value-$i"];
-      $success = tripal_analysis_update_property_by_id($analysisprop_id, $property, $value, $cv_name);
-      if (!$success) {
-        drupal_set_message(t("Failed to Update Property: %property => %value", array('%property' => $property), array('%value' => $value)));
-        $all_good = 0;
-      }
-    }
-    if ($all_good) {
-      drupal_set_message(t("Updated all Properties"));
-    }
-    drupal_goto('node/' . $form_state['values']['nid']);
-  }
-  // if the delete button was clicked then remove the property
-  elseif (preg_match('/delete-(\d+)/', $form_state['clicked_button']['#name'], $matches) ) {
-    $i = $matches[1];
-    $analysisprop_id = $form_state['values']["id-$i"];
-    $property = $form_state['values']["type-$i"];
-    $value = $form_state['values']["value-$i"];
-    $success = tripal_analysis_delete_property($analysisprop_id, $property);
-    if ($success) {
-      drupal_set_message(t("Deleted Property"));
-    }
-    else {
-      drupal_set_message(t("Unable to Delete Property"));
-    }
-  }
-  else {
-    drupal_set_message(t("Unrecognized Button Pressed"), 'error');
-  }
-}
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function theme_tripal_analysis_edit_ALL_properties_form($form) {
-  $output = '';
-
-  $output .= '<br /><fieldset>';
-  $output .= '<legend>Edit Already Existing Properties<span class="form-optional" title="This field is optional">(optional)</span></legend>';
-  $output .= '<p>Below is a list of already existing properties for this analysis, one property per line. The type refers to the type of '
-         .'property and the value is the value for that property. </p>';
-  $output .= '<table>';
-  $output .= '<tr><th>#</th><th>Type</th><th>Value</th><th></th></tr>';
-
-  for ($i=0; $i<=$form['num_properties']['#value']; $i++) {
-    if (isset($form["num-$i"])) {
-      $output .= '<tr><td>' . ($i+1) . '</td><td>' . drupal_render($form["num-$i"]["type-$i"]) . '</td><td>' . drupal_render($form["num-$i"]["value-$i"]) . '</td><td>' . drupal_render($form["num-$i"]["delete-$i"]) . '</td></tr>';
-      unset($form["num-$i"]);
-    }
-  }
-
-  $output .= '</table><br />';
-  $output .= drupal_render($form);
-  $output .= '</fieldset>';
-
-  return $output;
-}
-
-/**
- *
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_list_properties_for_node($properties) {
-
-  if (!empty($properties)) {
-    $output = '<table>';
-    $output .= '<tr><th>Type</th><th>Value</th></tr>';
-
-
-    if (!empty($properties) ) {
-      foreach ($properties as $p) {
-        $output .= '<tr><td>' . $p->type . '</td><td>' . $p->value . '</td></tr>';
-      } // end of foreach property
-    }
-
-    $output .= '</table>';
-
-  }
-  else {
-    $output = 'No properties exist for the current analysis';
-  }
-
-  return $output;
-}
-
-

+ 646 - 0
tripal_analysis/includes/tripal_analysis.form.inc

@@ -0,0 +1,646 @@
+<?php 
+/**
+ *  When editing or creating a new node of type 'chado_analysis' we need
+ *  a form.  This function creates the form that will be used for this.
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_form($node) {
+  tripal_core_ahah_init_form();
+  $form = array();
+
+  $analysis = $node->analysis;
+
+  // add in the description column. It is a text field and may not be included
+  // if the text is too big.
+  $analysis = tripal_core_expand_chado_vars($analysis, 'field', 'analysis.description');
+
+  // get form defaults
+  $analysis_id = $node->analysis_id;
+  if (!$analysis_id) {
+    $analysis_id = $analysis->analysis_id;
+  }
+  $analysisname = $node->analysisname;
+  if (!$analysisname) {
+    $analysisname = $analysis->name;
+  }
+  $program = $node->program;
+  if (!$program) {
+    $program = $analysis->program;
+  }
+  $programversion = $node->programversion;
+  if (!$programversion) {
+    $programversion = $analysis->programversion;
+  }
+  $algorithm = $node->algorithm;
+  if (!$algorithm) {
+    $algorithm = $analysis->algorithm;
+  }
+  $sourcename = $node->sourcename;
+  if (!$sourcename) {
+    $sourcename = $analysis->sourcename;
+  }
+  $sourceversion = $node->sourceversion;
+  if (!$sourceversion) {
+    $sourceversion = $analysis->sourceversion;
+  }
+  $sourceuri = $node->sourceuri;
+  if (!$sourceuri) {
+    $sourceuri = $analysis->sourceuri;
+  }
+  $timeexecuted = $node->timeexecuted;
+  if (!$timeexecuted) {
+    $timeexecuted = $analysis->timeexecuted;
+  }
+  $description = $node->description;
+  if (!$description) {
+    $description = $analysis->description;
+  }
+  
+  // on AHAH callbacks we want to keep a list of all the properties that have been removed
+  // we'll store this info in a hidden field and retrieve it here
+  $d_removed = $form_state['values']['removed'];
+
+  // get the number of new fields that have been aded via AHAH callbacks
+  $num_new = $form_state['values']['num_new'] ? $form_state['values']['num_new'] : 0;
+
+  // initialze default properties array. This is where we store the property defaults
+  $d_properties = array();
+  
+  
+  $form['title']= array(
+      '#type' => 'hidden',
+      '#default_value' => $node->title,
+  );
+  $form['analysis_id']= array(
+      '#type' => 'hidden',
+      '#default_value' => $analysis_id,
+  );
+  $form['analysisname']= array(
+      '#type' => 'textfield',
+      '#title' => t('Analysis Name'),
+      '#required' => TRUE,
+      '#default_value' => $analysisname,
+      '#description' => t("This should be a brief name that
+         describes the analysis succintly. This name will helps the user find analyses."),
+  );
+  $form['program']= array(
+      '#type' => 'textfield',
+      '#title' => t('Program'),
+      '#required' => TRUE,
+      '#default_value' => $program,
+      '#description' => t("Program name, e.g. blastx, blastp, sim4, genscan."),
+  );
+  $form['programversion']= array(
+      '#type' => 'textfield',
+      '#title' => t('Program Version'),
+      '#required' => TRUE,
+      '#default_value' => $programversion,
+      '#description' => t("Version description, e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]. Enter 'n/a' if no version is available."),
+  );
+  $form['algorithm']= array(
+      '#type' => 'textfield',
+      '#title' => t('Algorithm'),
+      '#required' => FALSE,
+      '#default_value' => $algorithm,
+      '#description' => t("Algorithm name, e.g. blast."),
+  );
+  $form['sourcename']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source Name'),
+      '#required' => TRUE,
+      '#default_value' => $sourcename,
+      '#description' => t('The name of the source data.  This could be a file name, data set name or a
+           small description for how the data was collected.  For long descriptions use the description field below'),
+
+  );
+  $form['sourceversion']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source Version'),
+      '#required' => FALSE,
+      '#default_value' => $sourceversion,
+      '#description' => t('If the source dataset has a version, include it here'),
+  );
+  $form['sourceuri']= array(
+      '#type' => 'textfield',
+      '#title' => t('Source URI'),
+      '#required' => FALSE,
+      '#default_value' => $sourceuri,
+      '#description' => t("This is a permanent URL or URI for the source of the analysis.
+         Someone could recreate the analysis directly by going to this URI and
+         fetching the source data (e.g. the blast database, or the training model)."),
+  );
+  // Get time saved in chado
+  $default_time = $timeexecuted;
+  $year = preg_replace("/^(\d+)-\d+-\d+ .*/", "$1", $default_time);
+  $month = preg_replace("/^\d+-0?(\d+)-\d+ .*/", "$1", $default_time);
+  $day = preg_replace("/^\d+-\d+-0?(\d+) .*/", "$1", $default_time);
+  // If the time is not set, use current time
+  if (!$default_time) {
+    $default_time = time();
+    $year = format_date($default_time, 'custom', 'Y');
+    $month = format_date($default_time, 'custom', 'n');
+    $day = format_date($default_time, 'custom', 'j');
+  }
+  $form['timeexecuted']= array(
+      '#type' => 'date',
+      '#title' => t('Time Executed'),
+      '#required' => TRUE,
+      '#default_value' => array(
+         'year' => $year,
+         'month' => $month,
+         'day' => $day,
+      ),
+  );
+  $form['description']= array(
+      '#type' => 'textarea',
+      '#rows' => 15,
+      '#title' => t('Materials & Methods (Description and/or Program Settings)'),
+      '#required' => FALSE,
+      '#default_value' => $description,
+      '#description' => t('Please provide all necessary information to allow
+         someone to recreate the analysis, including materials and methods
+         for collection of the source data and performing the analysis'),
+  );
+
+  return $form;
+}
+
+/**
+ * Validates the user input before creating an analysis node
+ *
+ * @ingroup tripal_analysis
+ */
+function chado_analysis_validate($node, &$form) {
+   // use the analysis parent to validate the node
+  tripal_analysis_validate($node, $form);
+}
+
+/**
+ * This validation is being used for three activities:
+ *   CASE A: Update a node that exists in both drupal and chado
+ *   CASE B: Synchronizing a node from chado to drupal
+ *   CASE C: Inserting a new node that exists in niether drupal nor chado
+ *
+ * @ingroup tripal_analysis
+ */
+function tripal_analysis_validate($node, &$form) {
+  
+ 
+  // Only nodes being updated will have an nid already
+  if (!is_null($node->nid)) {    
+    // CASE A: We are validating a form for updating an existing node
+    
+    // get the existing node    
+    $values = array('analysis_id' => $node->analysis_id);      
+    $result = tripal_core_chado_select('analysis', array('*'), $values);
+    $analysis = $result[0];
+      
+    // if the name has changed make sure it doesn't conflict with an existing name
+    if($analysis->name != $node->analysisname) {
+      $values = array('name' => $node->analysisname);
+      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
+      if($result and count($result) > 0) {
+        form_set_error('analysisname', 'Cannot update the analysis with this analysis name. An analysis with this name already exists.');
+        return;
+      }  
+    }
+    
+    // if the unique constraint has changed check to make sure it doesn't conflict with an
+    // existing record
+    if($analysis->program != $node->program or $analysis->programversion != $node->programversion or 
+       $analysis->sourcename != $node->sourcename) {
+      $values = array(
+        'program' => $node->program,
+        'programversion' => $node->programversion,
+        'sourcename' => $node->sourcename,
+      );
+      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
+      if ($result and count($result) > 0) {
+        if ($analysis->program != $node->program) {
+          $field = 'program';  
+        }
+        if ($analysis->programversion != $node->programversion) {
+          $field = 'programversion';  
+        }
+        if ($analysis->sourcename != $node->sourcename) {
+          $field = 'sourcename';  
+        }
+        form_set_error($field, 'Cannot update the analysis with this program,
+          program version and source name. An analysis with these values already exists.');
+        return;
+      }  
+    }
+  }
+  else{
+    // To differentiate if we are syncing or creating a new analysis altogther, see if an
+    // analysis_id already exists
+    if ($node->analysis_id and $node->analysis_id != 0) {
+      // CASE B: Synchronizing a node from chado to drupal
+      // we don't need to do anything.
+    }
+    else {
+      // CASE C: We are validating a form for inserting a new node
+      // The unique constraint for the chado analysis table is: program, programversion, sourcename
+      $values = array(
+        'program' => $node->program,
+        'programversion' => $node->programversion,
+        'sourcename' => $node->sourcename,
+      );
+      $analysis = tripal_core_chado_select('analysis', array('analysis_id'), $values);
+      if ($analysis and count($analysis) > 0) {
+        form_set_error('program', 'Cannot add the analysis with this program,
+          program version and source name. An analysis with these values already exists.');
+        return;
+      }
+      
+      // make sure we have a unique analysis name. This is not a requirement 
+      // for the analysis table but we use the analysis name for the Drupal node
+      // title, so it should be unique      
+      $values = array('name' => $node->analysisname);
+      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
+      if($result and count($result) > 0) {
+        form_set_error('analysisname', 'Cannot add the analysis with this analysis name. An analysis with this name already exists.');
+        return;
+      }
+    }
+  }
+}
+
+/*
+ *
+ */
+function chado_analysis_node_form_add_new_empty_props(&$form, $properties_select) {
+
+  // add one more blank set of property fields
+  $form['properties']['new']["new_id"] = array(
+    '#type'          => 'select',
+    '#options'       => $properties_select,
+    '#ahah' => array(
+      'path'    => "tripal_analysis/properties/description",
+      'wrapper' => 'tripal-analysis-new_value-desc',
+      'event'   => 'change',
+      'method'  => 'replace',          
+  ),
+  );
+  $form['properties']['new']["new_value"] = array(
+    '#type'          => 'textarea',
+    '#default_value' => '',
+    '#cols'          => 5,
+    '#rows'          => $rows,
+    '#description'   => '<div id="tripal-analysis-new_value-desc"></div>'
+    );
+    $form['properties']['new']["add"] = array(
+    '#type'         => 'image_button',      
+    '#value'        => t('Add'),
+    '#src'          => drupal_get_path('theme', 'tripal') . '/images/add.png',
+    '#ahah' => array(
+      'path'    => "tripal_analysis/properties/add",
+      'wrapper' => 'tripal-analysis-edit-properties-table',
+      'event'   => 'click',
+      'method'  => 'replace',          
+    ),
+    '#attributes' => array('onClick' => 'return false;'),
+    );
+}
+/*
+ *
+ */
+function chado_analysis_node_form_add_new_props(&$form, $form_state, &$d_properties, &$d_removed) {
+   
+  // first, add in all of the new properties that were added through a previous AHAH callback
+  $j = 0;
+  $num_properties++;
+
+  // we need to find the
+  if ($form_state['values']) {
+    foreach ($form_state['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
+        // AHAH callback or through the current AHAH callback
+        if($d_removed["$new_id-$rank"]) {
+          continue;
+        }
+        if($form_state['post']['remove-' . $new_id . '-' . $rank]) {
+          $d_removed["$new_id-$rank"] = 1;
+          continue;
+        }
+
+        // get this new_id information
+        $cvterm = tripal_core_chado_select('cvterm', array('name', 'definition'), array('cvterm_id' => $new_id));
+
+        // add it to the $d_properties array
+        $d_properties[$new_id][$rank]['name']  = $cvterm->name;
+        $d_properties[$new_id][$rank]['id']    = $new_id;
+        $d_properties[$new_id][$rank]['value'] = $value;
+        $d_properties[$new_id][$rank]['definition']  = $cvterm->definition;
+        $num_properties++;
+
+        // determine how many rows we need in the textarea
+        $rows = 1;
+
+
+        // add the new fields
+        $form['properties']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
+          '#type'          => 'item',
+          '#value'         => $cvterm[0]->name
+        );
+        $form['properties']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
+          '#type'          => 'textarea',
+          '#default_value' => $value,
+          '#cols'          => 50,
+          '#rows'          => $rows,
+          '#description'   => $cvterm->definition,
+        );
+
+        $form['properties']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
+          '#type'         => 'image_button',
+          '#value'        => t('Remove'),
+          '#src'          => drupal_get_path('theme', 'tripal') . '/images/minus.png',
+          '#ahah' => array(
+            'path'    => "tripal_analysis/properties/minus/$new_id/$rank",
+            'wrapper' => 'tripal-analysis-edit-properties-table',
+            'event'   => 'click',
+            'method'  => 'replace',
+        ),
+          '#attributes' => array('onClick' => 'return false;'),
+        );
+      }
+    }
+  }
+
+
+  // second add in any new properties added during this callback
+  if($form_state['post']['add']) {
+    $new_id = $form_state['values']['new_id'];
+    $new_value = $form_state['values']['new_value'];
+
+    // get the rank by counting the number of entries
+    $rank = count($d_properties[$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 $d_properties array
+    $d_properties[$new_id][$rank]['name']  = $cvterm->name;
+    $d_properties[$new_id][$rank]['id']    = $new_id;
+    $d_properties[$new_id][$rank]['value'] = $value;
+    $d_properties[$new_id][$rank]['definition']  = $cvterm->definition;
+    $num_properties++;
+
+    // determine how many rows we need in the textarea
+    $rows = 1;
+    if (preg_match('/Abstract/', $cvterm[0]->name)) {
+      $rows = 10;
+    }
+    if ($cvterm[0]->name == 'Authors') {
+      $rows = 2;
+    }
+
+    // add the new fields
+    $form['properties']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
+      '#type'          => 'item',
+      '#value'         => $cvterm[0]->name
+    );
+    $form['properties']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
+      '#type'          => 'textarea',
+      '#default_value' => $new_value,
+      '#cols'          => 50,
+      '#rows'          => $rows,
+      '#description'   => $cvterm->definition,
+    );
+
+    $form['properties']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
+      '#type'         => 'image_button',
+      '#value'        => t('Remove'),
+      '#src'          => drupal_get_path('theme', 'tripal') . '/images/minus.png',
+      '#ahah' => array(
+        'path'    => "tripal_analysis/properties/minus/$new_id/$rank",
+        'wrapper' => 'tripal-analysis-edit-properties-table',
+        'event'   => 'click',
+        'method'  => 'replace',
+    ),
+      '#attributes' => array('onClick' => 'return false;'),
+    );
+
+  }
+
+  return $num_properties;
+}
+/*
+ *
+ */
+function chado_analysis_node_form_add_analysisprop_table_props(&$form, $form_state, $analysis_id, &$d_properties, &$d_removed) {
+
+  // get the properties for this analysis
+  $num_properties = 0;
+
+  if(!$analysis_id) {
+    return $num_properties;
+  }
+
+  $sql = "
+    SELECT CVT.cvterm_id, CVT.name, CVT.definition, PP.value, PP.rank
+    FROM {analysisprop} PP
+      INNER JOIN {cvterm} CVT on CVT.cvterm_id = PP.type_id
+    WHERE PP.analysis_id = %d
+    ORDER BY CVT.name, PP.rank
+  ";
+  $analysis_props = chado_query($sql, $analysis_id);
+  while ($prop = db_fetch_object($analysis_props)) {
+
+    $type_id = $prop->cvterm_id;
+    $rank = count($d_properties[$type_id]);
+    
+    // skip any properties that the user requested to delete through a previous
+    // AHAH callback or through the current AHAH callback
+    if($d_removed["$type_id-$rank"]) {
+      continue;
+    }
+    if($form_state['post']['remove-' . $type_id . '-' . $rank]) {
+      $d_removed["$type_id-$rank"] = 1;
+      continue;
+    }
+
+    $d_properties[$type_id][$rank]['name']  = $prop->name;
+    $d_properties[$type_id][$rank]['id']    = $type_id;
+    $d_properties[$type_id][$rank]['value'] = $prop->value;
+    $d_properties[$type_id][$rank]['definition']  = $prop->definition;
+    $num_properties++;
+
+    $form['properties'][$type_id][$rank]["prop_id-$type_id-$rank"] = array(
+      '#type'          => 'item',
+      '#value'         => $prop->name,
+    );
+    $form['properties'][$type_id][$rank]["prop_value-$type_id-$rank"] = array(
+      '#type'          => 'textarea',
+      '#default_value' => $prop->value,
+      '#cols'          => 50,
+      '#rows'          => $rows,
+      '#description'   => $prop->definition,
+    );
+
+    $form['properties'][$type_id][$rank]["remove-$type_id-$rank"] = array(
+      '#type'         => 'image_button',
+      '#value'        => t('Remove'),
+      '#src'          => drupal_get_path('theme', 'tripal') . '/images/minus.png',
+      '#ahah' => array(
+        'path'    => "tripal_analysis/properties/minus/$type_id/$rank",
+        'wrapper' => 'tripal-analysis-edit-properties-table',
+        'event'   => 'click',
+        'method'  => 'replace',
+    ),
+      '#attributes' => array('onClick' => 'return false;'),
+    );
+  }
+  return $num_properties;
+}
+/*
+ *
+ */
+function tripal_analysis_theme_node_form_properties($form) {
+  $rows = array();
+
+  if ($form['properties']) {
+
+    // first add in the properties derived from the analysisprop table
+    // the array tree for these properties looks like this:
+    // $form['properties'][$type_id][$rank]["prop_id-$type_id-$rank"]
+    foreach ($form['properties'] 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']['new'][$type_id][$rank]["new_id-$new_id-$rank"]
+    foreach ($form['properties']['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['properties']['new']['new_id']),
+    drupal_render($form['properties']['new']['new_value']),
+    drupal_render($form['properties']['new']['add']),
+    );
+  }
+
+  $headers = array('Property Type','Value', '');
+  return theme('table', $headers, $rows, array('id'=> "tripal-analysis-edit-properties-table"));
+}
+
+/*
+ *
+ */
+function tripal_analysis_property_add() {
+  $status = TRUE;
+
+  // prepare and render the form
+  $form = tripal_core_ahah_prepare_form();
+
+  // we only want to return the properties as that's all we'll replace with this AHAh callback
+  $data = tripal_analysis_theme_node_form_properties($form);
+
+  // bind javascript events to the new objects that will be returned
+  // so that AHAH enabled elements will work.
+  $settings = tripal_core_ahah_bind_events();
+
+  // return the updated JSON
+  drupal_json(
+  array(
+      'status'   => $status, 
+      'data'     => $data,
+      'settings' => $settings,
+  )
+  );
+}
+/*
+ *
+ */
+function tripal_analysis_property_delete() {
+  $status = TRUE;
+
+  // prepare and render the form
+  $form = tripal_core_ahah_prepare_form();
+
+  // we only want to return the properties as that's all we'll replace with this AHAh callback
+  $data = tripal_analysis_theme_node_form_properties($form);
+
+  // bind javascript events to the new objects that will be returned
+  // so that AHAH enabled elements will work.
+  $settings = tripal_core_ahah_bind_events();
+
+  // return the updated JSON
+  drupal_json(
+    array(
+      'status'   => $status, 
+      'data'     => $data,
+      'settings' => $settings,
+    )
+  );
+}
+/*
+ *
+ */
+function tripal_analysis_property_get_description() {
+  $new_id = $_POST['new_id'];
+
+  $values = array('cvterm_id' => $new_id);
+  $cvterm = tripal_core_chado_select('cvterm', array('definition'), $values);
+
+  $description = '&nbsp;';
+  if ($cvterm[0]->definition) {
+    $description = $cvterm[0]->definition;
+  }
+  drupal_json(
+    array(
+      'status' => TRUE,
+      'data'   => '<div id="tripal-analysis-new_value-desc">' . $description . '</div>',
+    )
+  );
+}
+/*
+ *
+ */
+function theme_chado_analysis_node_form($form) {
+
+  $properties_table = tripal_analysis_theme_node_form_properties($form);
+
+  $markup  = drupal_render($form['analysis_id']);
+  $markup .= drupal_render($form['title']);
+  $markup .= drupal_render($form['type_id']);
+  $markup .= drupal_render($form['description']);
+  $markup .= "<b>Include Additional Details</b><br>You may add additional properties to this analysis by scrolling to the bottom of this table, selecting a property type from the dropdown and adding text.  You may add as many properties as desired by clicking the plus button on the right.  To remove a property, click the minus button";
+  $markup .= $properties_table;
+
+  $form['properties'] = array(
+    '#type' => 'markup',
+    '#value' =>  $markup,
+  );
+  return drupal_render($form);
+}

+ 9 - 0
tripal_analysis/tripal_analysis.install

@@ -105,6 +105,15 @@ function tripal_analysis_add_cvterms(){
   );     
   tripal_cv_add_cvterm($term, 'tripal', 0, 1 , 'tripal');     
   
+  
+  // the 'analysis_property' vocabulary is for user definable properties.  Even though we already have
+  // an analysis_type term in the 'tripal_analysis' vocabular we duplicate it here because the 
+  // tripal_analysis vocabulary is intended for use by the extension modules.  user's should not be able
+  // to directly modify properties set by extension modules for an analysis.
+  tripal_cv_add_cvterm(array('name' => 'Analysis Type','def' => 'The type of analysis was performed.'), 
+     'analysis_property', 0, 1, 'tripal');
+  tripal_cv_add_cvterm(array('name' => 'Analysis Type','def' => 'The type of analysis was performed.'), 
+     'analysis_property', 0, 1, 'tripal');
 }
 /**
  * Implementation of hook_uninstall().

+ 36 - 280
tripal_analysis/tripal_analysis.module

@@ -17,7 +17,7 @@
 require('api/tripal_analysis.api.inc');
 require('includes/tripal_analysis_privacy.inc');
 require('includes/tripal_analysis.admin.inc');
-require('includes/tripal_analysis-properties.inc');
+require('includes/tripal_analysis.fofrm.inc');
 
 
 /**
@@ -27,6 +27,7 @@ require('includes/tripal_analysis-properties.inc');
  */
 function tripal_analysis_init() {
   drupal_add_js(drupal_get_path('theme', 'tripal') . '/js/tripal_analysis.js');
+  drupal_add_css(drupal_get_path('theme', 'tripal') . '/css/tripal_analysis.css');    
 }
 
 /**
@@ -63,37 +64,30 @@ function tripal_analysis_menu() {
       'type' => MENU_NORMAL_ITEM,
       'file' => 'includes/tripal_analysis.admin.inc',
   );
-  
-  //Edit/Deleting Properties-------------
-/*  $items['node/%ta_node/edit_analysis_properties'] = array(
-    'title' => 'Edit Properties',
-    'description' => 'Analysis Properties',
-    'page callback' => 'tripal_analysis_edit_ALL_properties_page',
-    'page arguments' => array(1),
+ 
+    
+  // AJAX calls for adding/removing properties to a contact
+  $items['tripal_analysis/properties/add'] = array(
+    'page callback' => 'tripal_analysis_property_add',
+    'access arguments' => array('edit chado_analysis content'),
+    'type ' => MENU_CALLBACK,
+  );
+  $items['tripal_analysis/properties/description'] = array(
+    'page callback' => 'tripal_analysis_property_get_description',
+    'access arguments' => array('edit chado_analysis content'),
+    'type ' => MENU_CALLBACK,
+  );
+  $items['tripal_analysis/properties/minus/%/%'] = array(
+    'page callback' => 'tripal_analysis_property_delete',
+    'page arguments' => array(3, 4),
     'access arguments' => array('edit chado_analysis content'),
-    'type' => MENU_LOCAL_TASK,
-    'weight' => 8,
-  );*/
+    'type ' => MENU_CALLBACK,
+  );
+  
+  
   return $items;
 }
-/**
- * Implements Menu wildcard_load hook
- * Purpose: Allows the node ID of a chado analysis to be dynamically
- *   pulled from the path. The node is loaded from this node ID
- *   and supplied to the page as an arguement
- *
- * @ingroup tripal_analysis
- */
-function ta_node_load($nid) {  
-  if (is_numeric($nid)) {
-    $node = node_load($nid);
-    $analysis_id = chado_get_id_for_node('analysis', $node);
-    if ($analysis_id) {
-      return $node;
-    }
-  }
-  return FALSE;
-}
+
 
 /**
  * Provide information to drupal about the node types that we're creating
@@ -291,159 +285,7 @@ function chado_analysis_update($node) {
   drupal_write_record('node_revisions', $record, 'nid');
 }
 
-/**
- *  When editing or creating a new node of type 'chado_analysis' we need
- *  a form.  This function creates the form that will be used for this.
- *
- * @ingroup tripal_analysis
- */
-function chado_analysis_form($node) {
-
-  $analysis = $node->analysis;
 
-  // add in the description column. It is a text field and may not be included
-  // if the text is too big.
-  $analysis = tripal_core_expand_chado_vars($analysis, 'field', 'analysis.description');
-
-  // get form defaults
-  $analysis_id = $node->analysis_id;
-  if (!$analysis_id) {
-    $analysis_id = $analysis->analysis_id;
-  }
-  $analysisname = $node->analysisname;
-  if (!$analysisname) {
-    $analysisname = $analysis->name;
-  }
-  $program = $node->program;
-  if (!$program) {
-    $program = $analysis->program;
-  }
-  $programversion = $node->programversion;
-  if (!$programversion) {
-    $programversion = $analysis->programversion;
-  }
-  $algorithm = $node->algorithm;
-  if (!$algorithm) {
-    $algorithm = $analysis->algorithm;
-  }
-  $sourcename = $node->sourcename;
-  if (!$sourcename) {
-    $sourcename = $analysis->sourcename;
-  }
-  $sourceversion = $node->sourceversion;
-  if (!$sourceversion) {
-    $sourceversion = $analysis->sourceversion;
-  }
-  $sourceuri = $node->sourceuri;
-  if (!$sourceuri) {
-    $sourceuri = $analysis->sourceuri;
-  }
-  $timeexecuted = $node->timeexecuted;
-  if (!$timeexecuted) {
-    $timeexecuted = $analysis->timeexecuted;
-  }
-  $description = $node->description;
-  if (!$description) {
-    $description = $analysis->description;
-  }
-  $form = array();
-  $form['title']= array(
-      '#type' => 'hidden',
-      '#default_value' => $node->title,
-  );
-  $form['analysis_id']= array(
-      '#type' => 'hidden',
-      '#default_value' => $analysis_id,
-  );
-  $form['analysisname']= array(
-      '#type' => 'textfield',
-      '#title' => t('Analysis Name'),
-      '#required' => TRUE,
-      '#default_value' => $analysisname,
-      '#description' => t("This should be a brief name that
-         describes the analysis succintly. This name will helps the user find analyses."),
-  );
-  $form['program']= array(
-      '#type' => 'textfield',
-      '#title' => t('Program'),
-      '#required' => TRUE,
-      '#default_value' => $program,
-      '#description' => t("Program name, e.g. blastx, blastp, sim4, genscan."),
-  );
-  $form['programversion']= array(
-      '#type' => 'textfield',
-      '#title' => t('Program Version'),
-      '#required' => TRUE,
-      '#default_value' => $programversion,
-      '#description' => t("Version description, e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]. Enter 'n/a' if no version is available."),
-  );
-  $form['algorithm']= array(
-      '#type' => 'textfield',
-      '#title' => t('Algorithm'),
-      '#required' => FALSE,
-      '#default_value' => $algorithm,
-      '#description' => t("Algorithm name, e.g. blast."),
-  );
-  $form['sourcename']= array(
-      '#type' => 'textfield',
-      '#title' => t('Source Name'),
-      '#required' => TRUE,
-      '#default_value' => $sourcename,
-      '#description' => t('The name of the source data.  This could be a file name, data set name or a
-           small description for how the data was collected.  For long descriptions use the description field below'),
-
-  );
-  $form['sourceversion']= array(
-      '#type' => 'textfield',
-      '#title' => t('Source Version'),
-      '#required' => FALSE,
-      '#default_value' => $sourceversion,
-      '#description' => t('If the source dataset has a version, include it here'),
-  );
-  $form['sourceuri']= array(
-      '#type' => 'textfield',
-      '#title' => t('Source URI'),
-      '#required' => FALSE,
-      '#default_value' => $sourceuri,
-      '#description' => t("This is a permanent URL or URI for the source of the analysis.
-         Someone could recreate the analysis directly by going to this URI and
-         fetching the source data (e.g. the blast database, or the training model)."),
-  );
-  // Get time saved in chado
-  $default_time = $timeexecuted;
-  $year = preg_replace("/^(\d+)-\d+-\d+ .*/", "$1", $default_time);
-  $month = preg_replace("/^\d+-0?(\d+)-\d+ .*/", "$1", $default_time);
-  $day = preg_replace("/^\d+-\d+-0?(\d+) .*/", "$1", $default_time);
-  // If the time is not set, use current time
-  if (!$default_time) {
-    $default_time = time();
-    $year = format_date($default_time, 'custom', 'Y');
-    $month = format_date($default_time, 'custom', 'n');
-    $day = format_date($default_time, 'custom', 'j');
-  }
-  $form['timeexecuted']= array(
-      '#type' => 'date',
-      '#title' => t('Time Executed'),
-      '#required' => TRUE,
-      '#default_value' => array(
-         'year' => $year,
-         'month' => $month,
-         'day' => $day,
-      ),
-  );
-  $form['description']= array(
-      '#type' => 'textarea',
-      '#rows' => 15,
-      '#title' => t('Materials & Methods (Description and/or Program Settings)'),
-      '#required' => FALSE,
-      '#default_value' => $description,
-      '#description' => t('Please provide all necessary information to allow
-         someone to recreate the analysis, including materials and methods
-         for collection of the source data and performing the analysis'),
-  );
-
-  return $form;
-}
 
  /**
   *  When a node is requested by the user this function is called to allow us
@@ -490,106 +332,7 @@ function chado_analysis_view($node, $teaser = FALSE, $page = FALSE) {
 
 
 
-/**
- * Validates the user input before creating an analysis node
- *
- * @ingroup tripal_analysis
- */
-function chado_analysis_validate($node, &$form) {
-   // use the analysis parent to validate the node
-  tripal_analysis_validate($node, $form);
-}
 
-/**
- * This validation is being used for three activities:
- *   CASE A: Update a node that exists in both drupal and chado
- *   CASE B: Synchronizing a node from chado to drupal
- *   CASE C: Inserting a new node that exists in niether drupal nor chado
- *
- * @ingroup tripal_analysis
- */
-function tripal_analysis_validate($node, &$form) {
-  
- 
-  // Only nodes being updated will have an nid already
-  if (!is_null($node->nid)) {    
-    // CASE A: We are validating a form for updating an existing node
-    
-    // get the existing node    
-    $values = array('analysis_id' => $node->analysis_id);      
-    $result = tripal_core_chado_select('analysis', array('*'), $values);
-    $analysis = $result[0];
-      
-    // if the name has changed make sure it doesn't conflict with an existing name
-    if($analysis->name != $node->analysisname) {
-      $values = array('name' => $node->analysisname);
-      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if($result and count($result) > 0) {
-        form_set_error('analysisname', 'Cannot update the analysis with this analysis name. An analysis with this name already exists.');
-        return;
-      }  
-    }
-    
-    // if the unique constraint has changed check to make sure it doesn't conflict with an
-    // existing record
-    if($analysis->program != $node->program or $analysis->programversion != $node->programversion or 
-       $analysis->sourcename != $node->sourcename) {
-      $values = array(
-        'program' => $node->program,
-        'programversion' => $node->programversion,
-        'sourcename' => $node->sourcename,
-      );
-      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if ($result and count($result) > 0) {
-        if ($analysis->program != $node->program) {
-          $field = 'program';  
-        }
-        if ($analysis->programversion != $node->programversion) {
-          $field = 'programversion';  
-        }
-        if ($analysis->sourcename != $node->sourcename) {
-          $field = 'sourcename';  
-        }
-        form_set_error($field, 'Cannot update the analysis with this program,
-          program version and source name. An analysis with these values already exists.');
-        return;
-      }  
-    }
-  }
-  else{
-    // To differentiate if we are syncing or creating a new analysis altogther, see if an
-    // analysis_id already exists
-    if ($node->analysis_id and $node->analysis_id != 0) {
-      // CASE B: Synchronizing a node from chado to drupal
-      // we don't need to do anything.
-    }
-    else {
-      // CASE C: We are validating a form for inserting a new node
-      // The unique constraint for the chado analysis table is: program, programversion, sourcename
-      $values = array(
-        'program' => $node->program,
-        'programversion' => $node->programversion,
-        'sourcename' => $node->sourcename,
-      );
-      $analysis = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if ($analysis and count($analysis) > 0) {
-        form_set_error('program', 'Cannot add the analysis with this program,
-          program version and source name. An analysis with these values already exists.');
-        return;
-      }
-      
-      // make sure we have a unique analysis name. This is not a requirement 
-      // for the analysis table but we use the analysis name for the Drupal node
-      // title, so it should be unique      
-      $values = array('name' => $node->analysisname);
-      $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
-      if($result and count($result) > 0) {
-        form_set_error('analysisname', 'Cannot add the analysis with this analysis name. An analysis with this name already exists.');
-        return;
-      }
-    }
-  }
-}
 
 /**
  * Display help and module information
@@ -682,6 +425,11 @@ function tripal_analysis_theme() {
       'arguments' =>  array(NULL),  
       'path' => drupal_get_path('module', 'tripal_analysis') . '/theme', 
     ),
+    
+    // Themed Forms
+    'chado_analysis_node_form' => array(
+      'arguments' => array('form'),
+    ),
   );
 }
 /**
@@ -768,3 +516,11 @@ function tripal_analysis_views_api() {
 
 
 
+
+/*
+ * 
+ */
+function tripal_analysis_form_alter(&$form, &$form_state, $form_id) {
+  if ($form_id == "chado_analysis_node_form") {
+  }
+}

+ 4 - 9
tripal_contact/includes/tripal_contact.form.inc

@@ -24,7 +24,7 @@ function chado_contact_form(&$node, $form_state) {
     
   $d_title        = $form_state['values']['title']       ? $form_state['values']['title']       : $contact->name;
   $d_description  = $form_state['values']['description'] ? $form_state['values']['description'] : $contact->description;
-  $d_type_id      = $form_state['values']['type_id']     ? $form_state['values']['type_id'] : $contact->type_id->cvterm_id;
+  $d_type_id      = $form_state['values']['type_id']     ? $form_state['values']['type_id']     : $contact->type_id->cvterm_id;
   
   // on AHAH callbacks we want to keep a list of all the properties that have been removed
   // we'll store this info in a hidden field and retrieve it here
@@ -257,12 +257,7 @@ function chado_contact_node_form_add_new_props(&$form, $form_state, &$d_properti
 
         // determine how many rows we need in the textarea
         $rows = 1;
-        if (preg_match('/Abstract/', $cvterm[0]->name)) {
-          $rows = 10;
-        }
-        if ($cvterm[0]->name == 'Authors') {
-          $rows = 2;
-        }
+
 
         // add the new fields
         $form['properties']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
@@ -523,11 +518,11 @@ function tripal_contact_property_delete() {
 
   // return the updated JSON
   drupal_json(
-  array(
+    array(
       'status'   => $status, 
       'data'     => $data,
       'settings' => $settings,
-  )
+    )
   );
 }
 /*

+ 1 - 1
tripal_contact/tripal_contact.module

@@ -113,7 +113,7 @@ function tripal_contact_menu() {
     'type' => MENU_NORMAL_ITEM,
   );
   
-    // AJAX calls for adding/removing properties to a contact
+  // AJAX calls for adding/removing properties to a contact
   $items['tripal_contact/properties/add'] = array(
     'page callback' => 'tripal_contact_property_add',
     'access arguments' => array('edit chado_contact content'),