tripal.terms.api.inc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815
  1. <?php
  2. /**
  3. * @file
  4. * Provides an application programming interface (API) for working with
  5. * controlled vocaublary terms.
  6. */
  7. /**
  8. * @defgroup tripal_terms_api CV Terms
  9. * @ingroup tripal_api
  10. * @{
  11. * Tripal provides an application programming interface (API) for working with
  12. * controlled vocaublary terms. Tripal v3 is highly dependent on controlled
  13. * vocabularies for identifying all content types and fields attached to those
  14. * content types. However, Tripal v3 is also database agnostic. Therefore,
  15. * controlled vocabularies can be stored in any database back-end. By default
  16. * the tripal_chado module is used for storing controlled vocabularies. However,
  17. * if someone wanted to store controlled vocabularies in a database other than
  18. * Chado they can do so. These API functions provide a convenient wrapper for
  19. * accession controlled vocabularies no matter where they are stored.
  20. *
  21. * @}
  22. */
  23. /**
  24. * @section
  25. * Vocabulary Hooks.
  26. */
  27. /**
  28. * A hook for specifying information about the data store for vocabularies.
  29. *
  30. * The storage backend for controlled vocabularies has traditionally been
  31. * the Chado CV term tables. However, Tripal v3.0 introduces APIs for supporting
  32. * other backends. Therefore, this function indicates to Tripal which
  33. * data stores are capable of providing support for terms.
  34. *
  35. * @return
  36. * An array describing the storage backends implemented by the module. The
  37. * keys are storage backend names. To avoid name clashes, storage
  38. * backend names should be prefixed with the name of the module that
  39. * exposes them. The values are arrays describing the storage backend,
  40. * with the following key/value pairs:
  41. *
  42. * label: The human-readable name of the storage backend.
  43. * module: The name of the module providing the support for this backend.
  44. * description: A short description for the storage backend.
  45. * settings: An array whose keys are the names of the settings available for
  46. * the storage backend, and whose values are the default values for
  47. * those settings.
  48. *
  49. * @ingroup tripal_terms_api
  50. */
  51. function hook_vocab_storage_info() {
  52. return array(
  53. 'term_chado_storage' => array(
  54. 'label' => t('Chado'),
  55. 'description' => t('Integrates terms stored in the local Chado database with Tripal entities.'),
  56. 'settings' => array(),
  57. ),
  58. );
  59. }
  60. /**
  61. * Provides a form for importing vocabularies and their terms.
  62. *
  63. * Tripal allows for vocabularies to be stored separately from the biological
  64. * data. This hook allows the default term storage backend to provide an
  65. * approprite form for importing ontologies (either in OBO or OWL format).
  66. *
  67. * @param $form
  68. * @param $form_state
  69. *
  70. * @ingroup tripal_terms_api
  71. *
  72. */
  73. function hook_vocab_import_form($form, &$form_state) {
  74. return $form;
  75. }
  76. /**
  77. * Validates the hook_vocab_import_form().
  78. *
  79. * @param $form
  80. * @param $form_state
  81. *
  82. * @ingroup tripal_terms_api
  83. */
  84. function hook_vocab_import_form_validate($form, &$form_state) {
  85. }
  86. /**
  87. * Submits the hook_vocab_import_form().
  88. *
  89. * @param $form
  90. * @param $form_state
  91. *
  92. * @ingroup tripal_terms_api
  93. */
  94. function hook_vocab_import_form_submit($form, &$form_state) {
  95. }
  96. /**
  97. * Hook used by the default term storage backend to provide details for a term.
  98. *
  99. * This hook is called by the tripal_entity module to retrieve information
  100. * about the term from the storage backend. It must return an array with
  101. * a set of keys.
  102. *
  103. * @param $vocabulary
  104. * The vocabulary of the vocabulary in which the term is found.
  105. * @param $accession
  106. * The unique identifier (accession) for this term.
  107. *
  108. * @return
  109. * An array with at least the following keys:
  110. * -vocabulary : An associative array with the following keys:
  111. * -name: The short name for the vocabulary (e.g. SO, PATO, etc).
  112. * -description: The description of this vocabulary.
  113. * -url: The URL for the vocabulary.
  114. * -urlprefix : (optional) A URL to which the short_name and term
  115. * accession can be appended to form a complete URL for a term. If the
  116. * prefix does not support appending then the exact location for the
  117. * position of the short_name and the term accession will be
  118. * specified with the {db} and {accession} tags respectively.
  119. * -accession : The name unique ID of the term.
  120. * -url : The URL for the term.
  121. * -name : The name of the term.
  122. * -definition : The term's description.
  123. * any other keys may be added as desired. Returns NULL if the term
  124. * cannot be found.
  125. *
  126. * @ingroup tripal_terms_api
  127. */
  128. function hook_vocab_get_term($vocabulary, $accession) {
  129. // See the tripal_chado_vocab_get_term() function for an example.
  130. }
  131. /**
  132. * Retrieves a paged list of terms from a vocabulary.
  133. *
  134. * @param $vocabulary
  135. * The short name of the vocabulary.
  136. * @param $limit
  137. * The number of results to return.
  138. * @param $element
  139. * The pager element. This is equivalent to the element from the
  140. * pager_default_initialize() function of Drupal.
  141. *
  142. * @ingroup tripal_terms_api
  143. */
  144. function hook_vocab_get_terms($vocabulary, $limit = 25, $element = 0) {
  145. // See the tripal_chado_vocab_get_terms() function for an example.
  146. }
  147. /**
  148. * Hook used by the default term storage backend to provide children for a term.
  149. *
  150. * This hook is called by the tripal_entity module to retrieve a list of
  151. * children for a term from the storage backend. It must return an array
  152. * of terms where each term contains the same structure as that of the
  153. * hook_vocab_get_term().
  154. *
  155. * @param $vocabulary
  156. * The vocabulary of the vocabulary in which the term is found.
  157. * @param $accession
  158. * The unique identifier (accession) for this term.
  159. *
  160. * @return
  161. * An array of terms where each term contains the same structure as that of
  162. * the hook_vocab_get_term(), or an empty array if no children are present.
  163. *
  164. * @ingroup tripal_terms_api
  165. */
  166. function hook_vocab_get_term_children($vocabulary, $accession) {
  167. // See the tripal_chado_vocab_get_term_children() function for an example.
  168. }
  169. /**
  170. * Hook used by the default term storage backend to provide root terms.
  171. *
  172. * This hook is called by the tripal_entity module to retrieve a list of
  173. * root terms for a given vocabulary from the storage backend. It must return
  174. * an array of terms where each term contains the same structure as that of the
  175. * hook_vocab_get_term().
  176. *
  177. * @param $vocabulary
  178. * The vocabulary of the vocabulary in which the term is found.
  179. *
  180. * @return
  181. * An array of root terms where each term contains the same structure as that
  182. * of the hook_vocab_get_term(), or an empty array if no children are present.
  183. *
  184. * @ingroup tripal_terms_api
  185. */
  186. function hook_vocab_get_root_terms($vocabulary) {
  187. // See the tripal_chado_vocab_get_root_terms() function for an example.
  188. }
  189. /**
  190. * Hook used by the default term storage backend to provide details for a vocab.
  191. *
  192. * This hook is called by the tripal_entity module to retrieve information
  193. * about the vocabulary from the storage backend. It must return an array with
  194. * a set of keys.
  195. *
  196. * @param $vocabulary
  197. * The vocabulary of the vocabulary in which the term is found.
  198. *
  199. * @return
  200. * An array with at least the following keys:
  201. * - name : The full name of the vocabulary.
  202. * - short_name : The short name abbreviation for the vocabulary.
  203. * - description : A brief description of the vocabulary.
  204. * - url : (optional) A URL for the online resources for the vocabulary.
  205. * - urlprefix : (optional) A URL to which the short_name and term
  206. * accession can be appended to form a complete URL for a term. If the
  207. * prefix does not support appending then the exact location for the
  208. * position of the short_name and the term accession will be
  209. * specified with the {db} and {accession} tags respectively.
  210. *
  211. * @ingroup tripal_terms_api
  212. */
  213. function hook_vocab_get_vocabulary($vocabulary) {
  214. // See the tripal_chado_vocab_get_vocabulary() function for an example.
  215. }
  216. /**
  217. * Retrieves the list of vocabularies that are available on the site.
  218. *
  219. * @return
  220. * An array of vocabularies where each entry in the array is compatible
  221. * with the array returned by the tripal_get_vocabulary_details()
  222. * function.
  223. *
  224. * @ingroup tripal_terms_api
  225. */
  226. function hook_vocab_get_vocabularies() {
  227. // See the tripal_chado_vocab_get_vocabularies() function for an example.
  228. }
  229. /**
  230. * Hook used by the default term storage backend to add new terms.
  231. *
  232. * @param $details
  233. * An array with at least the following keys:
  234. * -vocabulary : An associative array with the following keys:
  235. * -name: The short name for the vocabulary (e.g. SO, PATO, etc).
  236. * -description: The description of this vocabulary.
  237. * -url: The URL for the vocabulary.
  238. * -urlprefix: (optional) A URL to which the short_name and term
  239. * accession can be appended to form a complete URL for a term. If the
  240. * prefix does not support appending then the exact location for the
  241. * position of the short_name and the term accession will be
  242. * specified with the {db} and {accession} tags respectively.
  243. * -accession : The name unique ID of the term.
  244. * -url : The URL for the term.
  245. * -name : The name of the term.
  246. * -definition : The term's description.
  247. * @return
  248. * TRUE if the term was added, FALSE otherwise. If the term already exists
  249. * it will be updated and the return value will be TRUE.
  250. *
  251. * @ingroup tripal_terms_api
  252. */
  253. function hook_vocab_add_term($details) {
  254. // See the tripal_chado_vocab_set_term() function for an example.
  255. }
  256. /**
  257. * Adds a term to the vocabulary storage backend.
  258. *
  259. * Use this function to add new terms dynamically to the vocabulary storage
  260. * backend. If the term already exists no new term is added.
  261. *
  262. * @param $details
  263. * An array with at least the following keys:
  264. * -vocabulary : An associative array with the following keys
  265. * -name: The short name for the vocabulary (e.g. SO, PATO, etc).
  266. * -description: The description of this vocabulary.
  267. * -url: The URL for the vocabulary.
  268. * -accession : The name unique ID of the term.
  269. * -url : The URL for the term.
  270. * -name : The name of the term.
  271. * -definition : The term's description.
  272. * @return
  273. * TRUE if the term was added, FALSE otherwise. If the term already exists
  274. * it will be updated and the return value will be TRUE.
  275. *
  276. * @ingroup tripal_terms_api
  277. */
  278. function tripal_add_term($details) {
  279. // TODO: we need some sort of administrative interface that lets the user
  280. // switch to the desired vocabulary type. For now, we'll just use the
  281. // first one in the list.
  282. $stores = module_invoke_all('vocab_storage_info');
  283. if (is_array($stores) and count($stores) > 0) {
  284. $keys = array_keys($stores);
  285. $module = $stores[$keys[0]]['module'];
  286. $function = $module . '_vocab_add_term';
  287. if (function_exists($function)) {
  288. return $function($details);
  289. }
  290. }
  291. }
  292. /**
  293. * Retrieves full information about a vocabulary term.
  294. *
  295. * @param $vocabulary
  296. * The vocabulary of the vocabulary in which the term is found.
  297. * @param $accession
  298. * The unique identifier (accession) for this term.
  299. *
  300. * @return
  301. * An array with at least the following keys:
  302. * - vocabulary : An array containing the following keys:
  303. * - name : The full name of the vocabulary.
  304. * - short_name : The short name abbreviation for the vocabulary.
  305. * - description : A brief description of the vocabulary.
  306. * - url : (optional) A URL for the online resources for the vocabulary.
  307. * - urlprefix : (optional) A URL to which the short_name and term
  308. * accession can be appended to form a complete URL for a term. If the
  309. * prefix does not support appending then the exact location for the
  310. * position of the short_name and the term accession will be
  311. * specified with the {db} and {accession} tags respectively.
  312. * - accession : The name unique ID of the term.
  313. * - url : The URL for the term.
  314. * - name : The name of the term.
  315. * - definition : The term's description.
  316. * any other keys may be added as desired. Returns NULL if the term
  317. * cannot be found.
  318. *
  319. * @ingroup tripal_terms_api
  320. */
  321. function tripal_get_term_details($vocabulary, $accession) {
  322. if (empty($vocabulary) OR empty($accession)) {
  323. tripal_report_error('tripal_term', TRIPAL_ERROR, "Unable to retrieve details for term due to missing vocabulary and/or accession");
  324. return FALSE;
  325. }
  326. // TODO: we need some sort of administrative interface that lets the user
  327. // switch to the desired vocabulary type. For now, we'll just use the
  328. // first one in the list.
  329. $stores = module_invoke_all('vocab_storage_info');
  330. if (is_array($stores) and count($stores) > 0) {
  331. $keys = array_keys($stores);
  332. $module = $stores[$keys[0]]['module'];
  333. $function = $module . '_vocab_get_term';
  334. if (function_exists($function)) {
  335. $term = $function($vocabulary, $accession);
  336. // If the vocabulary is missing then we have a problem.
  337. if (!array_key_exists('vocabulary', $term)) {
  338. tripal_report_error('tripal_term', TRIPAL_ERROR, "The term is missing a vocabulary entry.");
  339. return FALSE;
  340. }
  341. // Make sure the term has a URL. If it does not, then use the Tripal
  342. // interface as the URL for the term.
  343. $url_missing = FALSE;
  344. if (!$term['url']) {
  345. $url_missing = TRUE;
  346. $term['url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
  347. }
  348. if (!$term['vocabulary']['sw_url']) {
  349. $url_missing = TRUE;
  350. $term['vocabulary']['sw_url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
  351. }
  352. // Let the user know that the url is missing.
  353. if ($url_missing) {
  354. // tripal_add_notification(
  355. // "Missing CV term URL",
  356. // t("The controlled vocabulary, %vocab, is missing a URL. Tripal will handle " .
  357. // "this by linking to the cv/lookup page of this site. However, the correct " .
  358. // "should be updated for this site",
  359. // ['%vocab' => $term['vocabulary']['short_name']]),
  360. // 'Controlled Vocabularies',
  361. // NULL,
  362. // 'mising-vocab-' . $term['vocabulary']['short_name']
  363. // );
  364. }
  365. return $term;
  366. }
  367. }
  368. }
  369. /**
  370. * Retrieves the immediate children of the given term.
  371. *
  372. * @param $vocabulary
  373. * The vocabulary of the vocabulary in which the term is found.
  374. * @param $accession
  375. * The unique identifier (accession) for this term.
  376. *
  377. * @return
  378. * Returns an array of terms where each term is compatible with the
  379. * array returned by the tripal_get_term_details() function.
  380. *
  381. * @ingroup tripal_terms_api
  382. */
  383. function tripal_get_vocabulary_root_terms($vocabulary) {
  384. if (empty($vocabulary)) {
  385. tripal_report_error('tripal_term', TRIPAL_ERROR, 'Unable to retrieve details for term due to missing vocabulary.');
  386. }
  387. // TODO: we need some sort of administrative interface that lets the user
  388. // switch to the desired vocabulary type. For now, we'll just use the
  389. // first one in the list.
  390. $stores = module_invoke_all('vocab_storage_info');
  391. if (is_array($stores) and count($stores) > 0) {
  392. $keys = array_keys($stores);
  393. $module = $stores[$keys[0]]['module'];
  394. $function = $module . '_vocab_get_root_terms';
  395. if (function_exists($function)) {
  396. return $function($vocabulary);
  397. }
  398. }
  399. }
  400. /**
  401. * Retrieves the immediate children of the given term.
  402. *
  403. * @param $vocabulary
  404. * The vocabulary of the vocabulary in which the term is found.
  405. * @param $accession
  406. * The unique identifier (accession) for this term.
  407. *
  408. * @return
  409. * Returns an array of terms where each term is compatible with the
  410. * array returned by the tripal_get_term_details() function.
  411. *
  412. * @ingroup tripal_terms_api
  413. */
  414. function tripal_get_term_children($vocabulary, $accession) {
  415. if (empty($vocabulary) OR empty($accession)) {
  416. tripal_report_error('tripal_term', TRIPAL_ERROR, 'Unable to retrieve details for term due to missing vocabulary and/or accession.');
  417. }
  418. $stores = module_invoke_all('vocab_storage_info');
  419. if (is_array($stores) and count($stores) > 0) {
  420. $keys = array_keys($stores);
  421. $module = $stores[$keys[0]]['module'];
  422. $function = $module . '_vocab_get_term_children';
  423. if (function_exists($function)) {
  424. return $function($vocabulary, $accession);
  425. }
  426. }
  427. }
  428. /**
  429. * Retrieves full information about a vocabulary.
  430. *
  431. * Vocabularies are stored in a database backend. Tripal has no requirements
  432. * for how terms are stored. By default, the tripal_chado modules provides
  433. * storage for vocabularies and terms. This function will call the
  434. * hook_vocab_get_term() function for the database backend that is housing the
  435. * vocabularies and allow it to return the details about the term.
  436. *
  437. * @param $vocabulary
  438. * The vocabulary of the vocabulary in which the term is found.
  439. *
  440. * @return
  441. * An array with at least the following keys:
  442. * - name: The full name of the vocabulary.
  443. * - short_name: The short name abbreviation for the vocabulary.
  444. * - description: A brief description of the vocabulary.
  445. * - url: A URL for the online resources for the vocabulary.
  446. * - urlprefix: A URL to which the short_name and term
  447. * accession can be appended to form a complete URL for a term. If the
  448. * prefix does not support appending then the exact location for the
  449. * position of the short_name and the term accession will be
  450. * specified with the {db} and {accession} tags respectively.
  451. * - sw_url: The URL for mapping terms via the semantic web.
  452. * - num_terms: The number of terms loaded in the vocabulary.
  453. *
  454. * @ingroup tripal_terms_api
  455. */
  456. function tripal_get_vocabulary_details($vocabulary) {
  457. // TODO: we need some sort of administrative interface that lets the user
  458. // switch to the desired vocabulary type. For now, we'll just use the
  459. // first one in the list.
  460. $stores = module_invoke_all('vocab_storage_info');
  461. if (is_array($stores) and count($stores) > 0) {
  462. $keys = array_keys($stores);
  463. $module = $stores[$keys[0]]['module'];
  464. $function = $module . '_vocab_get_vocabulary';
  465. if (function_exists($function)) {
  466. return $function($vocabulary);
  467. }
  468. }
  469. }
  470. /**
  471. * Retrieves a paged list of terms from a vocabulary.
  472. *
  473. * @param $vocabulary
  474. * The short name of the vocabulary.
  475. * @param $limit
  476. * The number of results to return.
  477. * @param $element
  478. * The pager element. This is equivalent to the element from the
  479. * pager_default_initialize() function of Drupal.
  480. *
  481. * @ingroup tripal_terms_api
  482. */
  483. function tripal_get_vocabulary_terms($vocabulary, $limit = 25, $element = 0) {
  484. $stores = module_invoke_all('vocab_storage_info');
  485. if (is_array($stores) and count($stores) > 0) {
  486. $keys = array_keys($stores);
  487. $module = $stores[$keys[0]]['module'];
  488. $function = $module . '_vocab_get_terms';
  489. if (function_exists($function)) {
  490. return $function($vocabulary, $limit, $element);
  491. }
  492. }
  493. }
  494. /**
  495. * Retrieves the list of vocabularies that are available on the site.
  496. *
  497. * @return
  498. * An array of vocabularies where each entry in the array is compatible
  499. * with the array returned by the tripal_get_vocabulary_details()
  500. * function.
  501. *
  502. * @ingroup tripal_terms_api
  503. */
  504. function tripal_get_vocabularies() {
  505. $stores = module_invoke_all('vocab_storage_info');
  506. if (is_array($stores) and count($stores) > 0) {
  507. $keys = array_keys($stores);
  508. $module = $stores[$keys[0]]['module'];
  509. $function = $module . '_vocab_get_vocabularies';
  510. if (function_exists($function)) {
  511. return $function();
  512. }
  513. }
  514. }
  515. /**
  516. * Provides a term lookup form.
  517. *
  518. * It may be necessary at times for a form to provide to the user the ability
  519. * to lookup and use a controlled vocabulary term. However, a simple text box
  520. * or auto-lookup is not sufficient because a term name may be identical in
  521. * multiple vocabularies and the user would need to select the proper term.
  522. *
  523. * This function will add the form elements necessary to provide a lookup form.
  524. * The form elements should work with a flat form (no #tree set for the form)
  525. * or with a form in a TripalField.
  526. *
  527. * Use the tripal_get_term_lookup_form_result() function to retreive the
  528. * result in a form submit or validate.
  529. *
  530. * @param $form
  531. * The form (or $widget form).
  532. * @param $form_state
  533. * The form state.
  534. * @param $title
  535. * The title to give to the field set.
  536. * @param $description
  537. * A description for the lookup field element.
  538. * @param $is_required
  539. * Indicates if this form element is required.
  540. * @param $field_name
  541. * The name of the field, if this form is being added to a field widget.
  542. * @param $delta
  543. * The delta value for the field if this form is being added to a field
  544. * widget.
  545. *
  546. * @ingroup tripal_terms_api
  547. */
  548. function tripal_get_term_lookup_form(&$form, &$form_state, $default_name = '',
  549. $title = 'Vocabulary Term', $description = '', $is_required,
  550. $field_name = '', $delta = 0, $callback = '', $wrapper = '', $validate = [],
  551. $weight = 0) {
  552. if (!$callback) {
  553. $callback = 'tripal_get_term_lookup_form_ajax_callback';
  554. }
  555. if (!$wrapper) {
  556. $ajax_wrapper_id = 'tripal-vocab-select-form-' . $delta;
  557. if ($field_name) {
  558. $ajax_wrapper_id = $field_name . '-' . $delta;
  559. }
  560. }
  561. else {
  562. $ajax_wrapper_id = $wrapper;
  563. }
  564. $term_name = $default_name;
  565. if (array_key_exists('values', $form_state) and array_key_exists('term_name' . $delta, $form_state['values'])) {
  566. $term_name = $form_state['values']['term_name' . $delta];
  567. }
  568. if (array_key_exists('input', $form_state) and array_key_exists('term_name' . $delta, $form_state['input'])) {
  569. $term_name = $form_state['input']['term_name' . $delta];
  570. }
  571. if ($field_name and array_key_exists('input', $form_state) and array_key_exists($field_name, $form_state['input'])) {
  572. $term_name = $form_state['input'][$field_name]['und'][$delta]['term_match' . $delta]['term_name' . $delta];
  573. }
  574. if (!$description) {
  575. $description = t('Enter the name of the term that specifies the type. ' .
  576. 'The type must be the name of a term in a controlled vocabulary and ' .
  577. 'the controlled vocabulary should already be loaded into this site.');
  578. }
  579. $form_state['storage'][$ajax_wrapper_id]['term_match_field'] = $field_name;
  580. $form_state['storage'][$ajax_wrapper_id]['term_match_delta'] = $delta;
  581. $form['term_match' . $delta] = array(
  582. '#type' => 'fieldset',
  583. '#collapsible' => FALSE,
  584. '#collapsed' => FALSE,
  585. '#title' => t($title),
  586. '#prefix' => '<div id = "' . $ajax_wrapper_id . '">',
  587. '#suffix' => '</div>',
  588. '#weight' => $weight,
  589. );
  590. $form['term_match' . $delta]['term_name' . $delta] = array(
  591. '#title' => t('Type'),
  592. '#type' => 'textfield',
  593. '#description' => $description,
  594. '#required' => $is_required,
  595. '#default_value' => $term_name,
  596. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
  597. );
  598. $form['term_match' . $delta]['select_button' . $delta] = array(
  599. '#type' => 'button',
  600. '#value' => t('Lookup Term'),
  601. '#name' => 'select_cvterm_' . $ajax_wrapper_id,
  602. '#validate' => $validate,
  603. '#ajax' => array(
  604. 'callback' => $callback,
  605. 'wrapper' => $ajax_wrapper_id,
  606. 'effect' => 'fade',
  607. 'method' => 'replace'
  608. ),
  609. );
  610. if (empty($validate)) {
  611. $form['term_match' . $delta]['select_button' . $delta]['#limit_validation_errors'] = [];
  612. }
  613. // If the term has been provided by the user then we want to search for
  614. // matching terms in the database and let them select among any matches.
  615. if ($term_name) {
  616. $submit_disabled = TRUE;
  617. $form['term_match' . $delta]['terms_list' . $delta] = array(
  618. '#type' => 'fieldset',
  619. '#title' => t('Matching Terms'),
  620. '#description' => t('Please select the best matching term. If the
  621. same term exists in multiple vocabularies you will see more than
  622. one option below.')
  623. );
  624. $match = array(
  625. 'name' => $term_name,
  626. );
  627. // TODO: this should not call the chado functions because we're in the
  628. // tripal module.
  629. $terms = chado_generate_var('cvterm', $match, array('return_array' => TRUE));
  630. if ($terms) {
  631. $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
  632. }
  633. $num_terms = 0;
  634. $selected_term = '';
  635. // Let the user select from any matching terms. Sometimes there may be
  636. // more than one that match.
  637. foreach ($terms as $term) {
  638. // Save the user a click by setting the default value as 1 if there's
  639. // only one matching term.
  640. $checked = FALSE;
  641. $attrs = array();
  642. if ($num_terms == 0 and count($terms) == 1) {
  643. $checked = TRUE;
  644. $attrs = array('checked' => 'checked');
  645. }
  646. $term_element_name = 'term-' . $term->cvterm_id . '-' . $delta;
  647. $definition = property_exists($term, 'definition') ? $term->definition : '';
  648. $form['term_match' . $delta]['terms_list' . $delta][$term_element_name] = array(
  649. '#type' => 'checkbox',
  650. '#title' => $term->name,
  651. '#default_value' => $checked,
  652. '#attributes' => $attrs,
  653. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  654. '<br><b>Term ID: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  655. '<br><b>Definition:</b> ' . $definition,
  656. );
  657. if (array_key_exists('values', $form_state) and array_key_exists($term_element_name, $form_state['values']) and
  658. $form_state['values'][$term_element_name] == 1) {
  659. $selected_term = $term;
  660. }
  661. $num_terms++;
  662. }
  663. // Next find terms that are synonyms
  664. $match = array(
  665. 'synonym' => $term_name,
  666. );
  667. $termsyn = chado_generate_var('cvtermsynonym', $match, array('return_array' => TRUE));
  668. // Let the user select from any matching terms. Sometimes there may be
  669. // more than one that match.
  670. foreach ($termsyn as $synonym) {
  671. $term = $synonym->cvterm_id;
  672. // Save the user a click by setting the default value as 1 if there's
  673. // only one matching term.
  674. $checked = FALSE;
  675. $attrs = array();
  676. if ($num_terms == 0 and count($terms) == 1) {
  677. $checked = TRUE;
  678. $attrs = array('checked' => 'checked');
  679. }
  680. $term_element_name = 'term-' . $term->cvterm_id . '-' . $delta;
  681. $definition = property_exists($term, 'definition') ? $term->definition : '';
  682. $form['term_match' . $delta]['terms_list' . $delta][$term_element_name] = array(
  683. '#type' => 'checkbox',
  684. '#title' => $term->name,
  685. '#default_value' => $checked,
  686. '#attributes' => $attrs,
  687. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  688. '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  689. '<br><b>Definition:</b> ' . $definition .
  690. '<br><b>Synonym:</b> ' . $synonym->synonym,
  691. );
  692. if (array_key_exists('values', $form_state) and array_key_exists($term_element_name, $form_state['values']) and
  693. $form_state['values'][$term_element_name] == 1) {
  694. $selected_term = $term;
  695. }
  696. $num_terms++;
  697. }
  698. if ($num_terms == 0) {
  699. $form['term_match'. $delta]['terms_list'. $delta]['none'. $delta] = array(
  700. '#type' => 'item',
  701. '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>'
  702. );
  703. }
  704. }
  705. }
  706. /**
  707. * Returns the terms selected from the tripal_get_term_lookup_form.
  708. *
  709. * @param $form
  710. * The form (or $widget form).
  711. * @param $form_state
  712. * The form state.
  713. * @param $field_name
  714. * The name of the field, if this form is being added to a field widget.
  715. * @param $delta
  716. * The delta value for the field if this form is being added to a field
  717. * widget.
  718. *
  719. * @return
  720. * An array of term objects for each of the user selected terms.
  721. *
  722. * @ingroup tripal_terms_api
  723. */
  724. function tripal_get_term_lookup_form_result($form, $form_state, $field_name = '', $delta = 0) {
  725. $values = array();
  726. $selected = array();
  727. if ($field_name) {
  728. if (array_key_exists('term_match' . $delta, $form_state['values'][$field_name]['und'][$delta]) and
  729. array_key_exists('terms_list' . $delta, $form_state['values'][$field_name]['und'][$delta]['term_match'. $delta])) {
  730. $values = $form_state['values'][$field_name]['und'][$delta]['term_match'. $delta]['terms_list'. $delta];
  731. }
  732. }
  733. else {
  734. $values = array_key_exists('values', $form_state) ? $form_state['values'] : [];
  735. }
  736. if (is_array($values)) {
  737. foreach ($values as $key => $value) {
  738. $matches = array();
  739. if (preg_match("/^term-(\d+)-$delta$/", $key, $matches) and $values['term-' . $matches[1] . '-' . $delta]) {
  740. $cvterm_id = $matches[1];
  741. $selected[] = chado_generate_var('cvterm', array('cvterm_id' => $cvterm_id));
  742. }
  743. }
  744. }
  745. return $selected;
  746. }
  747. /**
  748. * Implements an AJAX callback for the tripal_chado_vocab_select_term_form.
  749. *
  750. * @ingroup tripal_terms_api
  751. */
  752. function tripal_get_term_lookup_form_ajax_callback($form, $form_state) {
  753. $ajax_wrapper_id = $form_state['triggering_element']['#ajax']['wrapper'];
  754. $field_name = $form_state['storage'][$ajax_wrapper_id]['term_match_field'];
  755. $delta = $form_state['storage'][$ajax_wrapper_id]['term_match_delta'];
  756. // If this form is in a field then we need to dig a bit deeper to return
  757. // the form elements.
  758. if ($field_name) {
  759. return $form[$field_name]['und'][$delta]['term_match'. $delta];
  760. }
  761. else {
  762. return $form['term_match' . $delta];
  763. }
  764. }