tripal_chado_views.api.inc 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209
  1. <?php
  2. /**
  3. * @file
  4. * Provides API functions that support direct integration of Chado tables with
  5. * Drupal Views.
  6. */
  7. /**
  8. * @defgroup tripal_chado_views_api Chado Views Integration
  9. * @ingroup tripal_chado_api
  10. * @{
  11. * Provides API functions that support direct integration of Chado tables with
  12. * Drupal Views. This is different from the entity and field integration that
  13. * Tripal v3 provides. Here Chado tables are directly integrated. Tripal
  14. * provides a web interface that allows site developers to customize how a
  15. * Chado table is integrate with Views. However, these functions provide
  16. * programmatic access to the same functionality.
  17. * @}
  18. */
  19. /**
  20. * Programatically enable view
  21. *
  22. * This should be used in a hook_menu definition as the callback to provide a
  23. * link to enable the view (first example). It can also be called directly if
  24. * needed (second example).
  25. *
  26. * @code
  27. * Create a URL that when the user navigates there, a given view will be
  28. * enabled.
  29. * You will still need to provide a link to this menu item somewhere
  30. * appropriate (ie: an admin landing page).
  31. *
  32. * function mymodule_menu () {
  33. * $items = array();
  34. *
  35. * //Create one of these for each of your default views
  36. * $items['admin/tripal/<PATH-TO-YOUR-ADMIN-SECTION>/views/<VIEW-MACHINE-NAME>/enable']
  37. * = array(
  38. * 'title' => 'Enable <VIEW-HUMAN-READABLE-NAME>',
  39. * 'page callback' => 'tripal_enable_view',
  40. * 'page arguments' => array('<VIEW-MACHINE-NAME>',
  41. * '<PATH-TO-REDIRECT-TO-AFTERWARDS>'),
  42. * 'access arguments' => array('<YOUR-PERMISSION-KEY>'),
  43. * 'type' => MENU_CALLBACK,
  44. * );
  45. *
  46. * return $items;
  47. * }
  48. *
  49. * Call this function directly to disable a view
  50. * The example shows enabling your own default view when your module is
  51. * enabled. This might be useful if you disable your view when your module is
  52. * disabled. function mymodule_enable() {
  53. *
  54. * $view_name = '<VIEW-MACHINE-NAME>';
  55. * tripal_enable_view($view_name);
  56. *
  57. * }
  58. * @endcode
  59. *
  60. * @param $view_name
  61. * The machine-name of the view to be enabled
  62. * @param $redirect_link
  63. * The path to redirect to. FALSE if no redirect needed
  64. *
  65. * @ingroup tripal_chado_views_api
  66. */
  67. function tripal_enable_view($view_name, $redirect_link = FALSE) {
  68. $status = variable_get('views_defaults', []);
  69. if (isset($status[$view_name])) {
  70. $status[$view_name] = FALSE;
  71. variable_set('views_defaults', $status);
  72. drupal_set_message("Successfully Enabled $view_name");
  73. }
  74. else {
  75. drupal_set_message("Unable to find a view by the name of '$view_name'. Unable to enable this view.", 'notice');
  76. }
  77. if ($redirect_link) {
  78. drupal_goto($redirect_link);
  79. }
  80. }
  81. /**
  82. * Programatically disable view.
  83. *
  84. * This should be used in a hook_menu definition as the callback to provide a
  85. * link to disable the view (first example). It can also be called directly if
  86. * needed (second example).
  87. *
  88. * @code
  89. * //Create a URL that when the user navigates there, a given view will be
  90. * //disabled.
  91. * //You will still need to provide a link to this menu item somewhere
  92. * //appropriate.
  93. *
  94. * function mymodule_menu() {
  95. * $items = array();
  96. *
  97. * //Create one of these for each of your default views
  98. * $items['admin/tripal/<PATH-TO-YOUR-ADMIN-SECTION>/views/<VIEW-MACHINE-NAME>/disable']
  99. * = array(
  100. * 'title' => 'Disable <VIEW-HUMAN-READABLE-NAME>',
  101. * 'page callback' => 'tripal_disable_view',
  102. * 'page arguments' => array('<VIEW-MACHINE-NAME>',
  103. * '<PATH-TO-REDIRECT-TO-AFTERWARDS>'),
  104. * 'access arguments' => array('<YOUR-PERMISSION-KEY>'),
  105. * 'type' => MENU_CALLBACK,
  106. * );
  107. *
  108. * return $items;
  109. * }
  110. *
  111. * //Call this function directly to disable a view
  112. * //The example shows disabling your own default view when your module is
  113. * //uninstalled
  114. * function mymodule_uninstall() {
  115. *
  116. * $view_name = '<VIEW-MACHINE-NAME>';
  117. * tripal_disable_view($view_name);
  118. *
  119. * }
  120. * @endcode
  121. *
  122. * @param $view_name
  123. * The machine-name of the view to be enabled
  124. * @param $redirect_link
  125. * The path to redirect to. FALSE if no redirect needed
  126. *
  127. * @ingroup tripal_chado_views_api
  128. */
  129. function tripal_disable_view($view_name, $redirect_link = FALSE) {
  130. $status = variable_get('views_defaults', []);
  131. if (isset($status[$view_name])) {
  132. $status[$view_name] = TRUE;
  133. variable_set('views_defaults', $status);
  134. drupal_set_message("Disabled $view_name");
  135. }
  136. else {
  137. drupal_set_message("Unable to find a view by the name of '$view_name'. Unable to disable this view.", 'notice');
  138. }
  139. if ($redirect_link) {
  140. drupal_goto($redirect_link);
  141. }
  142. }
  143. /**
  144. * Remove any drupal fields from a chado-based default view definition if chado
  145. * is external.
  146. * This ensures compatibility with an external chado database.
  147. *
  148. * You should be calling this function in your hook_views_default_views().
  149. * This function will only remove drupal tables if chado is external; thus you
  150. * do not need to worry about checking yourself. For example, the following is
  151. * a good hook_views_default_views():
  152. *
  153. * @code
  154. * function mymodule_views_default_views() {
  155. * $views = array();
  156. *
  157. * // NOTE: <VIEW-TYPE> describes the type of view:
  158. * // - 'admin' for views used for administration of your module
  159. * // - 'search' for views used to search data
  160. * // - 'list' for views used primarily as data listings
  161. * //
  162. * //<VIEW-HUMAN-READABLE-NAME>
  163. * $view = mymodule_defaultview_<VIEW-TYPE>_<VIEW-MACHINE-NAME>();
  164. * $view = tripal_make_view_compatible_with_external($view);
  165. * $views[$view->name] = $view;
  166. *
  167. * //<VIEW-HUMAN-READABLE-NAME>
  168. * $view = mymodule_defaultview_<VIEW-TYPE>_<VIEW-MACHINE-NAME>();
  169. * $view = tripal_make_view_compatible_with_external($view);
  170. * $views[$view->name] = $view;
  171. *
  172. * return $views;
  173. * }
  174. * function mymodule_defaultview_<VIEW-TYPE>_<VIEW-MACHINE-NAME>() {
  175. * //PASTE VIEWS EXPORT CODE HERE
  176. * return $view;
  177. * }
  178. *
  179. * function mymodule_defaultview_<VIEW-TYPE>_<VIEW-MACHINE-NAME>() {
  180. * //PASTE VIEWS EXPORT CODE HERE
  181. * return $view;
  182. * }
  183. * @endcode
  184. * Notice that the actual views export code is in a separate function.
  185. * This makes your hook_views_default_views() more readable.
  186. *
  187. * NOTE: Currently assumes all tables not in the tripal views integration
  188. * tables are Drupal tables.
  189. *
  190. * @param $view
  191. * The default view definition object
  192. *
  193. * @return
  194. * The default view with all relationships, fields, filters, sorts, arguments
  195. * for Drupal tables removed.
  196. *
  197. * @ingroup tripal_chado_views_api
  198. */
  199. function tripal_make_view_compatible_with_external($view) {
  200. $remove_table = [];
  201. // First check that the base table for the view is a chado table
  202. // If it's not then don't do any filtering.
  203. $setup_id = tripal_is_table_integrated($view->base_table);
  204. if (!$setup_id) {
  205. return $view;
  206. }
  207. // IF chado is external then remove all config relating to drupal tables.
  208. $is_local = isset($GLOBALS["chado_is_local"]) && $GLOBALS["chado_is_local"];
  209. if (!$is_local) {
  210. // Iterate through all displays.
  211. foreach ($view->display as $display_name => $display) {
  212. $display_options = $display->handler->display->display_options;
  213. $sections = ['fields', 'filters', 'sorts', 'relationships'];
  214. foreach ($sections as $section) {
  215. $display_options[$section] = (isset($display_options[$section])) ? $display_options[$section] : [];
  216. foreach ($display_options[$section] as $key => $defn) {
  217. // If the table has not already been encountered; check if it's in
  218. // tripal_views.
  219. if (!isset($remove_table[$defn['table']])) {
  220. // If the table is view then this is a general handler; thus keep.
  221. if ($defn['table'] == 'views') {
  222. $remove_table[$defn['table']] = FALSE;
  223. }
  224. // If this table is integrated then it is chado; thus keep.
  225. $setup_id = tripal_is_table_integrated($defn['table']);
  226. if ($setup_id) {
  227. $remove_table[$defn['table']] = FALSE;
  228. }
  229. else {
  230. $remove_table[$defn['table']] = TRUE;
  231. }
  232. }
  233. // Based on the $remove_table array, unset this field if its from a
  234. // drupal table.
  235. if ($remove_table[$defn['table']]) {
  236. unset($view->display[$display_name]->handler->display->display_options[$section][$key]);
  237. }
  238. }
  239. }
  240. }
  241. }
  242. return $view;
  243. }
  244. /**
  245. * Retrieve the priority of the lightest priority for a given table.
  246. *
  247. * NOTE: Uses lightest priority (drupal-style) where the range is from -10 to 10
  248. * and -10 is of highest priority.
  249. *
  250. * @param $table_name
  251. * The name of the table to retrieve the setup ID for. This can be either a
  252. * materialized view or a chado table
  253. *
  254. * @return
  255. * returns the lowest priority. If the table has not been integrated, a
  256. * priority of 10 is returned.
  257. *
  258. * @ingroup tripal_chado_views_api
  259. */
  260. function tripal_get_lightest_views_integration_priority($table_name) {
  261. // D7 TODO: Check DBTNG changes work.
  262. $sql = "SELECT priority FROM {tripal_views} WHERE table_name=:table ORDER BY priority ASC";
  263. $setup = db_query($sql, [':table' => $table_name]);
  264. $setup = $setup->fetchObject();
  265. if ($setup) {
  266. return $setup->priority;
  267. }
  268. else {
  269. // Default priority is 10.
  270. return 10;
  271. }
  272. }
  273. /**
  274. * Retrieve the views integration setup_id with the lightest priority for a
  275. * given table
  276. *
  277. * NOTE: Uses lightest priority (drupal-style) where the range is from -10 to 10
  278. * and -10 is of highest priority.
  279. *
  280. * @param $table_name
  281. * The name of the table to retrieve the setup ID for. This can be either a
  282. * materialized view or a chado table
  283. *
  284. * @return
  285. * On success, the setup_id to use for integration of this table; otherwise
  286. * FALSE
  287. *
  288. * @ingroup tripal_chado_views_api
  289. */
  290. function tripal_get_lightest_views_integration_setup($table_name) {
  291. // D7 TODO: Check DBTNG changes work.
  292. $sql = "SELECT setup_id FROM {tripal_views} WHERE table_name=:table ORDER BY priority ASC";
  293. $setup = db_query($sql, [':table' => $table_name]);
  294. $setup = $setup->fetchObject();
  295. if ($setup) {
  296. return $setup->setup_id;
  297. }
  298. else {
  299. return FALSE;
  300. }
  301. }
  302. /**
  303. * Retrieve the views integration setup_id with the given priority/table
  304. * combination.
  305. *
  306. * @param $table_name
  307. * The name of the table to retrieve the setup ID for. This can be either a
  308. * materialized view or a chado table
  309. * @param $priority
  310. * The priority of the integration to retrieve the setup_id for
  311. *
  312. * @return
  313. * On success, the setup_id to use for integration of this table; otherwise
  314. * FALSE
  315. *
  316. * @ingroup tripal_chado_views_api
  317. */
  318. function tripal_get_views_integration_setup_id($table_name, $priority) {
  319. // D7 TODO: Check DBTNG changes work
  320. $sql = "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority ORDER BY priority ASC";
  321. $setup = db_query($sql, [':table' => $table_name, ':priority' => $priority]);
  322. $setup = $setup->fetchObject();
  323. if ($setup) {
  324. return $setup->setup_id;
  325. }
  326. else {
  327. return FALSE;
  328. }
  329. }
  330. /**
  331. * Check to see if this table already has an integration record with the given
  332. * priority.
  333. *
  334. * @param $table_name
  335. * The name of the table to check for integration
  336. * @param $priority (optional)
  337. * The priority of record to check for
  338. *
  339. * @return
  340. * If the table is already integrated, the setup_id of the existing integration
  341. * record is returned (If priority is not specified this will be the lightest
  342. * record); Otherwise the table is not already integrated and FALSE is
  343. * returned.
  344. *
  345. * @ingroup tripal_chado_views_api
  346. */
  347. function tripal_is_table_integrated($table_name, $priority = NULL) {
  348. if ($priority) {
  349. // D7 TODO: Check DBTNG changes work.
  350. $sql = "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority";
  351. $setup = db_query($sql, [
  352. ':table' => $table_name,
  353. ':priority' => $priority,
  354. ]);
  355. $setup = $setup->fetchObject();
  356. }
  357. else {
  358. // D7 TODO: Check DBTNG changes work.
  359. $sql = "SELECT setup_id FROM {tripal_views} WHERE table_name=:table ORDER BY priority ASC";
  360. $setup = db_query($sql, [':table' => $table_name]);
  361. $setup = $setup->fetchObject();
  362. }
  363. if ($setup) {
  364. return $setup->setup_id;
  365. }
  366. else {
  367. return FALSE;
  368. }
  369. }
  370. /**
  371. * Checks if you are dealing with the lightest priority setup for a given table.
  372. * This is a good way to determine whether your modules integration is being
  373. * used by views.
  374. *
  375. * @param $setup_id
  376. * The ID of the setup to check (is this setup the lightest one?)
  377. * @param $table_name
  378. * The name of the table associated with this setup
  379. *
  380. * @return TRUE is this is the lightest priority; FALSE otherwise
  381. *
  382. * @ingroup tripal_chado_views_api
  383. */
  384. function tripal_is_lightest_priority_setup($setup_id, $table_name) {
  385. $lightest_priority_setup_id = tripal_get_lightest_views_integration_setup($table_name);
  386. if ($lightest_priority_setup_id == $setup_id) {
  387. return TRUE;
  388. }
  389. else {
  390. return FALSE;
  391. }
  392. }
  393. /**
  394. * Rebuilds all the default integrations.
  395. *
  396. * This essentially clears the cache in case you mess up the integrations in
  397. * your site. This should not be used during module development since it
  398. * really only rebuilds the integrations described by all enabled modules
  399. * in the site and if $delete_first is TRUE it can delete custom integrations
  400. * created by site administrators which will not make your module popular.
  401. *
  402. * @param $delete_first
  403. * If TRUE then all integrations are first deleted.
  404. *
  405. * @ingroup tripal_chado_views_api
  406. */
  407. function tripal_rebuild_views_integrations($delete_first = FALSE) {
  408. if ($delete_first) {
  409. tripal_chado_views_delete_all_integrations();
  410. }
  411. tripal_chado_views_integrate_all_chado_tables();
  412. // TODO: the function above should have a return value from which we can
  413. // determine if the message below is approprite.
  414. drupal_set_message('Successfully rebuilt default Chado Views Integrations');
  415. }
  416. /**
  417. * Add views integration records into the tripal_views* tables.
  418. *
  419. * This is the programatic way to add your own integrations to the tripal views
  420. * integrations list. Keep in mind that the priority set in your $defn_array
  421. * needs to be lighter than any existing integrations to be used by views and
  422. * that it should still be below 0 in order to allow site administrators to
  423. * override it should they need to.
  424. *
  425. * @param $defn_array
  426. * An array describing the structure and fields of the table.
  427. *
  428. * @return
  429. * True/False if completed successfully/not.
  430. *
  431. * Example usage (in hook_install()):
  432. * @code
  433. * $defn_array = array(
  434. * 'table' => 'feature', //tablename or materialized view name
  435. * 'name' => 'Sequence Features', // Human readable name
  436. * 'type' => 'chado', //either chado or mview depending on tablename
  437. * 'description' => 'Create a listing of features.', //description seen when
  438. * creating a view of this type
  439. * 'priority' => 10, //For Base tripal modules: 10; custom modules: 9 to 0;
  440. * 'base_table' => TRUE //either TRUE or FALSE depending on whether the
  441. * current table should show up in the add view list
  442. * 'fields' => array(
  443. * 'feature_id' => array(
  444. * 'name' => 'feature_id', //field name in database
  445. * 'title' => 'Feature ID', //human-readable name -seen in Views UI
  446. * 'description' => 'This is the unique identifier for features',
  447. * //help/description seen in Views UI
  448. * 'type' => 'int', // the type of field
  449. * 'handlers' => array( //possible keys are field, filter, sort,
  450. * argument, relationship
  451. * 'field' => array(
  452. * 'name' => 'chado_views_handler_numeric' //name of handler
  453. * ),
  454. * 'filter' => array( ... ),
  455. * ...
  456. * ),
  457. * // Describe any joins involving this field.
  458. * // Note: you can include both foreign keys (feature.type_id =>
  459. * cvterm.cvterm_id)
  460. * // and referring tables (ie: feature.feature_id <=
  461. * feature_relationship.subject_id)
  462. * 'joins' => array(
  463. * 'feature_relationship' => array( //table to join to.
  464. * 'subject_id' => array( //field in above table
  465. * (feature_relationship)
  466. * 'table' => 'featureprop', //table to join to
  467. * 'field' => 'feature_id', //field in above table
  468. * (feature_relationship)
  469. * 'handler' => 'views_join', //handler to use for joining
  470. * 'relationship_handler' => 'views_handler_relationship',
  471. * //handler to use when a relationship is added.
  472. * 'relationship_only' => FALSE, //whether to join automatically
  473. * (FALSE) or not (TRUE)
  474. * ),
  475. * ...
  476. * ),
  477. * ...
  478. * ),
  479. * )
  480. * ),
  481. * );
  482. * tripal_add_views_integration($defn_array);
  483. * @endcode
  484. *
  485. * @ingroup tripal_chado_views_api
  486. */
  487. function tripal_add_views_integration($defn_array, $setup_id = FALSE) {
  488. $no_errors = TRUE;
  489. if (empty($defn_array['table'])) {
  490. tripal_report_error('tripal_views', TRIPAL_WARNING, 'Recieved integration with no tablename: %defn', ['%defn' => print_r($defn_array, TRUE)]);
  491. $no_errors = FALSE;
  492. return $no_errors;
  493. }
  494. // First insert into tripal_views.
  495. $view_record = [
  496. 'table_name' => $defn_array['table'],
  497. 'name' => $defn_array['name'],
  498. 'comment' => $defn_array['description'],
  499. 'priority' => $defn_array['priority'],
  500. 'base_table' => $defn_array['base_table'],
  501. ];
  502. if ($setup_id) {
  503. $view_record['setup_id'] = $setup_id;
  504. }
  505. if ($defn_array['type'] == 'mview') {
  506. $mview = db_query("SELECT mview_id FROM {tripal_mviews} WHERE mv_table=:table", [':table' => $defn_array['table']]);
  507. $mview = $mview->fetchObject();
  508. $view_record['mview_id'] = $mview->mview_id;
  509. if (!$mview->mview_id) {
  510. return FALSE;
  511. }
  512. }
  513. if ($view_record['name']) { // && $view_record['comment']) { # SPF: commented out 9/24/2012 .. It's not required on the form
  514. if (isset($defn_array['additional_content'])) {
  515. // D7 TODO: Check DBTNG changes work
  516. $setup = db_query(
  517. "SELECT * FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
  518. [
  519. ':table' => $view_record['table_name'],
  520. ':priority' => $view_record['priority'],
  521. ]
  522. );
  523. $setup = $setup->fetchObject();
  524. if (empty($setup->setup_id)) {
  525. $status = drupal_write_record('tripal_views', $view_record);
  526. }
  527. else {
  528. $view_record['setup_id'] = $setup->setup_id;
  529. $status = drupal_write_record('tripal_views', $view_record, 'setup_id');
  530. }
  531. }
  532. else {
  533. $status = drupal_write_record('tripal_views', $view_record);
  534. }
  535. }
  536. else {
  537. $status = FALSE;
  538. drupal_set_message(t('Unable to integrate "%table" table due to a missing name field.', ['%table' => $defn_array['table']]), 'error');
  539. }
  540. if ($status) {
  541. // Need to update the tripal_views record so base_table can be false
  542. // this is a fix because drupal_write_record() puts in defaults if !isset()
  543. // and a variable is considered not set if it's null!
  544. // D7 TODO: Check DBTNG changes work.
  545. db_query(
  546. "UPDATE {tripal_views} SET base_table=:base WHERE table_name=:table AND priority=:priority",
  547. [
  548. ':base' => $defn_array['base_table'],
  549. ':table' => $defn_array['table'],
  550. ':priority' => $defn_array['priority'],
  551. ]
  552. );
  553. // Insert Field Definitions.
  554. foreach ($defn_array['fields'] as $key => $field) {
  555. // Set some defaults.
  556. $field['name'] = (isset($field['name'])) ? $field['name'] : $key;
  557. $field['title'] = (isset($field['title'])) ? $field['title'] : $field['name'];
  558. $field['type'] = (isset($field['type'])) ? $field['type'] : 'text';
  559. $field['description'] = (isset($field['description'])) ? $field['description'] : $field['name'];
  560. $field['handlers'] = (isset($field['handlers'])) ? $field['handlers'] : ['field' => ['name' => 'views_handler_field']];
  561. // Build the field record.
  562. $field_record = [
  563. 'setup_id' => $view_record['setup_id'],
  564. 'column_name' => $field['name'],
  565. 'name' => $field['title'],
  566. 'description' => $field['description'],
  567. 'type' => $field['type'],
  568. ];
  569. if ($view_record['setup_id'] && $field['name'] && $field['title'] && $field['description'] && $field['type']) {
  570. if (isset($defn_array['additional_content'])) {
  571. // D7 TODO: Check DBTNG changes work.
  572. $is_present = db_query(
  573. "SELECT true as present FROM {tripal_views_field} WHERE column_name=:column AND setup_id=:setup",
  574. [
  575. ':column' => $field_record['column_name'],
  576. ':setup' => $field_record['setup_id'],
  577. ]
  578. );
  579. $is_present = $is_present->fetchField();
  580. if (!$is_present) {
  581. $status = drupal_write_record('tripal_views_field', $field_record);
  582. }
  583. else {
  584. $status = drupal_write_record('tripal_views_field', $field_record, [
  585. 'setup_id',
  586. 'column_name',
  587. ]);
  588. }
  589. }
  590. else {
  591. $status = drupal_write_record('tripal_views_field', $field_record);
  592. }
  593. }
  594. else {
  595. drupal_set_message(t('Unable to integrate %name field due to missing required fields.', ['%name' => $field['name']]), 'error');
  596. $status = FALSE;
  597. }
  598. if ($status) {
  599. // Insert Handler Definitions.
  600. foreach ($field['handlers'] as $handler_type => $handler) {
  601. $handler_record = [
  602. 'setup_id' => $view_record['setup_id'],
  603. 'column_name' => $field['name'],
  604. 'handler_type' => $handler_type,
  605. 'handler_name' => $handler['name'],
  606. 'arguments' => serialize($handler),
  607. ];
  608. if ($view_record['setup_id'] && $field['name'] && $handler_type && $handler['name'] && $handler) {
  609. $status = drupal_write_record('tripal_views_handlers', $handler_record);
  610. }
  611. else {
  612. $status = FALSE;
  613. }
  614. if (!$status) {
  615. drupal_set_message(t('Unable to integrate %handler_type handler: %handler_name', [
  616. '%handler_type' => $handler_type,
  617. '%handler_name' => $handler['name'],
  618. ]), 'error');
  619. $no_errors = FALSE;
  620. }
  621. }
  622. // Insert Joins
  623. // Note: The new defn_array structure accounts for 1+ joins to the same
  624. // table (ie: feature_relationship => feature : subject_id & object_id)
  625. // by making $field['joins'] an array of left_field keys.
  626. if (!is_array($field['joins'])) {
  627. $field['joins'] = [];
  628. }
  629. foreach ($field['joins'] as $joins) {
  630. // To keep backwards compatibility with the old defn_array which just
  631. // jumped right into the table definition allowing only a single join,
  632. // we need to check for old defn_arrays and transform them into the
  633. // new format.
  634. if (isset($joins['table'])) {
  635. $left_field = $joins['field'];
  636. $joins = [
  637. $left_field => $joins,
  638. ];
  639. }
  640. // Loop on left fields.
  641. foreach ($joins as $left_field => $join) {
  642. $join_record = [
  643. 'setup_id' => $view_record['setup_id'],
  644. 'base_table' => $defn_array['table'],
  645. 'base_field' => $field['name'],
  646. 'left_table' => $join['table'],
  647. 'left_field' => $left_field,
  648. ];
  649. $join_record['handler'] = (!empty($join['handler'])) ? $join['handler'] : 'views_join';
  650. $join_record['relationship_handler'] = (!empty($join['relationship_handler'])) ? $join['relationship_handler'] : 'views_handler_relationship';
  651. $join_record['relationship_only'] = (!empty($join['relationship_only'])) ? $join['relationship_only'] : 0;
  652. if ($view_record['setup_id'] && $defn_array['table'] && $field['name'] && $join['table'] && $left_field) {
  653. $status = drupal_write_record('tripal_views_join', $join_record);
  654. }
  655. else {
  656. $status = FALSE;
  657. }
  658. if (!$status) {
  659. drupal_set_message(
  660. t(
  661. 'Unable to join %left_table.%left_field with %table.%field',
  662. [
  663. '%left_table' => $join['table'],
  664. '%left_field' => $left_field,
  665. '%table' => $defn_array['table'],
  666. '%field' => $field['name'],
  667. ]
  668. ),
  669. 'error'
  670. );
  671. $no_errors = FALSE;
  672. }
  673. }
  674. }
  675. }
  676. else {
  677. drupal_set_message(t('Unable to integrate %field_name field', ['%field_name' => $field['name']]), 'error');
  678. $no_errors = FALSE;
  679. }
  680. }
  681. }
  682. else {
  683. drupal_set_message(t('Unable to set default tripal views integration'), 'error');
  684. $no_errors = FALSE;
  685. }
  686. return $no_errors;
  687. }
  688. /**
  689. * Export Views integration records.
  690. *
  691. * This is a great way to create your own integration since it returns an
  692. * already defined integration in array form that you can modify. After
  693. * modifications simply set the priority to something lighter (but still
  694. * below 0) than any existing integrations and use
  695. * tripal_add_views_integration() to add it to the list of integrations.
  696. *
  697. * @param $setup_id
  698. * The unique setup id of the tripal views integration
  699. *
  700. * @return
  701. * A views integration definition array as used by
  702. * tripal_add_views_integration()
  703. *
  704. * @ingroup tripal_chado_views_api
  705. */
  706. function tripal_export_views_integration($setup_id) {
  707. // Main setup details
  708. // D7 TODO: Check DBTNG changes work
  709. $r = db_query("SELECT * FROM {tripal_views} WHERE setup_id=:setup", [':setup' => $setup_id]);
  710. $r = $r->fetchObject();
  711. $defn_array = [
  712. 'table' => $r->table_name,
  713. 'name' => $r->name,
  714. 'type' => ($r->mview_id) ? 'mview' : 'chado',
  715. 'description' => $r->comment,
  716. 'priority' => $r->priority,
  717. 'base_table' => $r->base_table,
  718. 'fields' => [],
  719. ];
  720. // Add fields.
  721. $resource = db_query("SELECT * FROM {tripal_views_field} WHERE setup_id=:setup", [':setup' => $setup_id]);
  722. foreach ($resource as $r) {
  723. $defn_array['fields'][$r->column_name] = [
  724. 'name' => $r->column_name,
  725. 'title' => $r->name,
  726. 'description' => $r->description,
  727. 'type' => $r->type,
  728. 'handlers' => [],
  729. 'joins' => [],
  730. ];
  731. }
  732. // Add handlers.
  733. $resource = db_query("SELECT * FROM {tripal_views_handlers} WHERE setup_id=:setup", [':setup' => $setup_id]);
  734. foreach ($resource as $r) {
  735. $defn_array['fields'][$r->column_name]['handlers'][$r->handler_type] = [
  736. 'name' => $r->handler_name,
  737. ];
  738. }
  739. // Add joins.
  740. $resource = db_query("SELECT * FROM {tripal_views_join} WHERE setup_id=:setup", [':setup' => $setup_id]);
  741. foreach ($resource as $r) {
  742. $defn_array['fields'][$r->base_field]['joins'][$r->left_table][$r->left_field] = [
  743. 'table' => $r->left_table,
  744. 'field' => $r->left_field,
  745. 'handler' => $r->handler,
  746. ];
  747. }
  748. return $defn_array;
  749. }
  750. /**
  751. * Removes a View Integration Entry when you only know the table the integration
  752. * was created for and the priority.
  753. *
  754. * This should only be used to remove integrations created by your own module
  755. * (possibly on uninstall of your module). To override existing integrations
  756. * simply create your own integration with a lighter priority using
  757. * tripal_clone_views_integration() or tripal_export_views_integration() to
  758. * create a template.
  759. *
  760. * @param $identifies
  761. * An array of identifiers where the keys indicate what the identifier is.
  762. * One of the following compinations must be present:
  763. * 1) table_name & priority: the name of the table & the priority to remove
  764. * a views integration entry for.
  765. * 2) setup_id: the setup_id of the entry to remove.
  766. * @param $options
  767. * An array of options, currently none are supported.
  768. *
  769. * @return
  770. * TRUE on Success; FALSE otherwise.
  771. *
  772. * @ingroup tripal_chado_views_api
  773. */
  774. function tripal_remove_views_integration($identifiers, $options = []) {
  775. // Remove the views integration using the table_name/priority combo.
  776. if (isset($identifiers['table_name'])) {
  777. $table_name = $identifiers['table_name'];
  778. $priority = $identifiers['priority'];
  779. $views = db_query(
  780. "SELECT * FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
  781. [
  782. ':table' => $table_name,
  783. ':priority' => $priority,
  784. ]
  785. );
  786. $views = $views->fetchObject();
  787. if ($views->setup_id) {
  788. $identifiers['setup_id'] = $views->setup_id;
  789. }
  790. }
  791. // Remove the views integration using the setup_id.
  792. if (isset($identifiers['setup_id'])) {
  793. db_query('DELETE FROM {tripal_views} WHERE setup_id=:setup', [':setup' => $identifiers['setup_id']]);
  794. db_query('DELETE FROM {tripal_views_field} WHERE setup_id=:setup', [':setup' => $identifiers['setup_id']]);
  795. db_query('DELETE FROM {tripal_views_handlers} WHERE setup_id=:setup', [':setup' => $identifiers['setup_id']]);
  796. db_query('DELETE FROM {tripal_views_join} WHERE setup_id=:setup', [':setup' => $identifiers['setup_id']]);
  797. return TRUE;
  798. }
  799. return FALSE;
  800. }
  801. /**
  802. * Update an existing Views Intregration Entry.
  803. * This essentially removes and then re-adds the integration.
  804. *
  805. * @param $setup_id
  806. * The setup ID of the views integration entry to update
  807. * @param $defn_array
  808. * An array describing the structure and fields of the table as is used in
  809. * tripal_add_views_integration().
  810. *
  811. * @ingroup tripal_chado_views_api
  812. */
  813. function tripal_update_views_integration($setup_id, $defn_array) {
  814. tripal_remove_views_integration(['setup_id' => $setup_id]);
  815. $defn_array['additional_content'] = TRUE;
  816. tripal_add_views_integration($defn_array, $setup_id);
  817. }
  818. /**
  819. * Clone an integration. This is often a great way to create your own
  820. * module-specific integration while still benifiting from an existing
  821. * (or even the lightest priority) integration.
  822. *
  823. * @param $table_name
  824. * The table for which you'd like to clone an integration
  825. * @param $new_priority
  826. * The priority of the clone; this is the integration which will be created.
  827. * If no priority is supplied then one lighter then the $template_priority
  828. * will be used.
  829. * @param $template_priority
  830. * The priority of the template to be used for the clone; this is an existing
  831. * integration. If no priority is supplied then the lightest priority will be
  832. * used.
  833. *
  834. * @ingroup tripal_chado_views_api
  835. */
  836. function tripal_clone_views_integration($table_name, $new_priority = NULL, $template_priority = NULL) {
  837. if (empty($template_priority)) {
  838. $template_setup_id = tripal_get_lightest_views_integration_setup($table_name);
  839. }
  840. else {
  841. $template_setup_id = tripal_get_views_integration_setup_id($table_name, $template_priority);
  842. }
  843. $defn_array = tripal_export_views_integration($template_setup_id);
  844. if (empty($new_priority)) {
  845. $defn_array['priority'] = $new_priority;
  846. }
  847. else {
  848. $new_priority = $defn_array['priority'] - 1;
  849. $defn_array['priority'] = $defn_array['priority'] - 1;
  850. }
  851. tripal_add_views_integration($defn_array);
  852. $setup_id = db_query(
  853. "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
  854. [
  855. ':table' => $table_name,
  856. ':priority' => $new_priority,
  857. ]
  858. );
  859. $setup_id = $setup_id->fetchField();
  860. if (empty($setup_id)) {
  861. tripal_report_error('tripal_views', TRIPAL_ERROR, 'Unable to clone the setup for %table in order to add the following field to the integration: %field.',
  862. ['%table' => $table_name, '%field' => print_r($field_array, TRUE)]);
  863. return FALSE;
  864. }
  865. else {
  866. return $setup_id;
  867. }
  868. }
  869. /**
  870. * Adds the given field to an existing or cloned integration. In the case of a
  871. * cloned integration, the lightest integration is used as the template for the
  872. * clone.
  873. *
  874. * NOTE: If that field already exists in the specified integration then it will
  875. * first be deleted and the new one added.
  876. *
  877. * @param $table_name
  878. * The name of the table the integration is for
  879. * @param $priority
  880. * The priority of the integration to use; pass NULL to use the lightest
  881. * integration
  882. * @param $field
  883. * An array describing the field ot add; uses the same format as the
  884. * $defn_array
  885. *
  886. * @return
  887. * TRUE if the field was added successfully; FALSE otherwise
  888. *
  889. * @ingroup tripal_chado_views_api
  890. */
  891. function tripal_add_field_to_views_integration($table_name, $priority, $field) {
  892. $no_errors = TRUE;
  893. // If no priority is supplied then add the field to the lightest integration.
  894. if (empty($priority)) {
  895. $priority = tripal_get_lightest_views_integration_priority($table_name);
  896. }
  897. // First get the setup_id
  898. // D7 TODO: Check DBTNG changes work
  899. $setup_id = db_query(
  900. "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
  901. [
  902. ':table' => $table_name,
  903. ':priority' => $priority,
  904. ]
  905. );
  906. $setup_id = $setup_id->fetchObject();
  907. // If there isn't an integration matching that table/priority combination
  908. // then clone the lightest priority integration.
  909. if (empty($setup_id)) {
  910. $setup_id = tripal_clone_views_integration($table_name, $priority);
  911. }
  912. // Now delete any existing field
  913. db_query("DELETE FROM {tripal_views_field} WHERE setup_id=:setup AND column_name=:column",
  914. [':setup' => $setup_id, 'column' => $field['name']]
  915. );
  916. db_query("DELETE FROM {tripal_views_handlers} WHERE setup_id=:setup AND column_name=:column",
  917. [':setup' => $setup_id, 'column' => $field['name']]
  918. );
  919. db_query("DELETE FROM {tripal_views_join} WHERE setup_id=:setup AND base_table=:table AND base_field=:field",
  920. [':setup' => $setup_id, ':table' => $table_name, ':field' => $field['name']]
  921. );
  922. // Now we need to add/update the field.
  923. $field_record = [
  924. 'setup_id' => $setup_id,
  925. 'column_name' => $field['name'],
  926. 'name' => $field['title'],
  927. 'description' => $field['description'],
  928. 'type' => $field['type'],
  929. ];
  930. if ($setup_id && $field['name'] && $field['title'] && $field['description'] && $field['type']) {
  931. if ($defn_array['additional_content']) {
  932. // D7 TODO: Check DBTNG changes work.
  933. $is = db_query(
  934. "SELECT true as present FROM {tripal_views_field} WHERE column_name=:column AND setup_id=:setup",
  935. [
  936. ':column' => $field_record['column_name'],
  937. ':setup' => $field_record['setup_id'],
  938. ]
  939. );
  940. $is = $is->fetchObject();
  941. if (!$is->present) {
  942. $status = drupal_write_record('tripal_views_field', $field_record);
  943. }
  944. else {
  945. $status = drupal_write_record('tripal_views_field', $field_record, [
  946. 'setup_id',
  947. 'column_name',
  948. ]);
  949. }
  950. }
  951. else {
  952. $status = drupal_write_record('tripal_views_field', $field_record);
  953. }
  954. }
  955. else {
  956. drupal_set_message(t('Unable to integrate %name field due to missing required fields.', ['%name' => $field['name']]), 'error');
  957. $status = FALSE;
  958. }
  959. if ($status) {
  960. // Insert Handler Definitions.
  961. foreach ($field['handlers'] as $handler_type => $handler) {
  962. $handler_record = [
  963. 'setup_id' => $setup_id,
  964. 'column_name' => $field['name'],
  965. 'handler_type' => $handler_type,
  966. 'handler_name' => $handler['name'],
  967. 'arguments' => serialize($handler),
  968. ];
  969. if ($setup_id && $field['name'] && $handler_type && $handler['name'] && $handler) {
  970. $status = drupal_write_record('tripal_views_handlers', $handler_record);
  971. }
  972. else {
  973. $status = FALSE;
  974. }
  975. if (!$status) {
  976. drupal_set_message(t('Unable to integrate %handler_type handler: %handler_name', [
  977. '%handler_type' => $handler_type,
  978. '%handler_name' => $handler['name'],
  979. ]), 'error');
  980. $no_errors = FALSE;
  981. }
  982. }
  983. // Insert Joins
  984. // Note: The new defn_array structure accounts for 1+ joins to the same
  985. // table (ie: feature_relationship => feature : subject_id & object_id)
  986. // by making $field['joins'] an array of left_field keys.
  987. if (!is_array($field['joins'])) {
  988. $field['joins'] = [];
  989. }
  990. foreach ($field['joins'] as $joins) {
  991. // To keep backwards compatibility with the old defn_array which just
  992. // jumped right into the table definition allowing only a single join,
  993. // we need to check for old defn_arrays and transform them into the
  994. // new format.
  995. if (isset($joins['table'])) {
  996. $left_field = $joins['field'];
  997. $joins = [
  998. $left_field => $joins,
  999. ];
  1000. }
  1001. // Loop on left fields.
  1002. foreach ($joins as $left_field => $join) {
  1003. $join_record = [
  1004. 'setup_id' => $setup_id,
  1005. 'base_table' => $defn_array['table'],
  1006. 'base_field' => $field['name'],
  1007. 'left_table' => $join['table'],
  1008. 'left_field' => $join['field'],
  1009. ];
  1010. if (!empty($join['handler'])) {
  1011. $join_record['handler'] = $join['handler'];
  1012. }
  1013. else {
  1014. $join_record['handler'] = 'views_join';
  1015. }
  1016. if ($setup_id && $defn_array['table'] && $field['name'] && $join['table'] && $join['field']) {
  1017. $status = drupal_write_record('tripal_views_join', $join_record);
  1018. }
  1019. else {
  1020. $status = FALSE;
  1021. }
  1022. if (!$status) {
  1023. drupal_set_message(
  1024. t(
  1025. 'Unable to join %left_table.%left_field with %table.%field',
  1026. [
  1027. '%left_table' => $join['table'],
  1028. '%left_field' => $join['field'],
  1029. '%table' => $defn_array['table'],
  1030. '%field' => $field['name'],
  1031. ]
  1032. ),
  1033. 'error'
  1034. );
  1035. $no_errors = FALSE;
  1036. }
  1037. }
  1038. }
  1039. }
  1040. else {
  1041. drupal_set_message(t('Unable to integrate %field_name field', ['%field_name' => $field['name']]), 'error');
  1042. $no_errors = FALSE;
  1043. }
  1044. return $no_errors;
  1045. }
  1046. /**
  1047. * Adds the given field to an existing or cloned integration. In the case of a
  1048. * cloned integration, the lightest integration is used as the template for the
  1049. * clone.
  1050. *
  1051. * NOTE: If that field already exists in the specified integration then it will
  1052. * first be deleted and the new one added.
  1053. *
  1054. * @param $table_name
  1055. * The name of the table the integration is for
  1056. * @param $priority
  1057. * The priority of the integration to use; pass NULL to use the lightest
  1058. * integration
  1059. * @param $field
  1060. * An array describing the join to add. it should contain the following keys:
  1061. * base_table, base_field, left_table, left_field, handler,
  1062. * relationship_handler, relationship_only
  1063. *
  1064. * @return
  1065. * TRUE if the field was added successfully; FALSE otherwise
  1066. *
  1067. * @ingroup tripal_chado_views_api
  1068. */
  1069. function tripal_add_join_to_views_integration($table_name, $priority, $join) {
  1070. $no_errors = TRUE;
  1071. // If no priority is supplied then add the field to the lightest integration.
  1072. if (empty($priority)) {
  1073. $priority = tripal_get_lightest_views_integration_priority($table_name);
  1074. }
  1075. // First get the setup_id.
  1076. $setup_id = db_query(
  1077. "SELECT setup_id FROM {tripal_views} WHERE table_name=:table AND priority=:priority",
  1078. [
  1079. ':table' => $table_name,
  1080. ':priority' => $priority,
  1081. ]
  1082. );
  1083. $setup_id = $setup_id->fetchField();
  1084. // If there isn't an integration matching that table/priority combination
  1085. // then clone the lightest priority integration.
  1086. if (empty($setup_id)) {
  1087. $setup_id = tripal_clone_views_integration($table_name, $priority);
  1088. }
  1089. // Add the setup_id to the join record passed in
  1090. $join['setup_id'] = $setup_id;
  1091. drupal_write_record('tripal_views_join', $join);
  1092. }
  1093. /**
  1094. * Remove a join from an integration. This is usually done after cloning an
  1095. * existing integration using tripal_clone_views_integration().
  1096. *
  1097. * @param $setup_id
  1098. * The setup_id of the integration to delete the join from.
  1099. * @param $base_table
  1100. * The name of the base table involved the join.
  1101. * @param $base_field
  1102. * The field from the base table involved in the join.
  1103. * @param $left_table
  1104. * The name of the other table involved in the join
  1105. * @param $left_field
  1106. * The name of the field from the other table involved in the join.
  1107. *
  1108. * @ingroup tripal_chado_views_api
  1109. */
  1110. function tripal_remove_join_from_views_integration($setup_id, $base_table, $base_field, $left_table, $left_field) {
  1111. db_query(
  1112. "DELETE FROM {tripal_views_join} WHERE setup_id=:setup AND base_table=:base-table AND base_field=:base-field AND left_table=:left-table AND left_field=:left-field",
  1113. [
  1114. ':setup' => $setup_id,
  1115. ':base-table' => $base_table,
  1116. ':base-field' => $base_field,
  1117. ':left-table' => $left_table,
  1118. ':left-field' => $left_field,
  1119. ]
  1120. );
  1121. }