Browse Source

Added Add/Edit/Delete of Properties, Extra Database References & Relationships to tripal_stock

laceysanderson 14 years ago
parent
commit
e356324fca

+ 129 - 1
tripal_stock/other_module_api_functions.inc

@@ -1,9 +1,98 @@
 <?php
 
+///////////////////////////////////////////////////////////////////////////
+// Module: tripal_core
+///////////////////////////////////////////////////////////////////////////
+
+/*************************************************************************
+ * Purpose: Get max rank for a given set of criteria
+ *   This function was developed with the many property tables in chado in mind
+ *
+ * @params tablename: the name of the chado table you want to select the max rank from
+ *    this table must contain a rank column of type integer
+ * @params where_options: array(
+ *													<column_name> => array(
+ *														'type' => <type of column: INT/STRING>,
+ *														'value' => <the vlaue you want to filter on>,
+ *														'exact' => <if TRUE use =; if FALSE use ~>,
+ *													)
+ *				)
+ *     where options should include the id and type for that table to correctly
+ *     group a set of records together where the only difference are the value and rank
+ * @return the maximum rank
+ *
+ */
+function get_max_chado_rank ($tablename, $where_options) {
+
+	$where= array();
+	//generate the where clause from supplied options
+	// the key is the column name
+	foreach ($where_options as $key => $val_array) {
+		if (preg_match('/INT/', $val_array['type'])) {
+			$where[] = $key."=".$val_array['value'];
+		} else {
+			if ($val_array['exact']) { $operator='='; }
+			else { $operator='~'; }
+			$where[] = $key.$operator."'".$val_array['value']."'";
+		}
+	}
+	
+  $previous_db = tripal_db_set_active('chado');
+  $result = db_fetch_object(db_query(
+    "SELECT max(rank) as max_rank, count(rank) as count FROM %s WHERE %s",
+    $tablename,
+    implode(' AND ',$where)
+  ));
+  tripal_db_set_active($previous_db);
+
+	if ($result->count > 0) {
+	  return $result->max_rank;
+	} else {
+		return -1;
+	}
+}
+
 ///////////////////////////////////////////////////////////////////////////
 // Module: tripal_cv
 ///////////////////////////////////////////////////////////////////////////
 
