tripal_chado_views.api.inc

Provides API functions that support direct integration of Chado tables with Drupal Views.

File

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