From: Marcin Haba Date: Mon, 27 Nov 2017 20:08:47 +0000 (+0100) Subject: baculum: New reworked restore wizard X-Git-Tag: Release-9.0.7~25 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=6c59aff866ff412b46f1d171fc99f2e303a509f0;p=bacula%2Fbacula baculum: New reworked restore wizard --- diff --git a/gui/baculum/protected/Web/Lang/en/messages.mo b/gui/baculum/protected/Web/Lang/en/messages.mo index 523418c02e..9468476621 100644 Binary files a/gui/baculum/protected/Web/Lang/en/messages.mo and b/gui/baculum/protected/Web/Lang/en/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/en/messages.po b/gui/baculum/protected/Web/Lang/en/messages.po index b9d0409fd1..c0c29fd9e1 100644 --- a/gui/baculum/protected/Web/Lang/en/messages.po +++ b/gui/baculum/protected/Web/Lang/en/messages.po @@ -1436,3 +1436,68 @@ msgstr "Resource %s \"%s\" removed successfully." msgid "Please unassign resource %s \"%s\" from these resources and try again." msgstr "Please unassign resource %s \"%s\" from these resources and try again." +msgid "Size:" +msgstr "Size:" + +msgid "JobId:" +msgstr "JobId:" + +msgid "Volume:" +msgstr "Volume:" + +msgid "InChanger:" +msgstr "InChanger:" + +msgid "Sum:" +msgstr "Sum:" + +msgid "Select" +msgstr "Select" + +msgid "Path:" +msgstr "Path:" + +msgid "Restore wizard" +msgstr "Restore wizard" + +msgid "This wizard enables you to do in easy way restore files to destination Bacula Client." +msgstr "This wizard enables you to do in easy way restore files to destination Bacula Client." + +msgid "To start, please select backup Client which data you want to restore." +msgstr "To start, please select backup Client which data you want to restore." + +msgid "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." +msgstr "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." + +msgid "Do not relocate files" +msgstr "Do not relocate files" + +msgid "Relocate files with prefix and/or suffix" +msgstr "Relocate files with prefix and/or suffix" + +msgid "Relocate files with regular expression" +msgstr "Relocate files with regular expression" + +msgid "Strip prefix:" +msgstr "Strip prefix:" + +msgid "Add prefix:" +msgstr "Add prefix:" + +msgid "Add suffix:" +msgstr "Add suffix:" + +msgid "RegexWhere:" +msgstr "RegexWhere:" + +msgid "File relocation option:" +msgstr "File relocation option:" + +msgid "In Changer" +msgstr "In Changer" + +msgid "Volume" +msgstr "Volume" + +msgid "During restore there will be used following volumes:" +msgstr "During restore there will be used following volumes:" diff --git a/gui/baculum/protected/Web/Lang/ja/messages.mo b/gui/baculum/protected/Web/Lang/ja/messages.mo index 4454027436..1f7b596877 100644 Binary files a/gui/baculum/protected/Web/Lang/ja/messages.mo and b/gui/baculum/protected/Web/Lang/ja/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/ja/messages.po b/gui/baculum/protected/Web/Lang/ja/messages.po index 1d460895ec..6ed67fd1b9 100644 --- a/gui/baculum/protected/Web/Lang/ja/messages.po +++ b/gui/baculum/protected/Web/Lang/ja/messages.po @@ -1187,3 +1187,68 @@ msgstr "Resource %s \"%s\" removed successfully." msgid "Please unassign resource %s \"%s\" from these resources and try again." msgstr "Please unassign resource %s \"%s\" from these resources and try again." +msgid "Size:" +msgstr "Size:" + +msgid "JobId:" +msgstr "JobId:" + +msgid "Volume:" +msgstr "Volume:" + +msgid "InChanger:" +msgstr "InChanger:" + +msgid "Sum:" +msgstr "Sum:" + +msgid "Select" +msgstr "Select" + +msgid "Path:" +msgstr "Path:" + +msgid "Restore wizard" +msgstr "Restore wizard" + +msgid "This wizard enables you to do in easy way restore files to destination Bacula Client." +msgstr "This wizard enables you to do in easy way restore files to destination Bacula Client." + +msgid "To start, please select backup Client which data you want to restore." +msgstr "To start, please select backup Client which data you want to restore." + +msgid "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." +msgstr "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." + +msgid "Do not relocate files" +msgstr "Do not relocate files" + +msgid "Relocate files with prefix and/or suffix" +msgstr "Relocate files with prefix and/or suffix" + +msgid "Relocate files with regular expression" +msgstr "Relocate files with regular expression" + +msgid "Strip prefix:" +msgstr "Strip prefix:" + +msgid "Add prefix:" +msgstr "Add prefix:" + +msgid "Add suffix:" +msgstr "Add suffix:" + +msgid "RegexWhere:" +msgstr "RegexWhere:" + +msgid "File relocation option:" +msgstr "File relocation option:" + +msgid "In Changer" +msgstr "In Changer" + +msgid "Volume" +msgstr "Volume" + +msgid "During restore there will be used following volumes:" +msgstr "During restore there will be used following volumes:" diff --git a/gui/baculum/protected/Web/Lang/pl/messages.mo b/gui/baculum/protected/Web/Lang/pl/messages.mo index 054235dd35..e37cc42c59 100644 Binary files a/gui/baculum/protected/Web/Lang/pl/messages.mo and b/gui/baculum/protected/Web/Lang/pl/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/pl/messages.po b/gui/baculum/protected/Web/Lang/pl/messages.po index 9c5eae621f..8dcc96ca93 100644 --- a/gui/baculum/protected/Web/Lang/pl/messages.po +++ b/gui/baculum/protected/Web/Lang/pl/messages.po @@ -1443,3 +1443,68 @@ msgstr "Zasób %s \"%s\" został usunięty pomyślnie." msgid "Please unassign resource %s \"%s\" from these resources and try again." msgstr "Proszę wypisać zasób %s \"%s\" z tych zasobów i spróbować ponownie." +msgid "Size:" +msgstr "Rozmiar:" + +msgid "JobId:" +msgstr "JobId:" + +msgid "Volume:" +msgstr "Wolumen:" + +msgid "InChanger:" +msgstr "InChanger:" + +msgid "Sum:" +msgstr "Suma:" + +msgid "Select" +msgstr "Wybierz" + +msgid "Path:" +msgstr "Lokalizacja:" + +msgid "Restore wizard" +msgstr "Konfigurator przywracania danych" + +msgid "This wizard enables you to do in easy way restore files to destination Bacula Client." +msgstr "Ten konfigurator umożliwia wykonanie w łatwy sposób przywracania plików do docelowego Klienta Bacula." + +msgid "To start, please select backup Client which data you want to restore." +msgstr "Aby zacząć, proszę wybrać Klienta kopii zapasowych, którego dane chcesz przywrócić." + +msgid "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." +msgstr "Są dwa sposoby na wybranie kopii zapasowych do przywrócenia. Sposób 'Tylko wybrany backup' dostarcza listę zadań, z których możliwe jest wybranie jednej kopii zapasowej. Jeśli potrzebujesz przywrócić dane z najnowszych backupów alternatywnie możesz użyć opcję 'Grupa najaktualniejszych backupów', która będzie wybierać backupy za ciebie bazując na zadaniu kopii zapasowych i zasobie fileset." + +msgid "Do not relocate files" +msgstr "Nie relokuj plików" + +msgid "Relocate files with prefix and/or suffix" +msgstr "Relokuj pliki z prefiksem i/lub sufiksem." + +msgid "Relocate files with regular expression" +msgstr "Relokuj pliki z użyciem wyrażenia regularnego" + +msgid "Strip prefix:" +msgstr "Obetnij prefiks:" + +msgid "Add prefix:" +msgstr "Dodaj prefiks:" + +msgid "Add suffix:" +msgstr "Dodaj sufiks:" + +msgid "RegexWhere:" +msgstr "RegexWhere:" + +msgid "File relocation option:" +msgstr "Opcja relokacji plików:" + +msgid "In Changer" +msgstr "In Changer" + +msgid "Volume" +msgstr "Wolumen" + +msgid "During restore there will be used following volumes:" +msgstr "Podczas zadania przywracania danych potrzebne będą następujące wolumeny:" diff --git a/gui/baculum/protected/Web/Lang/pt/messages.mo b/gui/baculum/protected/Web/Lang/pt/messages.mo index a4e3dcdf01..efb09de1e6 100644 Binary files a/gui/baculum/protected/Web/Lang/pt/messages.mo and b/gui/baculum/protected/Web/Lang/pt/messages.mo differ diff --git a/gui/baculum/protected/Web/Lang/pt/messages.po b/gui/baculum/protected/Web/Lang/pt/messages.po index 9cbacbd9ae..d2fd017694 100644 --- a/gui/baculum/protected/Web/Lang/pt/messages.po +++ b/gui/baculum/protected/Web/Lang/pt/messages.po @@ -1451,3 +1451,68 @@ msgstr "Resource %s \"%s\" removed successfully." msgid "Please unassign resource %s \"%s\" from these resources and try again." msgstr "Please unassign resource %s \"%s\" from these resources and try again." +msgid "Size:" +msgstr "Size:" + +msgid "JobId:" +msgstr "JobId:" + +msgid "Volume:" +msgstr "Volume:" + +msgid "InChanger:" +msgstr "InChanger:" + +msgid "Sum:" +msgstr "Sum:" + +msgid "Select" +msgstr "Select" + +msgid "Path:" +msgstr "Path:" + +msgid "Restore wizard" +msgstr "Restore wizard" + +msgid "This wizard enables you to do in easy way restore files to destination Bacula Client." +msgstr "This wizard enables you to do in easy way restore files to destination Bacula Client." + +msgid "To start, please select backup Client which data you want to restore." +msgstr "To start, please select backup Client which data you want to restore." + +msgid "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." +msgstr "There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource." + +msgid "Do not relocate files" +msgstr "Do not relocate files" + +msgid "Relocate files with prefix and/or suffix" +msgstr "Relocate files with prefix and/or suffix" + +msgid "Relocate files with regular expression" +msgstr "Relocate files with regular expression" + +msgid "Strip prefix:" +msgstr "Strip prefix:" + +msgid "Add prefix:" +msgstr "Add prefix:" + +msgid "Add suffix:" +msgstr "Add suffix:" + +msgid "RegexWhere:" +msgstr "RegexWhere:" + +msgid "File relocation option:" +msgstr "File relocation option:" + +msgid "In Changer" +msgstr "In Changer" + +msgid "Volume" +msgstr "Volume" + +msgid "During restore there will be used following volumes:" +msgstr "During restore there will be used following volumes:" diff --git a/gui/baculum/protected/Web/Pages/RestoreWizard.page b/gui/baculum/protected/Web/Pages/RestoreWizard.page index cb4c85f2d4..76d4dc2550 100644 --- a/gui/baculum/protected/Web/Pages/RestoreWizard.page +++ b/gui/baculum/protected/Web/Pages/RestoreWizard.page @@ -3,10 +3,12 @@ @@ -42,7 +44,7 @@
- +
@@ -54,106 +56,235 @@ +

