Index: database_pgsql_dump.inc =================================================================== RCS file: database_pgsql_dump.inc diff -N database_pgsql_dump.inc --- database_pgsql_dump.inc 20 Sep 2007 15:34:41 -0000 1.2 +++ /dev/null 1 Jan 1970 00:00:00 -0000 @@ -1,14 +0,0 @@ - 'fieldset', + '#title' => t('Status'), + '#collapsible' => false, + ); + if (variable_get('demo_reset_last', 0)) { + $reset_date = format_date(variable_get('demo_reset_last', 0)); + } + else { + $reset_date = t('Never'); + } + $form['status'][] = array( + '#value' => t('

Last reset: !date

', array('!date' => $reset_date)), + ); + $form['status'][] = array( + '#value' => t('

Default snapshot: !snapshot

', array('!snapshot' => variable_get('demo_dump_cron', t('None')))), + ); + + $fileconfig = demo_get_fileconfig(); + + $form['dump'] = array( + '#type' => 'fieldset', + '#title' => t('Dump settings'), + '#collapsible' => true, + '#collapsed' => (variable_get('demo_reset_interval', 0) ? false : true), + ); + $period = drupal_map_assoc(array(0, 1800, 3600, 7200, 10800, 14400, 18000, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); + $period[0] = t('disabled'); + $form['dump']['interval'] = array( + '#type' => 'select', + '#title' => t('Automatically reset site every'), + '#default_value' => variable_get('demo_reset_interval', 0), + '#options' => $period, + '#description' => t('Select how often this demonstration site is automatically reset. Ensure that you have chosen a snapshot for cron runs in Manage snapshots first. Note: This requires cron to run at least within this interval.', array('!manage' => url('admin/settings/demo/manage'))), + ); + + $form['dump']['path'] = array( + '#type' => 'textfield', + '#title' => t('Dump path'), + '#default_value' => $fileconfig['path'], + '#size' => 30, + '#description' => t('Enter a writable directory where dump files of this demonstration site are stored, f.e. %files. The name of this site (e.g. %confpath) is automatically appended to this directory.

Note: For security reasons you should store site dumps outside of the document root of your webspace!', array('%files' => file_directory_path() .'/demo', '%confpath' => $fileconfig['site'])), + ); + $form[] = array( + '#type' => 'submit', + '#value' => t('Save'), + ); + + return $form; +} + +function demo_admin_settings_submit($form, &$form_state) { + if (!file_check_directory($form_state['values']['path'], FILE_CREATE_DIRECTORY)) { + form_set_error('path', t('The snapshot directory %directory could not be created.', array('%directory' => $form_state['values']['path']))); + return FALSE; + } + else { + variable_set('demo_dump_path', $form_state['values']['path']); + } + variable_set('demo_reset_interval', $form_state['values']['interval']); + drupal_set_message(t('The configuration options have been saved.')); +} + +function demo_manage() { + $form['dump'] = array( + '#type' => 'fieldset', + '#title' => t('Available snapshots'), + ); + $form = array_merge_recursive($form, demo_get_dumps()); + $form[] = array( + '#type' => 'submit', + '#value' => t('Set as default snapshot for cron'), + ); + $form[] = array( + '#type' => 'submit', + '#value' => t('Delete selected snapshot'), + '#submit' => array('demo_manage_delete_submit'), + ); + + return $form; +} + +function demo_manage_submit($form, &$form_state) { + variable_set('demo_dump_cron', $form_state['values']['filename']); + drupal_set_message(t('Snapshot %title will be used for upcoming cron runs.', array('%title' => $form_state['values']['filename']))); +} + +function demo_manage_delete_submit($form, &$form_state) { + $files = demo_get_fileconfig($form_state['values']['filename']); + unlink($files['sqlfile']); + unlink($files['infofile']); + drupal_set_message(t('Snapshot %title has been deleted.', array('%title' => $form_state['values']['filename']))); +} + +function demo_dump() { + $form = array(); + $form['dump']['filename'] = array( + '#title' => t('File name'), + '#type' => 'textfield', + '#autocomplete_path' => 'demo/autocomplete', + '#required' => true, + '#maxlength' => 128, + '#description' => t('Enter the snapshot file name without file extension. Allowed characters are a-z, 0-9, dashes ("-"), underscores ("_") and dots.'), + ); + $form['dump']['description'] = array( + '#title' => t('Description'), + '#type' => 'textarea', + '#rows' => 2, + '#description' => t('Optionally enter a description for this snapshot here. If no description is given and a snapshot with the same filename already exists, the previous description is used.'), + ); + return confirm_form($form, t('Are you sure you want to create a new snapshot?'), 'admin/settings/demo', t('If the above filename already exists, creating a new snapshot will overwrite the existing snapshot. This action cannot be undone.'), t('Create'), t('Cancel')); +} + +function demo_dump_submit($form, &$form_state) { + global $db_type; + + // Generate info file. + $info = demo_set_info($form_state['values']); + if (!$info) { + return false; + } + + // Include database specific functions. + $engine = ($db_type == 'mysqli' ? 'mysql' : $db_type); + $inc_file = drupal_get_path('module', 'demo') .'/database_'. $engine .'_dump.inc'; + if (file_exists($inc_file)) { + require_once $inc_file; + + // Increase PHP's max_execution_time for large dumps. + @set_time_limit(600); + + // Perform dump. + $fileconfig = demo_get_fileconfig($info['filename']); + $exclude = array('{cache}', '{cache_content}', '{cache_filter}', '{cache_menu}', '{cache_page}', '{cache_views}', '{panels_object_cache}', '{watchdog}'); + $exclude = array_map('db_prefix_tables', $exclude); + demo_dump_db($fileconfig['sqlfile'], $exclude); + } + else { + drupal_set_message(t('@engine support not implemented yet.', array('@engine' => ucfirst($engine))), 'error'); + } + + $form_state['redirect'] = 'admin/settings/demo/manage'; +} + +function demo_reset_confirm() { + $form['dump'] = array( + '#type' => 'fieldset', + '#title' => t('Available snapshots'), + ); + $form = array_merge_recursive($form, demo_get_dumps()); + + return confirm_form($form, t('Are you sure you want to reset the site?'), 'admin/settings/demo', t('Resetting the site will overwrite all changes that have been made to this Drupal installation since the chosen snapshot.

THIS ACTION CANNOT BE UNDONE!

'), t('Reset'), t('Cancel')); +} + +function demo_reset_confirm_submit($form, &$form_state) { + // Increase PHP's max_execution_time for large dumps. + @set_time_limit(600); + + // Reset site to chosen snapshot. + demo_reset($form_state['values']['filename']); + // Save time of last reset. + variable_set('demo_reset_last', time()); + + $form_state['redirect'] = isset($form_state['values']['redirect']) ? $form_state['values']['redirect'] : 'admin/settings/demo'; +} + +function demo_reset($filename = 'demo_site', $verbose = TRUE) { + // Load any database information in front of reset. + $demo_dump_cron = variable_get('demo_dump_cron', $filename); + $fileconfig = demo_get_fileconfig($filename); + $version = demo_get_info($fileconfig['infofile'], 'version'); + $is_version_1_0_dump = version_compare($version, '1.1', '<'); + + if (file_exists($fileconfig['sqlfile']) && $fp = fopen($fileconfig['sqlfile'], 'r')) { + // Drop tables + $dt_watchdog = db_prefix_tables('{watchdog}'); + foreach (demo_enum_tables() as $table) { + // Skip watchdog, except for legacy dumps that included the watchdog table + if ($table != $dt_watchdog || $is_version_1_0_dump) { + db_query("DROP TABLE %s", $table); + } + } + + // Load data from snapshot + $query = ''; + $success = TRUE; + while (!feof($fp)) { + $line = fgets($fp, 16384); + if ($line && $line != "\n" && strncmp($line, '--', 2) && strncmp($line, '#', 1)) { + $query .= $line; + if (substr($line, -2) == ";\n") { + if (!_db_query($query, FALSE)) { + if ($verbose) { + // Don't use t() here, as the locale_* tables might not (yet) exist. + drupal_set_message(strtr('Query failed: %query', array('%query' => $query)), 'error'); + } + $success = FALSE; + } + $query = ''; + } + } + } + fclose($fp); + + if ($success) { + $message = t('Successfully restored database from %filename.', array('%filename' => $fileconfig['sqlfile'])); + } + else { + $message = t('Failed restoring database from %filename.', array('%filename' => $fileconfig['sqlfile'])); + } + if ($verbose) { + drupal_set_message($message); + } + watchdog('demo', $message, array(), $success ? WATCHDOG_NOTICE : WATCHDOG_ERROR); + + // Reset default dump to load on cron. + variable_set('demo_dump_cron', $demo_dump_cron); + return TRUE; + } + $message = t('Unable to open dump file %filename.', array('%filename' => $fileconfig['sqlfile'])); + if ($verbose) { + drupal_set_message($message, 'error'); + } + watchdog('demo', $message, array(), WATCHDOG_ERROR); + return FALSE; +} + +function demo_get_fileconfig($filename = 'demo_site') { + $fileconfig = array(); + + // Build dump path. + $fileconfig['path'] = variable_get('demo_dump_path', file_directory_path() .'/demo'); + $fileconfig['site'] = str_replace('sites', '', conf_path()); + $fileconfig['dumppath'] = $fileconfig['path'] . $fileconfig['site']; + + // Check if directory exists. + file_check_directory($fileconfig['path'], FILE_CREATE_DIRECTORY, 'path'); + if (!file_check_directory($fileconfig['dumppath'], FILE_CREATE_DIRECTORY, 'path')) { + return false; + } + + // Protect dump files. + $htaccess = $fileconfig['path'] ."/.htaccess"; + if (!is_file($htaccess)) { + $htaccess_lines = "# demo.module snapshots\n# Do not let the webserver serve anything under here!\n#\nDeny from all\n"; + if (($fp = fopen($htaccess, 'w')) && fputs($fp, $htaccess_lines)) { + fclose($fp); + chmod($htaccess, 0664); + } + } + + // Build SQL filename. + $fileconfig['sql'] = $filename .'.sql'; + $fileconfig['sqlfile'] = $fileconfig['path'] . $fileconfig['site'] .'/'. $filename .'.sql'; + + // Build info filename. + $fileconfig['info'] = $filename .'.info'; + $fileconfig['infofile'] = $fileconfig['path'] . $fileconfig['site'] .'/'. $filename .'.info'; + + return $fileconfig; +} + +function demo_get_dumps() { + $fileconfig = demo_get_fileconfig(); + + // Fetch list of available info files + $files = file_scan_directory($fileconfig['dumppath'], '.info$'); + + foreach ($files as $file => $object) { + $files[$file]->filemtime = filemtime($file); + $files[$file]->filesize = filesize(substr($file, 0, -4) .'sql'); + } + + // Sort snapshots by date (ascending file modification time) + uasort($files, create_function('$a, $b', 'return ($a->filemtime < $b->filemtime);')); + + $options = array(); + // Forms API does not pass selected value of individual radio buttons, + // so we manually insert an internal form value here. + $options['dump']['filename'] = array( + '#type' => 'value', + '#required' => true, + '#title' => t('Snapshot'), + ); + foreach ($files as $filename => $file) { + // Build basic file info + $files[$filename] = (array)$files[$filename]; + $info = demo_get_info($filename); + + // Convert file info for Forms API + $option = array(); + $option['#type'] = 'radio'; + $option['#name'] = 'filename'; + $option['#title'] = $info['filename'] .' ('. format_date($file->filemtime, 'small') .', '. format_size($file->filesize) .')'; + $option['#return_value'] = $info['filename']; + if ($info['filename'] == variable_get('demo_dump_cron', 'demo_site')) { + $option['#value'] = $info['filename']; + } + $option['#description'] = ''; + if (!empty($info['description'])) { + $option['#description'] .= $info['description'] .'

'; + } + if (count($info['modules']) > 1) { + // Remove required core modules and obvious modules from module list. + $info['modules'] = array_diff($info['modules'], array('block', 'filter', 'node', 'system', 'user', 'watchdog', 'demo')); + // Sort module list alphabetically. + sort($info['modules']); + $option['#description'] .= t('Modules: ') . implode(', ', $info['modules']); + } + $option['#attributes'] = array('onclick' => "$('.description', this.parentNode.parentNode).slideToggle();"); + + $options['dump'][] = $option; + } + + // Attach stylesheet to initially hide descriptions + drupal_add_js("$('div.form-item div.description', $('form')).hide();", 'inline', 'footer'); + + return $options; +} + +function demo_get_info($filename, $field = NULL) { + $info = array(); + + if (file_exists($filename)) { + $info = parse_ini_file($filename); + + if (isset($info['modules'])) { + $info['modules'] = explode(" ", $info['modules']); + } + else { + $info['modules'] = null; + } + + if (!isset($info['version'])) { + $info['version'] = '1.0'; + } + } + + if (isset($field)) { + return isset($info[$field]) ? $info[$field] : NULL; + } + else { + return $info; + } +} + +function demo_set_info($values = null) { + if (isset($values['filename']) && is_array($values)) { + // Check for valid filename + if (!preg_match('/^[-_\.a-zA-Z0-9]+$/', $values['filename'])) { + drupal_set_message(t('Dump filename %title must contain alphanumeric characters, dots, dashes and underscores only. Other characters, including blanks (spaces), are not allowed.', array('%title' => $values['filename'])), 'error'); + return false; + } + + if (!empty($values['description'])) { + // parse_ini_file() doesn't allow certain characters in description + $s = array("\r\n", "\r", "\n", '"'); + $r = array(' ', ' ', ' ', "'"); + $values['description'] = str_replace($s, $r, $values['description']); + } + else { + // If new description is empty, try to use previous description. + $old_file = demo_get_fileconfig($values['filename']); + $old_description = demo_get_info($old_file['infofile'], 'description'); + if (!empty($old_description)) { + $values['description'] = $old_description; + } + } + + // Set values + $infos = array(); + $infos['filename'] = $values['filename']; + $infos['description'] = '"'. $values['description'] .'"'; + $infos['modules'] = implode(' ', module_list()); + $infos['version'] = DEMO_DUMP_VERSION; + + // Write information to .info file + $fileconfig = demo_get_fileconfig($values['filename']); + $infofile = fopen($fileconfig['infofile'], 'w'); + foreach ($infos as $key => $info) { + fwrite($infofile, $key .' = '. $info ."\n"); + } + fclose($infofile); + + return $infos; + } +} + +/** + * Returns a list of tables in the active database. + * + * Only returns tables whose prefix matches the configured one (or ones, if + * there are multiple). + */ +function demo_enum_tables() { + global $db_prefix; + + $tables = array(); + + if (is_array($db_prefix)) { + // Create a regular expression for table prefix matching. + $rx = '/^'. implode('|', array_filter($db_prefix)) .'/'; + } + else if ($db_prefix != '') { + $rx = '/^'. $db_prefix .'/'; + } + + switch ($GLOBALS['db_type']) { + case 'mysql': + case 'mysqli': + $result = db_query("SHOW TABLES"); + break; + case 'pgsql': + $result = db_query("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", 'public'); + break; + } + + while ($table = db_fetch_array($result)) { + $table = reset($table); + if (is_array($db_prefix)) { + // Check if table name matches a configured prefix. + if (preg_match($rx, $table, $matches)) { + $table_prefix = $matches[0]; + $plain_table = substr($table, strlen($table_prefix)); + if ($db_prefix[$plain_table] == $table_prefix || $db_prefix['default'] == $table_prefix) { + $tables[] = $table; + } + } + } + else if ($db_prefix != '') { + if (preg_match($rx, $table)) { + $tables[] = $table; + } + } + else { + $tables[] = $table; + } + } + + return $tables; +} + +/** + * Retrieve a pipe delimited string of autocomplete suggestions for existing + * snapshot names. + */ +function demo_autocomplete($string = '') { + $matches = array(); + if ($string && $fileconfig = demo_get_fileconfig()) { + $string = preg_quote($string); + $files = file_scan_directory($fileconfig['dumppath'], $string .'.*\.info$'); + foreach ($files as $file) { + $matches[$file->name] = check_plain($file->name); + } + } + print drupal_to_js($matches); + exit(); +} + Index: demo.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/demo/demo.module,v retrieving revision 1.19.2.7 diff -u -p -r1.19.2.7 demo.module --- demo.module 12 Sep 2008 20:45:22 -0000 1.19.2.7 +++ demo.module 28 Oct 2008 18:33:07 -0000 @@ -6,18 +6,6 @@ * Demonstration Site module */ -define('DEMO_DUMP_VERSION', '1.1'); - -/** - * Implementation of hook_help(). - */ -function demo_help($path, $arg) { - switch ($path) { - case 'admin/settings/demo': - return; - } -} - /** * Implementation of hook_perm(). */ @@ -38,6 +26,7 @@ function demo_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('demo_admin_settings'), 'access arguments' => $admin_access, + 'file' => 'demo.admin.inc', ); $items['admin/settings/demo/maintenance'] = array( 'title' => 'Status', @@ -49,6 +38,7 @@ function demo_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('demo_manage'), 'access arguments' => $admin_access, + 'file' => 'demo.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 1, ); @@ -57,6 +47,7 @@ function demo_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('demo_dump'), 'access arguments' => $admin_access, + 'file' => 'demo.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 2, ); @@ -65,6 +56,7 @@ function demo_menu() { 'page callback' => 'drupal_get_form', 'page arguments' => array('demo_reset_confirm'), 'access arguments' => $admin_access, + 'file' => 'demo.admin.inc', 'type' => MENU_LOCAL_TASK, 'weight' => 3, ); @@ -72,20 +64,20 @@ function demo_menu() { 'title' => 'Demo Site autocomplete', 'page callback' => 'demo_autocomplete', 'access arguments' => $admin_access, + 'file' => 'demo.admin.inc', 'type' => MENU_CALLBACK, ); return $items; } +/** + * Implementation of hook_block(). + */ function demo_block($op = 'list', $delta = 0, $edit = array()) { switch ($op) { case 'list': - $blocks[0] = array( - 'info' => t('Demo site reset'), - 'weight' => 0, - 'enabled' => 1, - 'region' => 'right', - ); + $blocks[0] = array('info' => t('Demo site reset'), + 'status' => 1, 'region' => 'right', 'cache' => BLOCK_NO_CACHE); return $blocks; case 'view': @@ -97,184 +89,7 @@ function demo_block($op = 'list', $delta } } -function demo_admin_settings() { - global $base_url; - - $form['status'] = array( - '#type' => 'fieldset', - '#title' => t('Status'), - '#collapsible' => false, - ); - if (variable_get('demo_reset_last', 0)) { - $reset_date = format_date(variable_get('demo_reset_last', 0)); - } - else { - $reset_date = t('Never'); - } - $form['status'][] = array( - '#value' => t('

Last reset: !date

', array('!date' => $reset_date)), - ); - $form['status'][] = array( - '#value' => t('

Default snapshot: !snapshot

', array('!snapshot' => variable_get('demo_dump_cron', t('None')))), - ); - - $fileconfig = demo_get_fileconfig(); - - $form['dump'] = array( - '#type' => 'fieldset', - '#title' => t('Dump settings'), - '#collapsible' => true, - '#collapsed' => (variable_get('demo_reset_interval', 0) ? false : true), - ); - $period = drupal_map_assoc(array(0, 1800, 3600, 7200, 10800, 14400, 18000, 21600, 32400, 43200, 86400, 172800, 259200, 604800, 1209600, 2419200, 4838400, 9676800), 'format_interval'); - $period[0] = t('disabled'); - $form['dump']['interval'] = array( - '#type' => 'select', - '#title' => t('Automatically reset site every'), - '#default_value' => variable_get('demo_reset_interval', 0), - '#options' => $period, - '#description' => t('Select how often this demonstration site is automatically reset. Ensure that you have chosen a snapshot for cron runs in Manage snapshots first. Note: This requires cron to run at least within this interval.', array('!manage' => url('admin/settings/demo/manage'))), - ); - - $form['dump']['path'] = array( - '#type' => 'textfield', - '#title' => t('Dump path'), - '#default_value' => $fileconfig['path'], - '#size' => 30, - '#description' => t('Enter a writable directory where dump files of this demonstration site are stored, f.e. %files. The name of this site (e.g. %confpath) is automatically appended to this directory.

Note: For security reasons you should store site dumps outside of the document root of your webspace!', array('%files' => file_directory_path() .'/demo', '%confpath' => $fileconfig['site'])), - ); - $form[] = array( - '#type' => 'submit', - '#value' => t('Save'), - ); - - return $form; -} - -function demo_admin_settings_submit($form, &$form_state) { - if (!file_check_directory($form_state['values']['path'], FILE_CREATE_DIRECTORY)) { - form_set_error('path', t('The snapshot directory %directory could not be created.', array('%directory' => $form_state['values']['path']))); - return FALSE; - } - else { - variable_set('demo_dump_path', $form_state['values']['path']); - } - variable_set('demo_reset_interval', $form_state['values']['interval']); - drupal_set_message(t('The configuration options have been saved.')); -} - -function demo_manage() { - $form['dump'] = array( - '#type' => 'fieldset', - '#title' => t('Available snapshots'), - ); - $form = array_merge_recursive($form, demo_get_dumps()); - $form[] = array( - '#type' => 'submit', - '#value' => t('Set as default snapshot for cron'), - ); - $form[] = array( - '#type' => 'submit', - '#value' => t('Delete selected snapshot'), - '#submit' => array('demo_manage_delete_submit'), - ); - - return $form; -} - -function demo_manage_submit($form, &$form_state) { - variable_set('demo_dump_cron', $form_state['values']['filename']); - drupal_set_message(t('Snapshot %title will be used for upcoming cron runs.', array('%title' => $form_state['values']['filename']))); -} - -function demo_manage_delete_submit($form, &$form_state) { - $files = demo_get_fileconfig($form_state['values']['filename']); - unlink($files['sqlfile']); - unlink($files['infofile']); - drupal_set_message(t('Snapshot %title has been deleted.', array('%title' => $form_state['values']['filename']))); -} - -function demo_dump() { - $form = array(); - $form['dump']['filename'] = array( - '#title' => t('File name'), - '#type' => 'textfield', - '#autocomplete_path' => 'demo/autocomplete', - '#required' => true, - '#maxlength' => 128, - '#description' => t('Enter the snapshot file name without file extension. Allowed characters are a-z, 0-9, dashes ("-"), underscores ("_") and dots.'), - ); - $form['dump']['description'] = array( - '#title' => t('Description'), - '#type' => 'textarea', - '#rows' => 2, - '#description' => t('Optionally enter a description for this snapshot here. If no description is given and a snapshot with the same filename already exists, the previous description is used.'), - ); - return confirm_form($form, t('Are you sure you want to create a new snapshot?'), 'admin/settings/demo', t('If the above filename already exists, creating a new snapshot will overwrite the existing snapshot. This action cannot be undone.'), t('Create'), t('Cancel')); -} - -function demo_dump_submit($form, &$form_state) { - global $db_type; - - // Write .info file - $info = demo_set_info($form_state['values']); - if (!$info) { - return false; - } - - // Include database specific functions - switch ($db_type) { - case 'mysqli': - $engine = 'mysql'; - break; - default: - $engine = $db_type; - break; - } - require_once drupal_get_path('module', 'demo') ."/database_{$engine}_dump.inc"; - - // Increase PHP's max_execution_time for large dumps. - @set_time_limit(600); - - // Perform dump - $fileconfig = demo_get_fileconfig($info['filename']); - $exclude = array('{cache}', '{cache_content}', '{cache_filter}', '{cache_menu}', '{cache_page}', '{cache_views}', '{panels_object_cache}', '{watchdog}'); - $exclude = array_map('db_prefix_tables', $exclude); - demo_dump_db($fileconfig['sqlfile'], $exclude); - - $form_state['redirect'] = 'admin/settings/demo/manage'; -} - -function demo_reset_confirm() { - $form['dump'] = array( - '#type' => 'fieldset', - '#title' => t('Available snapshots'), - ); - $form = array_merge_recursive($form, demo_get_dumps()); - - return confirm_form($form, t('Are you sure you want to reset the site?'), 'admin/settings/demo', t('Resetting the site will overwrite all changes that have been made to this Drupal installation since the chosen snapshot.

THIS ACTION CANNOT BE UNDONE!

'), t('Reset'), t('Cancel')); -} - -function demo_reset_confirm_submit($form, &$form_state) { - // Increase PHP's max_execution_time for large dumps. - @set_time_limit(600); - - // Reset site to chosen snapshot - demo_reset($form_state['values']['filename']); - // Save time of last reset - variable_set('demo_reset_last', time()); - - if (isset($form_state['values']['redirect'])) { - $form_state['redirect'] = $form_state['values']['redirect']; - } - else { - $form_state['redirect'] = 'admin/settings/demo'; - } -} - function demo_reset_now() { - $form = array(); - $form['#submit'][] = 'demo_reset_confirm_submit'; $form['redirect'] = array( '#type' => 'value', '#value' => $_GET['q'], @@ -290,284 +105,12 @@ function demo_reset_now() { '#type' => 'submit', '#value' => t('Reset now'), ); - return $form; } -function demo_reset($filename = 'demo_site', $verbose = TRUE) { - // Load any database information in front of reset. - $demo_dump_cron = variable_get('demo_dump_cron', $filename); - $fileconfig = demo_get_fileconfig($filename); - $version = demo_get_info($fileconfig['infofile'], 'version'); - $is_version_1_0_dump = version_compare($version, '1.1', '<'); - - if (file_exists($fileconfig['sqlfile']) && $fp = fopen($fileconfig['sqlfile'], 'r')) { - // Drop tables - $dt_watchdog = db_prefix_tables('{watchdog}'); - foreach (demo_enum_tables() as $table) { - // Skip watchdog, except for legacy dumps that included the watchdog table - if ($table != $dt_watchdog || $is_version_1_0_dump) { - db_query("DROP TABLE %s", $table); - } - } - - // Load data from snapshot - $query = ''; - $success = TRUE; - while (!feof($fp)) { - $line = fgets($fp, 16384); - if ($line && $line != "\n" && strncmp($line, '--', 2) && strncmp($line, '#', 1)) { - $query .= $line; - if (substr($line, -2) == ";\n") { - if (!_db_query($query, FALSE)) { - if ($verbose) { - // Don't use t() here, as the locale_* tables might not (yet) exist. - drupal_set_message(strtr('Query failed: %query', array('%query' => $query)), 'error'); - } - $success = FALSE; - } - $query = ''; - } - } - } - fclose($fp); - - if ($success) { - $message = t('Successfully restored database from %filename.', array('%filename' => $fileconfig['sqlfile'])); - } - else { - $message = t('Failed restoring database from %filename.', array('%filename' => $fileconfig['sqlfile'])); - } - if ($verbose) { - drupal_set_message($message); - } - watchdog('demo', $message, array(), $success ? WATCHDOG_NOTICE : WATCHDOG_ERROR); - - // Reset default dump to load on cron. - variable_set('demo_dump_cron', $demo_dump_cron); - return TRUE; - } - $message = t('Unable to open dump file %filename.', array('%filename' => $fileconfig['sqlfile'])); - if ($verbose) { - drupal_set_message($message, 'error'); - } - watchdog('demo', $message, array(), WATCHDOG_ERROR); - return FALSE; -} - -function demo_get_fileconfig($filename = 'demo_site') { - $fileconfig = array(); - - // Build dump path. - $fileconfig['path'] = variable_get('demo_dump_path', file_directory_path() .'/demo'); - $fileconfig['site'] = str_replace('sites', '', conf_path()); - $fileconfig['dumppath'] = $fileconfig['path'] . $fileconfig['site']; - - // Check if directory exists. - file_check_directory($fileconfig['path'], FILE_CREATE_DIRECTORY, 'path'); - if (!file_check_directory($fileconfig['dumppath'], FILE_CREATE_DIRECTORY, 'path')) { - return false; - } - - // Protect dump files. - $htaccess = $fileconfig['path'] ."/.htaccess"; - if (!is_file($htaccess)) { - $htaccess_lines = "# demo.module snapshots\n# Do not let the webserver serve anything under here!\n#\nDeny from all\n"; - if (($fp = fopen($htaccess, 'w')) && fputs($fp, $htaccess_lines)) { - fclose($fp); - chmod($htaccess, 0664); - } - } - - // Build SQL filename. - $fileconfig['sql'] = $filename .'.sql'; - $fileconfig['sqlfile'] = $fileconfig['path'] . $fileconfig['site'] .'/'. $filename .'.sql'; - - // Build info filename. - $fileconfig['info'] = $filename .'.info'; - $fileconfig['infofile'] = $fileconfig['path'] . $fileconfig['site'] .'/'. $filename .'.info'; - - return $fileconfig; -} - -function demo_get_dumps() { - $fileconfig = demo_get_fileconfig(); - - // Fetch list of available info files - $files = file_scan_directory($fileconfig['dumppath'], '.info$'); - - foreach ($files as $file => $object) { - $files[$file]->filemtime = filemtime($file); - $files[$file]->filesize = filesize(substr($file, 0, -4) .'sql'); - } - - // Sort snapshots by date (ascending file modification time) - uasort($files, create_function('$a, $b', 'return ($a->filemtime < $b->filemtime);')); - - $options = array(); - // Forms API does not pass selected value of individual radio buttons, - // so we manually insert an internal form value here. - $options['dump']['filename'] = array( - '#type' => 'value', - '#required' => true, - '#title' => t('Snapshot'), - ); - foreach ($files as $filename => $file) { - // Build basic file info - $files[$filename] = (array)$files[$filename]; - $info = demo_get_info($filename); - - // Convert file info for Forms API - $option = array(); - $option['#type'] = 'radio'; - $option['#name'] = 'filename'; - $option['#title'] = $info['filename'] .' ('. format_date($file->filemtime, 'small') .', '. format_size($file->filesize) .')'; - $option['#return_value'] = $info['filename']; - if ($info['filename'] == variable_get('demo_dump_cron', 'demo_site')) { - $option['#value'] = $info['filename']; - } - $option['#description'] = ''; - if (!empty($info['description'])) { - $option['#description'] .= $info['description'] .'

'; - } - if (count($info['modules']) > 1) { - // Remove required core modules and obvious modules from module list. - $info['modules'] = array_diff($info['modules'], array('block', 'filter', 'node', 'system', 'user', 'watchdog', 'demo')); - // Sort module list alphabetically. - sort($info['modules']); - $option['#description'] .= t('Modules: ') . implode(', ', $info['modules']); - } - $option['#attributes'] = array('onclick' => "$('.description', this.parentNode.parentNode).slideToggle();"); - - $options['dump'][] = $option; - } - - // Attach stylesheet to initially hide descriptions - drupal_add_js("$('div.form-item div.description', $('form')).hide();", 'inline', 'footer'); - - return $options; -} - -function demo_get_info($filename, $field = NULL) { - $info = array(); - - if (file_exists($filename)) { - $info = parse_ini_file($filename); - - if (isset($info['modules'])) { - $info['modules'] = explode(" ", $info['modules']); - } - else { - $info['modules'] = null; - } - - if (!isset($info['version'])) { - $info['version'] = '1.0'; - } - } - - if (isset($field)) { - return isset($info[$field]) ? $info[$field] : NULL; - } - else { - return $info; - } -} - -function demo_set_info($values = null) { - if (isset($values['filename']) && is_array($values)) { - // Check for valid filename - if (!preg_match('/^[-_\.a-zA-Z0-9]+$/', $values['filename'])) { - drupal_set_message(t('Dump filename %title must contain alphanumeric characters, dots, dashes and underscores only. Other characters, including blanks (spaces), are not allowed.', array('%title' => $values['filename'])), 'error'); - return false; - } - - if (!empty($values['description'])) { - // parse_ini_file() doesn't allow certain characters in description - $s = array("\r\n", "\r", "\n", '"'); - $r = array(' ', ' ', ' ', "'"); - $values['description'] = str_replace($s, $r, $values['description']); - } - else { - // If new description is empty, try to use previous description. - $old_file = demo_get_fileconfig($values['filename']); - $old_description = demo_get_info($old_file['infofile'], 'description'); - if (!empty($old_description)) { - $values['description'] = $old_description; - } - } - - // Set values - $infos = array(); - $infos['filename'] = $values['filename']; - $infos['description'] = '"'. $values['description'] .'"'; - $infos['modules'] = implode(' ', module_list()); - $infos['version'] = DEMO_DUMP_VERSION; - - // Write information to .info file - $fileconfig = demo_get_fileconfig($values['filename']); - $infofile = fopen($fileconfig['infofile'], 'w'); - foreach ($infos as $key => $info) { - fwrite($infofile, $key .' = '. $info ."\n"); - } - fclose($infofile); - - return $infos; - } -} - -/** - * Returns a list of tables in the active database. - * - * Only returns tables whose prefix matches the configured one (or ones, if - * there are multiple). - */ -function demo_enum_tables() { - global $db_prefix; - - $tables = array(); - - if (is_array($db_prefix)) { - // Create a regular expression for table prefix matching. - $rx = '/^'. implode('|', array_filter($db_prefix)) .'/'; - } - else if ($db_prefix != '') { - $rx = '/^'. $db_prefix .'/'; - } - - switch ($GLOBALS['db_type']) { - case 'mysql': - case 'mysqli': - $result = db_query("SHOW TABLES"); - break; - case 'pgsql': - $result = db_query("SELECT table_name FROM information_schema.tables WHERE table_schema = '%s'", 'public'); - break; - } - - while ($table = db_fetch_array($result)) { - $table = reset($table); - if (is_array($db_prefix)) { - // Check if table name matches a configured prefix. - if (preg_match($rx, $table, $matches)) { - $table_prefix = $matches[0]; - $plain_table = substr($table, strlen($table_prefix)); - if ($db_prefix[$plain_table] == $table_prefix || $db_prefix['default'] == $table_prefix) { - $tables[] = $table; - } - } - } - else if ($db_prefix != '') { - if (preg_match($rx, $table)) { - $tables[] = $table; - } - } - else { - $tables[] = $table; - } - } - - return $tables; +function demo_reset_now_submit($form, &$form_state) { + require_once drupal_get_path('module', 'demo') .'/demo.admin.inc'; + demo_reset_confirm_submit($form, $form_state); } /** @@ -575,27 +118,12 @@ function demo_enum_tables() { */ function demo_cron() { if ($interval = variable_get('demo_reset_interval', 0)) { - // See if it's time for another reset + // See if it's time for a reset. if ((time() - $interval) >= variable_get('demo_reset_last', 0)) { - demo_reset(variable_get('demo_dump_cron', 'demo_site'), false); + require_once drupal_get_path('module', 'demo') .'/demo.admin.inc'; + demo_reset(variable_get('demo_dump_cron', 'demo_site'), FALSE); variable_set('demo_reset_last', time()); } } } -/** - * Retrieve a pipe delimited string of autocomplete suggestions for existing - * snapshot names. - */ -function demo_autocomplete($string = '') { - $matches = array(); - if ($string && $fileconfig = demo_get_fileconfig()) { - $string = preg_quote($string); - $files = file_scan_directory($fileconfig['dumppath'], $string .'.*\.info$'); - foreach ($files as $file) { - $matches[$file->name] = check_plain($file->name); - } - } - print drupal_to_js($matches); - exit(); -}