mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2024-11-15 20:47:15 +01:00
724 lines
26 KiB
PHP
724 lines
26 KiB
PHP
|
<?php
|
||
|
/*
|
||
|
**** COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
|
||
|
*
|
||
|
* Xentral (c) Xentral ERP Sorftware GmbH, Fuggerstrasse 11, D-86150 Augsburg, * Germany 2019
|
||
|
*
|
||
|
* This file is licensed under the Embedded Projects General Public License *Version 3.1.
|
||
|
*
|
||
|
* You should have received a copy of this license from your vendor and/or *along with this file; If not, please visit www.wawision.de/Lizenzhinweis
|
||
|
* to obtain the text of the corresponding license version.
|
||
|
*
|
||
|
**** END OF COPYRIGHT & LICENSE NOTICE *** DO NOT REMOVE ****
|
||
|
*/
|
||
|
?>
|
||
|
<?php
|
||
|
|
||
|
use Xentral\Components\Backup\Exception\LogException;
|
||
|
use Xentral\Components\Backup\Logger\BackupLog;
|
||
|
use Xentral\Components\Http\Request;
|
||
|
use Xentral\Modules\Backup\BackupGateway;
|
||
|
use Xentral\Modules\Backup\BackupService;
|
||
|
//use Xentral\Modules\Backup\BackupSystemConfigurationService;
|
||
|
use Xentral\Widgets\ChunkedUpload\ChunkedUploadRequestHandler;
|
||
|
|
||
|
class Backup
|
||
|
{
|
||
|
/** @var string MODULE_NAME */
|
||
|
const MODULE_NAME = 'Backup';
|
||
|
|
||
|
/** @var Application $app */
|
||
|
private $app;
|
||
|
|
||
|
/** @var BackupService $service */
|
||
|
private $oBackupService;
|
||
|
|
||
|
/** @var BackupGateway $gateway */
|
||
|
private $oBackupGateway;
|
||
|
|
||
|
/**
|
||
|
* Backup constructor.
|
||
|
*
|
||
|
* @param $app
|
||
|
* @param bool $intern
|
||
|
*/
|
||
|
public function __construct($app, $intern = false)
|
||
|
{
|
||
|
$this->app = $app;
|
||
|
try {
|
||
|
$this->oBackupService = $this->app->Container->get('BackupService');
|
||
|
$this->oBackupGateway = $this->app->Container->get('BackupGateway');
|
||
|
} catch (RuntimeException $e) {
|
||
|
$this->app->Tpl->Set('MESSAGE', '<div class="error">Backup Fehler: ' . $e->getMessage() . '</div>');
|
||
|
}
|
||
|
|
||
|
if($intern){
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
$id = $this->app->Secure->GetGET('id');
|
||
|
if(is_numeric($id)){
|
||
|
$this->app->Tpl->Set('SUBHEADING', ": " . $this->app->DB->Select("SELECT nummer FROM artikel WHERE id=$id LIMIT 1"));
|
||
|
}
|
||
|
|
||
|
$this->app->ActionHandlerInit($this);
|
||
|
|
||
|
$this->app->ActionHandler('list', 'BackupList');
|
||
|
$this->app->ActionHandler('create', 'BackupCreate');
|
||
|
$this->app->ActionHandler('recover', 'BackupRecover');
|
||
|
$this->app->ActionHandler('readstatus', 'ReadStatus');
|
||
|
$this->app->ActionHandler("downloadsnapshot", "BackupDownloadSnapshot");
|
||
|
$this->app->ActionHandler("delete", "BackupDelete");
|
||
|
$this->app->ActionHandler("importer", "BackupImporter");
|
||
|
|
||
|
|
||
|
$this->host = $this->app->Conf->WFdbhost;
|
||
|
$this->database = $this->app->Conf->WFdbname;
|
||
|
$this->user = $this->app->Conf->WFdbuser;
|
||
|
$this->password = $this->app->Conf->WFdbpass;
|
||
|
|
||
|
$this->pfad = (isset($this->app->Conf->WFbackup) && is_dir($this->app->Conf->WFbackup) ? rtrim($this->app->Conf->WFbackup, '/') . "/" : "../backup/snapshots/");
|
||
|
|
||
|
$this->app->ActionHandlerListen($app);
|
||
|
|
||
|
$this->app->erp->Headlines('Backup');
|
||
|
}
|
||
|
|
||
|
public function Install()
|
||
|
{
|
||
|
// ADD Cron
|
||
|
$check = $this->app->DB->SelectRow("SELECT `id` FROM `prozessstarter` WHERE `parameter` = 'backup' LIMIT 1");
|
||
|
$dateTime = new DateTime('tomorrow');
|
||
|
$startTime = sprintf('%s %s', $dateTime->format('Y-m-d'), '02:00:00');
|
||
|
if(empty($check)){
|
||
|
$this->app->erp->CheckProzessstarter('Backup', 'periodisch', 1440, $startTime, 'cronjob', 'backup', 1);
|
||
|
}else{
|
||
|
// Backword compatibility
|
||
|
$this->app->DB->Update(
|
||
|
sprintf(
|
||
|
"UPDATE `prozessstarter` SET `bezeichnung` = '%s', `periode`=%d, `recommended_period`=%d, `startzeit`='%s' WHERE `id` = %d",
|
||
|
'Backup', 1440, 1440, $startTime, $check['id']
|
||
|
)
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $app
|
||
|
* @param $name
|
||
|
* @param $erlaubtevars
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
/*public function TableSearch(&$app, $name, $erlaubtevars)
|
||
|
{
|
||
|
switch ($name) {
|
||
|
case 'backuplist':
|
||
|
$allowed['backup'] = array('list');
|
||
|
$heading = array('Name', 'Dateiname', 'Datum', 'Menü');
|
||
|
$width = array('30%', '30%', '20%', '8%');
|
||
|
$findcols = array('name', 'dateiname', 'datum', 'id');
|
||
|
$searchsql = array('name', "DATE_FORMAT(datum, '%d.%m.%Y %H:%i:%s')");
|
||
|
$sql = "SELECT SQL_CALC_FOUND_ROWS id, name, dateiname, DATE_FORMAT(datum, '%d.%m.%Y %H:%i:%s'), id as menu FROM backup";
|
||
|
$defaultorder = 4; //Optional wenn andere Reihenfolge gewuenscht
|
||
|
$defaultorderdesc = 1;
|
||
|
|
||
|
$where = "";
|
||
|
$count = "SELECT COUNT(id) FROM backup";
|
||
|
$menu = "<a href=\"#\" id=\"recover-backup\" data-id=\"%value%\"><img src=\"themes/{$this->app->Conf->WFconf['defaulttheme']}/images/backward.svg\" border=\"0\"></a>" . " <a href=\"index.php?module=backup&action=downloadsnapshot&id=%value%\"><img src=\"themes/{$this->app->Conf->WFconf['defaulttheme']}/images/download.svg\" border=\"0\"></a>" . " <a href=\"#\" onclick=DeleteDialog(\"index.php?module=backup&action=delete&id=%value%\");><img src=\"themes/{$this->app->Conf->WFconf['defaulttheme']}/images/delete.svg\" border=\"0\"></a>";
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
$erg = [];
|
||
|
foreach ($erlaubtevars as $k => $v) {
|
||
|
if(isset($$v)){
|
||
|
$erg[$v] = $$v;
|
||
|
}
|
||
|
}
|
||
|
return $erg;
|
||
|
}*/
|
||
|
|
||
|
/**
|
||
|
* @void
|
||
|
*/
|
||
|
public function BackupImporter()
|
||
|
{
|
||
|
$this->BackupMenu();
|
||
|
if(!empty($cmd = $this->app->Secure->GetGET('cmd')) && $cmd === 'upload'){
|
||
|
/** @var Request $request */
|
||
|
$request = $this->app->Container->get('Request');
|
||
|
|
||
|
/** @var ChunkedUploadRequestHandler $handler */
|
||
|
$handler = $this->app->Container->get('ChunkedUploadRequestHandler');
|
||
|
|
||
|
$tempDir = sys_get_temp_dir();
|
||
|
|
||
|
$saveDir = $this->app->erp->GetRootPath() . '/backup/snapshots/';
|
||
|
if(!file_exists($saveDir) && !mkdir($saveDir, 0777, true) && !is_dir($saveDir)){
|
||
|
throw new RuntimeException(sprintf('Directory "%s" was not created', $saveDir));
|
||
|
}
|
||
|
$response = $handler->handleRequest($request, $tempDir, $saveDir);
|
||
|
$response->send();
|
||
|
$this->app->erp->ExitWawi();
|
||
|
}
|
||
|
if(!empty($cmd = $this->app->Secure->GetGET('cmd')) && $cmd === 'completed'){
|
||
|
|
||
|
if($sFileName = $this->app->Secure->GetPOST('file_name')){
|
||
|
|
||
|
$fileNameExploded = explode('.', $sFileName);
|
||
|
array_pop($fileNameExploded);
|
||
|
$backupName = implode('.', $fileNameExploded);
|
||
|
$address = $this->app->User->GetAdresse();
|
||
|
|
||
|
$this->app->DB->Insert("INSERT INTO backup (adresse, name, dateiname, datum)
|
||
|
VALUES ('$address','$backupName','$sFileName',NOW())"
|
||
|
);
|
||
|
}
|
||
|
|
||
|
$xResponse = [
|
||
|
'completed' => true,
|
||
|
'message' => $this->app->erp->base64_url_encode('<div class="error">Backup Import erfolreich abgeschlossen.</div>')
|
||
|
];
|
||
|
|
||
|
$this->ViewJsonEncode($xResponse);
|
||
|
}
|
||
|
|
||
|
$this->app->ModuleScriptCache->IncludeWidgetNew('ChunkedUpload');
|
||
|
|
||
|
$this->app->Tpl->Parse('PAGE', "backup_upload.tpl");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return void
|
||
|
*/
|
||
|
public function BackupMenu()
|
||
|
{
|
||
|
$this->app->erp->MenuEintrag("index.php?module=backup&action=list", "Zurück zur Übersicht");
|
||
|
$this->app->erp->MenuEintrag("index.php?module=backup&action=list", "Übersicht");
|
||
|
//$this->app->erp->MenuEintrag("index.php?module=backup&action=importer", "Importer");
|
||
|
//$this->app->erp->MenuEintrag("BackupModule.createItem()", "Neuer Eintrag");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param mixed $xValue
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function ViewJsonEncode($xValue)
|
||
|
{
|
||
|
header('Content-type: application/json');
|
||
|
echo json_encode($xValue);
|
||
|
if(!empty($this->app->erp)){
|
||
|
$this->app->erp->ExitWawi();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* List all existing Backups
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function BackupList()
|
||
|
{
|
||
|
$this->BackupMenu();
|
||
|
$this->app->Tpl->Set('UEBERSCHRIFT', "Backup");
|
||
|
$this->app->Tpl->Set('KURZUEBERSCHRIFT', "Backup");
|
||
|
//$this->app->YUI->TableSearch('TAB1', 'backuplist', 'show', '', '', basename(__FILE__), __CLASS__);
|
||
|
$this->app->Tpl->Set('TABTEXT', "Backup");
|
||
|
|
||
|
$this->app->erp->checkActiveCronjob('backup');
|
||
|
/*
|
||
|
$processStarterEnabled = 1;
|
||
|
|
||
|
$backupConfService = $this->app->Container->get('BackupSystemConfigurationService');
|
||
|
if($backupConfService->tryCheckCronIsEnabled() === false){
|
||
|
$processStarterEnabled = 0;
|
||
|
$this->app->Tpl->Set('MESSAGE', '<div class="error">Es sieht so aus, als ob der Prozessstarter Backup nicht regelmäßig ausgeführt wird! Bitte aktivieren Sie diesen (<a href="index.php?module=prozessstarter&action=list" target="_blank">zu den Prozessstartern</a>)!</div>');
|
||
|
}
|
||
|
// $this->app->Tpl->Set('PROCESS_STARTER_STATUS', $processStarterEnabled);
|
||
|
|
||
|
$free = disk_free_space($this->app->erp->GetRootPath());
|
||
|
|
||
|
$free /= 1024 * 1024;
|
||
|
$minFree = (int)$this->app->DB->Select(
|
||
|
sprintf(
|
||
|
"SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024)
|
||
|
FROM information_schema.TABLES
|
||
|
WHERE table_schema = '%s'",
|
||
|
$this->app->Conf->WFdbname
|
||
|
)
|
||
|
) + 512;
|
||
|
|
||
|
$userdata = (int)$this->app->erp->GetKonfiguration('userdatasize');
|
||
|
|
||
|
if($free > 0 && $minFree + $userdata > $free){
|
||
|
$this->app->Tpl->Add(
|
||
|
'MESSAGE',
|
||
|
sprintf(
|
||
|
'<div class="error">Es ist nur %d MB Speicher auf dem System frei, es werden aber mindestens %d MB für ein
|
||
|
Datenbank-Backup benötigt.</div>',
|
||
|
$free, $minFree + $userdata
|
||
|
)
|
||
|
);
|
||
|
}*/
|
||
|
|
||
|
$sqlProcessStarter = "SELECT ps.letzteausfuerhung, ps.periode FROM `prozessstarter` AS `ps` WHERE ps.parameter = 'backup' LIMIT 1";
|
||
|
$pStarter = $this->app->DB->SelectRow($sqlProcessStarter);
|
||
|
// LATEST BACKUP
|
||
|
if($lastestBackup = $this->oBackupGateway->getLatestBackup()){
|
||
|
//$nextRun = (new DateTime($pStarter['letzteausfuerhung']))->getTimestamp() + (int)$pStarter['periode'] * 60;
|
||
|
|
||
|
$latestBackupTime = new DateTime($lastestBackup['datum']);
|
||
|
$latestMsg = sprintf('Letztes Backup %s Uhr.', $latestBackupTime->format('d.m.Y H:i'));
|
||
|
$this->app->Tpl->Add('MESSAGE_DOWNLOAD', '<div class="success backup-success">' . $latestMsg . '
|
||
|
<a class="button button-neutral remove-backup" data-url="index.php?module=backup&action=delete&id='.$lastestBackup['id'].'" role="button" href="#">Löschen</a>
|
||
|
<a href="index.php?module=backup&action=downloadsnapshot&id=' . $lastestBackup['id'] . '" class="button">Herunterladen</a>
|
||
|
</div>');
|
||
|
}
|
||
|
|
||
|
$dateTime = new DateTime('yesterday');
|
||
|
$startTime = $dateTime->format('Y-m-d');
|
||
|
$today = (new DateTime())->format('Y-m-d');
|
||
|
$backupFail = null;
|
||
|
$startIdSQL = "SELECT cl.id FROM cronjob_log AS `cl` WHERE cl.cronjob_name = 'backup' AND cl.status = 'start'
|
||
|
AND (DATE(cl.change_time)='%s' OR DATE(cl.change_time)='%s' )";
|
||
|
|
||
|
$allStartIds = $this->app->DB->SelectFirstCols(sprintf($startIdSQL, $startTime, $today));
|
||
|
if(!empty($allStartIds)){
|
||
|
$failSQL = "SELECT cl.id FROM cronjob_log AS `cl` WHERE cl.status='error' AND cl.parent_id IN (%s) ";
|
||
|
$backupFail = $this->app->DB->SelectArr(sprintf($failSQL, implode(',', $allStartIds)));
|
||
|
}
|
||
|
if($backupFail !== null){
|
||
|
$lastestLogError = '';
|
||
|
$logErrorMsg = '';
|
||
|
$dateError = '';
|
||
|
try {
|
||
|
$lastestLogError = $this->GetLogger()->tail(0, null, $this->GetLogger()->getPersistentFileName());
|
||
|
} catch (LogException $exception) {
|
||
|
// do nothing
|
||
|
}
|
||
|
if (!empty($lastestLogError)){
|
||
|
$lastestLogErrorExploded = explode(':', $lastestLogError);
|
||
|
$dateError = array_shift($lastestLogErrorExploded);
|
||
|
|
||
|
$dateError = sprintf('am %s', date('d.m.Y H:i', $dateError));
|
||
|
$logErrorMsg = sprintf('Grund: <strong>%s</strong>', implode('', $lastestLogErrorExploded));
|
||
|
}
|
||
|
|
||
|
$msgError = sprintf('Das letzte Backup konnte %s nicht erstellt werden. %s', $dateError, $logErrorMsg);
|
||
|
$msg = sprintf('<div class="error">%s</div>', $msgError);
|
||
|
$this->app->Tpl->Add('MESSAGE_ERROR', $msg);
|
||
|
}
|
||
|
// CHECK ZIP-TOOL
|
||
|
if(!$this->oBackupService->hasExecutableExtension('zip')){
|
||
|
if (extension_loaded('zip')) {
|
||
|
$msg = sprintf('<div class="error">%s</div>', 'Es fehlt das Kommandozeilen-Tool "zip" auf dem Server. Bitte installieren Sie, dass es auf der Kommandozeile verfügbar ist. Es ist nicht die Erweiterung php-zip gemeint, diese ist korrekt vorhanden');
|
||
|
} else{
|
||
|
$msg = sprintf('<div class="error">%s</div>', 'Es fehlt das Kommandozeilen-Tool "zip" auf dem Server. Bitte installieren Sie, dass es auf der Kommandozeile verfügbar ist');
|
||
|
}
|
||
|
$this->app->Tpl->Add('MESSAGE_ERROR', $msg);
|
||
|
}
|
||
|
if(!empty($pStarter) && $this->app->erp->isSystemBlockedByBackup()){
|
||
|
$lastRun = new DateTime($pStarter['letzteausfuerhung']);
|
||
|
$lastRunMsg = sprintf('Aktuell wird ein Backup (gestartet am %s um %s) erstellt! Bitte warten bis das Backup bereit steht.', $lastRun->format('d.m.Y'), $lastRun->format('H:i'));
|
||
|
$msg = sprintf('<div class="warning">%s</div>', $lastRunMsg);
|
||
|
$this->app->Tpl->Add('MESSAGE_RUNNING', $msg);
|
||
|
}
|
||
|
|
||
|
$this->app->Tpl->Parse('PAGE', 'backup.tpl');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a Backup
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function BackupCreate()
|
||
|
{
|
||
|
$name = $this->app->Secure->GetPOST("name");
|
||
|
$bStatus = false;
|
||
|
$message = $this->app->erp->base64_url_encode("<div class=\"info\">Sie müssen einen Namen für das Datenbank-Backup eingeben.</div>");
|
||
|
if(!empty($name)){
|
||
|
$adresse = $this->app->User->GetAdresse();
|
||
|
$name = preg_replace('/[^a-zA-Z]+/', '', $name);
|
||
|
$dateiname = date('Y-m-d_') . $this->database . '_' . $name . '.' . $this->oBackupService->getArchiveExtension();
|
||
|
|
||
|
if($this->app->DB->Select("SELECT '1' FROM backup WHERE dateiname='$dateiname' LIMIT 1") == '1'){
|
||
|
$message = $this->app->erp->base64_url_encode("<div class=\"info\">Ein Backup mit diesem Namen existiert bereits.</div>");
|
||
|
}else{
|
||
|
$sConfig = json_encode(
|
||
|
[
|
||
|
'action' => 'RunCreateJob',
|
||
|
'config' => $this->app->Conf,
|
||
|
'file_name' => $dateiname,
|
||
|
'options' => ['addr' => $adresse, 'name' => $name, 'ssid' => session_id(), 'user_id' => $this->app->User->GetID(), 'ip' => $_SERVER['REMOTE_ADDR']]
|
||
|
]);
|
||
|
|
||
|
$bStatus = $this->oBackupService->addToProcessStarter($sConfig);
|
||
|
$message = '';
|
||
|
if($bStatus === false){
|
||
|
$message = $this->app->erp->base64_url_encode("<div class=\"info\">Das Backup kann momentan nicht erstellt werden. Bitte versuchen Sie es später.</div>");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
$ErrorMsg = $this->app->erp->base64_url_encode(
|
||
|
"<div class=\"error\">Das Backup konnte nicht erstellt werden.</div>"
|
||
|
);
|
||
|
$genericSuccess = 'Backup Erstellung gestartet, Bitte warten bis das Backup bereit steht.';
|
||
|
$this->ViewJsonEncode([
|
||
|
'status' => $bStatus,
|
||
|
'message' => $message,
|
||
|
'generic_error' => $ErrorMsg,
|
||
|
'created_at' => time(),
|
||
|
'backup_file' => $dateiname,
|
||
|
'success_msg' => $this->app->erp->base64_url_encode("<div class=\"error2\">".$genericSuccess."</div>"),
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Deletes an existing Backup
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function BackupDelete()
|
||
|
{
|
||
|
$id = $this->app->Secure->GetGET("id");
|
||
|
|
||
|
$error = false;
|
||
|
|
||
|
if(is_numeric($id)){
|
||
|
$dateiname = $this->app->DB->Select("SELECT dateiname FROM backup WHERE id='$id' LIMIT 1");
|
||
|
|
||
|
if($dateiname != ''){
|
||
|
$backupFile = $this->pfad . $dateiname;
|
||
|
if(file_exists($backupFile)){
|
||
|
unlink($backupFile);
|
||
|
}
|
||
|
// REMOVE META FILE AS WELL
|
||
|
$asFile = explode('.', $backupFile);
|
||
|
array_pop($asFile);
|
||
|
$sMetaFile = implode('.', $asFile) . '.meta';
|
||
|
if(file_exists($sMetaFile)){
|
||
|
unlink($sMetaFile);
|
||
|
}
|
||
|
$this->app->DB->Delete("DELETE FROM backup WHERE id='$id' LIMIT 1");
|
||
|
$msg = $this->app->erp->base64_url_encode("<div class=\"error2\">Das Backup wurde erfolgreich gelöscht.</div>");
|
||
|
}else{
|
||
|
$error = true;
|
||
|
}
|
||
|
|
||
|
}else{
|
||
|
$error = true;
|
||
|
}
|
||
|
|
||
|
if($error){
|
||
|
$msg = $this->app->erp->base64_url_encode("<div class=\"error\">Das Backup konnte nicht gelöscht werden.</div>");
|
||
|
}
|
||
|
|
||
|
$this->app->Location->execute("index.php?module=backup&action=list&msg=$msg");
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Download a Backup
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function BackupDownloadSnapshot()
|
||
|
{
|
||
|
$id = $this->app->Secure->GetGET("id");
|
||
|
if(is_numeric($id)){
|
||
|
$dateiname = $this->app->DB->Select("SELECT dateiname FROM backup WHERE id='$id' LIMIT 1");
|
||
|
$pfad = $this->pfad . $dateiname;
|
||
|
if(file_exists($pfad)){
|
||
|
header("Content-Disposition: attachment; filename=$dateiname");
|
||
|
header('Content-Length: ' . filesize($pfad));
|
||
|
$this->readfile_chunked($pfad); //readfile will stream the file.
|
||
|
$this->app->ExitXentral();
|
||
|
}
|
||
|
$msg = $this->app->erp->base64_url_encode("<div class=\"error\">Die passende Snapshot Datei konnte nicht gefunden werden!</div>");
|
||
|
header("Location: ./index.php?module=backup&action=list&msg=$msg");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @param $filename
|
||
|
* @param bool $retbytes
|
||
|
*
|
||
|
* @return bool|int
|
||
|
*/
|
||
|
protected function readfile_chunked($filename, $retbytes = true)
|
||
|
{
|
||
|
$chunksize = 1 * (1024 * 1024); // how many bytes per chunk
|
||
|
$buffer = '';
|
||
|
$cnt = 0;
|
||
|
$handle = fopen($filename, 'rb');
|
||
|
if($handle === false){
|
||
|
return false;
|
||
|
}
|
||
|
while (!feof($handle)) {
|
||
|
$buffer = fread($handle, $chunksize);
|
||
|
echo $buffer;
|
||
|
if($retbytes){
|
||
|
$cnt += strlen($buffer);
|
||
|
}
|
||
|
}
|
||
|
$status = fclose($handle);
|
||
|
if($retbytes && $status){
|
||
|
return $cnt;
|
||
|
}
|
||
|
return $status;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return BackupLog
|
||
|
*/
|
||
|
protected function GetLogger()
|
||
|
{
|
||
|
return $this->app->Container->get('BackupLog');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reads running Backup status
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function ReadStatus()
|
||
|
{
|
||
|
$bStatus = false;
|
||
|
$saveDir = $this->app->erp->GetRootPath() . '/backup/snapshots/';
|
||
|
try {
|
||
|
$sMessage = $this->GetLogger()->tail();
|
||
|
} catch (LogException $exception) {
|
||
|
// Job net yet started
|
||
|
$sMessage = ':Please wait';
|
||
|
}
|
||
|
|
||
|
$asMessage = explode(':', $sMessage);
|
||
|
array_shift($asMessage);
|
||
|
$message = implode(' ', $asMessage);
|
||
|
if(trim($message) === '--END--'){
|
||
|
$msg = 'Backup-Vorgang erfolgreich abgeschlossen.';
|
||
|
$class = 'error2';
|
||
|
|
||
|
if($sFileName = $this->app->Secure->GetPOST('file_name')){
|
||
|
$asStatusCheckSum = $this->oBackupService->checkSumOnAfterRecovery($sFileName, $this->app->Conf->WFuserdata);
|
||
|
if($asStatusCheckSum !== null && !empty($asStatusCheckSum)){
|
||
|
$class = 'warning';
|
||
|
$msg = sprintf('Die folgende(n) Tabelle(n) <strong>%s</strong> konnten eventuell nicht komplett wiederhergestellt werden!',
|
||
|
implode(',', $asStatusCheckSum));
|
||
|
}
|
||
|
}
|
||
|
$bStatus = true;
|
||
|
$this->oBackupService->removeLoggerFiles();
|
||
|
$message = $this->app->erp->base64_url_encode('<div class="' . $class . '">' . $msg . '</div>');
|
||
|
}
|
||
|
|
||
|
if(trim($message) === 'ERROR'){
|
||
|
$backupFile = empty($this->app->Secure->GetPOST('backup_file')) ? null : $this->app->Secure->GetPOST('backup_file');
|
||
|
$this->app->erp->setMaintainance(false);
|
||
|
$delBackupFile = null !== $backupFile ? $saveDir . $backupFile : null;
|
||
|
$this->oBackupService->removeLoggerFiles($delBackupFile);
|
||
|
throw new RuntimeException('ERROR');
|
||
|
}
|
||
|
|
||
|
// CHECK IF STILL RUNNING
|
||
|
|
||
|
if((int)$createdAt = $this->app->Secure->GetPOST('created_at')){
|
||
|
$backupFile = empty($this->app->Secure->GetPOST('backup_file')) ? null : $this->app->Secure->GetPOST('backup_file');
|
||
|
$checkResult = $this->checkFalsePositive($backupFile);
|
||
|
if($checkResult){
|
||
|
$msg = 'Backup-Vorgang erfolgreich abgeschlossen.';
|
||
|
$class = 'error2';
|
||
|
$bStatus = true;
|
||
|
$this->oBackupService->removeLoggerFiles();
|
||
|
$message = $this->app->erp->base64_url_encode('<div class="' . $class . '">' . $msg . '</div>');
|
||
|
$this->ViewJsonEncode([
|
||
|
'finished' => $bStatus,
|
||
|
'message' => $message,
|
||
|
]);
|
||
|
}
|
||
|
$hangCheckStart = 600; // 10min
|
||
|
if($bStatus === false && (time() - $createdAt > $hangCheckStart) && $this->app->erp->isSystemBlockedByBackup() === false){
|
||
|
$this->app->erp->setMaintainance(false);
|
||
|
$delBackupFile = null !== $backupFile ? $saveDir . $backupFile : null;
|
||
|
|
||
|
$this->oBackupService->removeLoggerFiles($delBackupFile);
|
||
|
throw new RuntimeException('ERROR: Timeout override');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$this->ViewJsonEncode([
|
||
|
'finished' => $bStatus,
|
||
|
'message' => $message,
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
private function checkFalsePositive($backupFile = null)
|
||
|
{
|
||
|
$saveDir = $this->app->erp->GetRootPath() . '/backup/snapshots/';
|
||
|
|
||
|
if(null !== $backupFile){
|
||
|
$dbRecord = $this->app->DB->Select("SELECT '1' FROM backup WHERE dateiname='$backupFile' LIMIT 1") == '1';
|
||
|
if(file_exists($saveDir . $backupFile) && empty($dbRecord)){
|
||
|
// fix it add in db
|
||
|
$address = $this->app->User->GetAdresse();
|
||
|
$this->app->DB->Insert("INSERT INTO backup (adresse, name, dateiname, datum) VALUES ('$address',$backupFile,$backupFile,NOW())");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if(file_exists($saveDir . $backupFile) && !empty($dbRecord)){
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Runs the restore backup job
|
||
|
*
|
||
|
* @param stdClass $oConfig
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function RunRestoreJob($oConfig)
|
||
|
{
|
||
|
if(!empty($oConfig)){
|
||
|
$this->oBackupService->restore(
|
||
|
$this->stdClassToConfig($oConfig->config),
|
||
|
$oConfig->file_name,
|
||
|
(array)$oConfig->options
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Converts stdClass to Config Class
|
||
|
*
|
||
|
* @param stdClass $config
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
private function stdClassToConfig($config)
|
||
|
{
|
||
|
if(!($config instanceof stdClass)){
|
||
|
return $config;
|
||
|
}
|
||
|
return unserialize(sprintf(
|
||
|
'O:%d:"%s"%s',
|
||
|
strlen('Config'),
|
||
|
'Config',
|
||
|
strstr(strstr(serialize($config), '"'), ':')
|
||
|
));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Runs the create backup job
|
||
|
*
|
||
|
* @param stdClass $oConfig
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
public function RunCreateJob($oConfig)
|
||
|
{
|
||
|
if(!empty($oConfig)){
|
||
|
$this->oBackupService->create($this->stdClassToConfig($oConfig->config), $oConfig->file_name, (array)$oConfig->options);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Action recover backup
|
||
|
*
|
||
|
* @return void
|
||
|
* @throws Exception
|
||
|
*/
|
||
|
public function BackupRecover()
|
||
|
{
|
||
|
$id = (int)$this->app->Secure->GetPOST("id");
|
||
|
|
||
|
if(!empty($id) && ($dateiname = $this->app->DB->Select("SELECT dateiname FROM backup WHERE id='$id' LIMIT 1")) &&
|
||
|
file_exists($this->oBackupService->getArchivePath($dateiname, $this->app->Conf->WFuserdata))){
|
||
|
|
||
|
if(!empty($cmd = $this->app->Secure->GetGET('cmd')) && $cmd === 'check-meta'){
|
||
|
$this->CheckUserInDump();
|
||
|
}
|
||
|
|
||
|
$recoverOption = ['ssid' => session_id(), 'user_id' => $this->app->User->GetID(), 'ip' => $_SERVER['REMOTE_ADDR']];
|
||
|
|
||
|
if($oldDB = $this->app->Secure->GetPOST("old_db")){
|
||
|
$recoverOption['old_dbname'] = $oldDB;
|
||
|
}
|
||
|
|
||
|
$sConfig = json_encode(
|
||
|
[
|
||
|
'action' => 'RunRestoreJob',
|
||
|
'config' => $this->app->Conf,
|
||
|
'file_name' => $dateiname,
|
||
|
'options' => $recoverOption,
|
||
|
|
||
|
]
|
||
|
);
|
||
|
$ErrorMsg = $this->app->erp->base64_url_encode(
|
||
|
"<div class=\"error\">Das Backup konnte nicht wieder hergestellt werden.</div>"
|
||
|
);
|
||
|
$bStatus = $this->oBackupService->addToProcessStarter($sConfig);
|
||
|
$message = $bStatus === true ? '' :
|
||
|
$this->app->erp->base64_url_encode('<div class="error2">Das Backup kann momentan nicht gestartet werden. Bitte Versuchen Sie später.</div>');
|
||
|
$xResponse = [
|
||
|
'status' => $bStatus,
|
||
|
'message' => $message,
|
||
|
'file_name' => $dateiname,
|
||
|
'created_at' => time(),
|
||
|
'generic_error' => $ErrorMsg
|
||
|
];
|
||
|
}else{
|
||
|
$xResponse = [
|
||
|
'status' => false,
|
||
|
'missing_file' => true,
|
||
|
'message' => $this->app->erp->base64_url_encode('<div class="error">Backup konnte nicht gefunden werden.</div>')
|
||
|
];
|
||
|
}
|
||
|
$this->ViewJsonEncode($xResponse);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks whether the current userId exists in the running restored files
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function CheckUserInDump()
|
||
|
{
|
||
|
$xData = null;
|
||
|
$bStatus = false;
|
||
|
$iUserId = (int)$this->app->User->GetID();
|
||
|
$iBackupId = (int)$this->app->Secure->GetPOST('id');
|
||
|
if(!empty($hData = $this->oBackupGateway->getBackupById($iBackupId)) && ($xData = $this->oBackupService->getDumpMetaData($hData['dateiname'], $this->app->Conf->WFuserdata))){
|
||
|
// Check user admins
|
||
|
$bStatus = !empty($xData) && array_key_exists('users', $xData) && in_array($iUserId, $xData['users']);
|
||
|
}
|
||
|
|
||
|
$message = $bStatus === true ? "Achtung: Es existieren neuere Datensicherungen. Möchten Sie wirklich alle bisherigen Einstellungen löschen/zurücksetzen?\n\nAlle nach diesem Zeitpunkt getätigten Einstellungen und Importvorlagen gehen verloren."
|
||
|
: "Achtung: Beim Einspielen des Backups könnten Sie nicht mehr im Stande sein, sich auf dem System anzumelden. Möchten Sie wirklich alle bisherigen Einstellungen löschen / zurücksetzen? Alle nach diesem Zeitpunkt getätigten Einstellungen und Importvorlagen gehen verloren.";
|
||
|
|
||
|
$ps_message = $this->SystemHasRunningProcesses() === true ? 'Achtung: Es existieren laufenden Prozesse im System. Mit Ihrer Bestätigung, werden sie beendet.' : '';
|
||
|
$this->ViewJsonEncode(['status' => $bStatus, 'message' => $message, 'ps_message' => $ps_message]);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* @return bool
|
||
|
*/
|
||
|
protected function SystemHasRunningProcesses()
|
||
|
{
|
||
|
$xData = null;
|
||
|
$bStatus = false;
|
||
|
if($runningId = $this->app->DB->Select('SELECT id FROM prozessstarter WHERE aktiv=1 AND mutex=1 LIMIT 1')){
|
||
|
$bStatus = true;
|
||
|
}
|
||
|
return $bStatus;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|