Skip to content
Snippets Groups Projects
Commit 5636b7e7 authored by Ronan Dowling's avatar Ronan Dowling
Browse files

Adds drupal specific plugins

parent 67da91bd
No related branches found
No related tags found
No related merge requests found
......@@ -3,6 +3,9 @@ use BackupMigrate\Core\Config\Config;
require __DIR__.'/vendor/autoload.php';
define('BACKP_MIGRATE_MODULE_VERSION', '8.x-4.x-dev');
/**
* Back up a source to 1 or more destinations.
*
......@@ -79,6 +82,11 @@ function backup_migrate_backup_migrate_services_alter(\BackupMigrate\Core\Servic
$services->add('TempFileAdapter',
new \BackupMigrate\Drupal\File\DrupalTempFileAdapter(\Drupal::service('file_system'), 'temporary://', 'bam')
);
// $services->add('TempFileAdapter',
// new \BackupMigrate\Core\File\TempFileAdapter('/tmp/', 'bam')
// );
//
$services->add('TempFileManager',
new \BackupMigrate\Core\File\TempFileManager($services->get('TempFileAdapter'))
);
......@@ -89,6 +97,7 @@ function backup_migrate_backup_migrate_services_alter(\BackupMigrate\Core\Servic
);
$services->add('Archiver', new \BackupMigrate\Core\Service\PearTarArchiver());
$services->add('ArchiveWriter', new \BackupMigrate\Core\Service\TarArchiveWriter());
}
......@@ -106,9 +115,11 @@ function backup_migrate_backup_migrate_plugins_alter(\BackupMigrate\Core\Plugin\
$info = \Drupal\Core\Database\Database::getConnectionInfo('default', 'default');
$info = $info['default'];
if ($info['driver'] == 'mysql') {
$plugins->add(
'db',
new \BackupMigrate\Core\Source\MySQLiSource(new Config($info)));
$db = new \BackupMigrate\Core\Source\MySQLiSource(new Config($info));
$plugins->add('db', $db);
// Add the entire site
$plugins->add('site', new \BackupMigrate\Drupal\Source\DrupalSiteArchiveSource(new Config(['directory' => DRUPAL_ROOT]), $db));
}
// Add the public files
......@@ -117,12 +128,35 @@ function backup_migrate_backup_migrate_plugins_alter(\BackupMigrate\Core\Plugin\
new \BackupMigrate\Core\Source\FileDirectorySource(new Config(['directory' => 'public://']))
);
// Add a download destination.
$plugins->add('download', new \BackupMigrate\Drupal\Destination\DrupalBrowserDownloadDestination());
// Add a directory destination.
$plugins->add('public', new \BackupMigrate\Drupal\Destination\DrupalDirectoryDestination(new Config(['directory' => 'public://backups/'])));
// Add a private directory destination.
$plugins->add('private', new \BackupMigrate\Drupal\Destination\DrupalDirectoryDestination(new Config(['directory' => 'private://backups/'])));
// Add a debug destination.
$plugins->add('debug', new \BackupMigrate\Core\Destination\DebugDestination(new Config(['format' => 'html'])));
// Add a file naming filter.
$plugins->add('namer', new \BackupMigrate\Core\Filter\FileNamer());
// Add the Drupal utilities filter.
$plugins->add('utils', new \BackupMigrate\Drupal\Filter\DrupalUtils());
// Add a file metadata filter.
$plugins->add('metadata', new \BackupMigrate\Core\Filter\MetadataWriter(
new Config([
'generator' => 'Backup and Migrate for Drupal (https://www.drupal.org/project/backup_migrate)',
'generatorurl' => 'https://www.drupal.org/project/backup_migrate',
'generatorversion' => BACKP_MIGRATE_MODULE_VERSION
])
));
// Add an S3 destination.
// $bam->plugins()->add(
// new \BackupMigrate\Drupal\Destination\DrupalS3Destination(
......
......@@ -8,6 +8,7 @@
namespace BackupMigrate\Drupal\Config;
use BackupMigrate\Core\Config\ConfigInterface;
use Drupal\Core\Form\FormStateInterface;
/**
......@@ -31,11 +32,13 @@ class DrupalConfigHelper {
// Add the specified groups.
foreach ($plugin_schema['groups'] as $group_key => $item) {
$form[$group_key] = [
'#type' => 'fieldset',
'#title' => $item['title'],
'#tree' => FALSE,
];
if (!isset($form[$group_key])) {
$form[$group_key] = [
'#type' => 'fieldset',
'#title' => $item['title'],
'#tree' => FALSE,
];
}
}
// Add each of the fields.
......@@ -46,7 +49,11 @@ class DrupalConfigHelper {
$form_item['#type'] = 'textfield';
if (!empty($item['multiple'])) {
$form_item['#type'] = 'textarea';
$item['description'] .= t(' Add one item per line.');
$form_item['#description'] .= ' ' . t('Add one item per line.');
$form_item['#element_validate'] = [[new DrupalConfigHelper, 'validateMultiText']];
}
if (!empty($item['multiline'])) {
$form_item['#type'] = 'textarea';
}
break;
case 'password':
......@@ -97,4 +104,13 @@ class DrupalConfigHelper {
return $form;
}
/**
* Break a multi-line text value into an array.
*
* @param $element
* @param $form_state
*/
public static function validateMultiText(&$element, FormStateInterface &$form_state) {
$form_state->setValueForElement($element, array_map('trim', explode("\n", $element['#value'])));
}
}
\ No newline at end of file
<?php
/**
* @file
* Contains BackupMigrate\Drupal\Destination\DrupalDirectoryDestination
*/
namespace BackupMigrate\Drupal\Destination;
use BackupMigrate\Core\Destination\DirectoryDestination;
use BackupMigrate\Core\Exception\BackupMigrateException;
use BackupMigrate\Core\File\BackupFileReadableInterface;
use Drupal\Core\File\FileSystem;
/**
* Class DrupalDirectoryDestination
* @package BackupMigrate\Drupal\Destination
*/
class DrupalDirectoryDestination extends DirectoryDestination {
/**
* Do the actual file save. This function is called to save the data file AND
* the metadata sidecar file.
* @param \BackupMigrate\Core\File\BackupFileReadableInterface $file
* @throws \BackupMigrate\Core\Exception\BackupMigrateException
*/
function _saveFile(BackupFileReadableInterface $file) {
// Check if the directory exists.
$this->checkDirectory();
// @TODO Decide what the appropriate file_exists strategy should be.
file_unmanaged_move($file->realpath(), $this->confGet('directory') . $file->getFullName(), FILE_EXISTS_REPLACE);
}
/**
* Check that the directory can be used for backup.
*
* @throws \BackupMigrate\Core\Exception\BackupMigrateException
*/
protected function checkDirectory() {
// @TODO: Figure out if the file is or might be accessible via the web.
$dir = $this->confGet('directory');
$is_private = strpos($dir, 'private://') === 0;
// Attempt to create/prepare the directory if it is in the private directory
if ($is_private) {
if (!file_prepare_directory($dir, FILE_CREATE_DIRECTORY && FILE_MODIFY_PERMISSIONS)) {
throw new BackupMigrateException(
"The backup file could not be saved to '%dir' because the directory could not be created or cannot be written to. Please make sure your private files directory is writable by the web server.",
['%dir' => $dir]
);
}
}
// Not a private directory. Make sure it is outside the web root
else {
// If the file is local to the server.
$real = \Drupal::service('file_system')->realpath($dir);
if ($real) {
// If the file is within the docroot.
$in_root = strpos($real, DRUPAL_ROOT) === 0;
if ($in_root && !$is_private ) {
throw new BackupMigrateException(
"The backup file could not be saved to '%dir' because that directory may be publicly accessible via the web. Please save your backups to the private file directory or a directory outside of the web root.",
['%dir' => $dir]
);
}
}
}
// Do the regular exists/writable checks
parent::checkDirectory();
// @TODO: Warn if the realpath cannot be resolved (because we cannot determine if the file is publicly accessible)
}
}
\ No newline at end of file
<?php
/**
* @file
* Contains BackupMigrate\Drupal\Filter\DrupalUtils
*/
namespace BackupMigrate\Drupal\Filter;
use BackupMigrate\Core\File\BackupFileReadableInterface;
use BackupMigrate\Core\Plugin\PluginBase;
use BackupMigrate\Core\Config\Config;
use BackupMigrate\Core\Translation\TranslatableTrait;
use Drupal\Core\Database\Database;
/**
* Class DrupalUtils
* @package BackupMigrate\Drupal\Filter
*/
class DrupalUtils extends PluginBase {
use TranslatableTrait;
/**
* @var boolean Whether the site was put in maintenance mode before the operation.
*/
protected $maintenance_mode;
/**
* {@inheritdoc}
*/
public function configSchema($params = array()) {
$schema = array();
// Backup configuration
if ($params['operation'] == 'backup') {
$schema['groups']['advanced'] = [
'title' => 'Advanced Settings',
];
$schema['fields']['site_offline'] = [
'group' => 'advanced',
'type' => 'boolean',
'title' => $this->t('Take site offline'),
'description' => $this->t('Take the site offline during backup and show a maintenance message. Site will be taken back online once the backup is complete.'),
];
}
return $schema;
}
/**
* Get the default values for the plugin.
*
* @return \BackupMigrate\Core\Config\Config
*/
public function configDefaults() {
return new Config([
'disable_query_log' => TRUE,
'site_offline' => FALSE,
]);
}
/**
* Run before the backup/restore begins.
*/
public function setUp() {
$this->takeSiteOffline();
}
/**
* Run after the operation is complete.
*/
public function tearDown() {
$this->takeSiteOnline();
}
/**
* Take the site offline if we need to.
*/
protected function takeSiteOffline() {
// Take the site offline.
if ($this->confGet('site_offline') && !\Drupal::state()->get('system.maintenance_mode')) {
\Drupal::state()->set('system.maintenance_mode', TRUE);
$this->maintenance_mode = TRUE;
}
}
/**
* Take the site online if it was taken offline for this operation.
*/
protected function takeSiteOnline() {
// Take the site online again.
if ($this->maintenance_mode) {
\Drupal::state()->set('system.maintenance_mode', FALSE);
}
}
}
\ No newline at end of file
<?php
/**
* @file
* Contains BackupMigrate\Drupal\Source\DrupalPublicFilesSource
*/
namespace BackupMigrate\Drupal\Source;
use BackupMigrate\Core\Source\FileDirectorySource;
/**
* Class DrupalPublicFilesSource
* @package BackupMigrate\Drupal\Source
*/
class DrupalPublicFilesSource extends FileDirectorySource {
// @TODO: Add configuration defaults for excluded files
// @TODO: Allow modules to add their own excluded defaults
// @TODO: Fix the directory to 'public://'
}
\ No newline at end of file
<?php
use BackupMigrate\Core\Plugin\FileProcessorTrait;
use BackupMigrate\Core\Plugin\PluginBase;
use BackupMigrate\Core\Service\ArchiverInterface;
use BackupMigrate\Core\Source\FileDirectorySource;
use BackupMigrate\Core\Source\SourceInterface;
use BackupMigrate\Core\Translation\TranslatableTrait;
/**
* @file
* Contains ${NAMESPACE}\DrupalSiteArchiveSource
*/
namespace BackupMigrate\Drupal\Source;
use BackupMigrate\Core\Config\ConfigInterface;
use BackupMigrate\Core\Source\FileDirectorySource;
use BackupMigrate\Core\Source\SourceInterface;
/**
* Class DrupalSiteArchiveSource
* @package BackupMigrate\Drupal\Source
*/
class DrupalSiteArchiveSource extends FileDirectorySource
{
/**
* @var SourceInterface
*/
protected $db_source;
/**
* @param ConfigInterface|array $init
* @param \BackupMigrate\Core\Source\SourceInterface $db
* @param \BackupMigrate\Core\Source\SourceInterface $code
*/
public function __construct($init = array(), SourceInterface $db) {
parent::__construct($init);
$this->db_source = $db;
}
/**
* Get a list if files to be backed up from the given directory. Do not
* include files that match the 'exclude_filepaths' setting.
*
* @param string $dir The name of the directory to list.
* @return array
* @throws \BackupMigrate\Core\Exception\BackupMigrateException
* @throws \BackupMigrate\Core\Exception\IgnorableException
* @internal param $directory
*/
protected function getFilesToBackup($dir) {
$files = array();
// Add the database dump.
// @TODO: realpath contains the wrong filename and the PEAR archiver cannot rename files
$db = $this->getDbSource()->exportToFile();
$files['database.sql'] = $db->realpath();
// Add the manifest file.
$manifest = $this->getManifestFile();
$files['MANIFEST.ini'] = $manifest->realpath();
// Get all the files in the site.
foreach (parent::getFilesToBackup($dir) as $new => $real) {
// Prepend 'docroot' onto the local path.
$files['docroot/' . $new] = $real;
}
return $files;
}
/**
* Import to this source from the given backup file. This is the main restore
* function for this source.
*
* @param \BackupMigrate\Core\File\BackupFileReadableInterface $file
* The file to read the backup from. It will not be opened for reading
*/
public function importFromFile(\BackupMigrate\Core\File\BackupFileReadableInterface $file) {
// TODO: Implement importFromFile() method.
}
/**
* Get a file which contains the file
* @return \BackupMigrate\Core\File\BackupFileWritableInterface
*/
protected function getManifestFile() {
$out = $this->getTempFileManager()->create('ini');
$info = [
'Global' => [
'datestamp' => time(),
"formatversion" => "2011-07-02",
"generator" => "Backup and Migrate (http://drupal.org/project/backup_migrate)",
"generatorversion" => BACKP_MIGRATE_MODULE_VERSION,
],
'Site 0' => [
'version' => \Drupal::VERSION,
'name' => "Example.com",
'docroot' =>"docroot",
'sitedir' =>"docroot/sites/default",
'database-file-default' => "database.sql",
'database-file-driver' => "mysql",
'files-private' => "docroot/sites/default/private",
'files-public' => "docroot/sites/default/files"
]
];
$out->writeAll($this->arrayToINI($info));
return $out;
}
/**
* Translate a 2d array to an INI string which can be written to a file.
*
* @param array $info
* The array to convert. Must be an array of sections each of which is an array of field/value pairs.
* @return string
* The data in INI format.
*/
private function arrayToINI($info) {
$content = "";
foreach ($info as $section => $data) {
$content .= '['. $section .']' . "\n";
foreach ($data as $key => $val) {
$content .= $key . " = \"". $val ."\"\n";
}
$content .= "\n";
}
return $content;
}
/**
* @return SourceInterface
*/
public function getDbSource() {
return $this->db_source;
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment