views_data_export.module 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. <?php
  2. /**
  3. * @file
  4. * Provides the ability to export to specific
  5. */
  6. define('VIEWS_DATA_EXPORT_HEADER', 'header');
  7. define('VIEWS_DATA_EXPORT_BODY', 'body');
  8. define('VIEWS_DATA_EXPORT_FOOTER', 'footer');
  9. define('VIEWS_DATA_EXPORT_FINISHED', 'finished');
  10. define('VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX', 'views_data_export_index_');
  11. /**
  12. * Implements hook_init().
  13. */
  14. function views_data_export_init() {
  15. // We have to include our theme preprocessors here until:
  16. // http://drupal.org/node/1096770 is fixed.
  17. module_load_include('inc', 'views_data_export', 'theme/views_data_export.theme');
  18. }
  19. /**
  20. * Implementation of hook_views_api().
  21. */
  22. function views_data_export_views_api() {
  23. return array(
  24. 'api' => 2,
  25. );
  26. }
  27. /**
  28. * Implementation of hook_theme().
  29. */
  30. function views_data_export_theme() {
  31. // Make sure that views picks up the preprocess functions.
  32. module_load_include('inc', 'views_data_export', 'theme/views_data_export.theme');
  33. $hooks = array();
  34. $hooks['views_data_export_feed_icon'] = array(
  35. 'pattern' => 'views_data_export_feed_icon__',
  36. 'variables' => array(
  37. 'image_path' => NULL,
  38. 'url' => NULL,
  39. 'query' => '',
  40. 'text' => '',
  41. ),
  42. 'file' => 'theme/views_data_export.theme.inc',
  43. );
  44. $hooks['views_data_export_complete_page'] = array (
  45. 'variables' => array(
  46. 'file' => '',
  47. 'errors' => array(),
  48. 'return_url'=> '',
  49. ),
  50. 'file' => 'theme/views_data_export.theme.inc',
  51. );
  52. $hooks['views_data_export_message'] = array (
  53. 'variables' => array(
  54. 'message' => '',
  55. 'type' => 'info',
  56. ),
  57. 'file' => 'theme/views_data_export.theme.inc',
  58. );
  59. return $hooks;
  60. }
  61. /**
  62. * Implementation of hook_cron().
  63. */
  64. function views_data_export_cron() {
  65. views_data_export_garbage_collect();
  66. }
  67. /**
  68. * Removes any temporary index tables that have been left
  69. * behind. This is caused by batch processes which are
  70. * started but never finished.
  71. *
  72. * Removes all trace of exports from the database that
  73. * were created more than $expires seconds ago
  74. *
  75. * @param $expires
  76. * Seconds ago. Defaults to that given in the settings.
  77. * @param $chunk
  78. * The number of tables to test for and delete.
  79. * Defaults to that given in the settings. Pass -1
  80. * for this setting to remove any restriction and to
  81. * garbage collect all exports.
  82. */
  83. function views_data_export_garbage_collect($expires = NULL, $chunk = NULL) {
  84. if (lock_acquire('views_data_export_gc')) {
  85. if (!isset($expires)) {
  86. $expires = variable_get('views_data_export_gc_expires', 604800); // one week
  87. }
  88. if (!isset($chunk)) {
  89. $chunk = variable_get('views_data_export_gc_chunk', 30);
  90. }
  91. if ($chunk == -1) {
  92. $result = db_query("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", array(':timestamp' => REQUEST_TIME - $expires));
  93. }
  94. else {
  95. $result = db_query_range("SELECT eid FROM {views_data_export} WHERE time_stamp <= :timestamp ORDER BY time_stamp ASC", 0, $chunk, array(':timestamp' => REQUEST_TIME - $expires));
  96. }
  97. $eids_to_clear = array();
  98. foreach ($result as $row) {
  99. $eids_to_clear[] = $row->eid;
  100. }
  101. // We do two things to exports we want to garbage collect
  102. // 1. Delete the index table for it, if it is still around
  103. // 2. Delete the row from the exports table
  104. // 3. Delete the view from the object_cache
  105. if (count($eids_to_clear)) {
  106. foreach ($eids_to_clear as $eid) {
  107. // 1. Delete index table, if it is still around for some reason
  108. $table = VIEWS_DATA_EXPORT_INDEX_TABLE_PREFIX . $eid;
  109. if (db_table_exists($table)) {
  110. db_drop_table($table);
  111. }
  112. }
  113. // 2. Delete the entries in the exports table.
  114. db_delete('views_data_export')
  115. ->condition('eid', $eids_to_clear, 'IN')
  116. ->execute();
  117. // 3. Clear the cached views
  118. views_data_export_view_clear($eids_to_clear);
  119. }
  120. lock_release('views_data_export_gc');
  121. }
  122. }
  123. /**
  124. * Batch API callback.
  125. * Handles all batching operations by executing the appropriate view.
  126. */
  127. function _views_data_export_batch_process($export_id, $display_id, $exposed_input, &$context) {
  128. // Don't show the admin menu on batch page, some people don't like it.
  129. if (module_exists('admin_menu')) {
  130. module_invoke('admin_menu', 'suppress');
  131. }
  132. // Fetch the view in question from our cache
  133. $view = views_data_export_view_retrieve($export_id);
  134. $view->set_display($display_id);
  135. if (!empty($exposed_input)) {
  136. $view->set_exposed_input($exposed_input);
  137. }
  138. // Inform the data_export display which export it corresponds to and execute
  139. if (!isset($view->display_handler->batched_execution_state)) {
  140. $view->display_handler->batched_execution_state = new stdClass();
  141. }
  142. $view->display_handler->batched_execution_state->eid = $export_id;
  143. $view->display_handler->views_data_export_cached_view_loaded = TRUE;
  144. $view->execute_display($display_id);
  145. // Update batch api progress information
  146. $sandbox = $view->display_handler->batched_execution_state->sandbox;
  147. $context['finished'] = $sandbox['finished'];
  148. $context['message'] = $sandbox['message'];
  149. views_data_export_view_store($export_id, $view);
  150. }
  151. /**********/
  152. /** CRUD **/
  153. /**********/
  154. /**
  155. * Save a new export into the database.
  156. */
  157. function views_data_export_new($view_name, $view_display_id, $file) {
  158. // Insert new row into exports table
  159. $record = (object) array(
  160. 'view_name' => $view_name,
  161. 'view_display_id' => $view_display_id,
  162. 'time_stamp' => REQUEST_TIME,
  163. 'fid' => $file,
  164. 'batch_state' => VIEWS_DATA_EXPORT_HEADER,
  165. 'sandbox' => array(),
  166. );
  167. drupal_write_record('views_data_export', $record);
  168. return $record;
  169. }
  170. /**
  171. * Update an export row in the database
  172. */
  173. function views_data_export_update($state) {
  174. // Note, drupal_write_record handles serializing
  175. // the sandbox field as per our schema definition
  176. drupal_write_record('views_data_export', $state, 'eid');
  177. }
  178. /**
  179. * Get the information about a previous export.
  180. */
  181. function views_data_export_get($export_id) {
  182. $object = db_query("SELECT * FROM {views_data_export} WHERE eid = :eid", array(':eid' => (int)$export_id))->fetch();
  183. if ($object) {
  184. $object->sandbox = unserialize($object->sandbox);
  185. }
  186. return $object;
  187. }
  188. /**
  189. * Remove the information about an export.
  190. */
  191. function views_data_export_clear($export_id) {
  192. db_delete('views_data_export')
  193. ->condition('eid', $export_id)
  194. ->execute();
  195. views_data_export_view_clear($export_id);
  196. }
  197. /**
  198. * Store a view in the object cache.
  199. */
  200. function views_data_export_view_store($export_id, $view) {
  201. // Store a clean copy of the view.
  202. $_view = $view->clone_view();
  203. views_data_export_view_clear($export_id);
  204. $record = array(
  205. 'eid' => $export_id,
  206. 'data' => $_view,
  207. 'updated' => REQUEST_TIME,
  208. );
  209. drupal_write_record('views_data_export_object_cache', $record);
  210. }
  211. /**
  212. * Retrieve a view from the object cache.
  213. */
  214. function views_data_export_view_retrieve($export_id) {
  215. views_include('view');
  216. $data = db_query("SELECT * FROM {views_data_export_object_cache} WHERE eid = :eid", array(':eid' => $export_id))->fetch();
  217. if ($data) {
  218. $view = unserialize($data->data);
  219. }
  220. return $view;
  221. }
  222. /**
  223. * Clear a view from the object cache.
  224. *
  225. * @param $export_id
  226. * An export ID or an array of export IDs to clear from the object cache.
  227. */
  228. function views_data_export_view_clear($export_id) {
  229. db_delete('views_data_export_object_cache')
  230. ->condition('eid', $export_id)
  231. ->execute();
  232. }
  233. /**
  234. * Implements hook_file_presave().
  235. */
  236. function views_data_export_file_presave($file) {
  237. // Ensure temporary files really are temporary.
  238. // @see: https://drupal.org/node/2198399
  239. if (strpos($file->filename, 'views_data_export') === 0) {
  240. // There is no FILE_STATUS_TEMPORARY.
  241. $file->status = 0;
  242. }
  243. }