tripal_analysis.form.inc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782
  1. <?php
  2. /**
  3. * When editing or creating a new node of type 'chado_analysis' we need
  4. * a form. This function creates the form that will be used for this.
  5. *
  6. * @ingroup tripal_analysis
  7. */
  8. function chado_analysis_form($node, &$form_state) {
  9. $form = array();
  10. // Default values can come in the following ways:
  11. //
  12. // 1) as elements of the $node object. This occurs when editing an existing analysis
  13. // 2) in the $form_state['values'] array which occurs on a failed validation or
  14. // ajax callbacks from non submit form elements
  15. // 3) in the $form_state['input'[ array which occurs on ajax callbacks from submit
  16. // form elements and the form is being rebuilt
  17. //
  18. // set form field defaults
  19. $analysis_id = null;
  20. $analysisname = '';
  21. $program = '';
  22. $programversion = '';
  23. $algorithm = '';
  24. $sourcename = '';
  25. $sourceversion = '';
  26. $sourceuri = '';
  27. $timeexecuted = '';
  28. $description = '';
  29. $d_removed = array(); // lists removed properties
  30. $num_new = 0; // the number of new rows
  31. // if we are editing an existing node then the analysis is already part of the node
  32. if (property_exists($node, 'analysis')) {
  33. $analysis = $node->analysis;
  34. $analysis = tripal_core_expand_chado_vars($analysis, 'field', 'analysis.description');
  35. $analysis_id = $analysis->analysis_id;
  36. // get form defaults
  37. $analysisname = $analysis->name;
  38. $program = $analysis->program;
  39. $programversion = $analysis->programversion;
  40. $algorithm = $analysis->algorithm;
  41. $sourcename = $analysis->sourcename;
  42. $sourceversion = $analysis->sourceversion;
  43. $sourceuri = $analysis->sourceuri;
  44. $timeexecuted = $analysis->timeexecuted;
  45. $description = $analysis->description;
  46. // set the organism_id in the form
  47. $form['analysis_id'] = array(
  48. '#type' => 'value',
  49. '#value' => $analysis->analysis_id,
  50. );
  51. }
  52. // if we are re constructing the form from a failed validation or ajax callback
  53. // then use the $form_state['values'] values
  54. if (array_key_exists('values', $form_state)) {
  55. $analysisname = $form_state['values']['analysisname'];
  56. $program = $form_state['values']['program'];
  57. $programversion = $form_state['values']['programversion'];
  58. $algorithm = $form_state['values']['algorithm'];
  59. $sourcename = $form_state['values']['sourcename'];
  60. $sourceversion = $form_state['values']['sourceversion'];
  61. $sourceuri = $form_state['values']['sourceuri'];
  62. $timeexecuted = $form_state['values']['timeexecuted'];
  63. $description = $form_state['values']['description'];
  64. $d_removed = $form_state['values']['removed'];
  65. $num_new = $form_state['values']['num_new'] ? $form_state['values']['num_new'] : 0;
  66. }
  67. // if we are re building the form from after submission (from ajax call) then
  68. // the values are in the $form_state['input'] array
  69. if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
  70. $analysisname = $form_state['input']['analysisname'];
  71. $program = $form_state['input']['program'];
  72. $programversion = $form_state['input']['programversion'];
  73. $algorithm = $form_state['input']['algorithm'];
  74. $sourcename = $form_state['input']['sourcename'];
  75. $sourceversion = $form_state['input']['sourceversion'];
  76. $sourceuri = $form_state['input']['sourceuri'];
  77. $timeexecuted = $form_state['input']['timeexecuted'];
  78. $description = $form_state['input']['description'];
  79. $d_removed = $form_state['input']['removed'];
  80. $num_new = $form_state['input']['num_new'] ? $form_state['input']['num_new'] : 0;
  81. }
  82. $form['title']= array(
  83. '#type' => 'value',
  84. '#default_value' => $node->title,
  85. );
  86. $form['instructions'] = array(
  87. '#markup' => t('<b>Note</b>: When adding any type of data it is good to associate it with
  88. an analysis so that site visitors can identify the source of the data including
  89. necessary materials and methods. The fields below imply that all analyses
  90. are derived from some software package. But, data can also be derived via retreival
  91. from an external source or an analysis pipeline with multipel software components.
  92. In these cases, provide values for the fields below that best makes sense
  93. '),
  94. );
  95. $form['analysisname']= array(
  96. '#type' => 'textfield',
  97. '#title' => t('Analysis Name'),
  98. '#required' => TRUE,
  99. '#default_value' => $analysisname,
  100. '#description' => t("This should be a brief name that
  101. describes the analysis succintly. This name will helps the user find analyses."),
  102. );
  103. $form['program']= array(
  104. '#type' => 'textfield',
  105. '#title' => t('Program, Pipeline Name or Method Name'),
  106. '#required' => TRUE,
  107. '#default_value' => $program,
  108. '#description' => t("Program name, e.g. blastx, blastp, sim4, genscan. If the analysis was not derived from a software package, provide a very brief description of the pipeline or method."),
  109. );
  110. $form['programversion']= array(
  111. '#type' => 'textfield',
  112. '#title' => t('Program, Pipeline or Method version'),
  113. '#required' => TRUE,
  114. '#default_value' => $programversion,
  115. '#description' => t("Version description, e.g. TBLASTX 2.0MP-WashU [09-Nov-2000]. Enter 'n/a' if no version is available or applicable."),
  116. );
  117. $form['algorithm']= array(
  118. '#type' => 'textfield',
  119. '#title' => t('Algorithm'),
  120. '#required' => FALSE,
  121. '#default_value' => $algorithm,
  122. '#description' => t("Algorithm name, e.g. blast."),
  123. );
  124. $form['sourcename']= array(
  125. '#type' => 'textfield',
  126. '#title' => t('Source Name'),
  127. '#required' => TRUE,
  128. '#default_value' => $sourcename,
  129. '#description' => t('The name of the source data. This could be a file name, data set name or a
  130. small description for how the data was collected. For long descriptions use the description field below'),
  131. );
  132. $form['sourceversion']= array(
  133. '#type' => 'textfield',
  134. '#title' => t('Source Version'),
  135. '#required' => FALSE,
  136. '#default_value' => $sourceversion,
  137. '#description' => t('If the source dataset has a version, include it here'),
  138. );
  139. $form['sourceuri']= array(
  140. '#type' => 'textfield',
  141. '#title' => t('Source URI'),
  142. '#required' => FALSE,
  143. '#default_value' => $sourceuri,
  144. '#description' => t("This is a permanent URL or URI for the source of the analysis.
  145. Someone could recreate the analysis directly by going to this URI and
  146. fetching the source data (e.g. the blast database, or the training model)."),
  147. );
  148. // Get time saved in chado
  149. $default_time = $timeexecuted;
  150. $year = preg_replace("/^(\d+)-\d+-\d+ .*/", "$1", $default_time);
  151. $month = preg_replace("/^\d+-0?(\d+)-\d+ .*/", "$1", $default_time);
  152. $day = preg_replace("/^\d+-\d+-0?(\d+) .*/", "$1", $default_time);
  153. // If the time is not set, use current time
  154. if (!$default_time) {
  155. $default_time = REQUEST_TIME;
  156. $year = format_date($default_time, 'custom', 'Y');
  157. $month = format_date($default_time, 'custom', 'n');
  158. $day = format_date($default_time, 'custom', 'j');
  159. }
  160. $form['timeexecuted']= array(
  161. '#type' => 'date',
  162. '#title' => t('Time Executed'),
  163. '#required' => TRUE,
  164. '#default_value' => array(
  165. 'year' => $year,
  166. 'month' => $month,
  167. 'day' => $day,
  168. ),
  169. );
  170. $form['description']= array(
  171. '#type' => 'textarea',
  172. '#rows' => 15,
  173. '#title' => t('Materials & Methods (Description and/or Program Settings)'),
  174. '#required' => FALSE,
  175. '#default_value' => $description,
  176. '#description' => t('Please provide all necessary information to allow
  177. someone to recreate the analysis, including materials and methods
  178. for collection of the source data and performing the analysis'),
  179. );
  180. $form['properties'] = array(
  181. '#type' => 'fieldset',
  182. '#title' => t('Analysis Details'),
  183. '#description' => t('You may add additional properties by
  184. selecting a property type from the dropdown and adding text. You may add
  185. as many properties as desired by clicking the plus button on the right. To
  186. remove a property, click the minus button. If a property is not available
  187. you may add it by ' . l('adding the term', 'admin/tripal/tripal_cv/cvterm/add') . '
  188. to the <b>analysis_property</b> vocabulary within the <b>tripal</b> database'),
  189. );
  190. $form['properties']['table'] = array(
  191. '#type' => 'markup',
  192. '#value' => '',
  193. '#prefix' => '<div id="tripal-analysis-edit-properties-table">',
  194. '#suffix' => '</div>',
  195. );
  196. // get the analysis properties
  197. $properties_select = array();
  198. $properties_select[] = 'Select a Property';
  199. $properties_list = array();
  200. $sql = "
  201. SELECT DISTINCT CVT.cvterm_id, CVT.name, CVT.definition
  202. FROM {cvterm} CVT
  203. INNER JOIN {cv} ON CVT.cv_id = CV.cv_id
  204. WHERE
  205. CV.name = 'analysis_property' AND
  206. NOT CVT.is_obsolete = 1
  207. ORDER BY CVT.name ASC
  208. ";
  209. $prop_types = chado_query($sql);
  210. while ($prop = $prop_types->fetchObject()) {
  211. $properties_select[$prop->cvterm_id] = $prop->name;
  212. $properties_list[$prop->cvterm_id] = $prop;
  213. }
  214. // this array keeps track of all properties we have and allows the functions
  215. // below to select the next rank if a property is dupliated
  216. $ranks = array();
  217. // add in the properties from the Chado analysisprop table (only pertains to existing analyses)
  218. if ($analysis_id) {
  219. chado_analysis_node_form_add_analysisprop_table_props($form, $form_state, $analysis_id, $ranks, $d_removed);
  220. }
  221. // add in any new properties that have been added by the user through an AHAH callback
  222. chado_analysis_node_form_add_new_props($form, $form_state, $ranks, $d_removed);
  223. // add an empty row of field to allow for addition of a new property
  224. chado_analysis_node_form_add_new_empty_props($form, $form_state, $properties_select);
  225. $form['#theme'] = 'chado_analysis_form';
  226. return $form;
  227. }
  228. /**
  229. * This function is responsible for adding a blank row to the properties table for
  230. * adding a new property.
  231. */
  232. function chado_analysis_node_form_add_new_empty_props(&$form, &$form_state, $properties_select) {
  233. // get the field defaults either from $form_state['values'] or $form_state['input']
  234. $description = '';
  235. $text = '';
  236. $id = 0;
  237. if (array_key_exists('values', $form_state)) {
  238. $id = $form_state['values']['new_id'];
  239. $text = $form_state['values']['new_value'];
  240. }
  241. // if we have a property ID then get it's definition to display to the user
  242. if($id) {
  243. $values = array('cvterm_id' => $id);
  244. $cvterm = tripal_core_chado_select('cvterm', array('definition'), $values);
  245. if ($cvterm[0]->definition) {
  246. $description = $cvterm[0]->definition;
  247. }
  248. }
  249. $rows = 1;
  250. // add one more blank set of property fields
  251. $form['properties']['table']['new']["new_id"] = array(
  252. '#type' => 'select',
  253. '#options' => $properties_select,
  254. '#default_value' => $id,
  255. '#ajax' => array(
  256. 'callback' => "tripal_analysis_property_get_description",
  257. 'wrapper' => 'tripal-analysis-new_value',
  258. 'effect' => 'fade',
  259. 'method' => 'replace',
  260. ),
  261. );
  262. $form['properties']['table']['new']["new_value"] = array(
  263. '#type' => 'textarea',
  264. '#default_value' => $text,
  265. '#cols' => 50,
  266. '#rows' => $rows,
  267. '#prefix' => '<div id="tripal-analysis-new_value">',
  268. '#description' => $description,
  269. '#suffix' => '</div>',
  270. );
  271. $form['properties']['table']['new']["add"] = array(
  272. '#type' => 'button',
  273. '#value' => t('Add'),
  274. '#name' => 'add',
  275. '#ajax' => array(
  276. 'callback' => "tripal_analysis_property_ajax_update",
  277. 'wrapper' => 'tripal-analysis-edit-properties-table',
  278. 'effect' => 'fade',
  279. 'method' => 'replace',
  280. 'prevent' => 'click'
  281. ),
  282. // When this button is clicked, the form will be validated and submitted.
  283. // Therefore, we set custom submit and validate functions to override the
  284. // default form submit. In the validate function we set the form_state
  285. // to rebuild the form so the submit function never actually gets called,
  286. // but we need it or Drupal will run the default validate anyway.
  287. // we also set #limit_validation_errors to empty so fields that
  288. // are required that don't have values won't generate warnings.
  289. '#submit' => array('chado_anslysis_node_form_props_button_submit'),
  290. '#validate' => array('chado_anslysis_node_form_props_button_validate'),
  291. '#limit_validation_errors' => array(array('new_id')),
  292. );
  293. }
  294. /**
  295. * This function is used to rebuild the form if an ajax call is made vai a button.
  296. * The button causes the form to be submitted. We don't want this so we override
  297. * the validate and submit routines on the form button. Therefore, this function
  298. * only needs to tell Drupal to rebuild the form
  299. */
  300. function chado_anslysis_node_form_props_button_validate($form, &$form_state){
  301. if (array_key_exists('triggering_element', $form_state) and
  302. $form_state['triggering_element']['#name'] == 'add' and
  303. $form_state['input']['new_id'] == 0 ){
  304. form_set_error('new_id', "Please specify a property type");
  305. return;
  306. }
  307. $form_state['rebuild'] = TRUE;
  308. }
  309. /**
  310. * This function is just a dummy to override the default form submit on ajax calls for buttons
  311. */
  312. function chado_anslysis_node_form_props_button_submit($form, &$form_state){
  313. // do nothing
  314. }
  315. /**
  316. * This adds
  317. */
  318. function chado_analysis_node_form_add_new_props(&$form, &$form_state, &$ranks, &$d_removed) {
  319. // set some default values
  320. $j = 0;
  321. $num_properties = 0;
  322. $values = array();
  323. if (array_key_exists('values', $form_state)) {
  324. $values = $form_state['values'];
  325. }
  326. if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
  327. $values = $form_state['input'];
  328. }
  329. // first, add in all of the new properties that were added previously via this form
  330. foreach ($values as $element_name => $value) {
  331. if (preg_match('/new_value-(\d+)-(\d+)/', $element_name, $matches)) {
  332. $new_id = $matches[1];
  333. $rank = $matches[2];
  334. // skip any properties that the user requested to delete through a previous
  335. // ajax callback or through the current ajax callback
  336. if (array_key_exists("$new_id-$rank", $d_removed)) {
  337. continue;
  338. }
  339. if (array_key_exists('triggering_element', $form_state) and
  340. $form_state['triggering_element']['#name'] == 'remove-' . $new_id . '-' . $rank) {
  341. $d_removed["$new_id-$rank"] = 1;
  342. continue;
  343. }
  344. // get this new_id information
  345. $args = array('cvterm_id' => $new_id);
  346. $cvterm = tripal_core_chado_select('cvterm', array('name', 'definition'), $args);
  347. // add it to the $ranks array
  348. $ranks[$new_id][$rank]['name'] = $cvterm[0]->name;
  349. $ranks[$new_id][$rank]['id'] = $new_id;
  350. $ranks[$new_id][$rank]['value'] = $value;
  351. $ranks[$new_id][$rank]['definition'] = $cvterm[0]->definition;
  352. $num_properties++;
  353. // determine how many rows we need in the textarea
  354. $rows = 1;
  355. // add the new fields
  356. $form['properties']['table']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
  357. '#markup' => $cvterm[0]->name
  358. );
  359. $form['properties']['table']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
  360. '#type' => 'textarea',
  361. '#default_value' => $value,
  362. '#cols' => 50,
  363. '#rows' => $rows,
  364. '#description' => $cvterm[0]->definition,
  365. );
  366. $form['properties']['table']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
  367. '#type' => 'button',
  368. '#value' => t('Remove'),
  369. '#name' => "remove-$new_id-$rank",
  370. '#ajax' => array(
  371. 'callback' => "tripal_analysis_property_ajax_update",
  372. 'wrapper' => 'tripal-analysis-edit-properties-table',
  373. 'effect' => 'fade',
  374. 'event' => 'mousedown',
  375. 'method' => 'replace',
  376. 'prevent' => 'click'
  377. ),
  378. // When this button is clicked, the form will be validated and submitted.
  379. // Therefore, we set custom submit and validate functions to override the
  380. // default form submit. In the validate function we set the form_state
  381. // to rebuild the form so the submit function never actually gets called,
  382. // but we need it or Drupal will run the default validate anyway.
  383. // we also set #limit_validation_errors to empty so fields that
  384. // are required that don't have values won't generate warnings.
  385. '#submit' => array('chado_anslysis_node_form_props_button_submit'),
  386. '#validate' => array('chado_anslysis_node_form_props_button_validate'),
  387. '#limit_validation_errors' => array(),
  388. );
  389. }
  390. }
  391. // second add in any new properties added during this callback
  392. if (array_key_exists('triggering_element', $form_state) and
  393. $form_state['triggering_element']['#name'] == 'add' and
  394. $form_state['input']['new_id'] != 0) {
  395. $new_id = $form_state['input']['new_id'];
  396. $new_value = $form_state['input']['new_value'];
  397. // get the rank by counting the number of entries
  398. $rank = count($ranks[$new_id]);
  399. // get this new_id information
  400. $cvterm = tripal_core_chado_select('cvterm', array('name', 'definition'), array('cvterm_id' => $new_id));
  401. // add it to the $ranks array
  402. $ranks[$new_id][$rank]['name'] = $cvterm[0]->name;
  403. $ranks[$new_id][$rank]['id'] = $new_id;
  404. $ranks[$new_id][$rank]['value'] = $value;
  405. $ranks[$new_id][$rank]['definition'] = $cvterm[0]->definition;
  406. $num_properties++;
  407. // determine how many rows we need in the textarea
  408. $rows = 1;
  409. // add the new fields
  410. $form['properties']['table']['new'][$new_id][$rank]["new_id-$new_id-$rank"] = array(
  411. '#markup' => $cvterm[0]->name
  412. );
  413. $form['properties']['table']['new'][$new_id][$rank]["new_value-$new_id-$rank"] = array(
  414. '#type' => 'textarea',
  415. '#default_value' => $new_value,
  416. '#cols' => 50,
  417. '#rows' => $rows,
  418. '#description' => $cvterm[0]->definition,
  419. );
  420. $form['properties']['table']['new'][$new_id][$rank]["remove-$new_id-$rank"] = array(
  421. '#type' => 'button',
  422. '#value' => t('Remove'),
  423. '#name' => "remove-$new_id-$rank",
  424. '#ajax' => array(
  425. 'callback' => "tripal_analysis_property_ajax_update",
  426. 'wrapper' => 'tripal-analysis-edit-properties-table',
  427. 'effect' => 'fade',
  428. 'event' => 'mousedown',
  429. 'method' => 'replace',
  430. 'prevent' => 'click'
  431. ),
  432. // When this button is clicked, the form will be validated and submitted.
  433. // Therefore, we set custom submit and validate functions to override the
  434. // default form submit. In the validate function we set the form_state
  435. // to rebuild the form so the submit function never actually gets called,
  436. // but we need it or Drupal will run the default validate anyway.
  437. // we also set #limit_validation_errors to empty so fields that
  438. // are required that don't have values won't generate warnings.
  439. '#submit' => array('chado_anslysis_node_form_props_button_submit'),
  440. '#validate' => array('chado_anslysis_node_form_props_button_validate'),
  441. '#limit_validation_errors' => array(),
  442. );
  443. }
  444. return $num_properties;
  445. }
  446. /*
  447. *
  448. */
  449. function chado_analysis_node_form_add_analysisprop_table_props(&$form, $form_state, $analysis_id, &$ranks, &$d_removed) {
  450. // get the properties for this analysis
  451. $num_properties = 0;
  452. if (!$analysis_id) {
  453. return $num_properties;
  454. }
  455. $sql = "
  456. SELECT CVT.cvterm_id, CVT.name, CVT.definition, PP.value, PP.rank
  457. FROM {analysisprop} PP
  458. INNER JOIN {cvterm} CVT on CVT.cvterm_id = PP.type_id
  459. INNER JOIN {cv} CV on CVT.cv_id = CV.cv_id
  460. WHERE PP.analysis_id = :analysis_id and CV.name = 'analysis_property'
  461. ORDER BY CVT.name, PP.rank
  462. ";
  463. $analysis_props = chado_query($sql, array(':analysis_id' => $analysis_id));
  464. while ($prop = $analysis_props->fetchObject()) {
  465. $type_id = $prop->cvterm_id;
  466. $rank = 0;
  467. if(array_key_exists($type_id, $ranks)) {
  468. $rank = count($ranks[$type_id]);
  469. }
  470. // skip any properties that the user requested to delete through a previous
  471. // AHAH callback or through the current AHAH callback
  472. if (array_key_exists("$type_id-$rank", $d_removed)) {
  473. continue;
  474. }
  475. if (array_key_exists('triggering_element', $form_state) and
  476. $form_state['triggering_element']['#name'] == 'remove-' . $type_id . '-' . $rank) {
  477. $d_removed["$type_id-$rank"] = 1;
  478. continue;
  479. }
  480. $ranks[$type_id][$rank]['name'] = $prop->name;
  481. $ranks[$type_id][$rank]['id'] = $type_id;
  482. $ranks[$type_id][$rank]['value'] = $prop->value;
  483. $ranks[$type_id][$rank]['definition'] = $prop->definition;
  484. $num_properties++;
  485. $rows = 1;
  486. $form['properties']['table'][$type_id][$rank]["prop_id-$type_id-$rank"] = array(
  487. '#markup' => $prop->name,
  488. );
  489. $form['properties']['table'][$type_id][$rank]["prop_value-$type_id-$rank"] = array(
  490. '#type' => 'textarea',
  491. '#default_value' => $prop->value,
  492. '#cols' => 50,
  493. '#rows' => $rows,
  494. '#description' => $prop->definition,
  495. );
  496. $form['properties']['table'][$type_id][$rank]["remove-$type_id-$rank"] = array(
  497. '#type' => 'button',
  498. '#value' => t('Remove'),
  499. '#name' => "remove-$type_id-$rank",
  500. '#ajax' => array(
  501. 'callback' => "tripal_analysis_property_ajax_update",
  502. 'wrapper' => 'tripal-analysis-edit-properties-table',
  503. 'effect' => 'fade',
  504. 'event' => 'mousedown',
  505. 'method' => 'replace',
  506. 'prevent' => 'click'
  507. ),
  508. // When this button is clicked, the form will be validated and submitted.
  509. // Therefore, we set custom submit and validate functions to override the
  510. // default form submit. In the validate function we set the form_state
  511. // to rebuild the form so the submit function never actually gets called,
  512. // but we need it or Drupal will run the default validate anyway.
  513. // we also set #limit_validation_errors to empty so fields that
  514. // are required that don't have values won't generate warnings.
  515. '#submit' => array('chado_anslysis_node_form_props_button_submit'),
  516. '#validate' => array('chado_anslysis_node_form_props_button_validate'),
  517. '#limit_validation_errors' => array(),
  518. );
  519. }
  520. return $num_properties;
  521. }
  522. /**
  523. * Validates the user input before creating an analysis node
  524. *
  525. * @ingroup tripal_analysis
  526. */
  527. function chado_analysis_validate($node, &$form_state) {
  528. // use the analysis parent to validate the node
  529. tripal_analysis_validate($node, $form_state);
  530. }
  531. /**
  532. * This validation is being used for three activities:
  533. * CASE A: Update a node that exists in both drupal and chado
  534. * CASE B: Synchronizing a node from chado to drupal
  535. * CASE C: Inserting a new node that exists in niether drupal nor chado
  536. *
  537. * @ingroup tripal_analysis
  538. */
  539. function tripal_analysis_validate($node, &$form_state) {
  540. // remove surrounding white-space on submitted values
  541. $node->analysisname = trim($node->analysisname);
  542. $node->description = trim($node->description);
  543. $node->program = trim($node->program);
  544. $node->programversion = trim($node->programversion);
  545. $node->algorithm = trim($node->algorithm);
  546. $node->sourcename = trim($node->sourcename);
  547. $node->sourceversion = trim($node->sourceversion);
  548. $node->sourceuri = trim($node->sourceuri);
  549. // Only nodes being updated will have an nid already
  550. if (!is_null($node->nid)) {
  551. // CASE A: We are validating a form for updating an existing node
  552. // get the existing node
  553. $values = array('analysis_id' => $node->analysis_id);
  554. $result = tripal_core_chado_select('analysis', array('*'), $values);
  555. $analysis = $result[0];
  556. // if the name has changed make sure it doesn't conflict with an existing name
  557. if ($analysis->name != $node->analysisname) {
  558. $values = array('name' => $node->analysisname);
  559. $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
  560. if ($result and count($result) > 0) {
  561. form_set_error('analysisname', 'Cannot update the analysis with this analysis name. An analysis with this name already exists.');
  562. return;
  563. }
  564. }
  565. // if the unique constraint has changed check to make sure it doesn't conflict with an
  566. // existing record
  567. if ($analysis->program != $node->program or $analysis->programversion != $node->programversion or
  568. $analysis->sourcename != $node->sourcename) {
  569. $values = array(
  570. 'program' => $node->program,
  571. 'programversion' => $node->programversion,
  572. 'sourcename' => $node->sourcename,
  573. );
  574. $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
  575. if ($result and count($result) > 0) {
  576. if ($analysis->program != $node->program) {
  577. $field = 'program';
  578. }
  579. if ($analysis->programversion != $node->programversion) {
  580. $field = 'programversion';
  581. }
  582. if ($analysis->sourcename != $node->sourcename) {
  583. $field = 'sourcename';
  584. }
  585. form_set_error($field, 'Cannot update the analysis with this program,
  586. program version and source name. An analysis with these values already exists.');
  587. return;
  588. }
  589. }
  590. }
  591. else {
  592. // To differentiate if we are syncing or creating a new analysis altogther, see if an
  593. // analysis_id already exists
  594. if (property_exists($node, 'analysis_id') and $node->analysis_id != 0) {
  595. // CASE B: Synchronizing a node from chado to drupal
  596. // we don't need to do anything.
  597. }
  598. else {
  599. // CASE C: We are validating a form for inserting a new node
  600. // The unique constraint for the chado analysis table is: program, programversion, sourcename
  601. $values = array(
  602. 'program' => $node->program,
  603. 'programversion' => $node->programversion,
  604. 'sourcename' => $node->sourcename,
  605. );
  606. $analysis = tripal_core_chado_select('analysis', array('analysis_id'), $values);
  607. if ($analysis and count($analysis) > 0) {
  608. form_set_error('program', 'Cannot add the analysis with this program,
  609. program version and source name. An analysis with these values already exists.');
  610. return;
  611. }
  612. // make sure we have a unique analysis name. This is not a requirement
  613. // for the analysis table but we use the analysis name for the Drupal node
  614. // title, so it should be unique
  615. $values = array('name' => $node->analysisname);
  616. $result = tripal_core_chado_select('analysis', array('analysis_id'), $values);
  617. if ($result and count($result) > 0) {
  618. form_set_error('analysisname', 'Cannot add the analysis with this analysis name. An analysis with this name already exists.');
  619. return;
  620. }
  621. }
  622. }
  623. }
  624. /*
  625. *
  626. */
  627. function tripal_analysis_theme_node_form_properties($form) {
  628. $rows = array();
  629. if (array_key_exists('properties', $form)) {
  630. // first add in the properties derived from the analysisprop table
  631. // the array tree for these properties looks like this:
  632. // $form['properties']['table'][$type_id][$rank]["prop_id-$type_id-$rank"]
  633. foreach ($form['properties']['table'] as $type_id => $elements) {
  634. // there are other fields in the properties array so we only
  635. // want the numeric ones those are our type_id
  636. if (is_numeric($type_id)) {
  637. foreach ($elements as $rank => $element) {
  638. if (is_numeric($rank)) {
  639. $rows[] = array(
  640. drupal_render($element["prop_id-$type_id-$rank"]),
  641. drupal_render($element["prop_value-$type_id-$rank"]),
  642. drupal_render($element["remove-$type_id-$rank"]),
  643. );
  644. }
  645. }
  646. }
  647. }
  648. // second, add in any new properties added by the user through AHAH callbacks
  649. // the array tree for these properties looks like this:
  650. // $form['properties']['table']['new'][$type_id][$rank]["new_id-$new_id-$rank"]
  651. foreach ($form['properties']['table']['new'] as $type_id => $elements) {
  652. if (is_numeric($type_id)) {
  653. foreach ($elements as $rank => $element) {
  654. if (is_numeric($rank)) {
  655. $rows[] = array(
  656. drupal_render($element["new_id-$type_id-$rank"]),
  657. drupal_render($element["new_value-$type_id-$rank"]),
  658. drupal_render($element["remove-$type_id-$rank"]),
  659. );
  660. }
  661. }
  662. }
  663. }
  664. // finally add in a set of blank field for adding a new property
  665. $rows[] = array(
  666. drupal_render($form['properties']['table']['new']['new_id']),
  667. array(
  668. 'data' => drupal_render($form['properties']['table']['new']['new_value']),
  669. 'width' => '60%',
  670. ),
  671. drupal_render($form['properties']['table']['new']['add']),
  672. );
  673. }
  674. $headers = array('Property Type', 'Value', 'Actions');
  675. $table = array(
  676. 'header' => $headers,
  677. 'rows' => $rows,
  678. 'attributes' => array(),
  679. 'sticky' => TRUE,
  680. 'caption' => '',
  681. 'colgroups' => array(),
  682. 'empty' => '',
  683. );
  684. return theme_table($table);
  685. }
  686. /**
  687. * Form AJAX callback for adding a blank property row
  688. *
  689. * We only want to return the properties as that's all we'll replace with this callback
  690. */
  691. function tripal_analysis_property_ajax_update($form, $form_state) {
  692. $properties_html = tripal_analysis_theme_node_form_properties($form);
  693. $form['properties']['table'] = array(
  694. '#markup' => $properties_html,
  695. '#prefix' => '<div id="tripal-analysis-edit-properties-table">',
  696. '#suffix' => '</div>',
  697. );
  698. return $form['properties']['table'];
  699. }
  700. /**
  701. * Form AJAX callback for updating a property description. This
  702. * function only gets called when the property drop down is changed
  703. * on the bottom (empty) row of properties
  704. */
  705. function tripal_analysis_property_get_description($form, $form_state) {
  706. return $form['properties']['table']['new']["new_value"];
  707. }
  708. /**
  709. * We need to theme the analysis form so that the properties fields look good
  710. */
  711. function theme_chado_analysis_form($variables) {
  712. $form = $variables['form'];
  713. $properties_table = tripal_analysis_theme_node_form_properties($form);
  714. $markup = $properties_table;
  715. $form['properties']['table'] = array(
  716. '#markup' => $markup,
  717. '#prefix' => '<div id="tripal-analysis-edit-properties-table">',
  718. '#suffix' => '</div>',
  719. );
  720. $form['buttons']['#weight'] = 50;
  721. return drupal_render_children($form);
  722. }