+/*************************************************************************
+ * Purpose: To retrieve a chado cv object
+ *
+ * @params where_options: array(
+ *													<column_name> => array(
+ *														'type' => <type of column: INT/STRING>,
+ *														'value' => <the vlaue you want to filter on>,
+ *														'exact' => <if TRUE use =; if FALSE use ~>,
+ *													)
+ *				)
+ * @return chado cv object with all fields from the chado cv table
+ */
+function get_chado_cv ($where_options) {
+	$previous_db = tripal_db_set_active('chado');
+
+	$where= array();
+	//generate the where clause from supplied options
+	// the key is the column name
+	foreach ($where_options as $key => $val_array) {
+		if (preg_match('/INT/', $val_array['type'])) {
+			$where[] = $key."=".$val_array['value'];
+		} else {
+			if ($val_array['exact']) { $operator='='; }
+			else { $operator='~'; }
+			$where[] = $key.$operator."'".$val_array['value']."'";
+		}
+	}
+	
+  $r = db_fetch_object(db_query(
+    "SELECT * FROM cv WHERE ".implode(' AND ',$where)
+  ));
+  
+  tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
 /*************************************************************************
  * Purpose: Create an options array to be used in a form element
  *   which provides a list of all chado cvs
@@ -27,6 +116,43 @@ function get_chado_cv_options() {
 
 }
 
+/*************************************************************************
+ * Purpose: To retrieve a chado cvterm object
+ *
+ * @params where_options: array(
+ *													<column_name> => array(
+ *														'type' => <type of column: INT/STRING>,
+ *														'value' => <the vlaue you want to filter on>,
+ *														'exact' => <if TRUE use =; if FALSE use ~>,
+ *													)
+ *				)
+ * @return chado cvterm object with all fields from the chado cvterm table
+ */
+function get_chado_cvterm ($where_options) {
+	$previous_db = tripal_db_set_active('chado');
+
+	$where= array();
+	//generate the where clause from supplied options
+	// the key is the column name
+	foreach ($where_options as $key => $val_array) {
+		if (preg_match('/INT/', $val_array['type'])) {
+			$where[] = $key."=".$val_array['value'];
+		} else {
+			if ($val_array['exact']) { $operator='='; }
+			else { $operator='~'; }
+			$where[] = $key.$operator."'".$val_array['value']."'";
+		}
+	}
+	
+  $r = db_fetch_object(db_query(
+    "SELECT * FROM cvterm WHERE ".implode(' AND ',$where)
+  ));
+  
+  tripal_db_set_active($previous_db);
+
+  return $r;
+}
+
 /*************************************************************************
  * Purpose: Create an options array to be used in a form element
  *   which provides a list of all chado cvterms
@@ -87,6 +213,7 @@ function get_chado_db_options() {
 }
 
 /*************************************************************************
+ * Purpose: To retrieve a chado dbxref object
  *
  * @params where_options: array(
  *													<column_name> => array(
@@ -95,8 +222,9 @@ function get_chado_db_options() {
  *														'exact' => <if TRUE use =; if FALSE use ~>,
  *													)
  *				)
+ * @return chado dbxref object with all fields from the chado dbxref table
  */
-function get_chado_dbxref($where_options) {
+function get_chado_dbxref ($where_options) {
 	$previous_db = tripal_db_set_active('chado');
 
 	$where= array();

+ 126 - 1
tripal_stock/tripal_stock-api_functions.inc

@@ -7,7 +7,7 @@
  *			<stock_id> => <stock object created by node load>
  *		)
  */
-function get_chado_stocks() {
+function get_all_chado_stocks() {
 	$sql = "SELECT stock_id, nid from {chado_stock}";
 	$resource = db_query($sql);
 	$stocks = array();
@@ -17,6 +17,131 @@ function get_chado_stocks() {
 	return $stocks;
 }
 
+/*************************************************************************
+ * Purpose: Return all stocks that match a given criteria
+ *
+ * @params Criteria = array(
+ *						<column name> => array( 
+ *							'value'=> <value of column>, 
+ *							'regex' => <False if exact value, TRUE otherwise>,
+ *							'type' => <INT or STRING>
+ *						)
+ *         )
+ *			column name can be any of those for the stock table
+ *			additional column names include: accession, synonym
+ *	if you don't know which column the value is from and want to match
+ *		the value against any of name, uniquename, dbxref accessions, and synonyms
+ *   	use 'unknown' => array(
+ *												'value' => <value to search for>,
+ *												'columns' => array of columns to search 
+ *											)
+ *		where columns can be any combination of 'name', 'uniquename', 'stock','accession', or 'synonym'
+ * @params match_type: can be either ALL or ANY
+ *	 where ALL= mathcing stocks must match all criteria supplied
+ *				 ANY= matching stock need only match ONE of the supplied criteria
+ * @return an array of matching stock objects (produced using node_load)
+ *   matching the given criteria
+ */
+function get_chado_stocks($criteria, $match_type, $organism_id = NULL) {
+	
+	//Deal with unknown column----------------------------
+	if (!empty($criteria['unknown'])) {
+		$unknown_provided = TRUE;
+		$new_criteria = array();
+		foreach ($criteria['unknown']['columns'] as $column_name) {
+			$new_criteria[$column_name] = array('value' => $criteria['unknown']['value'], 'regex'=>TRUE);
+		}
+		$unknown_stocks = get_chado_stocks($new_criteria, 'ANY', $organism_id);
+	}
+	unset($criteria['unknown']); //so it's not mistaken as a column in the stock table
+	
+	// Determine all criteria------------------------------
+	$where = array(); // parts of the where portion of the SQL query
+	$joins = array(); //joins between the stock table and other tables
+	foreach ($criteria as $column_name => $v) {
+		if (preg_match("/accession/i",$column_name)) {
+			if ($v['regex']) { $operator = '~'; }
+			else { $operator = '='; }
+			
+			$where[] = 'dbxref.accession'.$operator."'".$v['value']."'";
+			$joins[] = 'LEFT JOIN stock_dbxref stock_dbxref ON stock_dbxref.stock_id=stock.stock_id';
+			$joins[] = 'LEFT JOIN dbxref dbxref ON dbxref.dbxref_id=stock_dbxref.dbxref_id';
+			
+		} elseif (preg_match("/synonym/i",$column_name)) {
+			if ($v['regex']) { $operator = '~'; }
+			else { $operator = '='; }
+			
+			$synonym_cvterm = get_chado_cvterm( array('name' => array('type'=>'STRING','exact'=>TRUE,'value'=>'synonym'),
+																								'cv_id' => array('type'=>'INT', 'value'=> variable_get('chado_stock_prop_types_cv', 'null')) ));
+			$where[] = '(stockprop.type_id='.$synonym_cvterm->cvterm_id.' AND stockprop.value'.$operator."'".$v['value']."')";
+			$joins[] = 'LEFT JOIN stockprop stockprop ON stockprop.stock_id=stock.stock_id';
+	
+		} else {
+			if ($v['regex']) { $operator = '~'; }
+			else { $operator = '='; }
+			
+			if (preg_match('/INT/', $v['type'])) {
+				$where[] = $column_name.$operator.$v['value'];
+			} else {
+				$where[] = $column_name.$operator."'".$v['value']."'";
+			}		
+		}
+	}
+	
+	//Build query-----------------------------------------
+	if (preg_match('/ANY/', $match_type)) {
+		$where_string = implode(' OR ',$where);
+	} else {
+		$where_string = implode(' AND ',$where);
+	}
+	if (sizeof($where) >= 1) {
+		$execute_query = TRUE;
+		$sql_query = 'SELECT stock.stock_id FROM stock '.implode(' ',$joins).' WHERE '.$where_string;
+	} elseif (!$unknown_provided) {	
+		$execute_query = TRUE;
+		$sql_query = 'SELECT stock.stock_id FROM stock';
+		drupal_set_message('You did not enter any criteria during the stock selection process. All stocks will be returned.','warning');
+	} else {
+		$execute_query = FALSE;
+	}
+	
+	if ($execute_query) {
+		//Get stock_ids---------------------------------------
+		$previous_db = tripal_db_set_active('chado');
+		$resource = db_query($sql_query);
+		tripal_db_set_active($previous_db);
+	
+		$stock_ids = array();	
+		while ($r = db_fetch_object($resource)) {
+			$stock_ids[] = $r->stock_id;
+		}
+		$stock_ids = array_unique($stock_ids);
+	
+		//Get Stocks------------------------------------------
+		$resource = db_query("SELECT nid FROM {chado_stock} WHERE stock_id IN (%s)",implode(',',$stock_ids));
+		$main_stocks = array();
+		while ($r = db_fetch_object($resource)) {
+			$main_stocks[] = node_load($r->nid);
+		}
+	}
+	
+	if (!empty($main_stocks)) {
+		if(!empty($unknown_stocks)){
+			return array_merge($unknown_stocks,$main_stocks);
+		} else {
+			return $main_stocks;
+		}
+	} else {
+		if(!empty($unknown_stocks)){
+			return $unknown_stocks;
+		} else {
+			drupal_set_message('No Stocks matched the given criteria','warning');
+			return array();
+		}
+	}
+
+}
+
 /*************************************************************************
  * Purpose: Return a given stock object using the nid or stock id
  *

+ 338 - 0
tripal_stock/tripal_stock-db_references.inc

@@ -0,0 +1,338 @@
+<?php
+
+/*************************************************************************************************************
+ * @section 
+ * Deals with Adding DB references
+ */
+
+function tripal_stock_add_ALL_dbreferences_page($node) {
+  $output = '';
+
+  $output .= implement_add_chado_properties_progress('db_references').'<br>';
+  $output .= '<b>All Database References should strictly pertain to THE CURRENT Individual</b><br>';
+  $output .= '<br><b>Current Database References</b><br>';
+  $output .= list_dbreferences_for_node($node->db_references);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_dbreference_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_add_chado_properties_navigate', 'db_references', $node->nid);
+  return $output;
+}
+
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Database References to Stocks
+ */
+function tripal_stock_add_ONE_dbreference_form($form_state, $node) {
+
+  $stock_id = $node->stock_id;
+
+  $form['db_nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $form['add_dbreference'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Database References') . '<span class="form-optional" title="This field is optional">(optional)</span>',
+  );
+
+  $form['add_properties']['db_stock_id'] = array(
+    '#type' => 'value',
+    '#value' => $stock_id,
+    '#required' => TRUE
+
+  );
+
+  $form['add_dbreference']['accession'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Accession Number'),
+  );
+
+  $form['add_dbreference']['db_description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description of Database Reference') . '<span class="form-optional" title="This field is optional">+</span>',
+    '#description' => t('Optionally enter a description about the database accession.')
+  );
+
+  $db_options = get_chado_db_options();
+  $db_options[0] = 'Select a Database';
+  ksort($db_options);
+  $form['add_dbreference']['database'] = array(
+    '#type' => 'select',
+    '#title' => t('Database'),
+    '#options' => $db_options,
+  );
+
+  $form['add_dbreference']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add Database Reference')
+  );
+
+  return $form;
+}
+
+function tripal_stock_add_ONE_dbreference_form_validate($form, &$form_state) {
+
+  // Only ensure db reference valid if adding
+  if ($form_state['clicked_button']['#value'] == t('Add Database Reference') ) {
+    //Do work of required validators
+    if ($form_state['values']['accession'] == '') {
+      form_set_error('accession', 'Accession field is Required.');
+    }
+
+    // Check database is valid db_id in chado
+    if ( $form_state['values']['database'] > 0) {
+    	$previous_db = tripal_db_set_active('chado');
+    	$tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM db WHERE db_id=%d",$form_state['values']['database']));
+    	tripal_db_set_active($previous_db);
+    	
+      if ($tmp_obj->count != 1) {
+        form_set_error('database', 'The database you selected is not valid. Please choose another one.'); 
+      }
+    } else {
+      form_set_error('database', 'Please select a database');
+    }
+
+    // Check Accession is unique for database
+    $previous_db = tripal_db_set_active('chado');
+    $tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM dbxref WHERE accession='%s'",$form_state['values']['accession']));
+    tripal_db_set_active($previous_db);
+    	
+    if ($tmp_obj->count > 0) {
+      form_set_error('accession', 'This accession has already been assigned to another stock.'); 
+    }
+
+  } //end of if adding
+
+} 
+
+function tripal_stock_add_ONE_dbreference_form_submit($form, &$form_state) {
+
+  // FIX: Sometimes on programatic submission of form (drupal_execute) values in the form state get lost
+  // 	  however, the post values always seem to be correct
+  if (empty($form_state['values']['db_stock_id'])) { $form_state['values']['db_stock_id'] = $form_state['clicked_button']['#post']['db_stock_id']; }
+
+  // Only Create if valid
+  if ($form_state['values']['database'] > 0) {
+    
+
+    // create dbxref
+    $previous_db = tripal_db_set_active('chado');
+    db_query(
+      "INSERT INTO dbxref (db_id, accession, description) VALUES (%d, '%s', '%s')",
+      $form_state['values']['database'],
+      $form_state['values']['accession'],
+      $form_state['values']['db_description']
+    );
+    tripal_db_set_active($previous_db);
+
+    //create stock_dbxref
+    $dbxref = get_chado_dbxref( array('db_id'=>array('type'=>'INT','value'=>$form_state['values']['database']), 
+    																	'accession'=>array('type'=>'STRING','exact'=>TRUE,'value'=>$form_state['values']['accession']) ) );
+		if (!empty($dbxref->dbxref_id)) {
+			$previous_db = tripal_db_set_active('chado');
+    	db_query(
+      	"INSERT INTO stock_dbxref (stock_id, dbxref_id) VALUES (%d, %d)",
+      	$form_state['values']['db_stock_id'],
+      	$dbxref->dbxref_id
+    	);
+			tripal_db_set_active($previous_db);
+
+    	drupal_set_message('Successfully Added Database Reference');
+    } else {
+    	drupal_set_message('Database reference NOT successfully created...','error');
+    } //end of if dbxref was created successfully
+  } //end of if valid db reference
+
+}
+
+/************************************************************************************************************* 
+ * @section                                    
+ * Deals with Editing and Deleting Properties  
+ */                                            
+
+function tripal_stock_edit_ALL_dbreferences_page($node) {
+  $output = '';
+
+  $output .= drupal_get_form('tripal_stock_edit_ALL_db_references_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_dbreference_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_back_to_stock_button', $node->nid);
+
+  return $output;
+}
+                                               
+/**                                            
+ * Implements Hook_form()                      
+ * Handles adding of Properties & Synonyms to Stocks
+ */                                        
+function tripal_stock_edit_ALL_db_references_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $i=0;
+  if (sizeof($node->db_references) != 0) {
+  foreach ($node->db_references as $ref) {
+    $i++;
+    $form["num-$i"] = array(
+      '#type' => 'item',
+      '#value' => $i.'.'
+    );
+
+    $form["accession-$i"] = array(
+      '#type' => 'textfield',
+      //'#title' => t('Accession'),
+      '#size' => 30,
+      '#required' => TRUE,
+      '#default_value' => $ref->accession
+    );
+
+    $db_options = get_chado_db_options();
+    $db_options[0] = 'Select a Database';
+    ksort($db_options);
+    $form["database-$i"] = array( 
+      '#type' => 'select', 
+      //'#title' => t('Database'),
+      '#options' => $db_options, 
+      '#default_value' => $ref->db_id
+    );
+
+
+    $form["id-$i"] = array(
+      '#type' => 'hidden',
+      '#value' => $ref->dbxref_id
+    );
+
+    $form["submit-$i"] = array(
+      '#type' => 'submit',
+      '#value' => t("Delete #$i")
+    );
+
+  }} //end of foreach db ref
+
+  $form['num_db_references'] = array(
+    '#type' => 'hidden',
+    '#value' => $i
+  );
+
+  $form["submit-edits"] = array(
+    '#type' => 'submit',
+    '#value' => t('Update DB References')
+  );
+
+  return $form;
+}
+
+function tripal_stock_edit_ALL_db_references_form_submit($form, &$form_state) {
+
+  if ($form_state['clicked_button']['#value'] == t('Update DB References') ) {
+
+     //Update all
+     for ($i=1; $i<=$form_state['values']['num_db_references']; $i++) {
+       tripal_stock_update_db_reference(
+					$form_state['values']["id-$i"], 
+					$form_state['values']["database-$i"], 
+					$form_state['values']["accession-$i"]
+			);
+     }
+     drupal_set_message("Updated all Database References");
+     drupal_goto('node/'.$form_state['values']['nid']);
+
+  } elseif ( preg_match('/Delete #(\d+)/', $form_state['clicked_button']['#value'], $matches) ) {
+
+     $i = $matches[1];
+     tripal_stock_delete_db_reference($form_state['values']["id-$i"]);
+     drupal_set_message("Deleted Database Reference");
+
+  } else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+}
+
+function tripal_stock_update_db_reference($dbxref_id, $database_id, $accession) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "UPDATE dbxref SET db_id=%d, accession='%s' WHERE dbxref_id=%d",
+    $database_id, 
+    $accession,
+    $dbxref_id
+  );
+  db_set_active($previous_db);
+
+}
+
+function tripal_stock_delete_db_reference($dbxref_id) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "DELETE FROM dbxref WHERE dbxref_id=%d",
+    $dbxref_id
+  );
+
+  db_query(
+    "DELETE FROM stock_dbxref WHERE dbxref_id=%d",
+    $dbxref_id
+  );
+  db_set_active($previous_db);
+
+}
+
+function theme_tripal_stock_edit_ALL_db_references_form ($form) {
+  $output = '';
+
+  $output .= '<br><fieldset>';
+  $output .= '<legend>Edit Existing Database References<span class="form-optional" title="This field is optional">(optional)</span></legend>';
+  $output .= '<p>Below is a list of already existing database references, one per line. When entering a database reference, the accession '
+  	     .'is a unique identifier for this stock in the specified database.</p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Database</th><th>Accession</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_db_references']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td><td>'
+    	       .drupal_render($form["database-$i"]).'</td><td>'
+	       .drupal_render($form["accession-$i"]).'</td><td>'
+	       .drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/*************************************************************************************************************
+ * @section
+ * Supplimentary functions
+ */
+
+/**
+ * 
+ */
+function list_dbreferences_for_node($db_references) {
+
+  if (!empty($db_references) ) {
+    $output = '<table>';
+    $output .= '<tr><th>Database</th><th>Accession</th></tr>';
+
+    foreach ($db_references as $db) {
+        $output .= '<tr><td>'.$db->db_name.'</td><td>'.$db->accession.'</td></tr>';
+    } // end of foreach db reference 
+
+    $output .= '</table>'; 
+
+  } else {
+    $output = 'No Database References Added to the Current Stock';
+  }              
+
+  return $output;
+}     

