Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #27

Merged
merged 12 commits into from
Nov 4, 2020
Merged

Dev #27

Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Configuration/EasyBackupConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,9 @@ public function getBackupDir(): string
{
return (string) $this->find('setting_backup_dir');
}

public function getPathsToBeBackuped(): string
{
return (string) $this->find('setting_paths_to_be_backuped');
}
}
73 changes: 31 additions & 42 deletions Controller/EasyBackupController.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ final class EasyBackupController extends AbstractController

public function __construct(string $dataDirectory, EasyBackupConfiguration $configuration)
{
$this->kimaiRootPath = dirname(dirname($dataDirectory)) . '/';
$this->kimaiRootPath = dirname(dirname($dataDirectory)).DIRECTORY_SEPARATOR;
$this->configuration = $configuration;
$this->dbUrl = $_ENV['DATABASE_URL'];
$this->filesystem = new Filesystem();
}

private function getBackupDirectory(): string
{
return $this->kimaiRootPath . $this->configuration->getBackupDir();
return $this->kimaiRootPath.$this->configuration->getBackupDir();
}

/**
Expand All @@ -83,8 +83,11 @@ public function indexAction(): Response
$filesAndDirs = array_diff($files, ['.', '..', self::GITIGNORE_NAME]);

foreach ($filesAndDirs as $fileOrDir) {
if (is_file($backupDir . $fileOrDir)) {
$filesizeInMb = round(filesize($backupDir . $fileOrDir) / 1048576, 2);
/* Make sure that only files are listet which match our wanted regex */

if (is_file($backupDir.$fileOrDir)
&& preg_match(self::REGEX_BACKUP_ZIP_NAME, $fileOrDir) == 1) {
$filesizeInMb = round(filesize($backupDir.$fileOrDir) / 1048576, 2);
$existingBackups[$fileOrDir] = $filesizeInMb;
}
}
Expand All @@ -107,15 +110,15 @@ public function createBackupAction(): Response

$backupName = date(self::BACKUP_NAME_DATE_FORMAT);
$backupDir = $this->getBackupDirectory();
$pluginBackupDir = $backupDir . $backupName . '/';
$pluginBackupDir = $backupDir.$backupName.DIRECTORY_SEPARATOR;

// Create the backup folder

$this->filesystem->mkdir($pluginBackupDir);

// If not yet existing, create a .gitignore to exclude the backup files.

$gitignoreFullPath = $backupDir . self::GITIGNORE_NAME;
$gitignoreFullPath = $backupDir.self::GITIGNORE_NAME;

if (!$this->filesystem->exists($gitignoreFullPath)) {
$this->filesystem->touch($gitignoreFullPath);
Expand All @@ -124,7 +127,7 @@ public function createBackupAction(): Response

// Save the specific kimai version and git head

$readMeFile = $pluginBackupDir . self::README_FILENAME;
$readMeFile = $pluginBackupDir.self::README_FILENAME;
$this->filesystem->touch($readMeFile);
$manifest = [
'git' => 'not available',
Expand All @@ -141,27 +144,11 @@ public function createBackupAction(): Response

// Backing up files and directories

$arrayOfPathsToBackup = [
'.env',
'config/packages/local.yaml',
'var/data/',
'var/plugins/',
'templates/invoice',
];

// Per default %kimai.invoice.documents% is:
// var/plugins/DemoBundle/Resources/invoices/
// var/invoices/
// templates/invoice/renderer/

$arrayOfPathsToBackup = array_merge(
$arrayOfPathsToBackup,
$this->getParameter('kimai.invoice.documents')
);
$arrayOfPathsToBackup = explode(PHP_EOL, $this->configuration->getPathsToBeBackuped());

foreach ($arrayOfPathsToBackup as $filename) {
$sourceFile = $this->kimaiRootPath . $filename;
$targetFile = $pluginBackupDir . $filename;
$sourceFile = $this->kimaiRootPath.$filename;
$targetFile = $pluginBackupDir.$filename;

if ($this->filesystem->exists($sourceFile)) {
if (is_dir($sourceFile)) {
Expand All @@ -171,13 +158,15 @@ public function createBackupAction(): Response
if (is_file($sourceFile)) {
$this->filesystem->copy($sourceFile, $targetFile);
}

// Todo: Add error messages
}
}

$sqlDumpName = $pluginBackupDir . self::SQL_DUMP_FILENAME;
$sqlDumpName = $pluginBackupDir.self::SQL_DUMP_FILENAME;

$this->backupDatabase($sqlDumpName);
$backupZipName = $backupDir . $backupName . '.zip';
$backupZipName = $backupDir.$backupName.'.zip';

$this->zipData($pluginBackupDir, $backupZipName);

Expand All @@ -194,7 +183,7 @@ public function createBackupAction(): Response
/**
* @Route(path="/download", name="download", methods={"GET"})
* @param Request $request
*
* @return Response
*/
public function downloadAction(Request $request): Response
Expand All @@ -204,7 +193,7 @@ public function downloadAction(Request $request): Response
// Validate the given user input (filename)

if (preg_match(self::REGEX_BACKUP_ZIP_NAME, $backupName)) {
$zipNameAbsolute = $this->getBackupDirectory() . $backupName;
$zipNameAbsolute = $this->getBackupDirectory().$backupName;

if ($this->filesystem->exists($zipNameAbsolute)) {
$response = new Response(file_get_contents($zipNameAbsolute));
Expand All @@ -225,7 +214,7 @@ public function downloadAction(Request $request): Response
/**
* @Route(path="/delete", name="delete", methods={"GET"})
* @param Request $request
*
* @return \Symfony\Component\HttpFoundation\Response
*/
public function deleteAction(Request $request)
Expand All @@ -235,7 +224,7 @@ public function deleteAction(Request $request)
// Validate the given user input (filename)

if (preg_match(self::REGEX_BACKUP_ZIP_NAME, $dirname)) {
$path = $this->getBackupDirectory() . $dirname;
$path = $this->getBackupDirectory().$dirname;

if ($this->filesystem->exists($path)) {
$this->filesystem->remove($path);
Expand Down Expand Up @@ -285,7 +274,7 @@ private function backupDatabase(string $sqlDumpName)
$this->filesystem->touch($sqlDumpName);

foreach ($outputArr as $line) {
$this->filesystem->appendToFile($sqlDumpName, $line . "\n");
$this->filesystem->appendToFile($sqlDumpName, $line."\n");
}
}
}
Expand All @@ -302,17 +291,17 @@ private function zipData($source, $destination)
$files = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source), \RecursiveIteratorIterator::SELF_FIRST);

foreach ($files as $file) {

// Ignore "." and ".." folders
if( in_array(substr($file, strrpos($file, '/')+1), array('.', '..')) )
if (in_array(substr($file, strrpos($file, DIRECTORY_SEPARATOR) + 1), ['.', '..'])) {
continue;
}

$file = realpath($file);

if (is_dir($file) === true) {
$zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));

$zip->addEmptyDir(str_replace($source.DIRECTORY_SEPARATOR, '', $file.DIRECTORY_SEPARATOR));
} elseif (is_file($file) === true) {
$zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
$zip->addFromString(str_replace($source.DIRECTORY_SEPARATOR, '', $file), file_get_contents($file));
}
}
} elseif (is_file($source) === true) {
Expand All @@ -337,7 +326,7 @@ private function checkStatus()
{
$status = [];

$path = $this->kimaiRootPath . 'var';
$path = $this->kimaiRootPath.'var';
$status["Path '$path' readable"] = is_readable($path);
$status["Path '$path' writable"] = is_writable($path);
$status["PHP extension 'zip' loaded"] = extension_loaded('zip');
Expand All @@ -347,7 +336,7 @@ private function checkStatus()
$status[$cmd] = exec($cmd);

$cmd = $this->configuration->getMysqlDumpCommand();
$cmd = explode(' ', $cmd)[0] . ' --version';
$cmd = explode(' ', $cmd)[0].' --version';
$status[$cmd] = exec($cmd);

return $status;
Expand All @@ -356,9 +345,9 @@ private function checkStatus()
private function getKimaiVersion(bool $full = false): string
{
if ($full) {
return Constants::SOFTWARE . ' - ' . Constants::VERSION . ' ' . Constants::STATUS;
return Constants::SOFTWARE.' - '.Constants::VERSION.' '.Constants::STATUS;
}

return Constants::VERSION . ' ' . Constants::STATUS;
return Constants::VERSION.' '.Constants::STATUS;
}
}
14 changes: 14 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ public function getConfigTreeBuilder()
/** @var ArrayNodeDefinition $rootNode */
$rootNode = $treeBuilder->getRootNode();

$arrayOfPathsToBackup = [
'.env',
'config/packages/local.yaml',
'var/data/',
'var/plugins/',
'var/invoices/',
'templates/invoice/',
'var/export/',
'templates/export/',
];

$rootNode
->addDefaultsIfNotSet()
->children()
Expand All @@ -33,6 +44,9 @@ public function getConfigTreeBuilder()
->scalarNode('setting_backup_dir')
->defaultValue('var/easy_backup/')
->end()
->scalarNode('setting_paths_to_be_backuped')
->defaultValue(implode(PHP_EOL, $arrayOfPathsToBackup))
->end()
->end()
->end();

Expand Down
7 changes: 7 additions & 0 deletions EventSubscriber/SystemConfigurationSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use App\Form\Model\Configuration;
use App\Form\Model\SystemConfiguration as SystemConfigurationModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;

class SystemConfigurationSubscriber implements EventSubscriberInterface
Expand Down Expand Up @@ -42,6 +43,12 @@ public function onSystemConfiguration(SystemConfigurationEvent $event)
->setTranslationDomain('system-configuration')
->setRequired(false)
->setType(TextType::class),
(new Configuration())
->setName('easy_backup.setting_paths_to_be_backuped')
->setLabel('easy_backup.setting_paths_to_be_backuped')
->setTranslationDomain('system-configuration')
->setRequired(false)
->setType(TextareaType::class),
])
);
}
Expand Down
23 changes: 20 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,21 @@ Make sure its writable by your webserver! We don't use the recommended

