sources.archivesource.inc 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. <?php
  2. /**
  3. * @file
  4. */
  5. require_once dirname(__FILE__) . '/sources.filesource.inc';
  6. /**
  7. * @file
  8. * A destination type for saving locally to the server.
  9. */
  10. /**
  11. * A destination type for saving locally to the server.
  12. *
  13. * @ingroup backup_migrate_destinations
  14. */
  15. class backup_migrate_files_destination_archivesource extends backup_migrate_destination_filesource {
  16. public $supported_ops = array('source');
  17. /**
  18. *
  19. */
  20. public function type_name() {
  21. return t("Site Archive Source");
  22. }
  23. /**
  24. * Declares the current files directory as a backup source..
  25. */
  26. public function sources() {
  27. $out = array();
  28. $out['archive'] = backup_migrate_create_destination('archive', array(
  29. 'machine_name' => 'archive',
  30. 'location' => '.',
  31. 'name' => t('Entire Site (code, files & DB)'),
  32. 'show_in_list' => FALSE,
  33. ));
  34. return $out;
  35. }
  36. /**
  37. * Returns a list of backup filetypes.
  38. */
  39. public function file_types() {
  40. return array(
  41. "sitearchive" => array(
  42. "extension" => "sitearchive.tar",
  43. "filemime" => "application/x-tar",
  44. "backup" => TRUE,
  45. "restore" => FALSE,
  46. ),
  47. );
  48. }
  49. /**
  50. * Gets the form for the settings for this destination.
  51. */
  52. public function backup_settings_default() {
  53. $out = parent::backup_settings_default();
  54. $excludes = explode("\n", $out['exclude_filepaths']);
  55. foreach ($excludes as $i => $exclude) {
  56. $excludes[$i] = 'public://' . $exclude;
  57. }
  58. $excludes[] = 'private://backup_migrate';
  59. $excludes[] = conf_path() . '/settings.php';
  60. $excludes[] = file_directory_temp();
  61. return array(
  62. 'exclude_filepaths' => implode("\n", $excludes),
  63. );
  64. }
  65. /**
  66. * Backup from this source.
  67. */
  68. public function _backup_to_file_php($file, $settings) {
  69. if ($this->check_libs()) {
  70. $base_dir = $this->get_realpath();
  71. $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
  72. $exclude = $this->get_excluded_paths($settings);
  73. $files = $this->get_files_to_backup($this->get_realpath(), $settings, $exclude);
  74. if ($files) {
  75. $manifest = $this->generate_manifest();
  76. $db = $this->get_db();
  77. $file->push_type('sitearchive');
  78. $gz = new Archive_Tar($file->filepath(), FALSE);
  79. $gz->addModify(array($manifest), $file->name . '/', dirname($manifest));
  80. $gz->addModify($files, $file->name . '/docroot', $base_dir);
  81. $gz->addModify($db, $file->name . '/', dirname($db));
  82. unlink($manifest);
  83. rmdir(dirname($manifest));
  84. unlink($db);
  85. rmdir(dirname($db));
  86. return $file;
  87. }
  88. backup_migrate_backup_fail('No files available.', array(), $settings);
  89. return FALSE;
  90. }
  91. return FALSE;
  92. }
  93. /**
  94. * Backup from this source.
  95. */
  96. public function _backup_to_file_cli($file, $settings) {
  97. if (!empty($settings->filters['use_cli']) && function_exists('backup_migrate_exec') && function_exists('escapeshellarg')) {
  98. $excluded_paths = empty($settings->filters['exclude_filepaths']) ? '' : $settings->filters['exclude_filepaths'];
  99. foreach ($this->get_excluded_paths($excluded_paths) as $path) {
  100. $exclude[] = '--exclude=' . escapeshellarg($path);
  101. }
  102. $exclude = implode(' ', $exclude);
  103. // Create a symlink in a temp directory so we can rename the file in the
  104. // archive.
  105. $temp = backup_migrate_temp_directory();
  106. $manifest = $this->generate_manifest();
  107. $db = $this->get_db();
  108. rename($db, $temp . '/database.sql');
  109. rename($manifest, $temp . '/MANIFEST.ini');
  110. $file->push_type('sitearchive');
  111. $link = $temp . '/docroot';
  112. $input = realpath($this->get_location());
  113. backup_migrate_exec("ln -s %input %link; tar --dereference -C %temp -rf %output $exclude .", array(
  114. '%output' => $file->filepath(),
  115. '%input' => $input,
  116. '%temp' => $temp,
  117. '%link' => $link,
  118. ));
  119. return $file;
  120. }
  121. return FALSE;
  122. }
  123. /**
  124. * Generates a manifest file.
  125. */
  126. public function generate_manifest() {
  127. $info = array(
  128. 'Global' => array(
  129. 'datestamp' => time(),
  130. 'formatversion' => '2011-07-02',
  131. 'generator' => 'Backup and Migrate (http://drupal.org/project/backup_migrate)',
  132. 'generatorversion' => BACKUP_MIGRATE_VERSION,
  133. ),
  134. 'Site 0' => array(
  135. 'version' => VERSION,
  136. 'name' => variable_get('site_name', ''),
  137. 'docroot' => 'docroot',
  138. 'sitedir' => 'docroot/' . conf_path(),
  139. 'database-file-default' => 'database.sql',
  140. 'database-file-driver' => 'mysql',
  141. ),
  142. );
  143. if ($private = variable_get('file_private_path', FALSE)) {
  144. $info['Site 0']['files-private'] = 'docroot/' . $private;
  145. }
  146. $info['Site 0']['files-public'] = 'docroot/' . variable_get('file_public_path', FALSE);
  147. $ini = $this->_array_to_ini($info);
  148. $tmpdir = backup_migrate_temp_directory();
  149. $filepath = $tmpdir . '/MANIFEST.ini';
  150. file_put_contents($filepath, $ini);
  151. return $filepath;
  152. }
  153. /**
  154. * Gets a database dump to add to the archive.
  155. */
  156. public function get_db() {
  157. require_once dirname(__FILE__) . '/destinations.inc';
  158. require_once dirname(__FILE__) . '/files.inc';
  159. require_once dirname(__FILE__) . '/filters.inc';
  160. require_once dirname(__FILE__) . '/profiles.inc';
  161. $file = new backup_file();
  162. // Clone the default settings so we can make changes without them leaking
  163. // out of this function.
  164. $settings = clone _backup_migrate_profile_saved_default_profile();
  165. $settings->source_id = 'db';
  166. $settings->filters['compression'] = 'none';
  167. // Execute the backup on the db with the default settings.
  168. $file = backup_migrate_filters_backup($file, $settings);
  169. // Generate a tmp file with the correct final title (because ArchiveTar
  170. // doesn't seem to allow renaming).
  171. $tmpdir = backup_migrate_temp_directory();
  172. $filepath = $tmpdir . '/database.sql';
  173. rename($file->filepath(), $filepath);
  174. return $filepath;
  175. }
  176. /**
  177. * Restores to this source.
  178. */
  179. public function _restore_from_file_php($file, &$settings) {
  180. $success = FALSE;
  181. if ($this->check_libs()) {
  182. $from = $file->pop_type();
  183. $temp = backup_migrate_temp_directory();
  184. $tar = new Archive_Tar($from->filepath());
  185. $tar->extractModify($temp, $file->name);
  186. // Parse the manifest.
  187. $manifest = $this->read_manifest($temp);
  188. // Currently only the first site in the archive is supported.
  189. $site = $manifest['Site 0'];
  190. $docroot = $temp . '/' . $site['docroot'];
  191. $sqlfile = $temp . '/' . $site['database-file-default'];
  192. $filepath = NULL;
  193. if (isset($site['files-private'])) {
  194. $filepath = $temp . '/' . $site['files-private'];
  195. }
  196. elseif (isset($site['files-public'])) {
  197. $filepath = $temp . '/' . $site['files-public'];
  198. }
  199. // Move the files from the temp directory.
  200. if ($filepath && file_exists($filepath)) {
  201. _backup_migrate_move_files($filepath, variable_get('file_public_path', conf_path() . '/files'));
  202. }
  203. else {
  204. _backup_migrate_message('Files were not restored because the archive did not seem to contain a files directory or was in a format that Backup and Migrate couldn\'t read', array(), 'warning');
  205. }
  206. // Restore the sql db.
  207. if ($sqlfile && file_exists($sqlfile)) {
  208. $db_settings = clone $settings;
  209. $db_settings->source_id = 'db';
  210. $file = new backup_file(array('filepath' => $sqlfile));
  211. $success = backup_migrate_filters_restore($file, $db_settings);
  212. }
  213. else {
  214. _backup_migrate_message('The database was not restored because the archive did not seem to contain a database backup or was in a format that Backup and Migrate couldn\'t read', array(), 'warning');
  215. }
  216. if ($docroot) {
  217. _backup_migrate_message('Backup and Migrate cannot restore the php code of the site for security reasons. You will have to copy the code to the server by hand if you wish to restore the full site.', array(), 'warning');
  218. }
  219. return $success && $file;
  220. }
  221. return FALSE;
  222. }
  223. /**
  224. * Restores to this source.
  225. */
  226. public function _restore_from_file_cli($file, &$settings) {
  227. // @todo implement the cli version of the restore.
  228. return FALSE;
  229. }
  230. /**
  231. * Generates a manifest file.
  232. */
  233. public function read_manifest($directory) {
  234. // Assume some defaults if values ore the manifest is missing.
  235. $defaults = array(
  236. 'docroot' => 'docroot',
  237. 'database-file-default' => 'database.sql',
  238. 'database-file-driver' => 'mysql',
  239. );
  240. $out = $this->_ini_to_array($directory . '/MANIFEST.ini');
  241. // Set the defaults.
  242. $out['Site 0'] = isset($out['Site 0']) ? $out['Site 0'] : array();
  243. $out['Site 0'] += $defaults;
  244. return $out;
  245. }
  246. /**
  247. * Converts an associated array to an ini format string.
  248. *
  249. * Only allows 2 levels of depth to allow parse_ini_file to parse.
  250. */
  251. public function _array_to_ini($sections) {
  252. $content = "";
  253. foreach ($sections as $section => $data) {
  254. $content .= '[' . $section . ']' . "\n";
  255. foreach ($data as $key => $val) {
  256. $content .= $key . " = \"" . $val . "\"\n";
  257. }
  258. $content .= "\n";
  259. }
  260. return $content;
  261. }
  262. /**
  263. * Converts an associated array to an ini format string.
  264. *
  265. * Only allows 2 levels of depth to allow parse_ini_file to parse.
  266. */
  267. public function _ini_to_array($path) {
  268. return parse_ini_file($path, TRUE);
  269. }
  270. }