<%[ Restore wizard ]%>

+

+ <%[ This wizard enables you to do in easy way restore files to destination Bacula Client. ]%> + <%[ To start, please select backup Client which data you want to restore. ]%> +

- - +
- +

+ <%[ There are two ways to select backup to restore. Only selected backup way provides list of jobs from which there is possible to select one job. If you need the latest backups from the Client alternatively you can use Group most recent backups way which will select backups for you basing on backup job name and fileset resource. ]%> +

- -
+ + + +
- -
-
-
- -
+
+
+

<%[ Backup for restore: ]%>

+ + + + +
<%=$this->getParent()->Data['name']%>
+
+
+ + + T + + + <%=$this->getParent()->Data['type']%> + + + + + L + + + <%=$this->getParent()->Data['level']%> + + + + +
<%=$this->getPage()->getModule('misc')->jobStates[$this->getParent()->Data['jobstatus']]['value']%>
+
+
+ + +
<%=$this->getParent()->Data['jobbytes']%>
+
+
+ + + + <%=$this->getParent()->Data['endtime']%> + + + + +
+
+
+
+
- - +
+
- +
- +
- +
+ + + <%[ Path: ]%> + + + document.getElementById('restore-browser-files-loading').style.display = 'block'; + + + document.getElementById('restore-browser-files-loading').style.display = 'none'; + +
- - -
- <%[ It seems that there is no files for choosing or file records in database for this job has been purged (file retention period expired) ]%> + + +
+ <%[ It seems that there is no files for choosing or file records in database for this job has been purged (file retention period expired) ]%> +
+
+ + + + <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file' %> <%=($this->getParent()->Data['name'] != '/') ? preg_replace('/\/$/', '', $this->getParent()->Data['name']) : '/'%> +
+ + <%=isset($this->getParent()->Data['lstat']['size']) ? $this->getParent()->Data['lstat']['size'] : '0'%>
- - - - - <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file' %> <%=($this->getParent()->Data['name'] != '/') ? preg_replace('/\/$/', '', $this->getParent()->Data['name']) : '/'%> - - - - - document.getElementById('restore-browser-files-loading').style.display = 'block'; - - - document.getElementById('restore-browser-files-loading').style.display = 'none'; - - - - - -
+ + + + document.getElementById('restore-browser-files-loading').style.display = 'block'; + + + document.getElementById('restore-browser-files-loading').style.display = 'none'; + + + + + +
@@ -176,10 +307,27 @@ <%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%> - <%=is_array($this->getParent()->Data['lstat']) && array_key_exists('mtime', $this->getParent()->Data['lstat']) ? date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime']) : ''%> + <%=$this->getParent()->Data['name']%> » MTIME:<%=date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime'])%> » <%[ Size: ]%><%=$this->getParent()->Data['lstat']['size']%> - + + + @@ -241,19 +392,39 @@
- +
+
+

