tripal_feature.chado_node.inc 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. <?php
  2. /**
  3. * @file
  4. * Implementation of hooks to create a feature content type
  5. */
  6. /**
  7. * Implements hook_node_info().
  8. *
  9. * Provide information to drupal about the node types that we're creating
  10. * in this module
  11. *
  12. * @ingroup tripal_legacy_feature
  13. */
  14. function tripal_feature_node_info() {
  15. $nodes = [];
  16. $nodes['chado_feature'] = [
  17. 'name' => t('Feature (Tripal v2 legacy)'),
  18. 'base' => 'chado_feature',
  19. 'description' => t('A feature from the chado database'),
  20. 'has_title' => TRUE,
  21. 'locked' => TRUE,
  22. 'chado_node_api' => [
  23. 'base_table' => 'feature',
  24. 'hook_prefix' => 'chado_feature',
  25. 'record_type_title' => [
  26. 'singular' => t('Feature'),
  27. 'plural' => t('Features'),
  28. ],
  29. 'sync_filters' => [
  30. 'type_id' => TRUE,
  31. 'organism_id' => TRUE,
  32. ],
  33. ],
  34. ];
  35. return $nodes;
  36. }
  37. /**
  38. * Implementation of hook_form().
  39. *
  40. * @ingroup tripal_legacy_feature
  41. */
  42. function chado_feature_form($node, &$form_state) {
  43. $form = [];
  44. // Default values can come in the following ways:
  45. //
  46. // 1) as elements of the $node object. This occurs when editing an existing feature
  47. // 2) in the $form_state['values'] array which occurs on a failed validation or
  48. // ajax callbacks from non submit form elements
  49. // 3) in the $form_state['input'[ array which occurs on ajax callbacks from submit
  50. // form elements and the form is being rebuilt
  51. //
  52. // set form field defaults
  53. $feature = NULL;
  54. $feature_id = NULL;
  55. $uniquename = '';
  56. $fname = '';
  57. $feature_type = '';
  58. $organism_id = '';
  59. $residues = '';
  60. $is_obsolete = '';
  61. $analyses = '';
  62. $references = '';
  63. $synonyms = '';
  64. // if we are editing an existing node then the feature is already part of the node
  65. if (property_exists($node, 'feature')) {
  66. $feature = $node->feature;
  67. $feature = chado_expand_var($feature, 'field', 'feature.residues');
  68. $feature_id = $feature->feature_id;
  69. $uniquename = $feature->uniquename;
  70. $fname = $feature->name;
  71. $feature_type = $feature->type_id->name;
  72. $organism_id = $feature->organism_id->organism_id;
  73. $residues = $feature->residues;
  74. $is_obsolete = $feature->is_obsolete;
  75. // get the synonyms from a previous post
  76. $synonyms = '';
  77. if (property_exists($node, 'synonyms')) {
  78. $synonyms = $node->synonyms;
  79. }
  80. // get synonyms from the database if we don't already have them
  81. if (!$synonyms) {
  82. $options = ['return_array' => 1];
  83. $feature = chado_expand_var($feature, 'table', 'feature_synonym', $options);
  84. $feature_synonyms = (isset($feature->feature_synonym)) ? $feature->feature_synonym : [];
  85. foreach ($feature_synonyms as $index => $synonym) {
  86. $synonyms .= $synonym->synonym_id->name . "\n";
  87. }
  88. }
  89. // keep track of the feature id
  90. $form['feature_id'] = [
  91. '#type' => 'value',
  92. '#value' => $feature_id,
  93. ];
  94. }
  95. // if we are re constructing the form from a failed validation or ajax callback
  96. // then use the $form_state['values'] values
  97. if (array_key_exists('values', $form_state) and isset($form_state['values']['uniquename'])) {
  98. $uniquename = $form_state['values']['uniquename'];
  99. $fname = $form_state['values']['fname'];
  100. $feature_type = $form_state['values']['feature_type'];
  101. $organism_id = $form_state['values']['organism_id'];
  102. $residues = $form_state['values']['residues'];
  103. $is_obsolete = $form_state['values']['is_obsolete'];
  104. $synonyms = $form_state['values']['synonyms'];
  105. }
  106. // if we are re building the form from after submission (from ajax call) then
  107. // the values are in the $form_state['input'] array
  108. if (array_key_exists('input', $form_state) and !empty($form_state['input'])) {
  109. $uniquename = $form_state['input']['uniquename'];
  110. $fname = $form_state['input']['fname'];
  111. $feature_type = $form_state['input']['feature_type'];
  112. $organism_id = $form_state['input']['organism_id'];
  113. $residues = $form_state['input']['residues'];
  114. $is_obsolete = array_key_exists('is_obsolete', $form_state['input']) ? $form_state['input']['is_obsolete'] : FALSE;
  115. $synonyms = $form_state['input']['synonyms'];
  116. }
  117. $form['fname'] = [
  118. '#type' => 'textfield',
  119. '#title' => t('Feature Name'),
  120. '#required' => TRUE,
  121. '#default_value' => $fname,
  122. '#description' => t('Enter the name used by humans to refer to this feature.'),
  123. '#maxlength' => 255,
  124. ];
  125. $form['uniquename'] = [
  126. '#type' => 'textfield',
  127. '#title' => t('Unique Feature Name'),
  128. '#required' => TRUE,
  129. '#default_value' => $uniquename,
  130. '#description' => t('Enter a unique name for this feature. This name must be unique for the organism and feature type.'),
  131. '#maxlength' => 255,
  132. ];
  133. //$type_options = tripal_get_cvterm_default_select_options('feature', 'type_id', 'feature types');
  134. //$type_options[0] = 'Select a Type';
  135. $type_cv = tripal_get_default_cv('feature', 'type_id');
  136. $cv_id = $type_cv->cv_id;
  137. $form['feature_type'] = [
  138. '#title' => t('Feature Type'),
  139. '#type' => 'textfield',
  140. '#description' => t("Choose the feature type."),
  141. '#required' => TRUE,
  142. '#default_value' => $feature_type,
  143. '#autocomplete_path' => "aadmin/tripal/storage/chado/auto_name/cvterm/$cv_id",
  144. ];
  145. // get the list of organisms
  146. $sql = "SELECT * FROM {Organism} ORDER BY genus, species";
  147. $org_rset = chado_query($sql);
  148. $organisms = [];
  149. $organisms[''] = '';
  150. while ($organism = $org_rset->fetchObject()) {
  151. $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
  152. }
  153. $form['organism_id'] = [
  154. '#title' => t('Organism'),
  155. '#type' => t('select'),
  156. '#description' => t("Choose the organism with which this feature is associated"),
  157. '#required' => TRUE,
  158. '#default_value' => $organism_id,
  159. '#options' => $organisms,
  160. ];
  161. // Get synonyms
  162. $syn_text = '';
  163. if ($synonyms) {
  164. if (is_array($synonyms)) {
  165. foreach ($synonyms as $synonym) {
  166. $syn_text .= "$synonym->name\n";
  167. }
  168. }
  169. else {
  170. $syn_text = $synonyms;
  171. }
  172. }
  173. $form['synonyms'] = [
  174. '#type' => 'textarea',
  175. '#title' => t('Synonyms'),
  176. '#required' => FALSE,
  177. '#default_value' => $syn_text,
  178. '#description' => t('Enter alternate names (synonmys) for this feature to help in searching and identification. You may enter as many alternate names as needed each on different lines.'),
  179. ];
  180. $form['residues'] = [
  181. '#type' => 'textarea',
  182. '#title' => t('Residues'),
  183. '#required' => FALSE,
  184. '#default_value' => $residues,
  185. '#description' => t('Enter the nucelotide sequences for this feature'),
  186. ];
  187. $checked = '';
  188. if ($is_obsolete == 't') {
  189. $checked = '1';
  190. }
  191. $form['is_obsolete'] = [
  192. '#type' => 'checkbox',
  193. '#title' => t('Is Obsolete'),
  194. '#required' => FALSE,
  195. '#default_value' => $checked,
  196. '#description' => t('Check this box if this sequence should be retired'),
  197. ];
  198. // PROPERTIES FORM
  199. //---------------------------------------------
  200. $prop_cv = tripal_get_default_cv('featureprop', 'type_id');
  201. $cv_id = $prop_cv ? $prop_cv->cv_id : NULL;
  202. $details = [
  203. 'property_table' => 'featureprop',
  204. // the name of the prop table
  205. 'chado_id' => $feature_id,
  206. // the value of feature_id for this record
  207. 'cv_id' => $cv_id
  208. // the cv.cv_id of the cv governing featureprop.type_id
  209. ];
  210. chado_add_node_form_properties($form, $form_state, $details);
  211. // ADDITIONAL DBXREFS FORM
  212. //---------------------------------------------
  213. $details = [
  214. 'linking_table' => 'feature_dbxref',
  215. // the name of the _dbxref table
  216. 'base_foreign_key' => 'feature_id',
  217. // the name of the key in your base chado table
  218. 'base_key_value' => $feature_id
  219. // the value of feature_id for this record
  220. ];
  221. chado_add_node_form_dbxrefs($form, $form_state, $details);
  222. // RELATIONSHIPS FORM
  223. //---------------------------------------------
  224. $relationship_cv = tripal_get_default_cv('feature_relationship', 'type_id');
  225. $cv_id = $relationship_cv ? $relationship_cv->cv_id : NULL;
  226. $details = [
  227. 'relationship_table' => 'feature_relationship',
  228. 'base_table' => 'feature',
  229. 'base_foreign_key' => 'feature_id',
  230. 'base_key_value' => $feature_id,
  231. 'nodetype' => 'feature',
  232. 'cv_id' => $cv_id,
  233. ];
  234. chado_add_node_form_relationships($form, $form_state, $details);
  235. return $form;
  236. }
  237. /**
  238. * Implementation of hook_validate().
  239. *
  240. * This validation is being used for three activities:
  241. * CASE A: Update a node that exists in both drupal and chado
  242. * CASE B: Synchronizing a node from chado to drupal
  243. * CASE C: Inserting a new node that exists in niether drupal nor chado
  244. *
  245. * @ingroup tripal_legacy_feature
  246. */
  247. function chado_feature_validate($node, $form, &$form_state) {
  248. // We only want to validate when the node is saved.
  249. // Since this validate can be called on AJAX and Deletion of the node
  250. // we need to make this check to ensure queries are not executed
  251. // without the proper values.
  252. if (property_exists($node, "op") and $node->op != 'Save') {
  253. return;
  254. }
  255. // we are syncing if we do not have a node ID but we do have a feature_id. We don't
  256. // need to validate during syncing so just skip it.
  257. if (!property_exists($node, 'nid') and property_exists($node, 'feature_id') and $node->feature_id != 0) {
  258. return;
  259. }
  260. // remove surrounding white-space on submitted values
  261. $node->uniquename = property_exists($node, 'uniquename') ? trim($node->uniquename) : '';
  262. $node->fname = property_exists($node, 'fname') ? trim($node->fname) : '';
  263. $node->feature_type = property_exists($node, 'feature_type') ? trim($node->feature_type) : '';
  264. $node->residues = property_exists($node, 'residues') ? trim($node->residues) : '';
  265. // Validating for an update
  266. if (property_exists($node, 'nid')) {
  267. // make sure the feature type is a real sequence ontology term
  268. $type = tripal_get_cvterm([
  269. 'name' => $node->feature_type,
  270. 'cv_id' => ['name' => 'sequence'],
  271. ]);
  272. if (!$type) {
  273. form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
  274. }
  275. // if this is an update, we want to make sure that a different feature for
  276. // the organism doesn't already have this uniquename. We don't want to give
  277. // two sequences the same uniquename
  278. if (property_exists($node, 'feature_id') and $node->feature_id != 0) {
  279. $sql = "
  280. SELECT *
  281. FROM {feature} F
  282. INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
  283. WHERE
  284. F.uniquename = :uname AND
  285. F.organism_id = :organism_id AND
  286. CVT.name = :cvtname AND
  287. NOT f.feature_id = :feature_id
  288. ";
  289. $args = [
  290. ':uname' => $node->uniquename,
  291. ':organism_id' => $node->organism_id,
  292. ':cvtname' => $node->feature_type,
  293. ':feature_id' => $node->feature_id,
  294. ];
  295. $result = chado_query($sql, $args)->fetchObject();
  296. if ($result) {
  297. form_set_error('uniquename', t("Feature update cannot proceed. The feature name '$node->uniquename' is not unique for this organism. Please provide a unique name for this feature."));
  298. }
  299. }
  300. }
  301. // Validating for an insert
  302. else {
  303. // make sure the feature type is a real sequence ontology term
  304. $type = tripal_get_cvterm([
  305. 'name' => $node->feature_type,
  306. 'cv_id' => ['name' => 'sequence'],
  307. ]);
  308. if (!$type) {
  309. form_set_error('feature_type', t("The feature type is not a valid name from the Sequence Ontology."));
  310. }
  311. // if this is an insert then we just need to make sure this name doesn't
  312. // already exist for this organism if it does then we need to throw an error
  313. $sql = "
  314. SELECT *
  315. FROM {feature} F
  316. INNER JOIN {cvterm} CVT ON F.type_id = CVT.cvterm_id
  317. WHERE
  318. F.uniquename = :name AND
  319. F.organism_id = :organism_id AND
  320. CVT.name = :cvtname
  321. ";
  322. $args = [
  323. ':name' => $node->uniquename,
  324. ':organism_id' => $node->organism_id,
  325. ':cvtname' => $node->feature_type,
  326. ];
  327. $result = chado_query($sql, $args)->fetchObject();
  328. if ($result) {
  329. form_set_error('uniquename', t("Feature insert cannot proceed. The feature name '$node->uniquename' already exists for this organism. Please provide a unique name for this feature."));
  330. }
  331. }
  332. }
  333. /**
  334. * Implement hook_node_access().
  335. *
  336. * This hook allows node modules to limit access to the node types they define.
  337. *
  338. * @param $node
  339. * The node on which the operation is to be performed, or, if it does not yet
  340. * exist, the type of node to be created
  341. *
  342. * @param $op
  343. * The operation to be performed
  344. *
  345. * @param $account
  346. * A user object representing the user for whom the operation is to be
  347. * performed
  348. *
  349. * @return
  350. * If the permission for the specified operation is not set then return FALSE.
  351. * If the permission is set then return NULL as this allows other modules to
  352. * disable access. The only exception is when the $op == 'create'. We will
  353. * always return TRUE if the permission is set.
  354. *
  355. * @ingroup tripal_legacy_feature
  356. */
  357. function tripal_feature_node_access($node, $op, $account) {
  358. $node_type = $node;
  359. if (is_object($node)) {
  360. $node_type = $node->type;
  361. }
  362. if ($node_type == 'chado_feature') {
  363. if ($op == 'create') {
  364. if (!user_access('create chado_feature content', $account)) {
  365. return NODE_ACCESS_DENY;
  366. }
  367. return NODE_ACCESS_ALLOW;
  368. }
  369. if ($op == 'update') {
  370. if (!user_access('edit chado_feature content', $account)) {
  371. return NODE_ACCESS_DENY;
  372. }
  373. }
  374. if ($op == 'delete') {
  375. if (!user_access('delete chado_feature content', $account)) {
  376. return NODE_ACCESS_DENY;
  377. }
  378. }
  379. if ($op == 'view') {
  380. if (!user_access('access chado_feature content', $account)) {
  381. return NODE_ACCESS_DENY;
  382. }
  383. }
  384. }
  385. return NODE_ACCESS_IGNORE;
  386. }
  387. /**
  388. * Implements hook_insert().
  389. *
  390. * When a new chado_feature node is created we also need to add information
  391. * to our chado_feature table. This function is called on insert of a new node
  392. * of type 'chado_feature' and inserts the necessary information.
  393. *
  394. * @ingroup tripal_legacy_feature
  395. */
  396. function chado_feature_insert($node) {
  397. $feature_id = '';
  398. // if there is a feature_id in the $node object then this must be a sync so
  399. // we can skip adding the feature as it is already there, although
  400. // we do need to proceed with insertion into the chado/drupal linking table.
  401. if (!property_exists($node, 'feature_id')) {
  402. $node->uniquename = trim($node->uniquename);
  403. $node->fname = trim($node->fname);
  404. $node->feature_type = trim($node->feature_type);
  405. $node->residues = trim($node->residues);
  406. // remove spaces, newlines from residues
  407. $residues = preg_replace("/[\n\r\s]/", "", $node->residues);
  408. $obsolete = 'FALSE';
  409. if ($node->is_obsolete) {
  410. $obsolete = 'TRUE';
  411. }
  412. // get the feature type id
  413. $values = [
  414. 'cv_id' => [
  415. 'name' => 'sequence',
  416. ],
  417. 'name' => $node->feature_type,
  418. ];
  419. $type = chado_select_record('cvterm', ['cvterm_id'], $values);
  420. $values = [
  421. 'organism_id' => $node->organism_id,
  422. 'name' => $node->fname,
  423. 'uniquename' => $node->uniquename,
  424. 'residues' => $residues,
  425. 'seqlen' => drupal_strlen($residues),
  426. 'is_obsolete' => $obsolete,
  427. 'type_id' => $type[0]->cvterm_id,
  428. 'md5checksum' => md5($residues),
  429. ];
  430. $feature = chado_insert_record('feature', $values);
  431. if (!$feature) {
  432. drupal_set_message(t('Unable to add feature.'), 'warning');
  433. tripal_report_error('tripal_feature', TRIPAL_WARNING, 'Insert feature: Unable to create feature where values: %values',
  434. ['%values' => print_r($values, TRUE)]);
  435. return;
  436. }
  437. $feature_id = $feature['feature_id'];
  438. // add the genbank accession and synonyms
  439. chado_feature_add_synonyms($node->synonyms, $feature_id);
  440. // * Properties Form *
  441. $details = [
  442. 'property_table' => 'featureprop',
  443. // the name of the prop table
  444. 'base_table' => 'feature',
  445. // the name of your chado base table
  446. 'foreignkey_name' => 'feature_id',
  447. // the name of the key in your base table
  448. 'foreignkey_value' => $feature_id
  449. // the value of the feature_id key
  450. ];
  451. chado_update_node_form_properties($node, $details);
  452. // * Additional DBxrefs Form *
  453. $details = [
  454. 'linking_table' => 'feature_dbxref',
  455. // the name of your _dbxref table
  456. 'foreignkey_name' => 'feature_id',
  457. // the name of the key in your base table
  458. 'foreignkey_value' => $feature_id
  459. // the value of the feature_id key
  460. ];
  461. chado_update_node_form_dbxrefs($node, $details);
  462. // * Relationships Form *
  463. $details = [
  464. 'relationship_table' => 'feature_relationship',
  465. 'foreignkey_value' => $feature_id,
  466. ];
  467. chado_update_node_form_relationships($node, $details);
  468. }
  469. else {
  470. $feature_id = $node->feature_id;
  471. }
  472. // Make sure the entry for this feature doesn't already exist in the
  473. // chado_feature table if it doesn't exist then we want to add it.
  474. $check_org_id = chado_get_id_from_nid('feature', $node->nid);
  475. if (!$check_org_id) {
  476. $record = new stdClass();
  477. $record->nid = $node->nid;
  478. $record->vid = $node->vid;
  479. $record->feature_id = $feature_id;
  480. drupal_write_record('chado_feature', $record);
  481. }
  482. }
  483. /**
  484. * Implements hook_update().
  485. *
  486. * @ingroup tripal_legacy_feature
  487. */
  488. function chado_feature_update($node) {
  489. $node->uniquename = trim($node->uniquename);
  490. $node->fname = trim($node->fname);
  491. $node->feature_type = trim($node->feature_type);
  492. $node->residues = trim($node->residues);
  493. $residues = preg_replace("/[\n\r\s]/", "", $node->residues);
  494. $obsolete = 'FALSE';
  495. if ($node->is_obsolete) {
  496. $obsolete = 'TRUE';
  497. }
  498. // get the feature type id
  499. $values = [
  500. 'cv_id' => [
  501. 'name' => 'sequence',
  502. ],
  503. 'name' => $node->feature_type,
  504. ];
  505. $type = chado_select_record('cvterm', ['cvterm_id'], $values);
  506. $feature_id = chado_get_id_from_nid('feature', $node->nid);
  507. if (sizeof($type) > 0) {
  508. $match = [
  509. 'feature_id' => $feature_id,
  510. ];
  511. $values = [
  512. 'organism_id' => $node->organism_id,
  513. 'name' => $node->fname,
  514. 'uniquename' => $node->uniquename,
  515. 'residues' => $residues,
  516. 'seqlen' => drupal_strlen($residues),
  517. 'is_obsolete' => $obsolete,
  518. 'type_id' => $type[0]->cvterm_id,
  519. 'md5checksum' => md5($residues),
  520. ];
  521. $options = ['return_record' => TRUE];
  522. $status = chado_update_record('feature', $match, $values, $options);
  523. // add the genbank synonyms
  524. chado_feature_add_synonyms($node->synonyms, $feature_id);
  525. // * Properties Form *
  526. $details = [
  527. 'property_table' => 'featureprop',
  528. // the name of the prop table
  529. 'base_table' => 'feature',
  530. // the name of your chado base table
  531. 'foreignkey_name' => 'feature_id',
  532. // the name of the key in your base table
  533. 'foreignkey_value' => $feature_id
  534. // the value of the feature_id key
  535. ];
  536. chado_update_node_form_properties($node, $details);
  537. // * Additional DBxrefs Form *
  538. $details = [
  539. 'linking_table' => 'feature_dbxref',
  540. // the name of your _dbxref table
  541. 'foreignkey_name' => 'feature_id',
  542. // the name of the key in your base table
  543. 'foreignkey_value' => $feature_id
  544. // the value of the feature_id key
  545. ];
  546. chado_update_node_form_dbxrefs($node, $details);
  547. // * Relationships Form *
  548. $details = [
  549. 'relationship_table' => 'feature_relationship',
  550. 'foreignkey_value' => $feature_id,
  551. ];
  552. chado_update_node_form_relationships($node, $details);
  553. }
  554. else {
  555. drupal_set_message(t('Unable to update feature.'), 'warning');
  556. tripal_report_error('tripal_feature', TRIPAL_WARNING,
  557. 'Update feature: Unable to update feature where values: %values',
  558. ['%values' => print_r($values, TRUE)]
  559. );
  560. }
  561. }
  562. /**
  563. * Implements hook_delete().
  564. *
  565. * @ingroup tripal_legacy_feature
  566. */
  567. function chado_feature_delete($node) {
  568. $feature_id = chado_get_id_from_nid('feature', $node->nid);
  569. // If we don't have a feature id for this node then this isn't a node of
  570. // type chado_library or the entry in the chado_library table was lost.
  571. if (!$feature_id) {
  572. return;
  573. }
  574. // Remove the drupal content.
  575. $sql_del = "DELETE FROM {chado_feature} WHERE nid = :nid AND vid = :vid";
  576. db_query($sql_del, [':nid' => $node->nid, ':vid' => $node->vid]);
  577. $sql_del = "DELETE FROM {node} WHERE nid = :nid AND vid = :vid";
  578. db_query($sql_del, [':nid' => $node->nid, ':vid' => $node->vid]);
  579. $sql_del = "DELETE FROM {node_revision} WHERE nid = :nid AND vid = :vid";
  580. db_query($sql_del, [':nid' => $node->nid, ':vid' => $node->vid]);
  581. // Remove data from feature tables of chado database. This will
  582. // cause a cascade delete and remove all data in referencing tables
  583. // for this feature. However, we need t specifically delete from the
  584. // featureloc table because the box() PLSQL function calls another
  585. // function that does not reference the 'chado' schema and causes an error
  586. // the chado_query function can handle this problem so we specificall delete
  587. // from that table to prevent the error. The same problem exists for the
  588. // frange.featuregroup table
  589. $previous_db = chado_set_active('chado');
  590. db_query("DELETE FROM frange.featuregroup WHERE subject_id = :feature_id", [':feature_id' => $feature_id]);
  591. db_query("DELETE FROM frange.featuregroup WHERE object_id = :feature_id", [':feature_id' => $feature_id]);
  592. db_query("DELETE FROM frange.featuregroup WHERE group_id = :feature_id", [':feature_id' => $feature_id]);
  593. db_query("DELETE FROM frange.featuregroup WHERE srcfeature_id = :feature_id", [':feature_id' => $feature_id]);
  594. chado_set_active($previous_db);
  595. chado_query("DELETE FROM {featureloc} WHERE feature_id = :feature_id", [':feature_id' => $feature_id]);
  596. chado_query("DELETE FROM {featureloc} WHERE srcfeature_id = :feature_id", [':feature_id' => $feature_id]);
  597. chado_query("DELETE FROM {feature} WHERE feature_id = :feature_id", [':feature_id' => $feature_id]);
  598. drupal_set_message(t("The feature and all associated data were removed"));
  599. }
  600. /**
  601. * Add synonyms to a feature
  602. *
  603. * @param $synonyms
  604. * A string containing synonyms separated by a return character
  605. * @param $feature_id
  606. * The feature to attach the synonyms to
  607. *
  608. * @ingroup tripal_legacy_feature
  609. */
  610. function chado_feature_add_synonyms($synonyms, $feature_id) {
  611. // separate synomys by carriage returns
  612. $synonyms = preg_replace("/[\n\r]+/", " ", $synonyms);
  613. // split the synonyms into an array based on a space as the delimieter
  614. $syn_array = [];
  615. $syn_array = explode(" ", $synonyms);
  616. // remove any old synonyms
  617. $feature_syn_dsql = "DELETE FROM {feature_synonym} WHERE feature_id = :feature_id";
  618. if (!chado_query($feature_syn_dsql, [':feature_id' => $feature_id])) {
  619. tripal_report_error('tripal_feature', TRIPAL_ERROR, "Could not remove synonyms from feature. ", []);
  620. return;
  621. }
  622. // return if we don't have any synonmys to add
  623. if (!$synonyms) {
  624. return;
  625. }
  626. // iterate through each synonym and add it to the database
  627. foreach ($syn_array as $syn) {
  628. // skip this item if it's empty
  629. if (!$syn) {
  630. break;
  631. }
  632. // check to see if we have this accession number already in the database
  633. // if so then don't add it again. it messes up drupal if the insert fails.
  634. // It is possible for the accession number to be present and not the feature
  635. $synonym_sql = "SELECT synonym_id FROM {synonym} WHERE name = :name";
  636. $synonym = chado_query($synonym_sql, [':name' => $syn])->fetchObject();
  637. if (!$synonym) {
  638. $synonym_isql = "
  639. INSERT INTO {synonym} (name, synonym_sgml, type_id)
  640. VALUES (:name, :synonym_sgml,
  641. (SELECT cvterm_id
  642. FROM {cvterm} CVT
  643. INNER JOIN {cv} ON CVT.cv_id = CV.cv_id
  644. WHERE CV.name = 'feature_property' and CVT.name = 'synonym')
  645. )
  646. ";
  647. if (!chado_query($synonym_isql, [
  648. ':name' => $syn,
  649. ':synonym_sgml' => $syn,
  650. ])) {
  651. tripal_report_error('tripal_feature', "Could not add synonym. ", [], TRIPAL_WARNING);
  652. return;
  653. }
  654. // now get the synonym we just added
  655. $synonym_sql = "SELECT synonym_id FROM {synonym} WHERE name = :name";
  656. $synonym = chado_query($synonym_sql, [':name' => $syn])->fetchObject();
  657. }
  658. // now add in our new sysnonym
  659. $feature_syn_isql = "
  660. INSERT INTO {feature_synonym} (synonym_id,feature_id,pub_id)
  661. VALUES (:synonym_id, :feature_id, :pub_id)";
  662. $args = [
  663. ':synonym_id' => $synonym->synonym_id,
  664. ':feature_id' => $feature_id,
  665. ':pub_id' => 1,
  666. ];
  667. if (!chado_query($feature_syn_isql, $args)) {
  668. tripal_report_error('tripal_feature', "Could not associate synonym with feature. ", [], TRIPAL_WARNING);
  669. return;
  670. }
  671. }
  672. }
  673. /**
  674. * Implements hook_load().
  675. *
  676. * When a node is requested by the user this function is called to allow us
  677. * to add auxiliary data to the node object.
  678. *
  679. * @ingroup tripal_legacy_feature
  680. */
  681. function chado_feature_load($nodes) {
  682. foreach ($nodes as $nid => $node) {
  683. // find the feature and add in the details
  684. $feature_id = chado_get_id_from_nid('feature', $nid);
  685. // if the nid does not have a matching record then skip this node.
  686. // this can happen with orphaned nodes.
  687. if (!$feature_id) {
  688. continue;
  689. }
  690. // build the feature variable
  691. $values = ['feature_id' => $feature_id];
  692. $feature = chado_generate_var('feature', $values);
  693. $nodes[$nid]->feature = $feature;
  694. // Now get the title
  695. $node->title = chado_get_node_title($node);
  696. }
  697. }
  698. /**
  699. * Implements hook_node_presave().
  700. * Acts on all content types.
  701. *
  702. * @ingroup tripal_legacy_feature
  703. */
  704. function tripal_feature_node_presave($node) {
  705. // set the title to ensure it is always unique
  706. switch ($node->type) {
  707. // This step is for setting the title for the Drupal node. This title
  708. // is permanent and thus is created to be unique. Title changes provided
  709. // by tokens are generated on the fly dynamically, but the node title
  710. // seen in the content listing needs to be set here. Do not call
  711. // the chado_get_node_title() function here to set the title as the node
  712. // object isn't properly filled out and the function will fail.
  713. case 'chado_feature':
  714. // for a form submission the fields are part of the node object
  715. // but for a sync the fields are in an object of the node
  716. $name = '';
  717. $uname = '';
  718. $type = '';
  719. $organism_id = NULL;
  720. if (property_exists($node, 'uniquename')) {
  721. $organism_id = $node->organism_id;
  722. $name = $node->name;
  723. $uname = $node->uniquename;
  724. $type = $node->feature_type;
  725. }
  726. else {
  727. if (property_exists($node, 'feature')) {
  728. $organism_id = $node->feature->organism_id;
  729. $name = $node->feature->name;
  730. $uname = $node->feature->uniquename;
  731. $type = $node->feature->cvtname;
  732. }
  733. }
  734. $values = ['organism_id' => $organism_id];
  735. $organism = chado_select_record('organism', [
  736. 'genus',
  737. 'species',
  738. ], $values);
  739. $node->title = "$name, $uname ($type) " . $organism[0]->genus . ' ' . $organism[0]->species;
  740. break;
  741. }
  742. }
  743. /**
  744. * Implements hook_node_insert().
  745. * Acts on all content types.
  746. *
  747. * @ingroup tripal_legacy_feature
  748. */
  749. function tripal_feature_node_insert($node) {
  750. // set the URL path after inserting. We do it here because we do not
  751. // know the feature_id in the presave
  752. switch ($node->type) {
  753. case 'chado_feature':
  754. // We still don't have a fully loaded node object in this hook. Therefore,
  755. // we need to simulate one so that the right values are available for
  756. // the URL to be determined.
  757. $feature_id = chado_get_id_from_nid('feature', $node->nid);
  758. $node->feature = chado_generate_var('feature', ['feature_id' => $feature_id]);
  759. // Now use the API to set the path.
  760. chado_set_node_url($node);
  761. // Now get the title.
  762. $node->title = chado_get_node_title($node);
  763. break;
  764. }
  765. }
  766. /**
  767. * Implements hook_node_update().
  768. * Acts on all content types.
  769. *
  770. * @ingroup tripal_legacy_feature
  771. */
  772. function tripal_feature_node_update($node) {
  773. // add items to other nodes, build index and search results
  774. switch ($node->type) {
  775. case 'chado_feature':
  776. // Now use the API to set the path.
  777. chado_set_node_url($node);
  778. // Now get the title
  779. $node->title = chado_get_node_title($node);
  780. break;
  781. }
  782. }
  783. /**
  784. * Implements hook_node_view().
  785. * Acts on all content types.
  786. *
  787. * @ingroup tripal_legacy_feature
  788. */
  789. function tripal_feature_node_view($node, $view_mode, $langcode) {
  790. switch ($node->type) {
  791. case 'chado_feature':
  792. // Show feature browser and counts
  793. if ($view_mode == 'full') {
  794. $node->content['tripal_feature_alignments'] = [
  795. '#theme' => 'tripal_feature_alignments',
  796. '#node' => $node,
  797. '#tripal_toc_id' => 'alignments',
  798. '#tripal_toc_title' => 'Alignments',
  799. ];
  800. $node->content['tripal_feature_analyses'] = [
  801. '#theme' => 'tripal_feature_analyses',
  802. '#node' => $node,
  803. '#tripal_toc_id' => 'analyses',
  804. '#tripal_toc_title' => 'Analyses',
  805. ];
  806. $node->content['tripal_feature_base'] = [
  807. '#theme' => 'tripal_feature_base',
  808. '#node' => $node,
  809. '#tripal_toc_id' => 'base',
  810. '#tripal_toc_title' => 'Overview',
  811. '#weight' => -100,
  812. ];
  813. $node->content['tripal_feature_properties'] = [
  814. '#theme' => 'tripal_feature_properties',
  815. '#node' => $node,
  816. '#tripal_toc_id' => 'properties',
  817. '#tripal_toc_title' => 'Properties',
  818. ];
  819. $node->content['tripal_feature_publications'] = [
  820. '#theme' => 'tripal_feature_publications',
  821. '#node' => $node,
  822. '#tripal_toc_id' => 'publications',
  823. '#tripal_toc_title' => 'Publications',
  824. ];
  825. $node->content['tripal_feature_references'] = [
  826. '#theme' => 'tripal_feature_references',
  827. '#node' => $node,
  828. '#tripal_toc_id' => 'references',
  829. '#tripal_toc_title' => 'Cross References',
  830. ];
  831. $node->content['tripal_feature_relationships'] = [
  832. '#theme' => 'tripal_feature_relationships',
  833. '#node' => $node,
  834. '#tripal_toc_id' => 'relationships',
  835. '#tripal_toc_title' => 'Relationships',
  836. ];
  837. $node->content['tripal_feature_seqence'] = [
  838. '#theme' => 'tripal_feature_sequence',
  839. '#node' => $node,
  840. '#tripal_toc_id' => 'sequences',
  841. '#tripal_toc_title' => 'Sequences',
  842. ];
  843. $node->content['tripal_feature_synonyms'] = [
  844. '#theme' => 'tripal_feature_synonyms',
  845. '#node' => $node,
  846. '#tripal_toc_id' => 'synonyms',
  847. '#tripal_toc_title' => 'Synonyms',
  848. ];
  849. $node->content['tripal_feature_terms'] = [
  850. '#theme' => 'tripal_feature_terms',
  851. '#node' => $node,
  852. '#tripal_toc_id' => 'terms',
  853. '#tripal_toc_title' => 'Annotated Terms',
  854. ];
  855. }
  856. if ($view_mode == 'teaser') {
  857. $node->content['tripal_feature_teaser'] = [
  858. '#theme' => 'tripal_feature_teaser',
  859. '#node' => $node,
  860. ];
  861. }
  862. break;
  863. case 'chado_organism':
  864. // Show feature browser and counts
  865. if ($view_mode == 'full') {
  866. $node->content['tripal_organism_feature_counts'] = [
  867. '#theme' => 'tripal_organism_feature_counts',
  868. '#node' => $node,
  869. '#tripal_toc_id' => 'feature_counts',
  870. '#tripal_toc_title' => 'Feature Summary',
  871. ];
  872. $node->content['tripal_organism_feature_browser'] = [
  873. '#theme' => 'tripal_organism_feature_browser',
  874. '#node' => $node,
  875. '#tripal_toc_id' => 'feature_browser',
  876. '#tripal_toc_title' => 'Feature Browser',
  877. ];
  878. }
  879. break;
  880. // TODO: handle these node types. Should we also have a feature browser?
  881. case 'chado_library':
  882. break;
  883. case 'chado_stock':
  884. break;
  885. case 'chado_analysis':
  886. break;
  887. }
  888. }
  889. /**
  890. * Implements [content_type]_chado_node_default_title_format().
  891. *
  892. * Defines a default title format for the Chado Node API to set the titles on
  893. * Chado Feature nodes based on chado fields.
  894. */
  895. function chado_feature_chado_node_default_title_format() {
  896. return '[feature.name], [feature.uniquename] ([feature.type_id>cvterm.name]) [feature.organism_id>organism.genus] [feature.organism_id>organism.species]';
  897. }
  898. /**
  899. * Implements hook_chado_node_default_url_format().
  900. *
  901. * Designates a default URL format for feature nodes.
  902. */
  903. function chado_feature_chado_node_default_url_format() {
  904. return '/feature/[feature.organism_id>organism.genus]/[feature.organism_id>organism.species]/[feature.type_id>cvterm.name]/[feature.uniquename]';
  905. }