tripal_stock.module 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256
  1. <?php
  2. /**
  3. * @file
  4. * Implements Tripal Stock Module hooks
  5. */
  6. /**
  7. * @defgroup tripal_stock Stock Module
  8. * @ingroup tripal_modules
  9. * @{
  10. * Provides functions for managing chado stocks including creating details pages for each stock
  11. *
  12. * The Tripal Stock Module provides functionality for adding, editing, deleting and accessing chado
  13. * stocks. The stock module was designed to store information about stock collections in a
  14. * laboratory. What is called a stock could also be called a strain or an accession. There is a lot
  15. * in common between a Drosophila stock and a Saccharomyces strain and an Arabidopsis line. They all
  16. * come from some taxon, have genotypes, physical locations in the lab, some conceivable
  17. * relationship with a publication, some conceivable relationship with a sequence feature (such as a
  18. * transgene), and could be described by some ontology term. For more information about the chado
  19. * Stock Module see the GMOD Wiki Page (http://gmod.org/wiki/Chado_Stock_Module)
  20. * @}
  21. */
  22. require_once("includes/tripal_stock.admin.inc");
  23. require_once("includes/tripal_stock.sync_stocks.inc");
  24. require_once("includes/other_module_api_functions.inc");
  25. require_once("includes/tripal_stock-secondary_tables.inc");
  26. require_once("includes/tripal_stock-properties.inc");
  27. require_once("includes/tripal_stock-relationships.inc");
  28. require_once("includes/tripal_stock-db_references.inc");
  29. require_once("api/tripal_stock.api.inc");
  30. /**
  31. * Implements hook_menu(): Adds menu items for the tripal_stock
  32. *
  33. * @return
  34. * Menu definitions for the tripal_stock
  35. *
  36. * @ingroup tripal_stock
  37. */
  38. function tripal_stock_menu() {
  39. $items = array();
  40. //Administrative settings menu-----------------
  41. $items['admin/tripal/tripal_stock'] = array(
  42. 'title' => 'Stocks',
  43. 'description' => 'Basic Description of Tripal Stock Module Functionality',
  44. 'page callback' => 'theme',
  45. 'page arguments' => array('tripal_stock_help'),
  46. 'access arguments' => array('administer tripal stocks'),
  47. 'type' => MENU_NORMAL_ITEM
  48. );
  49. $items['admin/tripal/tripal_stock/configuration'] = array(
  50. 'title' => 'Configuration',
  51. 'description' => 'Settings for Chado Stocks',
  52. 'page callback' => 'drupal_get_form',
  53. 'page arguments' => array('tripal_stock_help'),
  54. 'access arguments' => array('administer tripal stocks'),
  55. 'type' => MENU_NORMAL_ITEM
  56. );
  57. $items['admin/tripal/tripal_stock/sync'] = array(
  58. 'title' => ' Sync Stocks',
  59. 'description' => 'Sync stocks from Chado with Drupal',
  60. 'page callback' => 'drupal_get_form',
  61. 'page arguments' => array('tripal_stock_sync_form'),
  62. 'access arguments' => array('administer tripal stocks'),
  63. 'type' => MENU_NORMAL_ITEM,
  64. );
  65. // Adding Secondary Properties-----------------
  66. $items['node/%cs_node/properties'] = array(
  67. 'title' => 'Add Properties & Synonyms',
  68. 'description' => 'Settings for Chado Stocks',
  69. 'page callback' => 'tripal_stock_add_ALL_property_page',
  70. 'page arguments' => array(1),
  71. 'access arguments' => array('create chado_stock content'),
  72. 'type' => MENU_CALLBACK
  73. );
  74. $items['node/%cs_node/db_references'] = array(
  75. 'title' => 'Add Database References',
  76. 'description' => 'Settings for Chado Stocks',
  77. 'page callback' => 'tripal_stock_add_ALL_dbreferences_page',
  78. 'page arguments' => array(1),
  79. 'access arguments' => array('create chado_stock content'),
  80. 'type' => MENU_CALLBACK
  81. );
  82. $items['node/%cs_node/relationships'] = array(
  83. 'title' => 'Add Relationships',
  84. 'description' => 'Settings for Chado Stocks',
  85. 'page callback' => 'tripal_stock_add_ALL_relationships_page',
  86. 'page arguments' => array(1),
  87. 'access arguments' => array('create chado_stock content'),
  88. 'type' => MENU_CALLBACK
  89. );
  90. //Edit/Deleting Secondary Properties-------------
  91. $items['node/%cs_node/edit_properties'] = array(
  92. 'title' => 'Edit Properties',
  93. 'description' => 'Settings for Chado Stocks',
  94. 'page callback' => 'tripal_stock_edit_ALL_properties_page',
  95. 'page arguments' => array(1),
  96. 'access arguments' => array('edit chado_stock content'),
  97. 'type' => MENU_LOCAL_TASK,
  98. 'weight' => 8,
  99. );
  100. $items['node/%cs_node/edit_relationships'] = array(
  101. 'title' => 'Edit Relationships',
  102. 'description' => 'Settings for Chado Stocks',
  103. 'page callback' => 'tripal_stock_edit_ALL_relationships_page',
  104. 'page arguments' => array(1),
  105. 'access arguments' => array('edit chado_stock content'),
  106. 'type' => MENU_LOCAL_TASK,
  107. 'weight' => 9,
  108. );
  109. $items['node/%cs_node/edit_db_references'] = array(
  110. 'title' => 'Edit DB References',
  111. 'description' => 'Settings for Chado Stocks',
  112. 'page callback' => 'tripal_stock_edit_ALL_dbreferences_page',
  113. 'page arguments' => array(1),
  114. 'access arguments' => array('edit chado_stock content'),
  115. 'type' => MENU_LOCAL_TASK,
  116. 'weight' => 10,
  117. );
  118. // the menu link for addressing any stock (by name, uniquename, synonym)
  119. $items['stock/%'] = array(
  120. 'page callback' => 'tripal_stock_match_stocks_page',
  121. 'page arguments' => array(1),
  122. 'access arguments' => array('access chado_stock content'),
  123. 'type' => MENU_LOCAL_TASK,
  124. );
  125. return $items;
  126. }
  127. /**
  128. * Implements Menu wildcard_load hook
  129. *
  130. * Purpose: Allows the node ID of a chado stock to be dynamically
  131. * pulled from the path. The node is loaded from this node ID
  132. * and supplied to the page as an arguement. This is an example
  133. * of dynamic argument replacement using wildcards in the path.
  134. *
  135. * @param $nid
  136. * The node ID passed in from the path
  137. *
  138. * @return
  139. * The node object with the passed in nid
  140. *
  141. * @ingroup tripal_stock
  142. */
  143. function cs_node_load($nid) {
  144. if (is_numeric($nid)) {
  145. $node = node_load($nid);
  146. if ($node->type == 'chado_stock') {
  147. return $node;
  148. }
  149. }
  150. return FALSE;
  151. }
  152. /**
  153. * Implementation of hook_perm().
  154. *
  155. * Purpose: Set the permission types that the chado stock module uses
  156. *
  157. * @return
  158. * Listing of the possible permission catagories
  159. *
  160. * @ingroup tripal_stock
  161. */
  162. function tripal_stock_permissions() {
  163. return array(
  164. 'access chado_stock content' => array(
  165. 'title' => t('View Stocks'),
  166. 'description' => t('Allow users to view stock pages.'),
  167. ),
  168. 'create chado_stock content' => array(
  169. 'title' => t('Create Stocks'),
  170. 'description' => t('Allow users to create new stock pages.'),
  171. ),
  172. 'delete chado_stock content' => array(
  173. 'title' => t('Delete Stocks'),
  174. 'description' => t('Allow users to delete stock pages.'),
  175. ),
  176. 'edit chado_stock content' => array(
  177. 'title' => t('Edit Stocks'),
  178. 'description' => t('Allow users to edit stock pages.'),
  179. ),
  180. 'adminster tripal stock' => array(
  181. 'title' => t('Administer Stocks'),
  182. 'description' => t('Allow users to administer all stocks.'),
  183. ),
  184. );
  185. }
  186. /**
  187. * Implement hook_access().
  188. *
  189. * This hook allows node modules to limit access to the node types they define.
  190. *
  191. * @param $node
  192. * The node on which the operation is to be performed, or, if it does not yet exist, the
  193. * type of node to be created
  194. *
  195. * @param $op
  196. * The operation to be performed
  197. *
  198. * @param $account
  199. * A user object representing the user for whom the operation is to be performed
  200. *
  201. * @return
  202. * If the permission for the specified operation is not set then return FALSE. If the
  203. * permission is set then return NULL as this allows other modules to disable
  204. * access. The only exception is when the $op == 'create'. We will always
  205. * return TRUE if the permission is set.
  206. *
  207. * @ingroup tripal_stock
  208. */
  209. function chado_stock_node_access($node, $op, $account) {
  210. if ($op == 'create') {
  211. if (!user_access('create chado_stock content', $account)) {
  212. return FALSE;
  213. }
  214. return TRUE;
  215. }
  216. if ($op == 'update') {
  217. if (!user_access('edit chado_stock content', $account)) {
  218. return FALSE;
  219. }
  220. }
  221. if ($op == 'delete') {
  222. if (!user_access('delete chado_stock content', $account)) {
  223. return FALSE;
  224. }
  225. }
  226. if ($op == 'view') {
  227. if (!user_access('access chado_stock content', $account)) {
  228. return FALSE;
  229. }
  230. }
  231. return NULL;
  232. }
  233. /**
  234. * Implements hook_views_api()
  235. *
  236. * Purpose: Essentially this hook tells drupal that there is views support for
  237. * for this module which then includes tripal_stock.views.inc where all the
  238. * views integration code is
  239. *
  240. * @return
  241. * An array with fields important for views integration
  242. *
  243. * @ingroup tripal_stock
  244. */
  245. function tripal_stock_views_api() {
  246. return array(
  247. 'api' => 2.0,
  248. );
  249. }
  250. /**
  251. * Implements hook_theme(): Register themeing functions for this module
  252. *
  253. * @return
  254. * An array of themeing functions to register
  255. *
  256. * @ingroup tripal_stock
  257. */
  258. function tripal_stock_theme() {
  259. $theme_path = drupal_get_path('module', 'tripal_stock') . '/theme';
  260. $items = array(
  261. // property edit forms function templates
  262. 'tripal_stock_edit_ALL_properties_form' => array(
  263. 'arguments' => array('form'),
  264. 'function' => 'theme_tripal_stock_edit_ALL_properties_form',
  265. ),
  266. 'tripal_stock_edit_ALL_db_references_form' => array(
  267. 'arguments' => array('form'),
  268. 'function' => 'theme_tripal_stock_edit_ALL_db_references_form',
  269. ),
  270. 'tripal_stock_edit_ALL_relationships_form' => array(
  271. 'arguments' => array('form'),
  272. 'function' => 'theme_tripal_stock_edit_ALL_relationships_form',
  273. ),
  274. // tripal_stock templates
  275. 'tripal_stock_base' => array(
  276. 'arguments' => array('node' => NULL),
  277. 'template' => 'tripal_stock_base',
  278. 'path' => "$theme_path/tripal_stock",
  279. ),
  280. 'tripal_stock_properties' => array(
  281. 'arguments' => array('node' => NULL),
  282. 'template' => 'tripal_stock_properties',
  283. 'path' => "$theme_path/tripal_stock",
  284. ),
  285. 'tripal_stock_references' => array(
  286. 'arguments' => array('node' => NULL),
  287. 'template' => 'tripal_stock_references',
  288. 'path' => "$theme_path/tripal_stock",
  289. ),
  290. 'tripal_stock_relationships' => array(
  291. 'arguments' => array('node' => NULL),
  292. 'template' => 'tripal_stock_relationships',
  293. 'path' => "$theme_path/tripal_stock",
  294. ),
  295. 'tripal_stock_synonyms' => array(
  296. 'arguments' => array('node' => NULL),
  297. 'template' => 'tripal_stock_synonyms',
  298. 'path' => "$theme_path/tripal_stock",
  299. ),
  300. 'tripal_stock_collections' => array(
  301. 'arguments' => array('node' => NULL),
  302. 'template' => 'tripal_stock_collections',
  303. 'path' => "$theme_path/tripal_stock",
  304. ),
  305. 'tripal_stock_collections' => array(
  306. 'arguments' => array('node' => NULL),
  307. 'template' => 'tripal_stock_collections',
  308. 'path' => "$theme_path/tripal_stock",
  309. ),
  310. 'tripal_stock_phenotypes' => array(
  311. 'arguments' => array('node' => NULL),
  312. 'template' => 'tripal_stock_phenotypes',
  313. 'path' => "$theme_path/tripal_stock",
  314. ),
  315. 'tripal_stock_locations' => array(
  316. 'arguments' => array('node' => NULL),
  317. 'template' => 'tripal_stock_locations',
  318. 'path' => "$theme_path/tripal_stock",
  319. ),
  320. // tripal_organism templates
  321. 'tripal_organism_stocks' => array(
  322. 'arguments' => array('node' => NULL),
  323. 'template' => 'tripal_organism_stocks',
  324. 'path' => "$theme_path/tripal_organism",
  325. ),
  326. // help template
  327. 'tripal_stock_help' => array(
  328. 'template' => 'tripal_stock_help',
  329. 'arguments' => array(NULL),
  330. 'path' => $theme_path,
  331. ),
  332. );
  333. return $items;
  334. }
  335. /**
  336. * Implements hook_help()
  337. * Purpose: Adds a help page to the module list
  338. */
  339. function tripal_stock_help ($path, $arg) {
  340. if ($path == 'admin/help#tripal_stock') {
  341. return theme('tripal_stock_help', array());
  342. }
  343. }
  344. /**
  345. * Implements hook_node_info(): registers a stock node type
  346. *
  347. * @return
  348. * An array describing various details of the node
  349. *
  350. * @ingroup tripal_stock
  351. */
  352. function tripal_stock_node_info() {
  353. return array(
  354. 'chado_stock' => array(
  355. 'name' => t('Stock'),
  356. 'base' => 'chado_stock',
  357. 'description' => t('A Chado Stock is a collection of material that can be sampled and have experiments performed on it.'),
  358. 'has_title' => TRUE,
  359. 'has_body' => FALSE,
  360. ),
  361. );
  362. }
  363. /**
  364. * Implements hook_load(): Prepares the chado_stock node
  365. *
  366. * @param $node
  367. * The basic node containing all variables common to all nodes
  368. *
  369. * @return
  370. * A stock node containing all the variables from the basic node and all stock specific variables
  371. *
  372. * @ingroup tripal_stock
  373. */
  374. function chado_stock_load($node) {
  375. // get the stock details from chado
  376. $stock_id = chado_get_id_for_node('stock', $node->nid);
  377. // build the variable with all the stock details
  378. $values = array('stock_id' => $stock_id);
  379. $stock = tripal_core_generate_chado_var('stock', $values);
  380. // by default, the titles are saved using the unique constraint. We will
  381. // keep it the same, but remove the duplicate name if the unique name and name
  382. // are identical
  383. $title_type = variable_get('chado_stock_title', 'unique_constraint');
  384. if($title_type == 'unique_constraint') {
  385. if (strcmp($stock->name, $stock->uniquename)==0) {
  386. $node->title = $stock->name . " (" . $stock->type_id->name . ") " . $stock->organism_id->genus . " " . $stock->organism_id->species ;
  387. }
  388. // in previous version of Tripal, the stock title was simply the unique name.
  389. // so, we recreate the title just to be sure all of our stock pages are consistent
  390. else {
  391. $node->title = $stock->name . ", " . $stock->uniquename . " (" . $stock->type_id->name . ") " . $stock->organism_id->genus . " " . $stock->organism_id->species ;
  392. }
  393. }
  394. // set the title to be the stock name or uniquename as configured
  395. if($title_type == 'stock_name') {
  396. $node->title = $stock->name;
  397. }
  398. if($title_type == 'stock_unique_name') {
  399. $node->title = $stock->uniquename;
  400. }
  401. // add this to the node
  402. $additions = new stdClass();
  403. $additions->stock = $stock;
  404. return $additions;
  405. }
  406. /**
  407. * Implements hook_form(): Creates the main Add/Edit/Delete Form for chado stocks
  408. *
  409. * Parts to be added by this form
  410. * name,
  411. * uniquename,
  412. * description,
  413. * type => select from cvterm with key cvterm_id,
  414. * organism => select from available with key organism_id
  415. * main_db_reference => accession, version, description, db_name(select from dropdown)
  416. *
  417. * @param $node
  418. * An empty node object on insert OR the current stock node object on update
  419. * @param $form_state
  420. * The current state of the form
  421. *
  422. * @return
  423. * A description of the form to be rendered by drupal_get_form()
  424. *
  425. * @ingroup tripal_stock
  426. */
  427. function chado_stock_form($node, $form_state) {
  428. // Expand all fields needed
  429. $fields_needed = array('stock.uniquename', 'stock.name', 'stock.stock_id', 'stock.type_id', 'stock.organism_id', 'stock.description', 'stock.dbxref_id', 'dbxref.accession', 'dbxref.description', 'dbxref.db_id', 'db.db_id');
  430. foreach ($fields_needed as $field_name) {
  431. // Check to see if it's excluded and expand it if so
  432. if ($node->expandable_fields) {
  433. if (in_array($field_name, $node->expandable_fields)) {
  434. $node = tripal_core_expand_chado_vars($node, 'field', $field_name);
  435. }
  436. }
  437. }
  438. // This defines the path for the next step in a simulated multipart form
  439. // NOTE: The %node gets replaced with the nid in insert
  440. $form['next_step_path'] = array(
  441. '#type' => 'hidden',
  442. '#value' => 'node/%node/properties'
  443. );
  444. // If you don't want a multipart form set this to false
  445. // Will then do default redirect (to new node) on submit
  446. $form['simulate_multipart'] = array(
  447. '#type' => 'textfield',
  448. '#attributes' => array('style' => "display:none"),
  449. '#default_value' => TRUE
  450. );
  451. if (!isset($node->stock->uniquename)) {
  452. $form['progress'] = array(
  453. '#type' => 'item',
  454. '#value' => tripal_stock_add_chado_properties_progress('main')
  455. );
  456. }
  457. $form['names'] = array(
  458. '#type' => 'fieldset',
  459. '#title' => t('Stock Name')
  460. );
  461. $form['names']['sname'] = array(
  462. '#type' => 'textfield',
  463. '#title' => t('Name'),
  464. '#default_value' => $node->stock->name,
  465. '#required' => TRUE
  466. );
  467. $form['names']['uniquename'] = array(
  468. '#type' => 'textfield',
  469. '#title' => t('Unique Name'),
  470. '#default_value' => $node->stock->uniquename,
  471. '#required' => TRUE
  472. );
  473. $form['names']['stock_id'] = array(
  474. '#type' => 'hidden',
  475. '#value' => $node->stock->stock_id
  476. );
  477. $form['details'] = array(
  478. '#type' => 'fieldset',
  479. '#title' => t('Stock Details')
  480. );
  481. $type_options = tripal_cv_get_cvterm_options( variable_get('chado_stock_types_cv', 'NULL') );
  482. $type_options[0] = 'Select a Type';
  483. if ($node->nid == '') {
  484. $type_default = 0;
  485. }
  486. else {
  487. $type_default = $node->stock->type_id->cvterm_id;
  488. }
  489. $form['details']['type_id'] = array(
  490. '#type' => 'select',
  491. '#title' => t('Type of Stock'),
  492. '#options' => $type_options,
  493. '#default_value' => $type_default,
  494. '#required' => TRUE,
  495. );
  496. // get the list of organisms
  497. $sql = "SELECT * FROM {Organism} ORDER BY genus, species";
  498. $org_rset = chado_query($sql);
  499. $organisms = array();
  500. $organisms[''] = '';
  501. while ($organism = $org_rset->fetchObject()) {
  502. $organisms[$organism->organism_id] = "$organism->genus $organism->species ($organism->common_name)";
  503. }
  504. $form['details']['organism_id'] = array(
  505. '#type' => 'select',
  506. '#title' => t('Source Organism for stock'),
  507. '#default_value' => $node->stock->organism_id->organism_id,
  508. '#options' => $organisms,
  509. '#required' => TRUE
  510. );
  511. $form['details']['stock_description'] = array(
  512. '#type' => 'textarea',
  513. '#title' => t('Notes'),
  514. '#default_value' => $node->stock->description,
  515. '#description' => t('Briefly enter any notes on the above stock. This should not include phenotypes or genotypes.'),
  516. );
  517. $form['database_reference'] = array(
  518. '#type' => 'fieldset',
  519. '#title' => t('Stock Database Reference')
  520. );
  521. $form['database_reference']['accession'] = array(
  522. '#type' => 'textfield',
  523. '#title' => t('Accession'),
  524. '#default_value' => $node->stock->dbxref_id->accession
  525. );
  526. $form['database_reference']['db_description'] = array(
  527. '#type' => 'textarea',
  528. '#title' => t('Description of Database Reference'),
  529. '#default_value' => $node->stock->dbxref_id->description,
  530. '#description' => t('Optionally enter a description about the database accession.')
  531. );
  532. $db_options = tripal_db_get_db_options();
  533. $db_options[0] = 'Select a Database';
  534. if ($node->nid == '') {
  535. $db_default = 0; }
  536. else { $db_default = $node->stock->dbxref_id->db_id->db_id; }
  537. $form['database_reference']['database'] = array(
  538. '#type' => 'select',
  539. '#title' => t('Database'),
  540. '#options' => $db_options,
  541. '#default_value' => $db_default
  542. );
  543. return $form;
  544. }
  545. /**
  546. * Implements hook_validate(): Validate the input from the chado_stock node form
  547. *
  548. * @param $node
  549. * The current node including fields with the form element names and submitted values
  550. * @param $form
  551. * A description of the form to be rendered by drupal_get_form()
  552. *
  553. * @ingroup tripal_stock
  554. */
  555. function chado_stock_validate($node, &$form) {
  556. $int_in_chado_sql = "SELECT count(*) as count FROM {:table} WHERE :column = :value";
  557. $string_in_chado_sql = "SELECT count(*) as count FROM {:table} WHERE :column = :value";
  558. // if this is an update, we want to make sure that a different stock for
  559. // the organism doesn't already have this uniquename. We don't want to give
  560. // two sequences the same uniquename
  561. if ($node->stock_id) {
  562. $sql = "
  563. SELECT *
  564. FROM {stock} S
  565. INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id
  566. WHERE
  567. uniquename = :uname AND organism_id = :organism_id AND
  568. CVT.name = :cvtname AND NOT stock_id = :stock_id
  569. ";
  570. $result = chado_query($sql, array(':uname' => $node->uniquename,
  571. ':organism_id' => $node->organism_id, ':cvtname' => $node->stock_type,
  572. ':stock_id' => $node->stock_id))->fetchObject();
  573. if ($result) {
  574. form_set_error('uniquename', t("Stock update cannot proceed. The stock name '$node->uniquename' is not unique for this organism. Please provide a unique name for this stock."));
  575. }
  576. }
  577. // if this is an insert then we just need to make sure this name doesn't
  578. // already exist for this organism if it does then we need to throw an error
  579. else {
  580. $sql = "
  581. SELECT *
  582. FROM {Stock} S
  583. INNER JOIN {cvterm} CVT ON S.type_id = CVT.cvterm_id
  584. WHERE uniquename = :uname'AND organism_id = :organism_id AND CVT.name = :cvtname";
  585. $result = chado_query($sql, array(':uname' => $node->uniquename,
  586. ':organism_id' => $node->organism_id, ':cvtname' => $node->stock_type))->fetchObject();
  587. if ($result) {
  588. form_set_error('uniquename', t("Stock insert cannot proceed. The stock name '$node->uniquename' already exists for this organism. Please provide a unique name for this stock."));
  589. }
  590. }
  591. // Check Type of Stock is valid cvterm_id in chado ( $form['values']['details']['type_id'] )
  592. if ( $node->type_id == 0) {
  593. form_set_error('type_id', 'Please select a type of stock.');
  594. }
  595. else {
  596. $num_rows = chado_query($int_in_chado_sql, array(':table' => 'cvterm', ':column' => 'cvterm_id', ':value' => $node->type_id))->fetchObject();
  597. if ( $num_rows->count != 1) {
  598. form_set_error('type_id', "The type you selected is not valid. Please choose another one. (CODE:$num_rows)"); }
  599. }
  600. // Check Source Organism is valid organism_id in chado ( $form['values']['details']['organism_id'] )
  601. if ( $node->organism_id == 0) {
  602. form_set_error('organism_id', 'Please select a source organism for this stock');
  603. }
  604. else {
  605. $num_rows = chado_query($int_in_chado_sql, array(':table' => 'organism', ':column' => 'organism_id', ':value' => $node->organism_id))->fetchObject();
  606. if ( $num_rows->count != 1 ) {
  607. form_set_error('organism_id', "The organism you selected is not valid. Please choose another one. (CODE:$num_rows)"); }
  608. }
  609. // Check if Accession also database
  610. if ($node->accession != '') {
  611. if ($node->database == 0) {
  612. // there is an accession but no database selected
  613. form_set_error('database', 'You need to enter both a database and an accession for that database in order to add a database reference.');
  614. }
  615. }
  616. else {
  617. if ($node->database > 0) {
  618. // there is a database selected but no accession
  619. form_set_error('accession', 'You need to enter both a database and an accession for that database in order to add a database reference.');
  620. }
  621. }
  622. // Check database is valid db_id in chado ( $form['values']['database_reference']['database'] )
  623. if ( $node->database > 0) {
  624. $num_rows = chado_query($int_in_chado_sql, array(':table' => 'db', ':column' => 'db_id', ':value' => $node->database))->fetchObject();
  625. if ($num_rows->count != 1) {
  626. form_set_error('database', 'The database you selected is not valid. Please choose another one.'); }
  627. }
  628. }
  629. /**
  630. * Implements hook_insert(): Inserts data from chado_stock_form() into drupal and chado
  631. *
  632. * @param $node
  633. * The current node including fields with the form element names and submitted values
  634. *
  635. * @return
  636. * TRUE if the node was successfully inserted into drupal/chado; FALSE otherwise
  637. *
  638. * @ingroup tripal_stock
  639. */
  640. function chado_stock_insert($node) {
  641. // If the chado stock exists (e.g. this is only a syncing operation)
  642. // then don't create but simply link to node
  643. if ($node->chado_stock_exists) {
  644. if (!empty($node->stock_id)) {
  645. $sql = "INSERT INTO {chado_stock} (nid, vid, stock_id) VALUES (:nid, :vid, :stock_id)";
  646. db_query($sql, array(':nid' => $node->nid, ':vid' => $node->vid, ':stock_id' => $node->stock_id));
  647. }
  648. return $node;
  649. }
  650. // before we can add the stock, we must add the dbxref if one has been
  651. // provided by the user.
  652. $dbxref_status = 0;
  653. if (!empty($node->accession) ) {
  654. if (!empty($node->database) ) {
  655. $values = array(
  656. 'db_id' => $node->database,
  657. 'accession' => $node->accession,
  658. );
  659. if (!tripal_core_chado_select('dbxref', array(dbxref_id), $values)) {
  660. $values['description'] = $node->db_description;
  661. $values['version'] = '1';
  662. $dbxref_status = tripal_core_chado_insert('dbxref', $values);
  663. if (!$dbxref_status) {
  664. drupal_set_message(t('Unable to add database reference to this stock.'), 'warning');
  665. watchdog('tripal_stock',
  666. 'Insert Stock: Unable to create dbxref where values:%values',
  667. array('%values' => print_r($values, TRUE)),
  668. WATCHDOG_WARNING
  669. );
  670. }
  671. }
  672. else {
  673. $dbxref_status = 1;
  674. }
  675. }
  676. }
  677. // create stock including the dbxref
  678. $stock = '';
  679. if ($dbxref_status) {
  680. $values = array(
  681. 'dbxref_id' => array(
  682. 'db_id' => $node->database,
  683. 'accession' => $node->accession
  684. ),
  685. 'organism_id' => $node->organism_id,
  686. 'name' => $node->sname,
  687. 'uniquename' => $node->uniquename,
  688. 'description' => $node->stock_description,
  689. 'type_id' => $node->type_id
  690. );
  691. $stock = tripal_core_chado_insert('stock', $values);
  692. }
  693. // create a stock without a dbxref
  694. else {
  695. $values = array(
  696. 'organism_id' => $node->organism_id,
  697. 'name' => $node->sname,
  698. 'uniquename' => $node->uniquename,
  699. 'description' => $node->stock_description,
  700. 'type_id' => $node->type_id
  701. );
  702. $stock = tripal_core_chado_insert('stock', $values);
  703. }
  704. // if the stock creation was succesful then add the URL and the entry in the
  705. // chado_stock table
  706. if (is_array($stock)) {
  707. // convert the stock into an object
  708. $stock = (object) $stock;
  709. // add the entry to the chado_stock table
  710. $sql = "INSERT INTO {chado_stock} (nid, vid, stock_id) VALUES :nid, :vid, :stock_id)";
  711. db_query($sql, array(':nid' => $node->nid, ':vid' => $node->vid, ':stock_id' => $stock->stock_id));
  712. // Move on to next stage of Stock Creation based on next_stage_path field
  713. if ($node->simulate_multipart) {
  714. $next_stage_path = preg_replace('/%node/', $node->nid, $node->next_step_path);
  715. $_REQUEST['destination'] = $next_stage_path;
  716. }
  717. }
  718. else {
  719. drupal_set_message(t('Error during stock creation.'), 'error');
  720. watchdog('tripal_stock',
  721. 'Insert Stock: Unable to create stock where values:%values',
  722. array('%values' => print_r($values, TRUE)),
  723. WATCHDOG_WARNING
  724. );
  725. return FALSE;
  726. }
  727. }
  728. /**
  729. * Implements hook_update(): Handles Editing/Updating of main stock info
  730. *
  731. * NOTE: Currently just writes over all old data
  732. *
  733. * @param $node
  734. * The current node including fields with the form element names and submitted values
  735. *
  736. * @return
  737. * TRUE if the node was successfully updated in drupal/chado; FALSE otherwise
  738. *
  739. * @ingroup tripal_stock
  740. */
  741. function chado_stock_update($node) {
  742. if ($node->revision) {
  743. // there is no way to handle revisions in Chado but leave
  744. // this here just to make not we've addressed it.
  745. }
  746. //update dbxref
  747. if ($node->database) {
  748. if ($node->accession) {
  749. $dbxref_mode = '';
  750. $stock = tripal_core_chado_select(
  751. 'stock',
  752. array('dbxref_id', 'type_id'),
  753. array('stock_id' => $node->stock_id)
  754. );
  755. if ($stock[0]->dbxref_id) {
  756. $values = array(
  757. 'db_id' => $node->database,
  758. 'accession' => $node->accession,
  759. 'description' => $node->db_description
  760. );
  761. $dbxref_status = tripal_core_chado_update(
  762. 'dbxref',
  763. array('dbxref_id' => $stock[0]->dbxref_id),
  764. $values
  765. );
  766. $dbxref_mode = 'Update';
  767. }
  768. else {
  769. if ($stock[0]->type_id) {
  770. //create the dbxref
  771. //used the type_id as a control to check we have a stock but not a dbxref
  772. $values = array(
  773. 'db_id' => $node->database,
  774. 'accession' => $node->accession,
  775. 'description' => $node->db_description,
  776. 'version' => '1',
  777. );
  778. $dbxref_status = tripal_core_chado_insert(
  779. 'dbxref',
  780. $values
  781. );
  782. $dbxref_mode = 'Create';
  783. }
  784. else {
  785. drupal_set_message(t('Unable to find stock to Update'), 'error');
  786. watchdog(
  787. 'tripal_stock',
  788. 'Stock Update: Unable to find stock to update using values: %values',
  789. array('%values', print_r($values, TRUE)),
  790. WATCHDOG_ERROR
  791. );
  792. return FALSE;
  793. }
  794. }
  795. }
  796. }
  797. if (!$dbxref_status) {
  798. watchdog(
  799. 'tripal_stock',
  800. 'Stock Update: Unable to %mode main stock dbxref with values: %values',
  801. array('%values' => print_r($values, TRUE), '%mode' => $dbxref_mode),
  802. WATCHDOG_WARNING
  803. );
  804. }
  805. //can't change stock id which is all thats stored in drupal thus only update chado
  806. $update_values = array(
  807. 'organism_id' => $node->organism_id,
  808. 'name' => $node->sname,
  809. 'uniquename' => $node->uniquename,
  810. 'description' => $node->stock_description,
  811. 'type_id' => $node->type_id,
  812. );
  813. if ($dbxref_status) {
  814. $update_values['dbxref_id'] = array(
  815. 'db_id' => $node->database,
  816. 'accession' => $node->accession
  817. );
  818. }
  819. $status = tripal_core_chado_update('stock', array('stock_id' => $node->stock_id), $update_values);
  820. if (!$status) {
  821. drupal_set_message(t('Unable to update stock'), 'error');
  822. watchdog(
  823. 'tripal_stock',
  824. 'Stock Update: Unable to update stock using match values: %mvalues and update values: %uvalues',
  825. array('%mvalues' => print_r(array('stock_id' => $node->stock_id), TRUE), '%uvalues' => print_r($update_values, TRUE)),
  826. WATCHDOG_ERROR
  827. );
  828. }
  829. else {
  830. // set the URL for this stock page
  831. $values = array('stock_id' => $node->stock_id);
  832. $stock = tripal_core_chado_select('stock', array('*'), $values);
  833. }
  834. }
  835. /**
  836. * Implements hook_delete(): Handles deleting of chado_stocks
  837. *
  838. * NOTE: Currently deletes data -no undo or record-keeping functionality
  839. *
  840. * @param $node
  841. * The current node including fields with the form element names and submitted values
  842. *
  843. * @return
  844. * TRUE if the node was successfully deleted from drupal/chado; FALSE otherwise
  845. *
  846. * @ingroup tripal_stock
  847. */
  848. function chado_stock_delete($node) {
  849. // Set stock in chado: is_obsolete = TRUE
  850. chado_query("DELETE FROM {stock} WHERE stock_id = :stock_id", array(':stock_id' => $node->stock->stock_id));
  851. //remove drupal node and all revisions
  852. db_query("DELETE FROM {chado_stock} WHERE nid = :nid", array(':nid' => $node->nid));
  853. }
  854. /**
  855. * @ingroup tripal_stock
  856. */
  857. function tripal_stock_block_info() {
  858. $blocks['base']['info'] = t('Tripal Stock Details');
  859. $blocks['base']['cache'] = BLOCK_NO_CACHE;
  860. $blocks['properties']['info'] = t('Tripal Stock Properties');
  861. $blocks['properties']['cache'] = BLOCK_NO_CACHE;
  862. $blocks['references']['info'] = t('Tripal Stock References');
  863. $blocks['references']['cache'] = BLOCK_NO_CACHE;
  864. $blocks['relationships_as_object']['info'] = t('Tripal Stock Relationships');
  865. $blocks['relationships_as_object']['cache'] = BLOCK_NO_CACHE;
  866. $blocks['synonyms']['info'] = t('Tripal Stock Synonyms');
  867. $blocks['synonyms']['cache'] = BLOCK_NO_CACHE;
  868. $blocks['collections']['info'] = t('Tripal Stock Collections');
  869. $blocks['collections']['cache'] = BLOCK_NO_CACHE;
  870. $blocks['phenotypes']['info'] = t('Tripal Stock Phenotypes');
  871. $blocks['phenotypes']['cache'] = BLOCK_NO_CACHE;
  872. $blocks['genotypes']['info'] = t('Tripal Stock Genotypes');
  873. $blocks['genotypes']['cache'] = BLOCK_NO_CACHE;
  874. $blocks['locations']['info'] = t('Tripal Stock Locations');
  875. $blocks['locations']['cache'] = BLOCK_NO_CACHE;
  876. $blocks['orgstocks']['info'] = t('Tripal Organism Stocks');
  877. $blocks['orgstocks']['cache'] = BLOCK_NO_CACHE;
  878. return $blocks;
  879. }
  880. /**
  881. *
  882. * @ingroup tripal_stock
  883. */
  884. function tripal_stock_block_view($delta = '') {
  885. if (user_access('access chado_stock content') and arg(0) == 'node' and is_numeric(arg(1))) {
  886. $nid = arg(1);
  887. $node = node_load($nid);
  888. $block = array();
  889. switch ($delta) {
  890. case 'base':
  891. $block['subject'] = t('Stock Details');
  892. $block['content'] = theme('tripal_stock_base', $node);
  893. break;
  894. case 'properties':
  895. $block['subject'] = t('Properties');
  896. $block['content'] = theme('tripal_stock_properties', $node);
  897. break;
  898. case 'references':
  899. $block['subject'] = t('References');
  900. $block['content'] = theme('tripal_stock_references', $node);
  901. break;
  902. case 'relationships':
  903. $block['subject'] = t('Relationships');
  904. $block['content'] = theme('tripal_stock_relationships', $node);
  905. break;
  906. case 'synonyms':
  907. $block['subject'] = t('Synonyms');
  908. $block['content'] = theme('tripal_stock_synonyms', $node);
  909. break;
  910. case 'collections':
  911. $block['subject'] = t('Stock Collections');
  912. $block['content'] = theme('tripal_stock_collections', $node);
  913. break;
  914. case 'phenotypes':
  915. $block['subject'] = t('Stock Phenotypes');
  916. $block['content'] = theme('tripal_stock_phenotypes', $node);
  917. break;
  918. case 'genotypes':
  919. $block['subject'] = t('Stock Genotypes');
  920. $block['content'] = theme('tripal_stock_genotypes', $node);
  921. break;
  922. case 'locations':
  923. $block['subject'] = t('Stock Locations');
  924. $block['content'] = theme('tripal_stock_locations', $node);
  925. break;
  926. case 'orgstocks':
  927. $block['subject'] = t('Organism Stocks');
  928. $block['content'] = theme('tripal_organism_stocks', $node);
  929. break;
  930. }
  931. return $block;
  932. }
  933. }
  934. /**
  935. *
  936. *
  937. * @ingroup tripal_stock
  938. */
  939. function tripal_stock_preprocess_tripal_stock_relationships(&$variables) {
  940. // we want to provide a new variable that contains the matched stocks.
  941. $stock = $variables['node']->stock;
  942. // normally we would use tripal_core_expand_chado_vars to expand our
  943. // organism object and add in the relationships, however whan a large
  944. // number of relationships are present this significantly slows the
  945. // query, therefore we will manually perform the query
  946. $sql = "
  947. SELECT
  948. S.name, S.uniquename, S.stock_id, CS.nid,
  949. CVT.name as rel_type, CVTs.name as obj_type,
  950. SR.value
  951. FROM {stock_relationship} SR
  952. INNER JOIN {stock} S on SR.object_id = S.stock_id
  953. INNER JOIN {cvterm} CVT on SR.type_id = CVT.cvterm_id
  954. INNER JOIN {cvterm} CVTs on S.type_id = CVTs.cvterm_id
  955. LEFT JOIN public.chado_stock CS on S.stock_id = CS.stock_id
  956. WHERE SR.subject_id = :stock_id
  957. ";
  958. $as_subject = chado_query($sql, array(':stock_id' => $stock->stock_id));
  959. $sql = "
  960. SELECT
  961. S.name, S.uniquename, S.stock_id, CS.nid,
  962. CVT.name as rel_type, CVTs.name as sub_type,
  963. SR.value
  964. FROM {stock_relationship} SR
  965. INNER JOIN {stock} S on SR.subject_id = S.stock_id
  966. INNER JOIN {cvterm} CVT on SR.type_id = CVT.cvterm_id
  967. INNER JOIN {cvterm} CVTs on S.type_id = CVTs.cvterm_id
  968. LEFT JOIN public.chado_stock CS on S.stock_id = CS.stock_id
  969. WHERE SR.object_id = :stock_id
  970. ";
  971. $as_object = chado_query($sql, array(':stock_id' => $stock->stock_id));
  972. // combine both object and subject relationshisp into a single array
  973. $relationships = array();
  974. $relationships['object'] = array();
  975. $relationships['subject'] = array();
  976. // iterate through the object relationships
  977. while ($relationship = $as_object->fetchObject()) {
  978. // get the relationship and child types
  979. $rel_type = t(preg_replace('/_/', " ", $relationship->rel_type));
  980. $sub_type = t(preg_replace('/_/', " ", $relationship->sub_type));
  981. if (!array_key_exists($rel_type, $relationships['object'])) {
  982. $relationships['object'][$rel_type] = array();
  983. }
  984. if (!array_key_exists($sub_type, $relationships['object'][$rel_type])) {
  985. $relationships['object'][$rel_type][$sub_type] = array();
  986. }
  987. $relationships['object'][$rel_type][$sub_type][] = $relationship;
  988. }
  989. // now add in the subject relationships
  990. while ($relationship = $as_subject->fetchObject()) {
  991. // get the relationship and child types
  992. $rel_type = t(preg_replace('/_/', " ", $relationship->rel_type));
  993. $obj_type = t(preg_replace('/_/', " ", $relationship->obj_type));
  994. if (!array_key_exists($rel_type, $relationships['subject'])) {
  995. $relationships['subject'][$rel_type] = array();
  996. }
  997. if (!array_key_exists($obj_type, $relationships['subject'][$rel_type])) {
  998. $relationships['subject'][$rel_type][$obj_type] = array();
  999. }
  1000. $relationships['subject'][$rel_type][$obj_type][] = $relationship;
  1001. }
  1002. $stock->all_relationships = $relationships;
  1003. }
  1004. /**
  1005. * @ingroup tripal_stock
  1006. */
  1007. function tripal_stock_node_presave($node) {
  1008. switch ($node->type) {
  1009. case 'chado_stock':
  1010. $values = array('organism_id' => $node->organism_id);
  1011. $organism = tripal_core_chado_select('organism', array('genus','species'), $values);
  1012. $values = array('cvterm_id' => $node->type_id);
  1013. $cvterm = tripal_core_chado_select('cvterm', array('name'), $values);
  1014. $node->title = $node->sname . ', ' . $node->uniquename . ' (' . $cvterm[0]->name . ') ' . $organism[0]->genus . ' ' . $organism[0]->species;
  1015. break;
  1016. }
  1017. }
  1018. /**
  1019. * @ingroup tripal_stock
  1020. */
  1021. function tripal_stock_node_insert($node) {
  1022. switch ($node->type) {
  1023. case 'chado_stock':
  1024. if (!$node->stock_id) {
  1025. $sql = "SELECT * FROM {chado_stock} WHERE nid = :nid";
  1026. $chado_stock = db_query($sql, array(':nid' => $node->nid))->fetchObject();
  1027. $node->stock_id = $chado_stock->stock_id;
  1028. }
  1029. // remove any previous alias
  1030. db_query("DELETE FROM {url_alias} WHERE src = :src", array(':src' => "node/$node->nid"));
  1031. // set the URL for this stock page
  1032. $url_alias = tripal_stock_get_stock_url($node);
  1033. $path_alias = array("source" => "node/$node->nid", "alias" => $url_alias);
  1034. path_save($path_alias);
  1035. break;
  1036. }
  1037. }
  1038. /**
  1039. * @ingroup tripal_stock
  1040. */
  1041. function tripal_stock_node_update($node) {
  1042. switch ($node->type) {
  1043. case 'chado_stock':
  1044. // remove any previous alias
  1045. db_query("DELETE FROM {url_alias} WHERE src = :src", array(':src' => "node/$node->nid"));
  1046. // set the URL for this stock page
  1047. $url_alias = tripal_stock_get_stock_url($node);
  1048. $path_alias = array("source" => "node/$node->nid", "alias" => $url_alias);
  1049. path_save($path_alias);
  1050. break;
  1051. }
  1052. }
  1053. /**
  1054. * @ingroup tripal_stock
  1055. */
  1056. function tripal_stock_node_view($node, $view_mode, $langcode) {
  1057. switch ($node->type) {
  1058. case 'chado_organism':
  1059. if ($view_mode == 'full') {
  1060. // Show stock if the organism/feature is not at teaser view
  1061. $node->content['tripal_organism_stocks'] = array(
  1062. '#value' => theme('tripal_organism_stocks', $node),
  1063. );
  1064. }
  1065. break;
  1066. }
  1067. }
  1068. /*
  1069. * Uses the value provided in the $id argument to find all stocks that match
  1070. * that ID by name, stockname or synonym. If it matches uniquenly to a single
  1071. * stock it will redirect to that stock page, otherwise, a list of matching
  1072. * stocks is shown.
  1073. */
  1074. function tripal_stock_match_stocks_page($id) {
  1075. // if the URL alias configuration is set such that the URL
  1076. // always begins with 'stock' then we want to use the ID as it is and
  1077. // forward it on. Otherwise, try to find the matching stock.
  1078. $url_alias = variable_get('chado_stock_url_string', '/stock/[genus]/[species]/[type]/[uniquename]');
  1079. if (!$url_alias) {
  1080. $url_alias = '/stock/[genus]/[species]/[type]/[uniquename]';
  1081. }
  1082. $url_alias = preg_replace('/^\//', '', $url_alias); // remove any preceeding forward slash
  1083. if (preg_match('/^stock\//', $url_alias)) {
  1084. drupal_goto($id);
  1085. }
  1086. $sql = "
  1087. SELECT
  1088. S.name, S.uniquename, S.stock_id,
  1089. O.genus, O.species, O.organism_id,
  1090. CVT.cvterm_id, CVT.name as type_name,
  1091. CS.nid
  1092. FROM {stock} S
  1093. INNER JOIN {organism} O on S.organism_id = O.organism_id
  1094. INNER JOIN {cvterm} CVT on CVT.cvterm_id = S.type_id
  1095. INNER JOIN public.chado_stock CS on CS.stock_id = S.stock_id
  1096. WHERE
  1097. S.uniquename = :uname or S.name = :name
  1098. ";
  1099. $results = chado_query($sql, array(':uname' => $id, ':name' => $id));
  1100. $num_matches = 0;
  1101. // iterate through the matches and build the table for showing matches
  1102. $header = array('Uniquename', 'Name', 'Type', 'Species');
  1103. $rows = array();
  1104. $curr_match;
  1105. while ($match = $results->fetchObject()) {
  1106. $curr_match = $match;
  1107. $rows[] = array(
  1108. $match->uniquename,
  1109. "<a href=\"" . url("node/". $match->nid) ."\">" . $match->name . "</a>",
  1110. $match->type_name,
  1111. '<i>' . $match->genus . ' ' . $match->species . '</i>',
  1112. );
  1113. $num_matches++;
  1114. }
  1115. // if we have more than one match then generate the table, otherwise, redirect
  1116. // to the matched stock
  1117. if ($num_matches == 1) {
  1118. drupal_goto("node/" . $curr_match->nid);
  1119. }
  1120. if ($num_matches == 0) {
  1121. return "<p>No stocks matched the given name '$id'</p>";
  1122. }
  1123. $table_attrs = array(
  1124. 'class' => 'tripal-table tripal-table-horz'
  1125. );
  1126. $output = "<p>The following stocks match the name '$id'.</p>";
  1127. $output .= theme_table($header, $rows, $table_attrs, $caption);
  1128. return $output;
  1129. }