demo.admin.inc 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. <?php
  2. // $Id: demo.admin.inc,v 1.35 2011/01/09 03:11:12 sun Exp $
  3. /**
  4. * @file
  5. * Demonstration Site administrative pages
  6. */
  7. /**
  8. * Current version of SQL dump structure.
  9. */
  10. define('DEMO_DUMP_VERSION', '1.1');
  11. /**
  12. * Form builder for Demo module settings.
  13. */
  14. function demo_admin_settings($form, &$form_state) {
  15. if (!file_stream_wrapper_valid_scheme('private')) {
  16. form_set_error('', t('The <a href="@file-settings-url">private filesystem</a> must be configured in order to create or load snapshots.', array(
  17. '@file-settings-url' => url('admin/config/media/file-system', array(
  18. 'query' => drupal_get_destination(),
  19. )),
  20. )));
  21. }
  22. $form['demo_dump_path'] = array(
  23. '#type' => 'textfield',
  24. '#title' => t('Snapshot file system path'),
  25. '#field_prefix' => 'private://',
  26. '#default_value' => variable_get('demo_dump_path', 'demo'),
  27. '#required' => TRUE,
  28. );
  29. $form['#validate'][] = 'demo_admin_settings_validate';
  30. return system_settings_form($form);
  31. }
  32. /**
  33. * Form validation handler for demo_admin_settings().
  34. */
  35. function demo_admin_settings_validate($form, &$form_state) {
  36. if (!file_prepare_directory($form_state['values']['demo_dump_path'], FILE_CREATE_DIRECTORY)) {
  37. form_set_error('demo_dump_path', t('The snapshot directory %directory could not be created.', array('%directory' => $form_state['values']['demo_dump_path'])));
  38. }
  39. }
  40. /**
  41. * Form builder to manage snapshots.
  42. */
  43. function demo_manage_form($form, &$form_state) {
  44. $form['status'] = array(
  45. '#type' => 'container',
  46. '#title' => t('Status'),
  47. '#attributes' => array(
  48. 'class' => array('demo-status', 'clearfix'),
  49. ),
  50. '#attached' => array(
  51. 'css' => array(drupal_get_path('module', 'demo') . '/demo.admin.css'),
  52. ),
  53. );
  54. $reset_date = variable_get('demo_reset_last', 0);
  55. $form['status']['reset_last'] = array(
  56. '#type' => 'item',
  57. '#title' => t('Last reset'),
  58. '#markup' => $reset_date ? format_date($reset_date) : t('Never'),
  59. );
  60. $form['dump'] = demo_get_dumps();
  61. $form['actions'] = array('#type' => 'actions');
  62. $form['actions']['delete'] = array(
  63. '#type' => 'submit',
  64. '#value' => t('Delete'),
  65. '#submit' => array('demo_manage_delete_submit'),
  66. );
  67. // If there are no snapshots yet, hide the selection and form actions.
  68. if (empty($form['dump']['#options'])) {
  69. $form['dump']['#access'] = FALSE;
  70. $form['actions']['#access'] = FALSE;
  71. }
  72. return $form;
  73. }
  74. /**
  75. * Delete button submit handler for demo_manage_form().
  76. */
  77. function demo_manage_delete_submit($form, &$form_state) {
  78. $form_state['redirect'] = 'admin/structure/demo/delete/' . $form_state['values']['filename'];
  79. }
  80. /**
  81. * Form builder to confirm deletion of a snapshot.
  82. */
  83. function demo_delete_confirm($form, &$form_state, $filename) {
  84. $fileconfig = demo_get_fileconfig($filename);
  85. if (!file_exists($fileconfig['infofile'])) {
  86. return drupal_access_denied();
  87. }
  88. $form['filename'] = array(
  89. '#type' => 'value',
  90. '#value' => $filename,
  91. );
  92. return confirm_form($form, t('Are you sure you want to delete the snapshot %title?', array('%title' => $filename)), 'admin/structure/demo', t('This action cannot be undone.'), t('Delete'));
  93. }
  94. /**
  95. * Form submit handler for demo_delete_confirm().
  96. */
  97. function demo_delete_confirm_submit($form, &$form_state) {
  98. $files = demo_get_fileconfig($form_state['values']['filename']);
  99. unlink($files['sqlfile']);
  100. unlink($files['infofile']);
  101. drupal_set_message(t('Snapshot %title has been deleted.', array(
  102. '%title' => $form_state['values']['filename'],
  103. )));
  104. $form_state['redirect'] = 'admin/structure/demo';
  105. }
  106. /**
  107. * Form builder to create a new snapshot.
  108. */
  109. function demo_dump_form($form, &$form_state) {
  110. $form['#tree'] = TRUE;
  111. $form['dump']['filename'] = array(
  112. '#title' => t('Name'),
  113. '#type' => 'textfield',
  114. '#autocomplete_path' => 'demo/autocomplete',
  115. '#required' => TRUE,
  116. '#maxlength' => 128,
  117. '#description' => t('Allowed characters: a-z, 0-9, dashes ("-"), underscores ("_") and dots.'),
  118. );
  119. $form['dump']['description'] = array(
  120. '#title' => t('Description'),
  121. '#type' => 'textarea',
  122. '#rows' => 2,
  123. '#description' => t('Leave empty to retain the existing description when replacing a snapshot.'),
  124. );
  125. $form['dump']['tables'] = array(
  126. '#type' => 'value',
  127. '#value' => demo_enum_tables(),
  128. );
  129. if (empty($form_state['demo']['dump_exists'])) {
  130. $form['actions'] = array('#type' => 'actions');
  131. $form['actions']['submit'] = array(
  132. '#type' => 'submit',
  133. '#value' => t('Create'),
  134. );
  135. }
  136. else {
  137. $form = confirm_form($form,
  138. t('Are you sure you want to replace the existing %name snapshot?', array(
  139. '%name' => $form_state['values']['dump']['filename'],
  140. )),
  141. 'admin/structure/demo',
  142. t('A snapshot with the same name already exists and will be replaced. This action cannot be undone.')
  143. );
  144. }
  145. return $form;
  146. }
  147. /**
  148. * Form validation handler for demo_dump_form().
  149. */
  150. function demo_dump_form_validate(&$form, &$form_state) {
  151. if (empty($form_state['values']['confirm'])) {
  152. $fileconfig = demo_get_fileconfig($form_state['values']['dump']['filename']);
  153. if (file_exists($fileconfig['infofile']) || file_exists($fileconfig['sqlfile'])) {
  154. $form_state['demo']['dump_exists'] = TRUE;
  155. $form_state['rebuild'] = TRUE;
  156. }
  157. }
  158. }
  159. /**
  160. * Form submit handler for demo_dump_form().
  161. */
  162. function demo_dump_form_submit($form, &$form_state) {
  163. if ($fileconfig = _demo_dump($form_state['values']['dump'])) {
  164. drupal_set_message(t('Snapshot %filename has been created.', array(
  165. '%filename' => $form_state['values']['dump']['filename'],
  166. )));
  167. }
  168. $form_state['redirect'] = 'admin/structure/demo';
  169. }
  170. /**
  171. * Create a new snapshot.
  172. *
  173. * @param $options
  174. * A structured array of snapshot options:
  175. * - filename: The base output filename, without extension.
  176. * - default: Whether to set this dump as new default snapshot.
  177. * - description: A description for the snapshot. If a snapshot with the same
  178. * name already exists and this is left blank, the new snapshot will reuse
  179. * the existing description.
  180. * - tables: An array of tables to dump, keyed by table name (including table
  181. * prefix, if any). The value is an array of dump options:
  182. * - schema: Whether to dump the table schema.
  183. * - data: Whether to dump the table data.
  184. */
  185. function _demo_dump($options) {
  186. // Load database specific functions.
  187. if (!demo_load_include()) {
  188. return FALSE;
  189. }
  190. // Increase PHP's max_execution_time for large dumps.
  191. drupal_set_time_limit(600);
  192. // Generate the info file.
  193. $info = demo_set_info($options);
  194. if (!$info) {
  195. return FALSE;
  196. }
  197. // Allow other modules to alter the dump options.
  198. $fileconfig = demo_get_fileconfig($info['filename']);
  199. drupal_alter('demo_dump', $options, $info, $fileconfig);
  200. // Perform database dump.
  201. if (!demo_dump_db($fileconfig['sqlfile'], $options)) {
  202. return FALSE;
  203. }
  204. // Adjust file permissions.
  205. drupal_chmod($fileconfig['infofile']);
  206. drupal_chmod($fileconfig['sqlfile']);
  207. // Allow other modules to act on successful dumps.
  208. module_invoke_all('demo_dump', $options, $info, $fileconfig);
  209. return $fileconfig;
  210. }
  211. /**
  212. * Form builder to reset site to a snapshot.
  213. */
  214. function demo_reset_confirm($form, &$form_state) {
  215. $form['dump'] = demo_get_dumps();
  216. $form['warning'] = array(
  217. '#type' => 'container',
  218. '#attributes' => array(
  219. 'class' => array('messages', 'warning'),
  220. ),
  221. );
  222. $form['warning']['message'] = array(
  223. '#markup' => t('This action cannot be undone.'),
  224. );
  225. return confirm_form($form,
  226. t('Are you sure you want to reset the site?'),
  227. 'admin/structure/demo',
  228. t('Overwrites all changes that made to this site since the chosen snapshot.'),
  229. t('Reset')
  230. );
  231. }
  232. /**
  233. * Form submit handler for demo_reset_confirm().
  234. */
  235. function demo_reset_confirm_submit($form, &$form_state) {
  236. // Reset site to chosen snapshot.
  237. _demo_reset($form_state['values']['filename']);
  238. // Do not redirect from the reset confirmation form by default, as it is
  239. // likely that the user wants to reset all over again (e.g., keeping the
  240. // browser tab open).
  241. }
  242. /**
  243. * Reset site using snapshot.
  244. *
  245. * @param $filename
  246. * Base snapshot filename, without extension.
  247. * @param $verbose
  248. * Whether to output status messages.
  249. */
  250. function _demo_reset($filename, $verbose = TRUE) {
  251. // Load database specific functions.
  252. if (!demo_load_include()) {
  253. return FALSE;
  254. }
  255. // Increase PHP's max_execution_time for large dumps.
  256. drupal_set_time_limit(600);
  257. $fileconfig = demo_get_fileconfig($filename);
  258. if (!file_exists($fileconfig['sqlfile']) || !($fp = fopen($fileconfig['sqlfile'], 'r'))) {
  259. if ($verbose) {
  260. drupal_set_message(t('Unable to read file %filename.', array(
  261. '%filename' => $fileconfig['sqlfile'],
  262. )), 'error');
  263. }
  264. watchdog('demo', 'Unable to read file %filename.', array('%filename' => $fileconfig['sqlfile']), WATCHDOG_ERROR);
  265. return FALSE;
  266. }
  267. // Load any database information in front of reset.
  268. $info = demo_get_info($fileconfig['infofile']);
  269. module_invoke_all('demo_reset_before', $filename, $info, $fileconfig);
  270. // Retain special variables, so the (demonstration) site keeps operating after
  271. // the reset. Specify NULL instead of default values, so unconfigured
  272. // variables are not retained, resp., deleted after the reset.
  273. $variables = array(
  274. // Without the snapshot path, subsequent resets will not work.
  275. 'demo_dump_path' => variable_get('demo_dump_path', NULL),
  276. );
  277. // Temporarily disable foreign key checks for the time of import and before
  278. // dropping existing tables. Foreign key checks should already be re-enabled
  279. // as one of the last operations in the SQL dump file.
  280. // @see demo_dump_db()
  281. db_query("SET FOREIGN_KEY_CHECKS = 0;");
  282. // Drop tables.
  283. $is_version_1_0_dump = version_compare($info['version'], '1.1', '<');
  284. $watchdog = Database::getConnection()->prefixTables('{watchdog}');
  285. foreach (demo_enum_tables() as $table => $dump_options) {
  286. // Skip watchdog, except for legacy dumps that included the watchdog table
  287. if ($table != $watchdog || $is_version_1_0_dump) {
  288. db_query("DROP TABLE $table");
  289. }
  290. }
  291. // Load data from snapshot.
  292. $success = TRUE;
  293. $query = '';
  294. while (!feof($fp)) {
  295. $line = fgets($fp, 16384);
  296. if ($line && $line != "\n" && strncmp($line, '--', 2) && strncmp($line, '#', 1)) {
  297. $query .= $line;
  298. if (substr($line, -2) == ";\n") {
  299. $options = array(
  300. 'target' => 'default',
  301. 'return' => Database::RETURN_NULL,
  302. // 'throw_exception' => FALSE,
  303. );
  304. $stmt = Database::getConnection($options['target'])->prepare($query);
  305. if (!$stmt->execute(array(), $options)) {
  306. if ($verbose) {
  307. // Don't use t() here, as the locale_* tables might not (yet) exist.
  308. drupal_set_message(strtr('Query failed: %query', array('%query' => $query)), 'error');
  309. }
  310. $success = FALSE;
  311. }
  312. $query = '';
  313. }
  314. }
  315. }
  316. fclose($fp);
  317. // Retain variables.
  318. foreach ($variables as $key => $value) {
  319. if (isset($value)) {
  320. variable_set($key, $value);
  321. }
  322. else {
  323. variable_del($key);
  324. }
  325. }
  326. if ($success) {
  327. if ($verbose) {
  328. drupal_set_message(t('Restored site from %filename.', array('%filename' => $fileconfig['sqlfile'])));
  329. }
  330. watchdog('demo', 'Restored site from %filename.', array('%filename' => $fileconfig['sqlfile']), WATCHDOG_NOTICE);
  331. // Allow other modules to act on successful resets.
  332. module_invoke_all('demo_reset', $filename, $info, $fileconfig);
  333. }
  334. else {
  335. if ($verbose) {
  336. drupal_set_message(t('Failed to restore site from %filename.', array('%filename' => $fileconfig['sqlfile'])), 'error');
  337. }
  338. watchdog('demo', 'Failed to restore site from %filename.', array('%filename' => $fileconfig['sqlfile']), WATCHDOG_ERROR);
  339. }
  340. // Save request time of last reset, but not during re-installation via
  341. // demo_profile.
  342. if (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE !== 'install') {
  343. variable_set('demo_reset_last', REQUEST_TIME);
  344. }
  345. return $success;
  346. }
  347. function demo_get_fileconfig($filename = 'demo_site') {
  348. $fileconfig = array();
  349. // Build dump path.
  350. if (!file_stream_wrapper_valid_scheme('private')) {
  351. // @todo Temporarily throwing a form error here.
  352. // Don't break demo_profile.
  353. if (!defined('MAINTENANCE_MODE')) {
  354. form_set_error('', t('The <a href="@file-settings-url">private filesystem</a> must be configured in order to create or load snapshots.', array(
  355. '@file-settings-url' => url('admin/config/media/file-system', array(
  356. 'query' => drupal_get_destination(),
  357. )),
  358. )));
  359. }
  360. return FALSE;
  361. }
  362. $fileconfig['path'] = 'private://' . variable_get('demo_dump_path', 'demo');
  363. $fileconfig['dumppath'] = $fileconfig['path'];
  364. // @todo Update to D7?
  365. // Append site name if it is not included in file_directory_path() and if not
  366. // storing files in sites/all/files.
  367. $fileconfig['site'] = str_replace('sites', '', conf_path());
  368. /*
  369. if (strpos($fileconfig['path'], conf_path()) === FALSE && strpos($fileconfig['path'], '/all/') === FALSE) {
  370. $fileconfig['dumppath'] .= $fileconfig['site'];
  371. }
  372. */
  373. // Check if directory exists.
  374. if (!file_prepare_directory($fileconfig['dumppath'], FILE_CREATE_DIRECTORY)) {
  375. return FALSE;
  376. }
  377. // Protect dump files.
  378. file_create_htaccess($fileconfig['path'], TRUE);
  379. // Build SQL filename.
  380. $fileconfig['sql'] = $filename . '.sql';
  381. $fileconfig['sqlfile'] = $fileconfig['dumppath'] . '/' . $fileconfig['sql'];
  382. // Build info filename.
  383. $fileconfig['info'] = $filename . '.info';
  384. $fileconfig['infofile'] = $fileconfig['dumppath'] . '/' . $fileconfig['info'];
  385. return $fileconfig;
  386. }
  387. /**
  388. * Load database specific functions.
  389. */
  390. function demo_load_include() {
  391. $engine = db_driver();
  392. if (!module_load_include('inc', 'demo', 'database_' . $engine . '_dump')) {
  393. drupal_set_message(t('@database is not supported yet.', array('@database' => ucfirst($engine))), 'error');
  394. return FALSE;
  395. }
  396. return TRUE;
  397. }
  398. function demo_get_dumps() {
  399. $fileconfig = demo_get_fileconfig();
  400. // Fetch list of available info files
  401. $files = file_scan_directory($fileconfig['dumppath'], '/\.info$/');
  402. foreach ($files as $file => $object) {
  403. $files[$file]->filemtime = filemtime($file);
  404. $files[$file]->filesize = filesize(substr($file, 0, -4) . 'sql');
  405. }
  406. // Sort snapshots by date (ascending file modification time).
  407. uasort($files, create_function('$a, $b', 'return ($a->filemtime < $b->filemtime);'));
  408. $element = array(
  409. '#type' => 'radios',
  410. '#title' => t('Snapshot'),
  411. '#required' => TRUE,
  412. '#parents' => array('filename'),
  413. '#options' => array(),
  414. '#attributes' => array(
  415. 'class' => array('demo-snapshots-widget'),
  416. ),
  417. '#attached' => array(
  418. 'js' => array(drupal_get_path('module', 'demo') . '/demo.admin.js'),
  419. ),
  420. );
  421. foreach ($files as $filename => $file) {
  422. $info = demo_get_info($filename);
  423. // Prepare snapshot title.
  424. $title = t('@snapshot <small>(!date, !size)</small>', array(
  425. '@snapshot' => $info['filename'],
  426. '!date' => format_date($file->filemtime, 'small'),
  427. '!size' => format_size($file->filesize),
  428. ));
  429. // Prepare snapshot description.
  430. $description = '';
  431. if (!empty($info['description'])) {
  432. $description .= '<p>' . $info['description'] . '</p>';
  433. }
  434. // Add download links.
  435. $description .= '<p>' . t('Download: <a href="@info-file-url">.info file</a>, <a href="@sql-file-url">.sql file</a>', array(
  436. '@info-file-url' => url('demo/download/' . $file->name . '/info'),
  437. '@sql-file-url' => url('demo/download/' . $file->name . '/sql'),
  438. )) . '</p>';
  439. // Add module list.
  440. if (count($info['modules']) > 1) {
  441. // Remove required core modules and Demo from module list.
  442. $modules = array_diff($info['modules'], array('filter', 'node', 'system', 'user', 'demo'));
  443. // Sort module list alphabetically.
  444. sort($modules);
  445. $description .= t('Modules: @modules', array('@modules' => implode(', ', $modules)));
  446. }
  447. // Add the radio option element.
  448. $element['#options'][$info['filename']] = $title;
  449. $element[$info['filename']] = array(
  450. '#description' => $description,
  451. '#file' => $file,
  452. '#info' => $info,
  453. );
  454. }
  455. return $element;
  456. }
  457. function demo_get_info($filename, $field = NULL) {
  458. $info = array();
  459. if (file_exists($filename)) {
  460. $info = parse_ini_file($filename);
  461. if (isset($info['modules'])) {
  462. $info['modules'] = explode(" ", $info['modules']);
  463. }
  464. else {
  465. $info['modules'] = NULL;
  466. }
  467. if (!isset($info['version'])) {
  468. $info['version'] = '1.0';
  469. }
  470. }
  471. if (isset($field)) {
  472. return isset($info[$field]) ? $info[$field] : NULL;
  473. }
  474. else {
  475. return $info;
  476. }
  477. }
  478. function demo_set_info($values = NULL) {
  479. if (isset($values['filename']) && is_array($values)) {
  480. // Check for valid filename
  481. if (!preg_match('/^[-_\.a-zA-Z0-9]+$/', $values['filename'])) {
  482. drupal_set_message(t('Invalid filename. It must only contain alphanumeric characters, dots, dashes and underscores. Other characters, including spaces, are not allowed.'), 'error');
  483. return FALSE;
  484. }
  485. if (!empty($values['description'])) {
  486. // parse_ini_file() doesn't allow certain characters in description
  487. $s = array("\r\n", "\r", "\n", '"');
  488. $r = array(' ', ' ', ' ', "'");
  489. $values['description'] = str_replace($s, $r, $values['description']);
  490. }
  491. else {
  492. // If new description is empty, try to use previous description.
  493. $old_file = demo_get_fileconfig($values['filename']);
  494. $old_description = demo_get_info($old_file['infofile'], 'description');
  495. if (!empty($old_description)) {
  496. $values['description'] = $old_description;
  497. }
  498. }
  499. // Set values
  500. $infos = array();
  501. $infos['filename'] = $values['filename'];
  502. $infos['description'] = '"' . $values['description'] . '"';
  503. $infos['modules'] = implode(' ', module_list());
  504. $infos['version'] = DEMO_DUMP_VERSION;
  505. // Write information to .info file
  506. $fileconfig = demo_get_fileconfig($values['filename']);
  507. $infofile = fopen($fileconfig['infofile'], 'w');
  508. foreach ($infos as $key => $info) {
  509. fwrite($infofile, $key . ' = ' . $info . "\n");
  510. }
  511. fclose($infofile);
  512. return $infos;
  513. }
  514. }
  515. /**
  516. * Returns a list of tables in the active database.
  517. *
  518. * Only returns tables whose prefix matches the configured one (or ones, if
  519. * there are multiple).
  520. */
  521. function demo_enum_tables() {
  522. $tables = array();
  523. // Load database specific functions.
  524. if (!demo_load_include()) {
  525. return FALSE;
  526. }
  527. $connection = Database::getConnection();
  528. $db_options = $connection->getConnectionOptions();
  529. // Create a regex that matches the table prefix(es).
  530. // We are only interested in non-empty table prefixes.
  531. $prefixes = array();
  532. if (!empty($db_options['prefix'])) {
  533. if (is_array($db_options['prefix'])) {
  534. $prefixes = array_filter($db_options['prefix']);
  535. }
  536. elseif ($db_options['prefix'] != '') {
  537. $prefixes['default'] = $db_options['prefix'];
  538. }
  539. $rx = '/^' . implode('|', $prefixes) . '/';
  540. }
  541. // Query the database engine for the table list.
  542. $result = _demo_enum_tables();
  543. foreach ($result as $table) {
  544. if (!empty($prefixes)) {
  545. // Check if table name matches a configured prefix.
  546. if (preg_match($rx, $table, $matches)) {
  547. $table_prefix = $matches[0];
  548. $plain_table = substr($table, strlen($table_prefix));
  549. if ($prefixes[$plain_table] == $table_prefix || $prefixes['default'] == $table_prefix) {
  550. $tables[$table] = array('schema' => TRUE, 'data' => TRUE);
  551. }
  552. }
  553. }
  554. else {
  555. $tables[$table] = array('schema' => TRUE, 'data' => TRUE);
  556. }
  557. }
  558. // Apply default exclude list.
  559. $excludes = array(
  560. // Drupal core.
  561. '{cache}',
  562. '{cache_bootstrap}',
  563. '{cache_block}',
  564. '{cache_content}',
  565. '{cache_field}',
  566. '{cache_filter}',
  567. '{cache_form}',
  568. '{cache_menu}',
  569. '{cache_page}',
  570. '{cache_path}',
  571. '{cache_update}',
  572. '{watchdog}',
  573. // CTools.
  574. '{ctools_object_cache}',
  575. // Administration menu.
  576. '{cache_admin_menu}',
  577. // Panels.
  578. '{panels_object_cache}',
  579. // Views.
  580. '{cache_views}',
  581. '{cache_views_data}',
  582. '{views_object_cache}',
  583. );
  584. foreach (array_map(array($connection, 'prefixTables'), $excludes) as $table) {
  585. if (isset($tables[$table])) {
  586. $tables[$table]['data'] = FALSE;
  587. }
  588. }
  589. return $tables;
  590. }
  591. /**
  592. * Retrieve a pipe delimited string of autocomplete suggestions for existing snapshots.
  593. */
  594. function demo_autocomplete($string = '') {
  595. $matches = array();
  596. if ($string && $fileconfig = demo_get_fileconfig()) {
  597. $string = preg_quote($string);
  598. $files = file_scan_directory($fileconfig['dumppath'], '/' . $string . '.*\.info$/');
  599. foreach ($files as $file) {
  600. $matches[$file->name] = check_plain($file->name);
  601. }
  602. }
  603. drupal_json_output($matches);
  604. }
  605. /**
  606. * Transfer (download) a snapshot file.
  607. *
  608. * @param $filename
  609. * The snapshot filename to transfer.
  610. * @param $type
  611. * The file type, i.e. extension to transfer.
  612. *
  613. * @todo Allow to download an bundled archive of snapshot files.
  614. */
  615. function demo_download($filename, $type) {
  616. $fileconfig = demo_get_fileconfig($filename);
  617. if (!isset($fileconfig[$type . 'file']) || !file_exists($fileconfig[$type . 'file'])) {
  618. return MENU_NOT_FOUND;
  619. }
  620. // Force the client to re-download and trigger a file save download.
  621. $headers = array(
  622. 'Cache-Control: private',
  623. 'Content-Type: application/octet-stream',
  624. 'Content-Length: ' . filesize($fileconfig[$type . 'file']),
  625. 'Content-Disposition: attachment, filename=' . $fileconfig[$type],
  626. );
  627. file_transfer($fileconfig[$type . 'file'], $headers);
  628. }