AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S($sListTitleDictKey), 2)); if (count($aListData) > 0) { $oTable = DataTableUIBlockFactory::MakeForStaticData('', $aListConfig, array_reverse($aListData), $sTableId); $oTablePanel = PanelUIBlockFactory::MakeForInformation(''); $oTablePanel->AddSubBlock($oTable); $oTablePanel->AddCSSClass('ibo-datatable-panel'); $oBlockForList->AddSubBlock($oTablePanel); } else { $oBlockForList->AddSubBlock( AlertUIBlockFactory::MakeNeutral('', Dict::S($sNoRecordDictKey)) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } return $oBlockForList; } ///////////////////////////////////////////////////////////////////// // Main program // LoginWebPage::DoLogin(); // Check user rights and prompt if needed ApplicationMenu::CheckMenuIdEnabled('BackupStatus'); try { $sTransactionId = utils::GetNewTransactionId(); $oP = new iTopWebPage(Dict::S('bkp-status-title')); $oP->set_base(utils::GetAbsoluteUrlAppRoot().'pages/'); $oBackupTitle = TitleUIBlockFactory::MakeForPage(Dict::S('bkp-status-title'), 1); $oP->AddUiBlock($oBackupTitle); if (MetaModel::GetConfig()->Get('demo_mode')) { $oBackupDisabledCauseDemoMode = AlertUIBlockFactory::MakeForFailure( 'The feature is disabled.', 'iTop is in demonstration mode' ); $oBackupDisabledCauseDemoMode ->SetIsCollapsible(false) ->SetIsClosable(false); $oP->AddUiBlock($oBackupDisabledCauseDemoMode); } //--- Settings and checks $oBlockForChecks = new UIContentBlock(); $oBlockForChecks->AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('bkp-status-checks'), 2)); $oP->AddUiBlock($oBlockForChecks); // Availability of mysqldump // $sMySQLBinDir = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', ''); $sMySQLBinDir = utils::ReadParam('mysql_bindir', $sMySQLBinDir, true); $sMySQLDump = DBBackup::MakeSafeMySQLCommand($sMySQLBinDir, 'mysqldump'); $sCommand = "$sMySQLDump -V 2>&1"; $aOutput = []; $iRetCode = 0; exec($sCommand, $aOutput, $iRetCode); if ($iRetCode == 0) { $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForSuccess('', Dict::Format("bkp-mysqldump-ok", $aOutput[0])) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } else { if ($iRetCode == 1) { $sMySqlDump = Dict::Format("bkp-mysqldump-notfound", implode(' ', $aOutput)); } else { $sMySqlDump = Dict::Format("bkp-mysqldump-issue", $iRetCode); } $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForWarning($sMySqlDump) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } foreach ($aOutput as $sLine) { IssueLog::Info("$sCommand said: $sLine"); } // Destination directory // // Make sure the target directory exists and is writeable $sBackupDir = realpath(utils::GetDataPath().'backups/'); SetupUtils::builddir($sBackupDir); if (!is_dir($sBackupDir)) { $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForWarning('', Dict::Format('bkp-missing-dir', $sBackupDir)) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } else { $sBackupDir = realpath($sBackupDir); // just for cosmetic purpose (dir separator, as APPROOT contains a hardcoded '/') $sDiskSpaceReadable = SetupUtils::HumanReadableSize(SetupUtils::CheckDiskSpace($sBackupDir)); $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForInformation('', Dict::Format('bkp-free-disk-space', $sDiskSpaceReadable, $sBackupDir)) ->SetIsClosable(false) ->SetIsCollapsible(false) ); if (!is_writable($sBackupDir)) { $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForWarning(Dict::Format('bkp-dir-not-writeable', $sBackupDir)) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } } $sBackupDirAuto = $sBackupDir.'/auto/'; SetupUtils::builddir($sBackupDirAuto); $sBackupDirManual = $sBackupDir.'/manual/'; SetupUtils::builddir($sBackupDirManual); // Wrong format // $sBackupFile = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'file_name_format', BACKUP_DEFAULT_FORMAT); $oBackup = new DBBackupScheduled(); $sZipName = $oBackup->MakeName($sBackupFile); $sZipNameInfo = ''; if ($sZipName == '') { $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForWarning(Dict::Format('bkp-wrong-format-spec', $sBackupFile, BACKUP_DEFAULT_FORMAT)) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } else { $sZipNameInfo = Dict::Format('bkp-name-sample', $sZipName); } // Week Days // $sScheduleInfo = empty($sZipNameInfo) ? '' : $sZipNameInfo.'
'; $aWeekDayToString = [ 1 => Dict::S('DayOfWeek-Monday'), 2 => Dict::S('DayOfWeek-Tuesday'), 3 => Dict::S('DayOfWeek-Wednesday'), 4 => Dict::S('DayOfWeek-Thursday'), 5 => Dict::S('DayOfWeek-Friday'), 6 => Dict::S('DayOfWeek-Saturday'), 7 => Dict::S('DayOfWeek-Sunday'), ]; $aDayLabels = []; $oBackupExec = new BackupExec(); foreach ($oBackupExec->InterpretWeekDays() as $iDay) { $aDayLabels[] = $aWeekDayToString[$iDay]; } $sDays = implode(', ', $aDayLabels); $sBackupTime = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'time', '23:30'); $sScheduleInfo .= Dict::Format('bkp-week-days', $sDays, $sBackupTime); $iRetention = MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'retention_count', 5); $sScheduleInfo .= '
'.Dict::Format('bkp-retention', $iRetention); $oBlockForChecks->AddSubBlock( AlertUIBlockFactory::MakeForInformation('', $sScheduleInfo) ->SetIsClosable(false) ->SetIsCollapsible(false) ); //--- List of backups // $aFiles = $oBackup->ListFiles($sBackupDirAuto); $aFilesToDelete = []; while (count($aFiles) > $iRetention - 1) { $aFilesToDelete[] = array_shift($aFiles); } $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment()); if ($oRestoreMutex->IsLocked()) { $sDisableRestore = 'disabled="disabled"'; } else { $sDisableRestore = ''; } $sRestore = Dict::S('bkp-button-restore-now'); //--- 1st table: list the backups made in the background // $aDetails = []; $sButtonOnClickJS = ''; foreach ($oBackup->ListFiles($sBackupDirAuto) as $sBackupFile) { $sFileName = basename($sBackupFile); $sFilePath = 'auto/'.$sFileName; if (MetaModel::GetConfig()->Get('demo_mode')) { $sName = $sFileName; } else { $sAjax = utils::GetAbsoluteUrlModulePage( 'itop-backup', 'ajax.backup.php', [ 'operation' => 'download', 'file' => $sFilePath, 'transaction_id' => $sTransactionId, ] ); $sName = "".$sFileName.''; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName)); $sFileEscaped = addslashes($sFilePath); $oButton = ButtonUIBlockFactory::MakeNeutral($sRestore); $oButton->SetIsDisabled($oRestoreMutex->IsLocked()); if (in_array($sBackupFile, $aFilesToDelete)) { $aDetails[] = [ 'file' => $sName.' *', 'size' => $sSize, 'actions' => BlockRenderer::RenderBlockTemplates($oButton), ]; } else { $aDetails[] = ['file' => $sName, 'size' => $sSize, 'actions' => BlockRenderer::RenderBlockTemplates($oButton)]; } $sButtonOnClickJS .= '$("#'.$oButton->GetId().'").off("click").on("click", function () {LaunchRestoreNow("'.$sFileEscaped.'", "'.$sConfirmRestore.'");});'; } $aConfig = [ 'file' => ['label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')], 'size' => ['label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')], 'actions' => ['label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')], ]; $sTableId = 'datatable_background_backups'; $oP->AddUiBlock( GenerateBackupsList( 'bkp-status-backups-auto', 'bkp-status-backups-none', $aConfig, $aDetails, $sTableId ) ); $oP->add_ready_script( <<ListFiles($sBackupDirManual) as $sBackupFile) { $sFileName = basename($sBackupFile); $sFilePath = 'manual/'.$sFileName; if (MetaModel::GetConfig()->Get('demo_mode')) { $sName = $sFileName; } else { $sAjax = utils::GetAbsoluteUrlModulePage( 'itop-backup', 'ajax.backup.php', [ 'operation' => 'download', 'file' => $sFilePath, 'transaction_id' => $sTransactionId, ] ); $sName = "".$sFileName.''; } $sSize = SetupUtils::HumanReadableSize(filesize($sBackupFile)); $sConfirmRestore = addslashes(Dict::Format('bkp-confirm-restore', $sFileName)); $sFileEscaped = addslashes($sFilePath); $oButton = ButtonUIBlockFactory::MakeNeutral("$sRestore"); $oButton->SetIsDisabled($oRestoreMutex->IsLocked()); $aDetails[] = ['file' => $sName, 'size' => $sSize, 'actions' => BlockRenderer::RenderBlockTemplates($oButton)]; $sButtonOnClickJS .= '$("#'.$oButton->GetId().'").off("click").on("click", function () {LaunchRestoreNow("'.$sFileEscaped.'", "'.$sConfirmRestore.'");});'; } $aConfig = [ 'file' => ['label' => Dict::S('bkp-table-file'), 'description' => Dict::S('bkp-table-file+')], 'size' => ['label' => Dict::S('bkp-table-size'), 'description' => Dict::S('bkp-table-size+')], 'actions' => ['label' => Dict::S('bkp-table-actions'), 'description' => Dict::S('bkp-table-actions+')], ]; $sTableId = 'datatable_manual_backups'; $oP->AddUiBlock( GenerateBackupsList( 'bkp-status-backups-manual', 'bkp-status-backups-none', $aConfig, $aDetails, $sTableId ) ); $oP->add_ready_script( <<AddSubBlock(TitleUIBlockFactory::MakeNeutral(Dict::S('bkp-button-backup-now'), 2)); $oP->AddUiBlock($oBlockForBackupNow); // Ongoing operation ? // $oBackupMutex = new iTopMutex('backup.'.utils::GetCurrentEnvironment()); if ($oBackupMutex->IsLocked()) { $oBlockForBackupNow->AddSubBlock( AlertUIBlockFactory::MakeForFailure(Dict::S('bkp-backup-running')) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } $oRestoreMutex = new iTopMutex('restore.'.utils::GetCurrentEnvironment()); if ($oRestoreMutex->IsLocked()) { $oBlockForBackupNow->AddSubBlock( AlertUIBlockFactory::MakeForFailure(Dict::S('bkp-restore-running')) ->SetIsClosable(false) ->SetIsCollapsible(false) ); } // Next occurrence // /** @var \BackgroundTask $oTask */ $oTask = MetaModel::GetObjectByName(BackgroundTask::class, BackupExec::class, false); if ($oTask) { $oTimezone = new DateTimeZone(MetaModel::GetConfig()->Get('timezone')); $oNext = new DateTime($oTask->Get('next_run_date'), $oTimezone); $sNextOccurrence = Dict::Format( 'bkp-next-backup', $aWeekDayToString[$oNext->Format('N')], $oNext->Format('Y-m-d'), $oNext->Format('H:i') ); } else { $sNextOccurrence = Dict::S('bkp-next-backup-unknown'); } $oBlockForBackupNow->AddSubBlock( AlertUIBlockFactory::MakeForInformation('', $sNextOccurrence) ->SetIsClosable(false) ->SetIsCollapsible(false) ); // Do backup now // $sBackUpNow = Dict::S('bkp-button-backup-now'); $oLaunchBackupButton = ButtonUIBlockFactory::MakeForPrimaryAction($sBackUpNow); $oLaunchBackupButton->SetOnClickJsCode('LaunchBackupNow();'); $oBlockForBackupNow->AddSubBlock($oLaunchBackupButton); // restoration panels / hidden info $oRestoreSuccess = AlertUIBlockFactory::MakeForSuccess('', '', 'backup_success') ->AddCSSClass('ibo-is-hidden') ->SetIsCollapsible(false) ->SetIsClosable(true); $oBlockForBackupNow->AddSubBlock($oRestoreSuccess); $oRestoreFailure = AlertUIBlockFactory::MakeForFailure('', '', 'backup_errors') ->AddCSSClass('ibo-is-hidden') ->SetIsCollapsible(false) ->SetIsClosable(true); $oBlockForBackupNow->AddSubBlock($oRestoreFailure); $oBlockForBackupNow->AddHtml(''); $sConfirmBackup = addslashes(Dict::S('bkp-confirm-backup')); $sPleaseWaitBackup = addslashes(Dict::S('bkp-wait-backup')); $sPleaseWaitRestore = addslashes(Dict::S('bkp-wait-restore')); $sRestoreDone = addslashes(Dict::S('bkp-success-restore')); $sMySQLBinDir = addslashes(MetaModel::GetConfig()->GetModuleSetting('itop-backup', 'mysql_bindir', '')); $sDBHost = addslashes(MetaModel::GetConfig()->Get('db_host')); $sDBUser = addslashes(MetaModel::GetConfig()->Get('db_user')); $sDBPwd = addslashes(MetaModel::GetConfig()->Get('db_pwd')); $sDBName = addslashes(MetaModel::GetConfig()->Get('db_name')); $sDBSubName = addslashes(MetaModel::GetConfig()->Get('db_subname')); $sEnvironment = addslashes(utils::GetCurrentEnvironment()); $oModalSpinner = SpinnerUIBlockFactory::MakeMedium(null, $sPleaseWaitBackup); $sModalSpinnerHtml = BlockRenderer::RenderBlockTemplates($oModalSpinner); $oP->add_script( << $sPleaseWaitRestore' }); $('#backup_success').addClass('ibo-is-hidden'); $('#backup_errors').addClass('ibo-is-hidden'); var oParams = {}; oParams.operation = 'restore_get_token'; oParams.file = sBackupFile; oParams.transaction_id = "$sTransactionId"; $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ // Get the value of restore_token $('#restore_token').val(data.token); var oParams = {}; oParams.operation = 'restore_exec'; oParams.token = $("#restore_token").val(); // token to check auth + rights without loading MetaModel oParams.environment = '$sEnvironment'; // needed to load the config oParams.transaction_id = "$sTransactionId"; if (oParams.token.length > 0) { $.post(GetAbsoluteUrlModulePage('itop-backup', 'ajax.backup.php'), oParams, function(data){ if (data.search(/error|exceptio|notice|warning/i) != -1) { $('#backup_success').addClass('ibo-is-hidden'); $('#backup_errors').html(data); $('#backup_errors').removeClass('ibo-is-hidden'); } else { $('#backup_errors').addClass('ibo-is-hidden'); $('#backup_success').html('$sRestoreDone'); $('#backup_success').removeClass('ibo-is-hidden'); } oModal.dialog('close'); }); } else { $('button.restore').prop('disabled', true); oModal.dialog('close'); } }); } JS ); if (MetaModel::GetConfig()->Get('demo_mode')) { $oP->add_ready_script("$('button').prop('disabled', true).attr('title', 'Disabled in demonstration mode')"); } } catch (Exception $e) { $oP = new iTopWebPage(Dict::S('bkp-status-title')); $oP->p(''.$e->getMessage().''); } $oP->output();