tripal.terms.api.inc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. <?php
  2. /**
  3. * @file
  4. * Provides an application programming interface (API) for working with
  5. * controlled vocabulary 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 vocabulary 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 [
  53. 'term_chado_storage' => [
  54. 'label' => t('Chado'),
  55. 'description' => t('Integrates terms stored in the local Chado database with Tripal entities.'),
  56. 'settings' => [],
  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. * appropriate 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. *
  248. * @return
  249. * TRUE if the term was added, FALSE otherwise. If the term already exists
  250. * it will be updated and the return value will be TRUE.
  251. *
  252. * @ingroup tripal_terms_api
  253. */
  254. function hook_vocab_add_term($details) {
  255. // See the tripal_chado_vocab_set_term() function for an example.
  256. }
  257. /**
  258. * Adds a term to the vocabulary storage backend.
  259. *
  260. * Use this function to add new terms dynamically to the vocabulary storage
  261. * backend. If the term already exists no new term is added.
  262. *
  263. * @param $details
  264. * An array with at least the following keys:
  265. * -vocabulary : An associative array with the following keys
  266. * -name: The short name for the vocabulary (e.g. SO, PATO, etc).
  267. * -description: The description of this vocabulary.
  268. * -url: The URL for the vocabulary.
  269. * -accession : The name unique ID of the term.
  270. * -url : The URL for the term.
  271. * -name : The name of the term.
  272. * -definition : The term's description.
  273. *
  274. * @return
  275. * TRUE if the term was added, FALSE otherwise. If the term already exists
  276. * it will be updated and the return value will be TRUE.
  277. *
  278. * @ingroup tripal_terms_api
  279. */
  280. function tripal_add_term($details) {
  281. // TODO: we need some sort of administrative interface that lets the user
  282. // switch to the desired vocabulary type. For now, we'll just use the
  283. // first one in the list.
  284. $stores = module_invoke_all('vocab_storage_info');
  285. if (is_array($stores) and count($stores) > 0) {
  286. $keys = array_keys($stores);
  287. $module = $stores[$keys[0]]['module'];
  288. $function = $module . '_vocab_add_term';
  289. if (function_exists($function)) {
  290. return $function($details);
  291. }
  292. }
  293. }
  294. /**
  295. * Retrieves full information about a vocabulary term.
  296. *
  297. * @param $vocabulary
  298. * The vocabulary of the vocabulary in which the term is found.
  299. * @param $accession
  300. * The unique identifier (accession) for this term.
  301. *
  302. * @return
  303. * An array with at least the following keys:
  304. * - vocabulary : An array containing the following keys:
  305. * - name : The full name of the vocabulary.
  306. * - short_name : The short name abbreviation for the vocabulary.
  307. * - description : A brief description of the vocabulary.
  308. * - url : (optional) A URL for the online resources for the vocabulary.
  309. * - urlprefix : (optional) A URL to which the short_name and term
  310. * accession can be appended to form a complete URL for a term. If the
  311. * prefix does not support appending then the exact location for the
  312. * position of the short_name and the term accession will be
  313. * specified with the {db} and {accession} tags respectively.
  314. * - accession : The name unique ID of the term.
  315. * - url : The URL for the term.
  316. * - name : The name of the term.
  317. * - definition : The term's description.
  318. * any other keys may be added as desired. Returns NULL if the term
  319. * cannot be found.
  320. *
  321. * @ingroup tripal_terms_api
  322. */
  323. function tripal_get_term_details($vocabulary, $accession) {
  324. if (empty($vocabulary) OR empty($accession)) {
  325. tripal_report_error('tripal_term', TRIPAL_ERROR, "Unable to retrieve details for term due to missing vocabulary and/or accession");
  326. return FALSE;
  327. }
  328. // TODO: we need some sort of administrative interface that lets the user
  329. // switch to the desired vocabulary type. For now, we'll just use the
  330. // first one in the list.
  331. $stores = module_invoke_all('vocab_storage_info');
  332. if (is_array($stores) and count($stores) > 0) {
  333. $keys = array_keys($stores);
  334. $module = $stores[$keys[0]]['module'];
  335. $function = $module . '_vocab_get_term';
  336. if (function_exists($function)) {
  337. $term = $function($vocabulary, $accession);
  338. if (!$term) {
  339. tripal_report_error(
  340. 'tripal',
  341. TRIPAL_ERROR,
  342. "Unable to find term for :vocab, :accession using :function.",
  343. [':vocab' => $vocabulary, ':accession' => $accession, ':function' => $function]
  344. );
  345. return FALSE;
  346. }
  347. // If the vocabulary is missing then we have a problem.
  348. if (!array_key_exists('vocabulary', $term)) {
  349. tripal_report_error('tripal_term', TRIPAL_ERROR, "The term is missing a vocabulary entry.");
  350. return FALSE;
  351. }
  352. // Make sure the term has a URL. If it does not, then use the Tripal
  353. // interface as the URL for the term.
  354. $url_missing = FALSE;
  355. if (!$term['url']) {
  356. $url_missing = TRUE;
  357. $term['url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
  358. }
  359. if (!$term['vocabulary']['sw_url']) {
  360. $url_missing = TRUE;
  361. $term['vocabulary']['sw_url'] = url('cv/lookup/' . $term['vocabulary']['short_name'] . '/' . $term['accession'], ['absolute' => TRUE]);
  362. }
  363. // Let the user know that the url is missing.
  364. if ($url_missing) {
  365. // tripal_add_notification(
  366. // "Missing CV term URL",
  367. // t("The controlled vocabulary, %vocab, is missing a URL. Tripal will handle " .
  368. // "this by linking to the cv/lookup page of this site. However, the correct " .
  369. // "should be updated for this site",
  370. // ['%vocab' => $term['vocabulary']['short_name']]),
  371. // 'Controlled Vocabularies',
  372. // NULL,
  373. // 'mising-vocab-' . $term['vocabulary']['short_name']
  374. // );
  375. }
  376. return $term;
  377. }
  378. }
  379. }
  380. /**
  381. * Retrieves the immediate children of the given term.
  382. *
  383. * @param $vocabulary
  384. * The vocabulary of the vocabulary in which the term is found.
  385. * @param $accession
  386. * The unique identifier (accession) for this term.
  387. *
  388. * @return
  389. * Returns an array of terms where each term is compatible with the
  390. * array returned by the tripal_get_term_details() function.
  391. *
  392. * @ingroup tripal_terms_api
  393. */
  394. function tripal_get_vocabulary_root_terms($vocabulary) {
  395. if (empty($vocabulary)) {
  396. tripal_report_error('tripal_term', TRIPAL_ERROR, 'Unable to retrieve details for term due to missing vocabulary.');
  397. }
  398. // TODO: we need some sort of administrative interface that lets the user
  399. // switch to the desired vocabulary type. For now, we'll just use the
  400. // first one in the list.
  401. $stores = module_invoke_all('vocab_storage_info');
  402. if (is_array($stores) and count($stores) > 0) {
  403. $keys = array_keys($stores);
  404. $module = $stores[$keys[0]]['module'];
  405. $function = $module . '_vocab_get_root_terms';
  406. if (function_exists($function)) {
  407. return $function($vocabulary);
  408. }
  409. }
  410. }
  411. /**
  412. * Retrieves the immediate children of the given term.
  413. *
  414. * @param $vocabulary
  415. * The vocabulary of the vocabulary in which the term is found.
  416. * @param $accession
  417. * The unique identifier (accession) for this term.
  418. *
  419. * @return
  420. * Returns an array of terms where each term is compatible with the
  421. * array returned by the tripal_get_term_details() function.
  422. *
  423. * @ingroup tripal_terms_api
  424. */
  425. function tripal_get_term_children($vocabulary, $accession) {
  426. if (empty($vocabulary) OR empty($accession)) {
  427. tripal_report_error('tripal_term', TRIPAL_ERROR, 'Unable to retrieve details for term due to missing vocabulary and/or accession.');
  428. }
  429. $stores = module_invoke_all('vocab_storage_info');
  430. if (is_array($stores) and count($stores) > 0) {
  431. $keys = array_keys($stores);
  432. $module = $stores[$keys[0]]['module'];
  433. $function = $module . '_vocab_get_term_children';
  434. if (function_exists($function)) {
  435. return $function($vocabulary, $accession);
  436. }
  437. }
  438. }
  439. /**
  440. * Retrieves full information about a vocabulary.
  441. *
  442. * Vocabularies are stored in a database backend. Tripal has no requirements
  443. * for how terms are stored. By default, the tripal_chado modules provides
  444. * storage for vocabularies and terms. This function will call the
  445. * hook_vocab_get_term() function for the database backend that is housing the
  446. * vocabularies and allow it to return the details about the term.
  447. *
  448. * @param $vocabulary
  449. * The vocabulary of the vocabulary in which the term is found.
  450. *
  451. * @return
  452. * An array with at least the following keys:
  453. * - name: The full name of the vocabulary.
  454. * - short_name: The short name abbreviation for the vocabulary.
  455. * - description: A brief description of the vocabulary.
  456. * - url: A URL for the online resources for the vocabulary.
  457. * - urlprefix: A URL to which the short_name and term
  458. * accession can be appended to form a complete URL for a term. If the
  459. * prefix does not support appending then the exact location for the
  460. * position of the short_name and the term accession will be
  461. * specified with the {db} and {accession} tags respectively.
  462. * - sw_url: The URL for mapping terms via the semantic web.
  463. * - num_terms: The number of terms loaded in the vocabulary.
  464. *
  465. * @ingroup tripal_terms_api
  466. */
  467. function tripal_get_vocabulary_details($vocabulary) {
  468. // TODO: we need some sort of administrative interface that lets the user
  469. // switch to the desired vocabulary type. For now, we'll just use the
  470. // first one in the list.
  471. $stores = module_invoke_all('vocab_storage_info');
  472. if (is_array($stores) and count($stores) > 0) {
  473. $keys = array_keys($stores);
  474. $module = $stores[$keys[0]]['module'];
  475. $function = $module . '_vocab_get_vocabulary';
  476. if (function_exists($function)) {
  477. return $function($vocabulary);
  478. }
  479. }
  480. }
  481. /**
  482. * Retrieves a paged list of terms from a vocabulary.
  483. *
  484. * @param $vocabulary
  485. * The short name of the vocabulary.
  486. * @param $limit
  487. * The number of results to return.
  488. * @param $element
  489. * The pager element. This is equivalent to the element from the
  490. * pager_default_initialize() function of Drupal.
  491. *
  492. * @ingroup tripal_terms_api
  493. */
  494. function tripal_get_vocabulary_terms($vocabulary, $limit = 25, $element = 0) {
  495. $stores = module_invoke_all('vocab_storage_info');
  496. if (is_array($stores) and count($stores) > 0) {
  497. $keys = array_keys($stores);
  498. $module = $stores[$keys[0]]['module'];
  499. $function = $module . '_vocab_get_terms';
  500. if (function_exists($function)) {
  501. return $function($vocabulary, $limit, $element);
  502. }
  503. }
  504. }
  505. /**
  506. * Retrieves the list of vocabularies that are available on the site.
  507. *
  508. * @return
  509. * An array of vocabularies where each entry in the array is compatible
  510. * with the array returned by the tripal_get_vocabulary_details()
  511. * function.
  512. *
  513. * @ingroup tripal_terms_api
  514. */
  515. function tripal_get_vocabularies() {
  516. $stores = module_invoke_all('vocab_storage_info');
  517. if (is_array($stores) and count($stores) > 0) {
  518. $keys = array_keys($stores);
  519. $module = $stores[$keys[0]]['module'];
  520. $function = $module . '_vocab_get_vocabularies';
  521. if (function_exists($function)) {
  522. return $function();
  523. }
  524. }
  525. }
  526. /**
  527. * Provides a term lookup form.
  528. *
  529. * It may be necessary at times for a form to provide to the user the ability
  530. * to lookup and use a controlled vocabulary term. However, a simple text box
  531. * or auto-lookup is not sufficient because a term name may be identical in
  532. * multiple vocabularies and the user would need to select the proper term.
  533. *
  534. * This function will add the form elements necessary to provide a lookup form.
  535. * The form elements should work with a flat form (no #tree set for the form)
  536. * or with a form in a TripalField.
  537. *
  538. * Use the tripal_get_term_lookup_form_result() function to retreive the
  539. * result in a form submit or validate.
  540. *
  541. * @param $form
  542. * The form (or $widget form).
  543. * @param $form_state
  544. * The form state.
  545. * @param $title
  546. * The title to give to the field set.
  547. * @param $description
  548. * A description for the lookup field element.
  549. * @param $is_required
  550. * Indicates if this form element is required.
  551. * @param $field_name
  552. * The name of the field, if this form is being added to a field widget.
  553. * @param $delta
  554. * The delta value for the field if this form is being added to a field
  555. * widget.
  556. *
  557. * @ingroup tripal_terms_api
  558. */
  559. function tripal_get_term_lookup_form(&$form, &$form_state, $default_name = '',
  560. $title = 'Vocabulary Term', $description = '', $is_required,
  561. $field_name = '', $delta = 0, $callback = '', $wrapper = '', $validate = [],
  562. $weight = 0) {
  563. if (!$callback) {
  564. $callback = 'tripal_get_term_lookup_form_ajax_callback';
  565. }
  566. if (!$wrapper) {
  567. $ajax_wrapper_id = 'tripal-vocab-select-form-' . $delta;
  568. if ($field_name) {
  569. $ajax_wrapper_id = $field_name . '-' . $delta;
  570. }
  571. }
  572. else {
  573. $ajax_wrapper_id = $wrapper;
  574. }
  575. $term_name = $default_name;
  576. if (array_key_exists('values', $form_state) and array_key_exists('term_name' . $delta, $form_state['values'])) {
  577. $term_name = $form_state['values']['term_name' . $delta];
  578. }
  579. if (array_key_exists('input', $form_state) and array_key_exists('term_name' . $delta, $form_state['input'])) {
  580. $term_name = $form_state['input']['term_name' . $delta];
  581. }
  582. if ($field_name and array_key_exists('input', $form_state) and array_key_exists($field_name, $form_state['input'])) {
  583. $term_name = $form_state['input'][$field_name]['und'][$delta]['term_match' . $delta]['term_name' . $delta];
  584. }
  585. if (!$description) {
  586. $description = t('Enter the name of the term that specifies the type. ' .
  587. 'The type must be the name of a term in a controlled vocabulary and ' .
  588. 'the controlled vocabulary should already be loaded into this site.');
  589. }
  590. $form_state['storage'][$ajax_wrapper_id]['term_match_field'] = $field_name;
  591. $form_state['storage'][$ajax_wrapper_id]['term_match_delta'] = $delta;
  592. $form['term_match' . $delta] = [
  593. '#type' => 'fieldset',
  594. '#collapsible' => FALSE,
  595. '#collapsed' => FALSE,
  596. '#title' => t($title),
  597. '#prefix' => '<div id = "' . $ajax_wrapper_id . '">',
  598. '#suffix' => '</div>',
  599. '#weight' => $weight,
  600. ];
  601. $form['term_match' . $delta]['term_name' . $delta] = [
  602. '#title' => t('Type'),
  603. '#type' => 'textfield',
  604. '#description' => $description,
  605. '#required' => $is_required,
  606. '#default_value' => $term_name,
  607. '#autocomplete_path' => "admin/tripal/storage/chado/auto_name/cvterm/",
  608. ];
  609. $form['term_match' . $delta]['select_button' . $delta] = [
  610. '#type' => 'button',
  611. '#value' => t('Lookup Term'),
  612. '#name' => 'select_cvterm_' . $ajax_wrapper_id,
  613. '#validate' => $validate,
  614. '#ajax' => [
  615. 'callback' => $callback,
  616. 'wrapper' => $ajax_wrapper_id,
  617. 'effect' => 'fade',
  618. 'method' => 'replace',
  619. ],
  620. ];
  621. if (empty($validate)) {
  622. $form['term_match' . $delta]['select_button' . $delta]['#limit_validation_errors'] = [];
  623. }
  624. // If the term has been provided by the user then we want to search for
  625. // matching terms in the database and let them select among any matches.
  626. if ($term_name) {
  627. $submit_disabled = TRUE;
  628. $form['term_match' . $delta]['terms_list' . $delta] = [
  629. '#type' => 'fieldset',
  630. '#title' => t('Matching Terms'),
  631. '#description' => t('Please select the best matching term. If the
  632. same term exists in multiple vocabularies you will see more than
  633. one option below.'),
  634. ];
  635. $match = [
  636. 'name' => $term_name,
  637. ];
  638. // TODO: this should not call the chado functions because we're in the
  639. // tripal module.
  640. $terms = chado_generate_var('cvterm', $match, ['return_array' => TRUE]);
  641. if ($terms) {
  642. $terms = chado_expand_var($terms, 'field', 'cvterm.definition');
  643. }
  644. $num_terms = 0;
  645. $selected_term = '';
  646. // Let the user select from any matching terms. Sometimes there may be
  647. // more than one that match.
  648. foreach ($terms as $term) {
  649. // Save the user a click by setting the default value as 1 if there's
  650. // only one matching term.
  651. $checked = FALSE;
  652. $attrs = [];
  653. if ($num_terms == 0 and count($terms) == 1) {
  654. $checked = TRUE;
  655. $attrs = ['checked' => 'checked'];
  656. }
  657. $term_element_name = 'term-' . $term->cvterm_id . '-' . $delta;
  658. $definition = property_exists($term, 'definition') ? $term->definition : '';
  659. $form['term_match' . $delta]['terms_list' . $delta][$term_element_name] = [
  660. '#type' => 'checkbox',
  661. '#title' => $term->name,
  662. '#default_value' => $checked,
  663. '#attributes' => $attrs,
  664. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  665. '<br><b>Term ID: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  666. '<br><b>Definition:</b> ' . $definition,
  667. ];
  668. if (array_key_exists('values', $form_state) and array_key_exists($term_element_name, $form_state['values']) and
  669. $form_state['values'][$term_element_name] == 1) {
  670. $selected_term = $term;
  671. }
  672. $num_terms++;
  673. }
  674. // Next find terms that are synonyms
  675. $match = [
  676. 'synonym' => $term_name,
  677. ];
  678. $termsyn = chado_generate_var('cvtermsynonym', $match, ['return_array' => TRUE]);
  679. // Let the user select from any matching terms. Sometimes there may be
  680. // more than one that match.
  681. foreach ($termsyn as $synonym) {
  682. $term = $synonym->cvterm_id;
  683. // Save the user a click by setting the default value as 1 if there's
  684. // only one matching term.
  685. $checked = FALSE;
  686. $attrs = [];
  687. if ($num_terms == 0 and count($terms) == 1) {
  688. $checked = TRUE;
  689. $attrs = ['checked' => 'checked'];
  690. }
  691. $term_element_name = 'term-' . $term->cvterm_id . '-' . $delta;
  692. $definition = property_exists($term, 'definition') ? $term->definition : '';
  693. $form['term_match' . $delta]['terms_list' . $delta][$term_element_name] = [
  694. '#type' => 'checkbox',
  695. '#title' => $term->name,
  696. '#default_value' => $checked,
  697. '#attributes' => $attrs,
  698. '#description' => '<b>Vocabulary:</b> ' . $term->cv_id->name . ' (' . $term->dbxref_id->db_id->name . ') ' . $term->cv_id->definition .
  699. '<br><b>Term: </b> ' . $term->dbxref_id->db_id->name . ':' . $term->dbxref_id->accession . '. ' .
  700. '<br><b>Definition:</b> ' . $definition .
  701. '<br><b>Synonym:</b> ' . $synonym->synonym,
  702. ];
  703. if (array_key_exists('values', $form_state) and array_key_exists($term_element_name, $form_state['values']) and
  704. $form_state['values'][$term_element_name] == 1) {
  705. $selected_term = $term;
  706. }
  707. $num_terms++;
  708. }
  709. if ($num_terms == 0) {
  710. $form['term_match' . $delta]['terms_list' . $delta]['none' . $delta] = [
  711. '#type' => 'item',
  712. '#markup' => '<i>' . t('There is no term that matches the entered text.') . '</i>',
  713. ];
  714. }
  715. }
  716. }
  717. /**
  718. * Returns the terms selected from the tripal_get_term_lookup_form.
  719. *
  720. * @param $form
  721. * The form (or $widget form).
  722. * @param $form_state
  723. * The form state.
  724. * @param $field_name
  725. * The name of the field, if this form is being added to a field widget.
  726. * @param $delta
  727. * The delta value for the field if this form is being added to a field
  728. * widget.
  729. *
  730. * @return
  731. * An array of term objects for each of the user selected terms.
  732. *
  733. * @ingroup tripal_terms_api
  734. */
  735. function tripal_get_term_lookup_form_result($form, $form_state, $field_name = '', $delta = 0) {
  736. $values = [];
  737. $selected = [];
  738. if ($field_name) {
  739. if (array_key_exists('term_match' . $delta, $form_state['values'][$field_name]['und'][$delta]) and
  740. array_key_exists('terms_list' . $delta, $form_state['values'][$field_name]['und'][$delta]['term_match' . $delta])) {
  741. $values = $form_state['values'][$field_name]['und'][$delta]['term_match' . $delta]['terms_list' . $delta];
  742. }
  743. }
  744. else {
  745. $values = array_key_exists('values', $form_state) ? $form_state['values'] : [];
  746. }
  747. if (is_array($values)) {
  748. foreach ($values as $key => $value) {
  749. $matches = [];
  750. if (preg_match("/^term-(\d+)-$delta$/", $key, $matches) and $values['term-' . $matches[1] . '-' . $delta]) {
  751. $cvterm_id = $matches[1];
  752. $selected[] = chado_generate_var('cvterm', ['cvterm_id' => $cvterm_id]);
  753. }
  754. }
  755. }
  756. return $selected;
  757. }
  758. /**
  759. * Implements an AJAX callback for the tripal_chado_vocab_select_term_form.
  760. *
  761. * @ingroup tripal_terms_api
  762. */
  763. function tripal_get_term_lookup_form_ajax_callback($form, $form_state) {
  764. $ajax_wrapper_id = $form_state['triggering_element']['#ajax']['wrapper'];
  765. $field_name = $form_state['storage'][$ajax_wrapper_id]['term_match_field'];
  766. $delta = $form_state['storage'][$ajax_wrapper_id]['term_match_delta'];
  767. // If this form is in a field then we need to dig a bit deeper to return
  768. // the form elements.
  769. if ($field_name) {
  770. return $form[$field_name]['und'][$delta]['term_match' . $delta];
  771. }
  772. else {
  773. return $form['term_match' . $delta];
  774. }
  775. }