+ 386 - 0
tripal_stock/tripal_stock-properties.inc

@@ -0,0 +1,386 @@
+<?php
+
+/*************************************************************************************************************
+ * @section
+ * Deals with Adding Properties
+ */
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Properties & Synonyms to Stocks
+ */
+function tripal_stock_add_ALL_property_page($node) {
+  $output = '';
+
+  $output .= implement_add_chado_properties_progress('properties').'<br>';
+  $output .= '<b>All Properties should strictly pertain to THE CURRENT Individual</b><br>';
+  $output .= '<br><b>Current Properties</b><br>';
+  $output .= list_properties_for_node($node->properties, $node->synonyms);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_property_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_add_chado_properties_navigate', 'properties', $node->nid);
+  return $output;
+}
+
+function tripal_stock_add_ONE_property_form($form_state, $node) {
+  $form = array();
+  $stock_id = $node->stock_id;
+
+  $form['add_properties'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Property') . '<span class="form-optional" title="This field is optional"> (optional)</span>',
+  ); 
+
+  $form['prop_nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $tmp_obj = get_chado_cvterm(array('name' => array('type'=>'STRING','exact'=>TRUE, 'value'=>'synonym'),
+  																	'cv_id' => array('type'=>'INT','value'=>variable_get('chado_stock_prop_types_cv', 'null') )));
+	$synonym_id = $tmp_obj->cvterm_id;
+	
+  $prop_type_options = get_chado_cvterm_options( variable_get('chado_stock_prop_types_cv', 'null') ); 
+  $prop_type_options[0] = 'Select a Type';
+  ksort($prop_type_options);
+  $form['add_properties']['prop_type_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Type of Property'),
+    '#options' => $prop_type_options,
+  );
+
+  $form['add_properties']['prop_value'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Value') . '<span class="form-optional" title="This field is optional">+</span>',
+  );
+
+  $form['add_properties']['preferred_synonym'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Preferred Synonym (only applicable if type is synonym)'),
+  );
+
+  $form['add_properties']['prop_stock_id'] = array(
+    '#type' => 'value',
+    '#value' => $stock_id,
+    '#required' => TRUE
+  );
+
+  $form['add_properties']['submit-add'] = array(
+    '#type' => 'submit',         
+    '#value' => t('Add Property')
+  );
+
+  return $form;
+}
+
+function tripal_stock_add_ONE_property_form_validate($form, &$form_state) {
+
+  // Only Require if Adding Property
+  if ($form_state['clicked_button']['#value'] == t('Add Property') ) {
+
+    // Check that Selected a type
+    if ( $form_state['values']['prop_type_id'] == 0) {
+      form_set_error('prop_type_id', 'Please select a type of property.');
+    } else {
+      // Check that type is in chado
+      $previous_db = tripal_db_set_active('chado');
+      $num_rows = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d", $form_state['values']['prop_type_id']));
+      tripal_db_set_active($previous_db);
+      if ( $num_rows->count != 1) {
+        form_set_error('prop_type_id', "The type you selected is not valid. Please choose another one. (CODE:$num_rows)");
+      } // end of if more or less than 1 row
+    } // if no prop type
+
+    // only check preferred synonym if type is synonym
+    if ($form_state['values']['preferred_synonym'] == 1) {
+    $tmp_obj = get_chado_cvterm(array('name' => array('type'=>'STRING','exact'=>TRUE, 'value'=>'synonym'),
+  																	'cv_id' => array('type'=>'INT','value'=>variable_get('chado_stock_prop_types_cv', 'null') )));
+      if ($form_state['values']['prop_type_id'] != $tmp_obj->cvterm_id) {
+        form_set_error('preferred_synonym', 'Preferred Synonym Checkbox Only Applicable if Type of Property is Synonym');
+      }
+    }
+
+  } // if add Property
+
+}
+
+function tripal_stock_add_ONE_property_form_submit($form, &$form_state) {
+
+  // if there is a property add it (only won't be a property if clicked next step w/ no property)
+  if ($form_state['values']['prop_type_id'] != 0) {
+    //determine the rank for this property
+    $max_rank = get_max_chado_rank('stockprop', 
+    															array('stock_id'=>array('type'=>'INT','value'=>$form_state['values']['prop_stock_id']), 
+    																		'type_id'=>array('type'=>'INT','value'=> $form_state['values']['prop_type_id']) ));
+    if ($max_rank == -1) { $rank = 0; 
+    } else { $rank = $max_rank+1; }
+    
+    $previous_db = tripal_db_set_active('chado');
+    db_query(
+      "INSERT INTO stockprop (stock_id, type_id, value, rank) VALUES (%d, %d, '%s', %d)",
+      $form_state['values']['prop_stock_id'],
+      $form_state['values']['prop_type_id'],
+      $form_state['values']['prop_value'],
+      $rank
+    );
+    tripal_db_set_active($previous_db);
+
+    drupal_set_message("Successfully Added Property");
+
+    // Set Preferred Synonym
+    if ($form_state['values']['preferred_synonym'] == 1) {
+
+      //use update node form so that both title and name get set
+      $node = node_load($form_state['values']['prop_nid']);
+      $node->title = $form_state['values']['prop_value'];
+      $node_form_state = array( 
+        'values' => array(
+					'title' => $form_state['values']['prop_value'], 
+					'op' => 'Save'
+				) 
+      );
+      module_load_include('inc', 'node', 'node.pages');
+      drupal_execute('chado_stock_node_form', $node_form_state, $node);
+
+    }
+
+  } //end of if property to add
+}
+
+/*************************************************************************************************************
+ * @section
+ * Deals with Editing and Deleting Properties
+ */
+
+function tripal_stock_edit_ALL_properties_page($node) {
+  $output = '';
+
+  $output .= drupal_get_form('tripal_stock_edit_ALL_properties_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_property_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_back_to_stock_button', $node->nid);
+
+  return $output;
+}
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Properties & Synonyms to Stocks
+ */
+function tripal_stock_edit_ALL_properties_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+
+  $i=0;
+  if (is_array($node->properties) && is_array($node->synonyms)) { 
+  	$all_properties = array_merge($node->properties, $node->synonyms);
+  } elseif (is_array($node->properties)) { 
+  	$all_properties = $node->properties; 
+  } elseif (is_array($node->synonyms)) { 
+  	$all_properties = $node->synonyms;
+  } else { $all_properties = array(); }
+
+  if (sizeof($all_properties) != 0) {
+  foreach ($all_properties as $property) {
+    $i++;
+    $form["num-$i"] = array(
+      '#type' => 'item',
+      '#value' => $i.'.'
+    );
+
+    $form["id-$i"] = array(
+      '#type' => 'hidden',
+      '#value' => $property->stockprop_id
+    );
+
+    $prop_type_options = get_chado_cvterm_options( variable_get('chado_stock_prop_types_cv', 'null') );
+    ksort($prop_type_options);
+ 
+    $default = array_search($property->type, $prop_type_options);
+
+    $form["type-$i"] = array(
+      '#type' => 'select',
+      //'#title' => t('Type of Property'),
+      '#options' => $prop_type_options,
+      '#default_value' => $default 
+    );
+
+    $form["value-$i"] = array(
+      '#type' => 'textfield',
+      //'#title' => t('Value'),
+      '#default_value' => $property->value
+    );
+
+    if ($property->type == 'synonym') {
+      $form["preferred-$i"] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Preferred Synonym'),
+      );
+    }
+
+    $form["submit-$i"] = array(
+      '#type' => 'submit',
+      '#value' => t("Delete #$i")
+    );
+
+  }} //end of foreach property
+
+  $form['num_properties'] = array(
+    '#type' => 'hidden',
+    '#value' => $i
+  );
+
+  $form["submit-edits"] = array(
+    '#type' => 'submit',
+    '#value' => t('Update Properties')
+  );
+
+  return $form;
+}
+
+function tripal_stock_edit_ALL_properties_form_submit($form, &$form_state) {
+
+  if ($form_state['clicked_button']['#value'] == t('Update Properties') ) {
+     //Update all
+     for ($i=1; $i<=$form_state['values']['num_properties']; $i++) {
+       tripal_stock_update_property($form_state['values']["id-$i"], $form_state['values']["type-$i"], $form_state['values']["value-$i"], $form_state['values']["preferred-$i"], $form_state['values']["nid"]);
+     }
+     drupal_set_message("Updated all Properties");
+     drupal_goto('node/'.$form_state['values']['nid']);
+  } elseif ( preg_match('/Delete #(\d+)/', $form_state['clicked_button']['#value'], $matches) ) {
+     $i = $matches[1];
+     tripal_stock_delete_property($form_state['values']["id-$i"], $form_state['values']["type-$i"], $form_state['values']["value-$i"]);
+     drupal_set_message("Deleted Property");
+  } else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+  
+}
+
+function tripal_stock_update_property($stockprop_id, $cvterm_id, $value, $preferred, $nid) {
+
+	$previous_db = tripal_db_set_active('chado');
+	$old_obj = db_fetch_object(db_query("SELECT * FROM stockprop WHERE stockprop_id=%d",$stockprop_id));
+	tripal_db_set_active($previous_db);
+	dpm($old_obj);
+	
+	// if they changed the type need to check rank
+	//   (if there is another property of the same type then rank needs to be increased to prevent collisions)
+	if ($cvterm_id == $old_obj->type_id) {
+	  $previous_db = tripal_db_set_active('chado');
+  	db_query(
+    	"UPDATE stockprop SET type_id=%d, value='%s' WHERE stockprop_id=%d",
+    	$cvterm_id, 
+    	$value,
+    	$stockprop_id
+  	);
+  	tripal_db_set_active($previous_db);
+  } else {
+      //determine the rank for this property
+    $max_rank = get_max_chado_rank('stockprop', 
+    															array('stock_id'=>array('type'=>'INT','value'=> $old_obj->stock_id), 
+    																		'type_id'=>array('type'=>'INT','value'=> $cvterm_id ) ));
+    if ($max_rank == -1) { $rank = 0; 
+    } else { $rank = $max_rank+1; }
+	  $previous_db = tripal_db_set_active('chado');
+  	db_query(
+    	"UPDATE stockprop SET type_id=%d, value='%s', rank=%d WHERE stockprop_id=%d",
+    	$cvterm_id, 
+    	$value,
+    	$rank,
+    	$stockprop_id
+  	);
+  	tripal_db_set_active($previous_db);  	
+  }
+
+  // Set Preferred Synonym
+  //use update node form so that both title and name get set                                                                                                                                                                       
+  if ($preferred) {
+    $node = node_load($nid);                                                                                                                                                                            
+    $node->title = $value;                                                                                                                                                                              
+    $node_form_state = array(                                                                                                                                                                                                        
+      'values' => array(                                                                                                                                                                                                             
+        'title' => $value,                                                                                                                                               
+        'op' => 'Save'                                                                                                                                                                                
+      )                                                                                                                                                                                                            
+    );                                                                                                                                                                                                                               
+    module_load_include('inc', 'node', 'node.pages');                                                                                                                                                                                
+    drupal_execute('chado_stock_node_form', $node_form_state, $node);  
+  }
+}
+
+function tripal_stock_delete_property($stockprop_id) {
+
+  $previous_db = tripal_db_set_active('chado');
+  db_query(
+    "DELETE FROM stockprop WHERE stockprop_id=%d",
+    $stockprop_id
+  );
+  tripal_db_set_active($previous_db);
+
+}
+
+function theme_tripal_stock_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 stock, one property per line. The type refers to the type of '
+  	     .'property and the value is the value for that property. For example, if this stock has a seed coat colour of green then '
+	     .'the property type=sead coat colour and the value=green. When the type of property is synonym, there is an extra checkbox '
+	     .'allowing you to specify which is the <b>Preferred Synonym</b>. This will change the current name of the stock.</p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Type</th><th>Value</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_properties']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td><td>'.drupal_render($form["type-$i"]).'</td><td>'.drupal_render($form["value-$i"]).drupal_render($form["preferred-$i"]).'</td><td>'.drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/*************************************************************************************************************
+ * @section
+ * Supplimentary functions
+ */
+
+/**
+ * 
+ */
+function list_properties_for_node($properties, $synonyms) {
+
+  if (!empty($properties) OR !empty($synonyms) ) {
+    $output = '<table>';
+    $output .= '<tr><th>Type</th><th>Value</th></tr>';
+
+    if (!empty($synonyms) ) {
+      foreach ($synonyms as $s) {
+        $output .= '<tr><td>synonym</td><td>'.$s.'</td></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 Added to the Current Stock';
+  }
+
+  return $output;
+}

+ 467 - 0
tripal_stock/tripal_stock-relationships.inc

@@ -0,0 +1,467 @@
+<?php
+
+/*************************************************************************************************************
+ * @section
+ * Deals with Adding Relationships
+ */
+
+function tripal_stock_add_ALL_relationships_page($node) {
+  $output = '';
+
+  $output .= implement_add_chado_properties_progress('relationships').'<br>';
+  $output .= '<b>All Relationships should include the CURRENT Individual ('.$node->uniquename.')</b><br>';
+  $output .= '<br><b>Current Relationships</b><br>';
+  $output .= list_relationships_for_node($node->uniquename, $node->subject_relationships, $node->object_relationships);
+  $output .= '<br><br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_relationship_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_add_chado_properties_navigate', 'relationships', $node->nid);
+  return $output;
+}
+
+/**
+ * Implements Hook_form()
+ * Handles adding of Relationships to Stocks
+ */
+function tripal_stock_add_ONE_relationship_form($form_state, $node) {
+
+  $stock_id = $node->stock_id;
+  $organism_id = $node->organism->organism_id;
+  $_SESSION['organism'] = $organism_id; //needed for autocomplete enter stock to work
+
+  $form['rel_nid'] = array(
+    '#type' => 'hidden', 
+    '#value' => $node->nid
+  ); 
+
+  $form['add_relationships'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Add Relationships') . '<span class="form-optional" title="This field is optional">(optional)</span>',
+  );
+
+  $form['add_relationships']['description'] = array(
+    '#type' => 'item',
+    '#value' => t('Relationships are specified as follows: (Subject) (Type of Relationship) (Object). For example, Fred is_the_paternal_parent_of Matty, where Fred & Matty are both stocks.')
+  );
+
+  $form['add_relationships']['subject_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subject'),
+    '#description' => 'The Uniquename, Name, Database Reference or Synonym of a Stock can be used here',
+  );
+
+  $type_options = get_chado_cvterm_options( variable_get('chado_stock_relationship_cv', 'null') );
+  $type_options[0] = 'Select a Type';
+  ksort($type_options);
+  $form['add_relationships']['type_id'] = array(
+    '#type' => 'select',
+    '#title' => t('Type of Relationship'),
+    '#options' => $type_options
+
+  );
+
+  $form['add_relationships']['object_id'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Object'),
+    '#description' => 'The Uniquename, Name, Database Reference or Synonym of a Stock can be used here',
+  );
+
+  $form['add_relationships']['r_description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Notes on the relationship') . '<span class="form-optional" title="This field is optional">+</span>',
+    '#description' => t('Should not include Genotypes and Phenotypes'),
+  );
+
+  $form['add_relationships']['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Add a Relationship')
+  );
+
+  $form['add_relationships']['r_stock_id'] = array(
+    '#type' => 'value',
+    '#value' => $stock_id,
+    '#required' => TRUE
+
+  );
+
+  $form['add_relationships']['r_stock_uniquename'] = array( 
+    '#type' => 'value',
+    '#value' => $node->uniquename,
+    '#required' => TRUE
+  );
+
+  return $form;
+
+}
+
+function tripal_stock_add_ONE_relationship_form_validate($form, &$form_state) {
+
+  //Require Validation if adding
+  if ($form_state['clicked_button']['#value'] == t('Add a Relationship') ) {
+    // check valid stock selected for subject
+    $criteria = array('unknown' => array('value'=> $form_state['values']['subject_id'], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+    $subject_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+    if (sizeof($subject_results) > 1) {
+      $links= array();
+      for ($i=0; $i<sizeof($subject_results); $i++) { $links[] = l($i+1, "node/".$subject_results[$i]->nid); }
+      $message = "Too many stocks match '".$form_state['values']['subject_id']."'! "
+      	       	 . " Please refine your input to match ONLY ONE stock. <br>"
+		 . "To aid in this process, here are the stocks that match your initial input: "
+		 .join(', ',$links);
+      form_set_error('subject_id', $message);
+    } elseif (sizeof($subject_results) < 1) {
+      form_set_error('subject_id', "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+    } elseif (sizeof($subject_results) == 1) {
+      $form_state['values']['subject_id'] = $subject_results[0]->stock_id;
+    }
+
+    // check valid stock selected for object
+    $criteria = array('unknown' => array('value'=> $form_state['values']['object_id'], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+    $object_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+    if (sizeof($object_results) > 1) {
+      $links= array();
+      for ($i=0; $i<sizeof($object_results); $i++) { $links[] = l($i+1, "node/".$object_results[$i]->nid); } 
+      $message = "Too many stocks match '".$form_state['values']['object_id']."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>"
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+      form_set_error('object_id', $message);
+    } elseif (sizeof($object_results) < 1) {
+      form_set_error('object_id', "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));                                                                         
+    } elseif (sizeof($object_results) == 1) {
+      $form_state['values']['object_id'] = $object_results[0]->stock_id;
+    }
+
+    // check valid type selected
+    if ($form_state['values']['type_id'] == 0) {
+      form_set_error('type_id', 'Please select a type of relationship.');
+    } else {
+    	$previous_db = tripal_db_set_active('chado');
+    	$tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d",$form_state['values']['type_id']));
+    	tripal_db_set_active($previous_db);
+    	
+      if ($tmp_obj->count != 1) {
+        form_set_error('type_id', 'The type you selected is not valid. Please choose another one.');
+      }
+    }
+
+    // check either subject or object is the current stock
+    if ( $subject_results[0]->nid != $form_state['values']['rel_nid'] ) {
+      if ( $object_results[0]->nid != $form_state['values']['rel_nid'] ) {
+        form_set_error('subject_id', 'Either Subject or Object must be the current stock ('.$form_state['values']['r_stock_uniquename'].').');
+      }
+    }
+  } //end of require validation if adding relationship
+}
+
+function tripal_stock_add_ONE_relationship_form_submit($form, &$form_state) {
+
+  if ($form_state['values']['subject_id'] > 0) {
+    $previous_db = db_set_active('chado');
+    db_query(
+      "INSERT INTO stock_relationship (subject_id, type_id, object_id, value) VALUES (%d, %d, %d, '%s')",
+      $form_state['values']['subject_id'],
+      $form_state['values']['type_id'],
+      $form_state['values']['object_id'],
+      $form_state['values']['r_description']
+    );
+    db_set_active($previous_db);
+
+    drupal_set_message('Successfully Added Relationship.');
+  } //end of insert relationship
+
+}
+
+/************************************************************************************************************* 
+ * @section                                           
+ * Deals with Editing and Deleting Properties         
+ */
+
+function tripal_stock_edit_ALL_relationships_page($node) {
+  $output = '';
+	
+  $output .= drupal_get_form('tripal_stock_edit_ALL_relationships_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('tripal_stock_add_ONE_relationship_form', $node);
+  $output .= '<br>';
+  $output .= drupal_get_form('implement_back_to_stock_button', $node->nid);
+
+  return $output;
+}
+                                                      
+/**                                                   
+ * Implements Hook_form()                             
+ * Handles adding of Properties & Synonyms to Stocks  
+ */                                                   
+function tripal_stock_edit_ALL_relationships_form($form_state, $node) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->nid
+  );
+  
+  $form['r_stock_uniquename'] = array(
+    '#type' => 'hidden',
+    '#value' => $node->uniquename
+  );
+
+  $i=0;
+  $relationships = array_merge($node->object_relationships, $node->subject_relationships);
+  if (sizeof($relationships) != 0) {
+  foreach ($relationships as $r) {
+
+    $i++;
+    $form["num-$i"] = array(
+      '#type' => 'item',
+      '#value' => $i.'.'
+    );
+
+    $form["id-$i"] = array(
+      '#type' => 'hidden',
+      '#value' => $r->stock_relationship_id
+    );
+
+    //Enter relationship specific fields
+    if ( !empty($r->subject_id) ) { 
+      $default = $r->subject_uniquename;
+      $description = l($r->subject_name, 'node/'.$r->subject_nid); 
+    } else { $default = $node->uniquename; $description = 'Current Stock'; }
+    $form["subject_id-$i"] = array(
+      '#type' => 'textfield',      
+      //'#title' => t('Subject'), 
+      '#required'   => TRUE,
+      '#size' => 30,
+      '#default_value' => $default,
+      '#description' => $description,
+    ); 
+
+    $type_options = get_chado_cvterm_options( variable_get('chado_stock_relationship_cv', 'null') );
+    ksort($type_options);          
+    $form["type_id-$i"] = array(  
+      '#type' => 'select',    
+      //'#title' => t('Type of Relationship'), 
+      '#options' => $type_options,
+      '#required' => TRUE,
+      '#default_value' => $r->relationship_type_id
+    );
+
+    if ( !empty($r->object_id) ) { 
+      $default = $r->object_uniquename;
+      $description = l($r->object_name, 'node/'.$r->object_nid);
+    } else { $default = $node->uniquename; $description = 'Current Stock'; }
+    $form["object_id-$i"] = array(
+      '#type' => 'textfield',          
+      //'#title' => t('Object'),      
+      '#required'   => TRUE,
+      '#size' => 30,
+      '#default_value' => $default,
+      '#description' => $description
+    );
+
+    $form["submit-$i"] = array(
+      '#type' => 'submit',
+      '#value' => t("Delete #$i")
+    );
+
+  }} //end of foreach relationship
+
+  $form['num_relationships'] = array(
+    '#type' => 'hidden',
+    '#value' => $i
+  );
+
+  $form["submit-edits"] = array(
+    '#type' => 'submit',
+    '#value' => t('Update Relationships')
+  );
+
+  return $form;
+}
+
+function tripal_stock_edit_ALL_relationships_form_validate($form, &$form_state) {
+
+  // Only Require if Updating Relationships
+  if ($form_state['clicked_button']['#value'] == t('Update Relationships') ) {
+
+    for ($i=1; $i<=$form_state['values']['num_relationships']; $i++) {
+      
+      // check valid stock selected for subject
+      $criteria = array('unknown' => array('value'=>$form_state['values']["subject_id-$i"], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+      $subject_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+      if (sizeof($subject_results) > 1) {
+        $links= array();
+        for ($j=0; $j<sizeof($subject_results); $j++) { $links[] = l($j+1, "node/".$subject_results[$j]->nid); }
+        $message = "Too many stocks match '".$form_state['values']["subject_id-$i"]."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>" 
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+        form_set_error("subject_id-$i", $message);
+      } elseif (sizeof($subject_results) < 1) { 
+        form_set_error("subject_id-$i", "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+      } elseif (sizeof($subject_results) == 1) {
+        $form_state['values']["subject_id-$i"] = $subject_results[0]->stock_id;
+      } 
+
+      // check valid stock selected for object
+      $criteria = array('unknown' => array('value'=> $form_state['values']["object_id-$i"], 
+      																		'columns'=>array('name','uniquename','accession','synonym') ));
+      $object_results = get_chado_stocks($criteria,'ANY',$_SESSION['organism']);
+      if (sizeof($object_results) > 1) {
+        $links= array();
+        for ($j=0; $j<sizeof($object_results); $j++) { $links[] = l($j+1, "node/".$object_results[$j]->nid); }
+        $message = "Too many stocks match '".$form_state['values']["object_id-$i"]."'! "
+                 . "Please refine your input to match ONLY ONE stock. <br>" 
+                 . "To aid in this process, here are the stocks that match your initial input: "
+                 .join(', ',$links);
+        form_set_error("object_id-$i", $message);
+      } elseif (sizeof($object_results) < 1) {
+        form_set_error("object_id-$i", "There are no stocks matching your input. Please check your input for typos and/or lookup the stock ".l('here', 'stocks'));
+      } elseif (sizeof($object_results) == 1) {
+        $form_state['values']["object_id-$i"] = $object_results[0]->stock_id;
+      } 
+
+      // check valid type selected
+      if ($form_state['values']["type_id-$i"] == 0) {
+        form_set_error('type_id', 'Please select a type of relationship.');
+      } else {
+    		$previous_db = tripal_db_set_active('chado');
+    		$tmp_obj = db_fetch_object(db_query("SELECT count(*) as count FROM cvterm WHERE cvterm_id=%d",$form_state['values']["type_id-$i"]));
+    		tripal_db_set_active($previous_db);
+    	
+      	if ($tmp_obj->count != 1) {
+          form_set_error("type_id-$i", 'The type you selected is not valid. Please choose another one.');
+        }
+      }
+
+      // check either subject or object is the current stock
+      if ( $subject_results[0]->nid != $form_state['values']['nid'] ) {
+        if ( $object_results[0]->nid != $form_state['values']['nid'] ) {
+          form_set_error("subject_id-$i", 'Either Subject or Object must be the current stock ('.$form_state['values']['r_stock_uniquename'].').');
+        }
+      }
+
+    } // end of for each relationship
+  } //end of if updating relationships
+
+}
+
+function tripal_stock_edit_ALL_relationships_form_submit($form, &$form_state) {
+
+  if ($form_state['clicked_button']['#value'] == t('Update Relationships') ) {
+     //Update all
+     for ($i=1; $i<=$form_state['values']['num_relationships']; $i++) {
+
+       //process stock textfields
+       tripal_stock_update_relationship(
+				$form_state['values']["id-$i"], 
+				$form_state['values']["subject_id-$i"],
+				$form_state['values']["type_id-$i"], 
+				$form_state['values']["object_id-$i"]
+			);
+     }
+     drupal_set_message("Updated all Relationships");
+     drupal_goto('node/'.$form_state['values']['nid']);
+
+  } elseif ( preg_match('/Delete #(\d+)/', $form_state['clicked_button']['#value'], $matches) ) {
+
+     $i = $matches[1];
+     tripal_stock_delete_relationship($form_state['values']["id-$i"]);
+     drupal_set_message("Deleted Relationship");
+
+  } elseif ($form_state['clicked_button']['#value'] == t('Back to Stock') ) {
+    drupal_goto('node/'.$form_state['values']['nid']);
+  } else {
+    drupal_set_message("Unrecognized Button Pressed",'error');
+  }
+
+}
+
+function tripal_stock_update_relationship ($stock_relationship_id, $subject_id, $cvterm_id, $object_id) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "UPDATE stock_relationship SET subject_id=%d, type_id=%d, object_id=%d WHERE stock_relationship_id=%d",
+    $subject_id,
+    $cvterm_id, 
+    $object_id,
+    $stock_relationship_id
+  );
+  db_set_active($previous_db);
+
+}
+
+function tripal_stock_delete_relationship ($stock_relationship_id) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "DELETE FROM stock_relationship WHERE stock_relationship_id=%d",
+    $stock_relationship_id
+  );
+  db_set_active($previous_db);
+
+}
+
+function theme_tripal_stock_edit_ALL_relationships_form ($form) {
+  $output = '';
+  
+  $output .= '<br><fieldset>';
+  $output .= '<legend>Edit Already Existing Relationships<span class="form-optional" title="This field is optional">(optional)</span></legend>';
+  $output .= '<p>Each relationship for this stock is listed below, one per line. The textboxes indicating '
+  	    .'the subject and object of the relationship can contain the uniquename, name, database '
+	    .'reference or synonym of a stock of the same organism.</p>';
+  $output .= '<table>';
+  $output .= '<tr><th>#</th><th>Subject</th><th>Type</th><th>Object</th><th></th></tr>';
+
+  for ($i=1; $i<=$form['num_relationships']['#value']; $i++) {
+    $output .= '<tr><td>'.drupal_render($form["num-$i"]).'</td><td>'
+    	       .drupal_render($form["subject_id-$i"]).'</td><td>'
+	       .drupal_render($form["type_id-$i"]).'</td><td>'
+	       .drupal_render($form["object_id-$i"]).'</td><td>'
+	       .drupal_render($form["submit-$i"]).'</td></tr>';
+  }
+
+  $output .= '</table><br>';
+  $output .= drupal_render($form);
+  $output .= '</fieldset>';
+
+  return $output;
+}
+
+/*************************************************************************************************************
+ * @section
+ * Supplementary Functions
+ */
+
+/**
+ *
+ */
+function list_relationships_for_node($stock_name, $subject_relationships, $object_relationships) {
+
+  if (!empty($subject_relationships) OR !empty($object_relationships) ) {
+    $output = '<table>';
+    $output .= '<tr><th>Subject</th><th>Relationship Type</th><th>Object</th></tr>';
+
+    if (!empty($subject_relationships) ) {
+      foreach ($subject_relationships as $s) {
+        $output .= '<tr><td>'.$s->subject_name.'</td><td>'.$s->relationship_type.'</td><td>'.$stock_name.'</td></tr>';
+      }
+    }
+
+    if (!empty($object_relationships) ) {
+      foreach ($object_relationships as $o) {
+        $output .= '<tr><td>'.$stock_name.'</td><td>'.$o->relationship_type.'</td><td>'.$o->object_name.'</td></tr>';
+      } // end of foreach property
+    }
+
+    $output .= '</table>';
+  } else {
+    $output = 'No Relationships Involving the Current Stock'; 
+  }
+
+  return $output;
+
+}

+ 190 - 0
tripal_stock/tripal_stock-secondary_tables.inc

@@ -0,0 +1,190 @@
+<?php
+// $Id$
+
+function implement_back_to_stock_button($form_state, $nid) {
+  $form = array();
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $nid
+  );
+
+  $form["submit-back"] = array( 
+    '#type' => 'submit',
+    '#value' => t('Back to Stock') 
+  );
+
+  return $form;
+}
+
+function implement_back_to_stock_button_submit($form, $form_state) {
+  drupal_goto('node/'.$form_state['values']['nid']);
+}
+
+function implement_add_chado_properties_progress($current) {
+
+    $value = '<div class="form_progress"><div class="form_progress-text">'; 
+
+    if ($current == 'main') { $value .= '<span id="form-step-current">Create Basic Stock</span>'; } 
+    else { $value .= '<span id="form-step">Create Basic Stock</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';
+
+    if ($current == 'properties') { $value .= '<span id="form-step-current">Add Synonyms & Properties</span>'; }
+    else { $value .= '<span id="form-step">Add Synonyms & Properties</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';                       
+
+    if ($current == 'db_references') { $value .= '<span id="form-step-current">Add Database References</span>'; }
+    else { $value .= '<span id="form-step">Add Database References</span>'; }
+
+    $value .= '<span id="form-segway">  >>  </span>';         
+
+    if ($current == 'relationships') { $value .= '<span id="form-step-current">Add Relationships</span>'; }
+    else { $value .= '<span id="form-step">Add Relationships</span>'; }
+
+    $value .= '</div></div>';
+
+    return $value;
+
+}
+
+function implement_add_chado_properties_navigate($form_state, $step, $nid) {
+  $form = array();
+
+  $form['current_step'] = array(
+    '#type' => 'hidden',
+    '#value' => $step
+  );
+
+  // Use this field to set all the steps and the path to each form
+  // where each step is of the form name;path and each step is separated by ::
+  $steps =array(
+    'properties' => 'node/%node/properties',
+    'db_references' => 'node/%node/db_references',
+    'relationships' => 'node/%node/relationships'
+  );
+  $steps_value = array();
+  foreach ($steps as $k => $v) { $steps_value[] = $k.';'.$v; }
+  $form['steps'] = array(
+    '#type' => 'hidden',
+    '#value' => implode('::', $steps_value)
+  );
+
+  $form['first_step'] = array(
+    '#type' => 'hidden',
+    '#value' => 'properties'
+  );
+
+  $form['last_step'] = array(
+    '#type' => 'hidden',
+    '#value' => 'relationships'
+  );
+
+  $form['nid'] = array(
+    '#type' => 'hidden',
+    '#value' => $nid
+  );
+
+  if ($step != $form['first_step']['#value']) {
+    $form['submit-prev'] = array(
+      '#type' => 'submit',
+      '#value' => t('Previous Step')
+    );
+  }
+
+  if ($step != $form['last_step']['#value']) {
+    $form['submit-next'] = array( 
+      '#type' => 'submit',
+      '#value' => t('Next Step')
+    );
+  }
+
+  if ($step == $form['last_step']['#value']) {
+    $form['submit-finish'] = array(
+      '#type' => 'submit', 
+      '#value' => t('Finish')
+    );
+  }
+
+  return $form;
+}
+
+function implement_add_chado_properties_navigate_submit($form, $form_state) {
+
+  $raw_steps = preg_split('/::/', $form_state['values']['steps']); 
+
+  $steps = array();
+  $current_index = 'EMPTY';
+  $i=0;
+
+  foreach ($raw_steps as $raw_step) {
+    $step = preg_split('/;/', $raw_step);
+    $steps[$i] = $step;
+    
+    if ($step[0] == $form_state['values']['current_step']) {
+      $current_index = $i;
+    }
+
+    $i++;
+  }
+  $num_steps = $i;
+  
+  if (strcmp($current_index,'EMPTY') == 0) {
+    // No Matching Step
+    drupal_set_message('Could not determine next step -'.$form_state['values']['current_step'].', please contact the administrator', 'error');
+  } elseif ($current_index == 0) {
+    $next_goto = $steps[$current_index+1][1];
+  } elseif ($current_index == ($num_steps-1)) {
+    $prev_goto = $steps[$current_index-1][1];
+    $next_goto = 'node/%node';
+  } else {
+    $prev_goto = $steps[$current_index-1][1];
+    $next_goto = $steps[$current_index+1][1];
+  }
+
+  if ($form_state['clicked_button']['#value'] == t('Previous Step') ) {
+    //replace %node
+    $prev_goto = preg_replace('/%node/', $form_state['values']['nid'], $prev_goto);
+    $_REQUEST['destination'] = $prev_goto;
+  } elseif ($form_state['clicked_button']['#value'] == t('Next Step') ) {
+    $next_goto = preg_replace('/%node/', $form_state['values']['nid'], $next_goto);
+    $_REQUEST['destination'] = $next_goto;
+  } elseif ($form_state['clicked_button']['#value'] == t('Finish') ) {
+    $next_goto = preg_replace('/%node/', $form_state['values']['nid'], $next_goto);
+    $_REQUEST['destination'] = $next_goto;
+  }
+
+}
+
+/*************************************************************************************************************
+ * Implements Hook_form()
+ * Handles setting the is_obsolete property of stocks
+ */
+function tripal_stock_is_obsolete_form($node, $stock_id) {
+
+  $form['make_obsolete'] = array(
+    '#type' => 'submit',
+    '#value' => t('Mark Stock as Obsolete')
+  );
+
+  $form['make_obsolete_stock_id'] = array(
+    '#type' => 'value',
+    '#value' => $stock_id,
+    '#required' => TRUE
+  );
+
+  return $form;
+}
+
+function tripal_stock_is_obsolete_form_submit($form, &$form_state) {
+
+  $previous_db = db_set_active('chado');
+  db_query(
+    "UPDATE stock SET is_obsolete='t' WHERE stock_id=%d",
+    $form_state['values']['make_obsolete_stock_id']
+  );
+  db_set_active($previous_db);
+
+}
+

+ 97 - 4
tripal_stock/tripal_stock.module

@@ -2,9 +2,15 @@
 // $Id$
 
 require("tripal_stock-administration.inc");
+
 require("tripal_stock-api_functions.inc");
 require("other_module_api_functions.inc");
 
+require("tripal_stock-secondary_tables.inc");
+require("tripal_stock-properties.inc");
+require("tripal_stock-relationships.inc");
+require("tripal_stock-db_references.inc");
+
 /*************************************************************************
  * Implementation of hook_menu()
  * Purpose: Add menu items for this module
@@ -16,7 +22,7 @@ require("other_module_api_functions.inc");
 function tripal_stock_menu() {
   $items = array();
 
-  //Administrative settings menu
+  //Administrative settings menu-----------------
   $items['admin/tripal/tripal_stock'] = array(
     'title' => t('Stocks'),
     'description' => t('Settings for Chado Stocks'),
@@ -26,7 +32,7 @@ function tripal_stock_menu() {
     'type' => MENU_NORMAL_ITEM
   );
   
-  //Displaying stocks
+  //Displaying stocks----------------------------
   $items['stocks'] = array(
   	'menu_name' => ('primary-links'),
     'title' => t('Stocks'),
@@ -35,9 +41,83 @@ function tripal_stock_menu() {
     'type' => MENU_NORMAL_ITEM,  
   );
 
+  // Adding Secondary Properties-----------------
+  $items['node/%cs_node/properties'] = array(       
+    'title' => t('Add Properties & Synonyms'),                         
+    'description' => t('Settings for Chado Stocks'),
+    'page callback' => 'tripal_stock_add_ALL_property_page',           
+    'page arguments' => array(1), 
+    'access arguments' => array('create chado_stock content'),
+    'type' => MENU_CALLBACK
+  ); 
+
+  $items['node/%cs_node/db_references'] = array(                        
+    'title' => t('Add Database References'),                   
+    'description' => t('Settings for Chado Stocks'),              
+    'page callback' => 'tripal_stock_add_ALL_dbreferences_page',                         
+    'page arguments' => array(1),
+    'access arguments' => array('create chado_stock content'),
+    'type' => MENU_CALLBACK
+  ); 
+
+  $items['node/%cs_node/relationships'] = array(                      
+    'title' => t('Add Relationships'),                      
+    'description' => t('Settings for Chado Stocks'),               
+    'page callback' => 'tripal_stock_add_ALL_relationships_page',                          
+    'page arguments' => array(1),
+    'access arguments' => array('create chado_stock content'),
+    'type' => MENU_CALLBACK
+  );
+
+  //Edit/Deleting Secondary Properties-------------
+  $items['node/%cs_node/edit_properties'] = array(
+    'title' => t('Edit Properties'),
+    'description' => t('Settings for Chado Stocks'),
+    'page callback' => 'tripal_stock_edit_ALL_properties_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_stock content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 8,
+  );
+
+    $items['node/%cs_node/edit_relationships'] = array(
+    'title' => t('Edit Relationships'),
+    'description' => t('Settings for Chado Stocks'), 
+    'page callback' => 'tripal_stock_edit_ALL_relationships_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_stock content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 9,
+  );
+
+  $items['node/%cs_node/edit_db_references'] = array(
+    'title' => t('Edit DB References'),
+    'description' => t('Settings for Chado Stocks'),
+    'page callback' => 'tripal_stock_edit_ALL_dbreferences_page',
+    'page arguments' => array(1),
+    'access arguments' => array('edit chado_stock content'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 10,
+  );
   return $items;
 }
 
+/*************************************************************************
+ * Implements Menu wildcard_load hook
+ * Purpose: Allows the node ID of a chado stock to be dynamically 
+ *   pulled from the path. The node is loaded from this node ID
+ *   and supplied to the page as an arguement
+ */
+function cs_node_load($nid) {
+  if (is_numeric($nid)) {
+    $node = node_load($nid);
+    if ($node->type == 'chado_stock') {
+      return $node;
+    }
+  }
+
+  return FALSE;
+}
 
 /*************************************************************************
  * Implementation of hook_perm()
@@ -105,6 +185,19 @@ function tripal_stock_theme() {
 		'tripal_stock_stock_table' => array (
 			'arguments' => array('stocks'),
 		),
+		// Property edit forms--------------------------
+    'tripal_stock_edit_ALL_properties_form' => array(
+      'arguments' => array('form'),
+      'function' => 'theme_tripal_stock_edit_ALL_properties_form',
+    ),
+    'tripal_stock_edit_ALL_db_references_form' => array(
+      'arguments' => array('form'),
+      'function' => 'theme_tripal_stock_edit_ALL_db_references_form',
+    ),
+    'tripal_stock_edit_ALL_relationships_form' => array(
+      'arguments' => array('form'),
+      'function' => 'theme_tripal_stock_edit_ALL_relationships_form',
+    ),
 	);
 }
 
@@ -115,7 +208,7 @@ function tripal_stock_show_stocks () {
 	$sql = "SELECT COUNT(stock_id) FROM {chado_stock}";
    $no_stocks = db_result(db_query($sql));
    if($no_stocks != 0) {
-      $stocks = get_chado_stocks();
+      $stocks = get_all_chado_stocks();
       if($no_stocks != count($stocks)) {
          drupal_set_message("Synchronization needed.");
       }
@@ -348,7 +441,7 @@ function chado_stock_form($node, $form_state) {
   if (!isset($node->uniquename)) {
     $form['progress'] = array( 
       '#type' => 'item', 
-      '#value' => 'blank', //implement_add_chado_properties_progress('main')
+      '#value' => implement_add_chado_properties_progress('main')
     );
   }