<%[ During restore there will be used following volumes: ]%>

+ + + + + + + + + + + + + + + +
<%[ In Changer ]%><%[ Volume ]%>
+ +
- +
@@ -273,6 +444,68 @@
+
+
<%[ File relocation option: ]%>
+
+
+
+ +
+
+ + +
@@ -291,7 +524,6 @@
<%[ Backup for restore: ]%>
-
@@ -343,6 +575,34 @@
<%[ Restore job priority: ]%>
<%=$this->RestoreJobPriority->Text%>
+
+
<%[ File relocation option: ]%>
+
+ + + +
+
+
+
+
<%[ Strip prefix: ]%>
+
<%=$this->RestoreStripPrefix->Text%>
+
+
+
<%[ Add prefix: ]%>
+
<%=$this->RestoreAddPrefix->Text%>
+
+
+
<%[ Add suffix: ]%>
+
<%=$this->RestoreAddSuffix->Text%>
+
+
+
+
+
<%[ RegexWhere: ]%>
+
<%=$this->RestoreRegexWhere->Text%>
+
+
diff --git a/gui/baculum/protected/Web/Pages/RestoreWizard.php b/gui/baculum/protected/Web/Pages/RestoreWizard.php index f395a7812b..aeffaec46a 100644 --- a/gui/baculum/protected/Web/Pages/RestoreWizard.php +++ b/gui/baculum/protected/Web/Pages/RestoreWizard.php @@ -23,261 +23,300 @@ Prado::using('Application.Web.Class.BaculumWebPage'); Prado::using('System.Exceptions.TException'); Prado::using('System.Web.UI.WebControls.TWizard'); -Prado::using('System.Web.UI.ActiveControls.TActiveDropDownList'); -Prado::using('System.Web.UI.ActiveControls.TActivePanel'); +Prado::using('System.Web.UI.WebControls.TDataGrid'); Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton'); Prado::using('System.Web.UI.ActiveControls.TActiveImageButton'); Prado::using('System.Web.UI.ActiveControls.TDropContainer'); Prado::using('System.Web.UI.ActiveControls.TDraggable'); -Prado::using('System.Web.UI.ActiveControls.TActiveRadioButton'); Prado::using('System.Web.UI.ActiveControls.TActiveDataGrid'); Prado::using('System.Web.UI.ActiveControls.TCallback'); +Prado::using('System.Web.UI.ActiveControls.TActiveTextBox'); class RestoreWizard extends BaculumWebPage { - private $job_levels_to_restore = array('F' => 'Full', 'I' => 'Incremental', 'D'=> 'Differential'); - private $jobs = null; - private $filesets = null; - private $storages = null; - private $clients = null; - private $browser_root_dir = array('name' => '.', 'type' => 'dir'); + /** + * Job levels allowed to restore. + */ + + private $joblevel = array('F', 'I', 'D'); + /** + * Job statuses allowed to restore. + */ + private $jobstatus = array('T', 'W', 'A', 'E', 'e', 'f'); + + /** + * File browser special directories. + */ + private $browser_root_dir = array('name' => '.', 'type' => 'dir', 'fileid' => null); private $browser_up_dir = array('name' => '..', 'type' => 'dir'); + /** + * Used to provide in template selected by user single jobid to restore. + */ + public $restore_single_jobid; + + /** + * Stores file relocation option. Used in template. + */ + public $file_relocation_opt; + + /** + * FIle browser elements for which 'Add' button is unavailable. + */ public $excluded_elements_from_add = array('.', '..'); + /** + * Prefix for Bvfs path. + */ const BVFS_PATH_PREFIX = 'b2'; public function onInit($param) { parent::onInit($param); if(!$this->IsPostBack && !$this->IsCallBack) { - $this->setBrowserFiles(array()); - $this->setFileVersions(array()); - $this->setFilesToRestore(array()); - $this->markFileToRestore(null, null); - $this->setRestoreJobs(); - $_SESSION['restore_path'] = array(); + $this->resetWizard(); + $this->loadBackupClients(); } } - public function onLoad($param) { - parent::onLoad($param); - if($this->RestoreWizard->ActiveStepIndex == 0) { - $this->jobs = $this->getModule('api')->get(array('jobs'))->output; - $this->filesets = $this->getModule('api')->get(array('filesets', 'info'))->output; + /** + * Wizard next button callback actions. + * + * @param TWizard $sender sender object + * @param TWizardNavigationEventParameter $param sender parameters + * @return none + */ + public function wizardNext($sender, $param) { + if ($param->CurrentStepIndex === 0) { + $this->loadBackupsForClient(); + $this->loadGroupBackupToRestore(); + $this->loadGroupFileSetToRestore(); + $this->loadRestoreClients(); + if (isset($_SESSION['restore_single_jobid'])) { + $this->restore_single_jobid = $_SESSION['restore_single_jobid']; } - $this->clients = $this->getModule('api')->get(array('clients'))->output; - - if(!$this->IsCallBack && (($this->RestoreWizard->ActiveStepIndex == 1 && $this->getPage()->BackupToRestore->ItemCount > 0) || $this->RestoreWizard->ActiveStepIndex == 3)) { - $this->setFileVersions(array()); - $this->setBrowserPath(''); - $this->prepareFilesToRestore(); - $this->prepareVersionsToRestore(); + } elseif ($param->CurrentStepIndex === 1) { + if ($this->Request->contains('backup_to_restore')) { + $_SESSION['restore_single_jobid'] = $this->Request['backup_to_restore']; } - } - - public function addFileToRestore($sender, $param) { - $fileid = null; - if (isset($param->callbackParameter)) { - list(, , , $sourceElementID, , ) = explode('_', $sender->ClientID, 6); - $fileid = $param->callbackParameter; - } else { - $control = $param->getDroppedControl(); - $item = $control->getNamingContainer(); - list(, , , $sourceElementID, , ) = explode('_', $param->getDragElementID(), 6); // I know that it is ugly. - } - if($sourceElementID == $this->VersionsDataGrid->ID) { - if (is_null($fileid)) { - $fileid = $this->VersionsDataGrid->getDataKeys()->itemAt($item->getItemIndex()); + $this->setFileVersions(); + $this->loadSelectedFiles(); + $this->loadFileVersions(); + $this->goToPath(); + } elseif ($param->CurrentStepIndex === 2) { + $this->loadRequiredVolumes(); + } elseif ($param->CurrentStepIndex === 3) { + if (isset($_SESSION['file_relocation'])) { + $this->file_relocation_opt = $_SESSION['file_relocation']; } - $fileProperties = $this->getFileVersions($fileid); - } else { - if (is_null($fileid)) { - $fileid = $this->DataGridFiles->getDataKeys()->itemAt($item->getItemIndex()); + } elseif ($param->CurrentStepIndex === 4) { + if ($this->Request->contains('file_relocation')) { + $_SESSION['file_relocation'] = $this->Request['file_relocation']; } - $fileProperties = $this->getBrowserFile($fileid); - } - if($fileProperties['name'] != $this->browser_root_dir['name'] && $fileProperties['name'] != $this->browser_up_dir['name']) { - $this->markFileToRestore($fileid, $fileProperties); - $this->prepareFilesToRestore(); + $this->file_relocation_opt = $_SESSION['file_relocation']; } } - public function removeSelectedFile($sender, $param) { - $fileid = $param->CallbackParameter; - $this->unmarkFileToRestore($fileid); - $this->prepareFilesToRestore(); - } - - public function getVersions($sender, $param) { - list($filename, $pathid, $filenameid, $jobid) = explode('|', $param->CallbackParameter, 4); - if($filenameid == 0) { - $this->setBrowserPath($filename); - return; + /** + * Wizard prev button callback actions. + * + * @param TWizard $sender sender object + * @param TWizardNavigationEventParameter $param sender parameters + * @return none + */ + public function wizardPrev($sender, $param) { + if ($param->CurrentStepIndex === 2) { + $this->restore_single_jobid = $_SESSION['restore_single_jobid']; + $this->loadBackupsForClient(); + } elseif ($param->CurrentStepIndex === 3) { + $this->setFileVersions(); + $this->loadSelectedFiles(); + $this->loadFileVersions(); + $this->goToPath(); + } elseif ($param->CurrentStepIndex === 5) { + $this->file_relocation_opt = $_SESSION['file_relocation']; } - $clientname = $this->BackupClientName->SelectedValue; - $versions = $this->getModule('api')->get(array('bvfs', 'versions', $clientname, $jobid, $pathid, $filenameid))->output; - $fileVersions = $this->getModule('misc')->parseFileVersions($filename, $versions); - $this->setFileVersions($fileVersions); - $this->VersionsDataGrid->dataSource = $fileVersions; - $this->VersionsDataGrid->dataBind(); - $this->prepareFilesToRestore(); - } - - public function refreshSelectedFiles($sender, $param) { - $this->prepareFilesToRestore(); - $this->SelectedVersionsDropper->render($param->NewWriter); - } - - public function NextStep($sender, $param) { - } - - public function PreviousStep($sender, $param) { } + /** + * Cancel wizard. + * + * @return none + */ public function wizardStop($sender, $param) { + $this->resetWizard(); $this->goToDefaultPage(); } - public function setJobs($sender, $param) { - $jobs_list = $jobs_group_list = array(); - $clientid = null; - for ($i = 0; $i < count($this->clients); $i++) { - if ($this->clients[$i]->name === $this->BackupClientName->SelectedValue) { - $clientid = $this->clients[$i]->clientid; - break; - } - } - if(is_array($this->jobs)) { - foreach($this->jobs as $job) { - if(array_key_exists($job->level, $this->job_levels_to_restore) && $job->type == 'B' && $job->jobstatus == 'T' && $job->clientid == $clientid) { - $jobs_list[$job->jobid] = sprintf('[%s] %s, %s, %s', $job->jobid, $job->name, $this->job_levels_to_restore[$job->level], $job->endtime); - $jobs_group_list[$job->name] = $job->name; - } - } - } - - $fileset_list = array(); - for ($i = 0; $i < count($this->filesets); $i++) { - $fileset_list[$this->filesets[$i]->filesetid] = $this->filesets[$i]->fileset . ' (' . $this->filesets[$i]->createtime . ')'; - } - asort($fileset_list); - - $this->BackupToRestore->dataSource = $jobs_list; - $this->BackupToRestore->dataBind(); - $this->GroupBackupToRestore->dataSource = $jobs_group_list; - $this->GroupBackupToRestore->dataBind(); - $this->GroupBackupFileSet->dataSource = $fileset_list; - $this->GroupBackupFileSet->dataBind(); - $this->setRestoreClients($sender, $param); - } - - public function setBackupClients($sender, $param) { - if(!$this->IsPostBack) { - $client_list = array(); - foreach($this->clients as $client) { - $client_list[$client->name] = $client->name; - } - $this->BackupClientName->dataSource = $client_list; - $this->BackupClientName->dataBind(); + /** + * Load backup clients list (step 1). + * + * @param TActiveDropDownList $sender sender object + * @param TCommandParameter $param parameters object + * @return none + */ + public function loadBackupClients() { + $client_list = array(); + $clients = $this->getModule('api')->get(array('clients'))->output; + for ($i = 0; $i < count($clients); $i++) { + $client_list[$clients[$i]->name] = $clients[$i]->name; } - } - - public function setRestoreClients($sender, $param) { - if(!$this->IsPostBack || $sender->ID == $this->BackupClientName->ID) { - $client_list = array(); - foreach($this->clients as $client) { - $client_list[$client->name] = $client->name; - } - $this->RestoreClient->SelectedValue = $this->BackupClientName->SelectedValue; - $this->RestoreClient->dataSource = $client_list; - $this->RestoreClient->dataBind(); + asort($client_list); + $this->BackupClientName->dataSource = $client_list; + $this->BackupClientName->dataBind(); + } + + /** + * Load restore client list. + * + * @return none + */ + public function loadRestoreClients() { + $client_list = array(); + $clients = $this->getModule('api')->get(array('clients'))->output; + for ($i = 0; $i < count($clients); $i++) { + $client_list[$clients[$i]->name] = $clients[$i]->name; } - } - - public function setBackupClient($sender, $param) { - $this->ClientToRestore->SelectedValue = $param->SelectedValue; - } - - public function setBackupSelection($sender, $param) { + $this->RestoreClient->SelectedValue = $this->BackupClientName->SelectedValue; + $this->RestoreClient->dataSource = $client_list; + $this->RestoreClient->dataBind(); + } + + /** + * Load backups for selected client (Step 2). + * + * @return none + */ + public function loadBackupsForClient() { + $clientid = $this->getBackupClientId(); + $jobs_for_client = $this->getModule('api')->get(array('clients', 'jobs', $clientid))->output; + $jobs = $this->getModule('misc')->objectToArray($jobs_for_client); + $this->BackupsToRestore->DataSource = array_filter($jobs, array($this, 'isBackupJobToRestore')); + $this->BackupsToRestore->dataBind(); + } + + /** + * Check if job can be used in restore. + * + * @param array $job job properties + * @return true if job should be listed to restore, otherwise false + */ + private function isBackupJobToRestore($job) { + return ($job['type'] === 'B' && in_array($job['level'], $this->joblevel) && in_array($job['jobstatus'], $this->jobstatus)); + } + + public function loadBackupSelection($sender, $param) { $this->GroupBackupToRestoreField->Display = ($sender->ID == $this->GroupBackupSelection->ID) ? 'Dynamic' : 'None'; $this->BackupToRestoreField->Display = ($sender->ID == $this->OnlySelectedBackupSelection->ID) ? 'Dynamic' : 'None'; - $this->setBrowserFiles(array()); - $this->setFileVersions(array()); - $this->setFilesToRestore(array()); + $this->setBrowserFiles(); + $this->setFileVersions(); + $this->setFilesToRestore(); $this->markFileToRestore(null, null); $_SESSION['restore_path'] = array(); } - public function resetFileBrowser($sender, $param) { - $this->markFileToRestore(null, null); - $this->setBrowserPath($this->browser_root_dir['name']); - } - - private function prepareBrowserFiles($files) { - $this->setBrowserFiles($files); - $this->DataGridFiles->dataSource = $files; - @$this->DataGridFiles->dataBind(); - } - - private function prepareVersionsToRestore() { - $this->VersionsDataGrid->dataSource = $_SESSION['files_versions']; - $this->VersionsDataGrid->dataBind(); - } - private function prepareFilesToRestore() { - $this->SelectedVersionsDataGrid->dataSource = $_SESSION['restore']; - $this->SelectedVersionsDataGrid->dataBind(); - } - - private function setBrowserPath($path) { - if(!empty($path)) { - if($path == $this->browser_up_dir['name']) { - array_pop($_SESSION['restore_path']); - } elseif($path == $this->browser_root_dir['name']) { - $_SESSION['restore_path'] = array(); - } else { - array_push($_SESSION['restore_path'], $path); + /** + * Get selected backup client identifier. + * + * @return mixed client identifier or null if no clientid found + */ + public function getBackupClientId() { + $clientid = null; + $clients = $this->getModule('api')->get(array('clients'))->output; + for ($i = 0; $i < count($clients); $i++) { + if ($clients[$i]->name === $this->BackupClientName->SelectedValue) { + $clientid = $clients[$i]->clientid; + break; + } + } + return $clientid; + } + + /** + * Load backup jobs to restore for group most recent backups feature. + * + * @return none + */ + public function loadGroupBackupToRestore() { + $jobs = $this->getModule('api')->get(array('jobs'))->output; + $jobs = $this->getModule('misc')->objectToArray($jobs); + $clientid = $this->getBackupClientId(); + $job_group_list = array(); + for ($i = 0; $i < count($jobs); $i++) { + $job = $this->getModule('misc')->objectToArray($jobs[$i]); + if ($this->isBackupJobToRestore($jobs[$i]) && $jobs[$i]['clientid'] === $clientid) { + $job_group_list[$jobs[$i]['name']] = $jobs[$i]['name']; } } - $this->setBrowserLocalizator(); - $this->prepareBrowserContent(); - } - private function getBrowserPath($stringFormat = false) { - return ($stringFormat === true) ? implode($_SESSION['restore_path']) : $_SESSION['restore_path']; + $this->GroupBackupToRestore->dataSource = $job_group_list; + $this->GroupBackupToRestore->dataBind(); } - private function setBrowserLocalizator() { - $localization = $this->getBrowserPath(true); - $this->PathField->HeaderText = mb_strlen($localization) > 56 ? '...' . mb_substr($localization, -56) : $localization; + /** + * Load filesets to restore for group most recent backups feature. + * + * @return none + */ + public function loadGroupFileSetToRestore() { + $filesets = $this->getModule('api')->get(array('filesets', 'info'))->output; + $fileset_list = array(); + for ($i = 0; $i < count($filesets); $i++) { + $fileset_list[$filesets[$i]->filesetid] = $filesets[$i]->fileset . ' (' . $filesets[$i]->createtime . ')'; + } + asort($fileset_list); + + $this->GroupBackupFileSet->dataSource = $fileset_list; + $this->GroupBackupFileSet->dataBind(); } + /** + * Prepare left file browser content. + * + * @return none + */ private function prepareBrowserContent() { $jobids = $this->getElementaryBackup(); $elements = array(); - if (!is_null($jobids)) { - // generating Bvfs may takes a moment - $this->generateBvfsCacheByJobids($jobids); - $bvfs_dirs_list = $this->getModule('api')->set(array('bvfs', 'lsdirs'), array('jobids' => $jobids, 'path' => $this->getBrowserPath(true))); - $bvfs_dirs = is_object($bvfs_dirs_list) ? $bvfs_dirs_list->output : array(); - $dirs = $this->getModule('misc')->parseBvfsList($bvfs_dirs); - $bvfs_files_list = $this->getModule('api')->set(array('bvfs', 'lsfiles'), array('jobids' => $jobids, 'path' => $this->getBrowserPath(true))); - $bvfs_files = is_object($bvfs_files_list) ? $bvfs_files_list->output : array(); - $files = $this->getModule('misc')->parseBvfsList($bvfs_files); + if (!empty($jobids)) { + // generating Bvfs may take a moment + $this->generateBvfsCache($jobids); + + // get directories list + $bvfs_dirs = $this->getModule('api')->set( + array('bvfs', 'lsdirs'), + array('jobids' => $jobids, 'path' => implode($_SESSION['restore_path'])) + ); + $dirs = $this->getModule('misc')->parseBvfsList($bvfs_dirs->output); + + // get files list + $bvfs_files = $this->getModule('api')->set( + array('bvfs', 'lsfiles'), + array('jobids' => $jobids, 'path' => implode($_SESSION['restore_path'])) + ); + $files = $this->getModule('misc')->parseBvfsList($bvfs_files->output); + $elements = array_merge($dirs, $files); - if(count($this->getBrowserPath()) > 0) { + if(count($_SESSION['restore_path']) > 0) { array_unshift($elements, $this->browser_root_dir); } } - $this->prepareBrowserFiles($elements); + $this->loadBrowserFiles($elements); } + /* + * Get single elementary backup job identifiers. + * + * @return string comma separated job identifiers + */ private function getElementaryBackup() { - $jobids = null; - if($this->OnlySelectedBackupSelection->Checked === true) { - $jobs = $this->getModule('api')->get(array('bvfs', 'getjobids', $this->BackupToRestore->SelectedValue)); + $jobids = ''; + if($this->OnlySelectedBackupSelection->Checked && isset($_SESSION['restore_single_jobid'])) { + $jobs = $this->getModule('api')->get( + array('bvfs', 'getjobids', $_SESSION['restore_single_jobid']) + ); $ids = is_object($jobs) ? $jobs->output : array(); - foreach($ids as $jobid) { + foreach ($ids as $jobid) { if(preg_match('/^([\d\,]+)$/', $jobid, $match) == 1) { $jobids = $match[1]; break; @@ -294,7 +333,7 @@ class RestoreWizard extends BaculumWebPage $this->GroupBackupFileSet->SelectedValue ); $jobs_recent = $this->getModule('api')->get($params); - if(count($jobs_recent->output) > 0) { + if (count($jobs_recent->output) > 0) { $ids = $jobs_recent->output; $jobids = implode(',', $ids); } @@ -302,14 +341,210 @@ class RestoreWizard extends BaculumWebPage return $jobids; } - private function generateBvfsCacheByJobids($jobids) { - $this->getModule('api')->set(array('bvfs', 'update'), array('jobids' => $jobids)); + /** + * Load path callback method. + * Used for manually typed paths in path field. + * + * @param TActiveLinkButton $sender sender object + * @param TEventParameter $param events parameter + * @return none + */ + public function loadPath($sender, $param) { + $path = explode('/', $this->PathField->Text); + $path_len = count($path); + for ($i = 0; $i < count($path); $i++) { + if ($i == ($path_len - 1) && empty($path[$i])) { + // last path dir is slash so not add slash to last element + break; + } + $path[$i] .= '/'; + } + $this->goToPath($path, true); + } + + /** + * Go to specific path in the file browser. + * There is possible to pass both single directory 'somedir' + * or whole path '/etc/somedir'. + * + * @param string $path path to go + * @param bool $full_path determines if $path param is full path or relative path (singel directory) + * @return none + */ + private function goToPath($path = '', $full_path = false) { + if(!empty($path) && !$full_path) { + if($path == $this->browser_up_dir['name']) { + array_pop($_SESSION['restore_path']); + } elseif($path == $this->browser_root_dir['name']) { + $_SESSION['restore_path'] = array(); + } else { + array_push($_SESSION['restore_path'], $path); + } + } + if ($full_path && is_array($path)) { + $_SESSION['restore_path'] = $path; + } + $this->setBrowserPath(); + $this->prepareBrowserContent(); + } + + /** + * Add/mark file to restore. + * Used as callback to drag&drop browser elements. + * + * @param object $sender sender object + * @param object $param param object + * @return none + */ + public function addFileToRestore($sender, $param) { + $fileid = null; + $source_element_id = null; + $file_prop = array(); + if (isset($param->CallbackParameter)) { + $id_parts = explode('_', $sender->ClientID, 6); + $source_element_id = $id_parts[3]; + $fileid = $param->CallbackParameter; + } else { + $control = $param->getDroppedControl(); + $item = $control->getNamingContainer(); + $id_parts = explode('_', $param->getDragElementID(), 6); + $source_element_id = $id_parts[3]; + } + if($source_element_id == $this->VersionsDataGrid->ID) { + if (is_null($fileid)) { + $fileid = $this->VersionsDataGrid->getDataKeys()->itemAt($item->getItemIndex()); + } + $file_prop = $this->getFileVersions($fileid); + } else { + if (is_null($fileid)) { + $fileid = $this->DataGridFiles->getDataKeys()->itemAt($item->getItemIndex()); + } + $file_prop = $this->getBrowserFile($fileid); + } + if($file_prop['name'] != $this->browser_root_dir['name'] && $file_prop['name'] != $this->browser_up_dir['name']) { + $this->markFileToRestore($fileid, $file_prop); + $this->loadSelectedFiles(); + } + } + + /** + * Remove file from files marked to restre. + * + * @param TActiveImageButton $sender remove button object + * @param TEventParameter $param param object + * @return none + */ + public function removeSelectedFile($sender, $param) { + $fileid = $param->CallbackParameter; + $this->unmarkFileToRestore($fileid); + $this->loadSelectedFiles(); + } + + /** + * Get file backed up versions. + * Called as callback on file element click. + * + * @param TCallback $sender sender object + * @param object $param param object + * @return none + */ + public function getVersions($sender, $param) { + list($filename, $pathid, $filenameid, $jobid) = explode('|', $param->CallbackParameter, 4); + if($filenameid == 0) { + $this->goToPath($filename); + return; + } + $clientname = $this->BackupClientName->SelectedValue; + $versions = $this->getModule('api')->get(array('bvfs', 'versions', $clientname, $jobid, $pathid, $filenameid))->output; + $file_versions = $this->getModule('misc')->parseFileVersions($filename, $versions); + $this->setFileVersions($file_versions); + $this->VersionsDataGrid->dataSource = $file_versions; + $this->VersionsDataGrid->dataBind(); + $this->loadSelectedFiles(); + } + + /** + * Refresh/re-render selected files list. + * + * @param TDropContainer $sender sender object + * @param TEventParameter $param param object + * @return none + */ + public function refreshSelectedFiles($sender, $param) { + $this->loadSelectedFiles(); + $this->SelectedVersionsDropper->render($param->NewWriter); + } + + /* + * Load file browser files to list. + * + * @param array $files files to list. + * @return none + */ + private function loadBrowserFiles($files) { + $this->setBrowserFiles($files); + $this->DataGridFiles->dataSource = $files; + $this->DataGridFiles->dataBind(); + } + + /** + * Load file versions area. + * + * @return none; + */ + private function loadFileVersions() { + $this->VersionsDataGrid->dataSource = $_SESSION['files_versions']; + $this->VersionsDataGrid->dataBind(); + } + + /** + * Load selected files in drop area. + * + * @return none + */ + private function loadSelectedFiles() { + $this->SelectedVersionsDataGrid->dataSource = $_SESSION['restore']; + $this->SelectedVersionsDataGrid->dataBind(); } - private function setFileVersions($versions) { + /** + * Set file browser path field. + * + * @return none + */ + private function setBrowserPath() { + $this->PathField->Text = implode($_SESSION['restore_path']); + } + + /** + * Generate Bvfs cache by job identifiers. + * + * @param string $jobids comma separated job identifiers + * @return none + */ + private function generateBvfsCache($jobids) { + $this->getModule('api')->set( + array('bvfs', 'update'), + array('jobids' => $jobids) + ); + } + + /** + * Set versions for selected file. + * + * @param array $versions file versions data + * @return none + */ + private function setFileVersions($versions = array()) { $_SESSION['files_versions'] = $versions; } + /** + * Get file versions for specified fileid. + * + * @param integer $fileid file identifier + * @return none + */ private function getFileVersions($fileid) { $versions = array(); foreach($_SESSION['files_versions'] as $file) { @@ -321,10 +556,22 @@ class RestoreWizard extends BaculumWebPage return $versions; } - private function setBrowserFiles($files) { + /** + * Set browser files. + * + * @param array $files file list + * @return none + */ + private function setBrowserFiles($files = array()) { $_SESSION['files_browser'] = $files; } + /** + * Get browser file by fileid. + * + * @param integer $fileid file identifier + * @return none + */ private function getBrowserFile($fileid) { $element = array(); foreach($_SESSION['files_browser'] as $file) { @@ -336,6 +583,13 @@ class RestoreWizard extends BaculumWebPage return $element; } + /** + * Mark file to restore. + * + * @param integer $fileid file identifier + * @param array $file file properties to mark + * @return none + */ private function markFileToRestore($fileid, $file) { if($fileid === null) { $_SESSION['restore'] = array(); @@ -344,60 +598,119 @@ class RestoreWizard extends BaculumWebPage } } + /** + * Unmark file to restore. + * + * @param integer $fileid file identifier + * @return none + */ private function unmarkFileToRestore($fileid) { if(array_key_exists($fileid, $_SESSION['restore'])) { unset($_SESSION['restore'][$fileid]); } } + /** + * Get files to restore. + * + * @return array list with files to restore + */ public function getFilesToRestore() { return $_SESSION['restore']; } - public function setFilesToRestore($files) { + /** + * Set files to restore + * + * @param array $files files to restore + * @return none + */ + public function setFilesToRestore($files = array()) { $_SESSION['restore'] = $files; } - public function getRestoreElements($asObject = false) { + /** + * Get all restore elements (fileids and dirids). + * + * @param bool $as_object return result as object + * @return array list fileids and dirids + */ + public function getRestoreElements($as_object = false) { $fileids = array(); $dirids = array(); - foreach($this->getFilesToRestore() as $fileid => $properties) { - if($properties['type'] == 'dir') { + $findexes = array(); + foreach ($this->getFilesToRestore() as $fileid => $properties) { + if ($properties['type'] == 'dir') { $dirids[] = $properties['pathid']; - } elseif($properties['type'] == 'file') { + } elseif ($properties['type'] == 'file') { $fileids[] = $fileid; + if ($properties['lstat']['linkfi'] !== 0) { + $findexes[] = $properties['jobid'] . ',' . $properties['lstat']['linkfi']; + } } } - $ret = array('fileid' => $fileids, 'dirid' => $dirids); - if($asObject === true) { + $ret = array('fileid' => $fileids, 'dirid' => $dirids, 'findex' => $findexes); + if($as_object === true) { $ret = (object)$ret; } return $ret; } + /** + * Wizard finish method. + * + * @return none + */ public function wizardCompleted() { $jobids = $this->getElementaryBackup(); $path = self::BVFS_PATH_PREFIX . getmypid(); $restore_elements = $this->getRestoreElements(); $cmd_props = array('jobids' => $jobids, 'path' => $path); + $is_element = false; if(count($restore_elements['fileid']) > 0) { $cmd_props['fileid'] = implode(',', $restore_elements['fileid']); + $is_element = true; } if(count($restore_elements['dirid']) > 0) { $cmd_props['dirid'] = implode(',', $restore_elements['dirid']); + $is_element = true; } + if (count($restore_elements['findex']) > 0) { + $cmd_props['findex'] = implode(',', $restore_elements['findex']); + $is_element = true; + } + + $jobid = null; + if ($is_element) { + $this->getModule('api')->create(array('bvfs', 'restore'), $cmd_props); + $restore_props = array(); + $restore_props['rpath'] = $path; + $restore_props['client'] = $this->RestoreClient->SelectedValue; + $restore_props['priority'] = intval($this->RestoreJobPriority->Text); + if ($_SESSION['file_relocation'] == 2) { + if (!empty($this->RestoreStripPrefix->Text)) { + $restore_props['strip_prefix'] = $this->RestoreStripPrefix->Text; + } + if (!empty($this->RestoreAddPrefix->Text)) { + $restore_props['add_prefix'] = $this->RestoreAddPrefix->Text; + } + if (!empty($this->RestoreAddSuffix->Text)) { + $restore_props['add_suffix'] = $this->RestoreAddSuffix->Text; + } + } elseif ($_SESSION['file_relocation'] == 3) { + if (!empty($this->RestoreRegexWhere->Text)) { + $restore_props['regex_where'] = $this->RestoreRegexWhere->Text; + } + } + if (!array_key_exists('add_prefix', $restore_props)) { + $restore_props['where'] = $this->RestorePath->Text; + } + $restore_props['replace'] = $this->ReplaceFiles->SelectedValue; + $restore_props['restorejob'] = $this->RestoreJob->SelectedValue; - $this->getModule('api')->create(array('bvfs', 'restore'), $cmd_props); - $restore_props = array(); - $restore_props['rpath'] = $path; - $restore_props['client'] = $this->RestoreClient->SelectedValue; - $restore_props['priority'] = intval($this->RestoreJobPriority->Text); - $restore_props['where'] = $this->RestorePath->Text; - $restore_props['replace'] = $this->ReplaceFiles->SelectedValue; - $restore_props['restorejob'] = $this->RestoreJob->SelectedValue; - - $ret = $this->getModule('api')->create(array('jobs', 'restore'), $restore_props); - $jobid = $this->getModule('misc')->findJobIdStartedJob($ret->output); + $ret = $this->getModule('api')->create(array('jobs', 'restore'), $restore_props); + $jobid = $this->getModule('misc')->findJobIdStartedJob($ret->output); + } $url_params = array('open' => 'Job'); if (is_numeric($jobid)) { $url_params['id'] = $jobid; @@ -405,8 +718,13 @@ class RestoreWizard extends BaculumWebPage $this->goToDefaultPage($url_params); } - private function setRestoreJobs() { - $restore_job_tasks = $this->Application->getModule('api')->get(array('jobs', 'tasks', 'type', 'R'))->output; + /** + * Load restore jobs on the list. + * + * @return none + */ + private function loadRestoreJobs() { + $restore_job_tasks = $this->getModule('api')->get(array('jobs', 'tasks', 'type', 'R'))->output; $jobs = array(); foreach ($restore_job_tasks as $director => $restore_jobs) { $jobs = array_merge($jobs, $restore_jobs); @@ -414,5 +732,40 @@ class RestoreWizard extends BaculumWebPage $this->RestoreJob->DataSource = array_combine($jobs, $jobs); $this->RestoreJob->dataBind(); } + + private function loadRequiredVolumes() { + $volumes = array(); + foreach ($this->getFilesToRestore() as $fileid => $props) { + // it can be expensive for many restore paths + $result = $this->getModule('api')->get(array('volumes', 'required', $props['jobid'], $fileid)); + if ($result->error === 0) { + for ($i = 0; $i < count($result->output); $i++) { + $volumes[$result->output[$i]->volume] = array( + 'volume' => $result->output[$i]->volume, + 'inchanger' => $result->output[$i]->inchanger + ); + } + } + } + $this->RestoreVolumes->DataSource = array_values($volumes); + $this->RestoreVolumes->dataBind(); + } + + /** + * Reset wizard. + * All fields are back to initial form. + * + * @return none + */ + private function resetWizard() { + $this->setBrowserFiles(); + $this->setFileVersions(); + $this->setFilesToRestore(); + $this->markFileToRestore(null, null); + $this->loadRestoreJobs(); + $_SESSION['restore_path'] = array(); + $_SESSION['restore_single_jobid'] = null; + unset($_SESSION['file_relocation']); + } } ?> diff --git a/gui/baculum/themes/Baculum-v1/restore-wizard.css b/gui/baculum/themes/Baculum-v1/restore-wizard.css new file mode 100644 index 0000000000..b10ac8b3dc --- /dev/null +++ b/gui/baculum/themes/Baculum-v1/restore-wizard.css @@ -0,0 +1,70 @@ +.wizard { +} + +.steps { + background-image: none; + min-height: 520px; +} + +#restore-browser { + width: 100%; + height: 100%; +} + +#restore-browser-files { + width: 315px; + height: 434px; +} + +#restore-browser-files, #restore-browser-versions, #restore-browser-selected { + overflow: auto; + width: 474px; + background-color: white; + border-radius: 4px; + padding: 4px; + -moz-user-select: -moz-none; + -webkit-user-select: none; + -ms-user-select: none; +} + +#restore-browser-files { + height: 506px; +} + +#restore-browser-versions { + height: 206px; +} + +#restore-browser-selected { + height: 282px; +} + +#restore-browser-files-loading { + position: absolute; + z-index: 10; + width: 482px; + height: 514px; + border-radius: 4px; + background: rgba(182,182,182,0.7) url('progress.gif') no-repeat center center; +} + +.restore-browser-element-size { + float: right; + width: 100px; +} + +.restore-browser-element-size { + line-height: 24px; +} + +.file-browser-watermark { + padding: 77px 10px; + text-align: center; + font-size: 21px; + color: rgb(185, 184, 184); +} + +#backup-to-restore-field > div { + height: 467px; + overflow-y: auto; +}