### What files are backed up?

Currently backuped directories and files are:
Currently per default backuped directories (incl. sub directories) and files are:

```
.env
config/packages/local.yaml
var/data/
var/plugins/
%kimai.invoice.documents%
var/invoices/
templates/invoice
var/export/
templates/export/
```
You are free to edit this list via the Kimai settings page. Place each filename or paths in a seperate line. Make sure that there are no empty lines. Root path is your kimai installation path.

![Update the paths to your needs](https://github.com/mxgross/EasyBackupBundle/blob/master/screenshot_files_and_paths_to_be_backed_up.jpg?raw=true)

According to the [backup docu](https://www.kimai.org/documentation/backups.html) the Kimai version should be saved to.
Also the current git head.
Expand All @@ -83,6 +89,12 @@ Per default it is
```
/usr/bin/mysqldump --user={user} --password={password} --host={host} --port={port} --single-transaction --force {database}
```

On a windows system with XAMPP as webserver the command could look like this
```
C:\xampp\mysql\bin\mysqldump --user={user} --password={password} --host={host} --port={port} --single-transaction --force {database}
```

You can remove or add parameters here if you need to. The variables in the curly braces will be replaced during the execution of the backup. All information for these variables are gathered from the DATABASE_URL defined in the .env file.
```
# DATABASE_URL=mysql://user:password@host:port/database
Expand All @@ -101,5 +113,10 @@ By default, this are assigned to all users with the role `ROLE_SUPER_ADMIN`.
**Please adjust the permission settings in your user administration.**

## Restore
Currently the plugin has no automation to restore a backup. You have to do it by hand. Just copy the backuped directories and files into your Kimai2 installation. Some hints can be found here: [Official Kimai2 backup and restore docu](https://www.kimai.org/documentation/backups.html)
Currently the plugin has no automation to restore a backup per click. You have to do it by hand. Just copy the backuped directories and files into your Kimai2 installation. Some hints can be found here: [Official Kimai2 backup and restore docu](https://www.kimai.org/documentation/backups.html)

If you are using a mysql/mariadb database you can import the backuped .sql file with tools like phpMyAdmin or by the command
```
mysql -u username -p database_name < file.sql
```
For additional information please lookup the mysql commands documentation.
4 changes: 4 additions & 0 deletions Resources/translations/system-configuration.de.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<source>label.easy_backup.setting_backup_dir</source>
<target>Backup Verzeichnis</target>
</trans-unit>
<trans-unit id="label.easy_backup.setting_paths_to_be_backuped">
<source>label.easy_backup.setting_paths_to_be_backuped</source>
<target>Zu sichernde Dateien und Pfade</target>
</trans-unit>
</body>
</file>
</xliff>
4 changes: 4 additions & 0 deletions Resources/translations/system-configuration.en.xliff
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
<source>label.easy_backup.setting_backup_dir</source>
<target>Backup directory</target>
</trans-unit>
<trans-unit id="label.easy_backup.setting_paths_to_be_backuped">
<source>label.easy_backup.setting_paths_to_be_backuped</source>
<target>Files and paths to be backed up</target>
mxgross marked this conversation as resolved.
Show resolved Hide resolved
</trans-unit>
</body>
</file>
</xliff>
Binary file added screenshot_files_and_paths_to_be_backed_up.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.