3 * Bacula(R) - The Network Backup Solution
4 * Baculum - Bacula web interface
6 * Copyright (C) 2013-2017 Kern Sibbald
8 * The main author of Baculum is Marcin Haba.
9 * The original author of Bacula is Kern Sibbald, with contributions
10 * from many others, a complete list can be found in the file AUTHORS.
12 * You may use this file and others of this release according to the
13 * license defined in the LICENSE file, which includes the Affero General
14 * Public License, v3.0 ("AGPLv3") and some additional permissions and
15 * terms pursuant to its AGPLv3 Section 7.
17 * This notice must be preserved when any source code is
18 * conveyed and/or propagated.
20 * Bacula(R) is a registered trademark of Kern Sibbald.
23 Prado::using('Application.Web.Class.BaculumWebPage');
24 Prado::using('System.Exceptions.TException');
25 Prado::using('System.Web.UI.WebControls.TWizard');
26 Prado::using('System.Web.UI.ActiveControls.TActiveDropDownList');
27 Prado::using('System.Web.UI.ActiveControls.TActivePanel');
28 Prado::using('System.Web.UI.ActiveControls.TActiveLinkButton');
29 Prado::using('System.Web.UI.ActiveControls.TActiveImageButton');
30 Prado::using('System.Web.UI.ActiveControls.TDropContainer');
31 Prado::using('System.Web.UI.ActiveControls.TDraggable');
32 Prado::using('System.Web.UI.ActiveControls.TActiveRadioButton');
33 Prado::using('System.Web.UI.ActiveControls.TActiveDataGrid');
34 Prado::using('System.Web.UI.ActiveControls.TCallback');
36 class RestoreWizard extends BaculumWebPage
38 private $job_levels_to_restore = array('F' => 'Full', 'I' => 'Incremental', 'D'=> 'Differential');
40 private $filesets = null;
41 private $storages = null;
42 private $clients = null;
43 private $browser_root_dir = array('name' => '.', 'type' => 'dir');
44 private $browser_up_dir = array('name' => '..', 'type' => 'dir');
46 public $excluded_elements_from_add = array('.', '..');
48 const BVFS_PATH_PREFIX = 'b2';
50 public function onPreInit($param) {
51 parent::onPreInit($param);
52 $this->Application->getModule('web_users')->loginUser();
55 public function onInit($param) {
56 parent::onInit($param);
57 if(!$this->IsPostBack && !$this->IsCallBack) {
58 $this->setBrowserFiles(array());
59 $this->setFileVersions(array());
60 $this->setFilesToRestore(array());
61 $this->markFileToRestore(null, null);
62 $this->setRestoreJobs();
63 $_SESSION['restore_path'] = array();
67 public function onLoad($param) {
68 parent::onLoad($param);
69 if($this->RestoreWizard->ActiveStepIndex == 0) {
70 $this->jobs = $this->getModule('api')->get(array('jobs'))->output;
71 $this->filesets = $this->getModule('api')->get(array('filesets', 'info'))->output;
73 $this->clients = $this->getModule('api')->get(array('clients'))->output;
75 if(!$this->IsCallBack && (($this->RestoreWizard->ActiveStepIndex == 1 && $this->getPage()->BackupToRestore->ItemCount > 0) || $this->RestoreWizard->ActiveStepIndex == 3)) {
76 $this->setFileVersions(array());
77 $this->setBrowserPath('');
78 $this->prepareFilesToRestore();
79 $this->prepareVersionsToRestore();
83 public function addFileToRestore($sender, $param) {
85 if (isset($param->callbackParameter)) {
86 list(, , , $sourceElementID, , ) = explode('_', $sender->ClientID, 6);
87 $fileid = $param->callbackParameter;
89 $control = $param->getDroppedControl();
90 $item = $control->getNamingContainer();
91 list(, , , $sourceElementID, , ) = explode('_', $param->getDragElementID(), 6); // I know that it is ugly.
93 if($sourceElementID == $this->VersionsDataGrid->ID) {
94 if (is_null($fileid)) {
95 $fileid = $this->VersionsDataGrid->getDataKeys()->itemAt($item->getItemIndex());
97 $fileProperties = $this->getFileVersions($fileid);
99 if (is_null($fileid)) {
100 $fileid = $this->DataGridFiles->getDataKeys()->itemAt($item->getItemIndex());
102 $fileProperties = $this->getBrowserFile($fileid);
104 if($fileProperties['name'] != $this->browser_root_dir['name'] && $fileProperties['name'] != $this->browser_up_dir['name']) {
105 $this->markFileToRestore($fileid, $fileProperties);
106 $this->prepareFilesToRestore();
110 public function removeSelectedFile($sender, $param) {
111 $fileid = $param->CallbackParameter;
112 $this->unmarkFileToRestore($fileid);
113 $this->prepareFilesToRestore();
116 public function getVersions($sender, $param) {
117 list($filename, $pathid, $filenameid, $jobid) = explode('|', $param->CallbackParameter, 4);
118 if($filenameid == 0) {
119 $this->setBrowserPath($filename);
122 $clientname = $this->BackupClientName->SelectedValue;
123 $versions = $this->getModule('api')->get(array('bvfs', 'versions', $clientname, $jobid, $pathid, $filenameid))->output;
124 $fileVersions = $this->getModule('misc')->parseFileVersions($filename, $versions);
125 $this->setFileVersions($fileVersions);
126 $this->VersionsDataGrid->dataSource = $fileVersions;
127 $this->VersionsDataGrid->dataBind();
128 $this->prepareFilesToRestore();
131 public function refreshSelectedFiles($sender, $param) {
132 $this->prepareFilesToRestore();
133 $this->SelectedVersionsDropper->render($param->NewWriter);
136 public function NextStep($sender, $param) {
139 public function PreviousStep($sender, $param) {
142 public function wizardStop($sender, $param) {
143 $this->goToDefaultPage();
146 public function setJobs($sender, $param) {
147 $jobs_list = $jobs_group_list = array();
149 for ($i = 0; $i < count($this->clients); $i++) {
150 if ($this->clients[$i]->name === $this->BackupClientName->SelectedValue) {
151 $clientid = $this->clients[$i]->clientid;
155 if(is_array($this->jobs)) {
156 foreach($this->jobs as $job) {
157 if(array_key_exists($job->level, $this->job_levels_to_restore) && $job->type == 'B' && $job->jobstatus == 'T' && $job->clientid == $clientid) {
158 $jobs_list[$job->jobid] = sprintf('[%s] %s, %s, %s', $job->jobid, $job->name, $this->job_levels_to_restore[$job->level], $job->endtime);
159 $jobs_group_list[$job->name] = $job->name;
164 $fileset_list = array();
165 for ($i = 0; $i < count($this->filesets); $i++) {
166 $fileset_list[$this->filesets[$i]->filesetid] = $this->filesets[$i]->fileset . ' (' . $this->filesets[$i]->createtime . ')';
168 asort($fileset_list);
170 $this->BackupToRestore->dataSource = $jobs_list;
171 $this->BackupToRestore->dataBind();
172 $this->GroupBackupToRestore->dataSource = $jobs_group_list;
173 $this->GroupBackupToRestore->dataBind();
174 $this->GroupBackupFileSet->dataSource = $fileset_list;
175 $this->GroupBackupFileSet->dataBind();
176 $this->setRestoreClients($sender, $param);
179 public function setBackupClients($sender, $param) {
180 if(!$this->IsPostBack) {
181 $client_list = array();
182 foreach($this->clients as $client) {
183 $client_list[$client->name] = $client->name;
185 $this->BackupClientName->dataSource = $client_list;
186 $this->BackupClientName->dataBind();
190 public function setRestoreClients($sender, $param) {
191 if(!$this->IsPostBack || $sender->ID == $this->BackupClientName->ID) {
192 $client_list = array();
193 foreach($this->clients as $client) {
194 $client_list[$client->name] = $client->name;
196 $this->RestoreClient->SelectedValue = $this->BackupClientName->SelectedValue;
197 $this->RestoreClient->dataSource = $client_list;
198 $this->RestoreClient->dataBind();
202 public function setBackupClient($sender, $param) {
203 $this->ClientToRestore->SelectedValue = $param->SelectedValue;
206 public function setBackupSelection($sender, $param) {
207 $this->GroupBackupToRestoreField->Display = ($sender->ID == $this->GroupBackupSelection->ID) ? 'Dynamic' : 'None';
208 $this->BackupToRestoreField->Display = ($sender->ID == $this->OnlySelectedBackupSelection->ID) ? 'Dynamic' : 'None';
209 $this->setBrowserFiles(array());
210 $this->setFileVersions(array());
211 $this->setFilesToRestore(array());
212 $this->markFileToRestore(null, null);
213 $_SESSION['restore_path'] = array();
216 public function resetFileBrowser($sender, $param) {
217 $this->markFileToRestore(null, null);
218 $this->setBrowserPath($this->browser_root_dir['name']);
221 private function prepareBrowserFiles($files) {
222 $this->setBrowserFiles($files);
223 $this->DataGridFiles->dataSource = $files;
224 @$this->DataGridFiles->dataBind();
227 private function prepareVersionsToRestore() {
228 $this->VersionsDataGrid->dataSource = $_SESSION['files_versions'];
229 $this->VersionsDataGrid->dataBind();
232 private function prepareFilesToRestore() {
233 $this->SelectedVersionsDataGrid->dataSource = $_SESSION['restore'];
234 $this->SelectedVersionsDataGrid->dataBind();
237 private function setBrowserPath($path) {
239 if($path == $this->browser_up_dir['name']) {
240 array_pop($_SESSION['restore_path']);
241 } elseif($path == $this->browser_root_dir['name']) {
242 $_SESSION['restore_path'] = array();
244 array_push($_SESSION['restore_path'], $path);
247 $this->setBrowserLocalizator();
248 $this->prepareBrowserContent();
251 private function getBrowserPath($stringFormat = false) {
252 return ($stringFormat === true) ? implode($_SESSION['restore_path']) : $_SESSION['restore_path'];
255 private function setBrowserLocalizator() {
256 $localization = $this->getBrowserPath(true);
257 $this->PathField->HeaderText = mb_strlen($localization) > 56 ? '...' . mb_substr($localization, -56) : $localization;
260 private function prepareBrowserContent() {
261 $jobids = $this->getElementaryBackup();
263 if (!is_null($jobids)) {
264 // generating Bvfs may takes a moment
265 $this->generateBvfsCacheByJobids($jobids);
266 $bvfs_dirs_list = $this->getModule('api')->set(array('bvfs', 'lsdirs'), array('jobids' => $jobids, 'path' => $this->getBrowserPath(true)));
267 $bvfs_dirs = is_object($bvfs_dirs_list) ? $bvfs_dirs_list->output : array();
268 $dirs = $this->getModule('misc')->parseBvfsList($bvfs_dirs);
269 $bvfs_files_list = $this->getModule('api')->set(array('bvfs', 'lsfiles'), array('jobids' => $jobids, 'path' => $this->getBrowserPath(true)));
270 $bvfs_files = is_object($bvfs_files_list) ? $bvfs_files_list->output : array();
271 $files = $this->getModule('misc')->parseBvfsList($bvfs_files);
272 $elements = array_merge($dirs, $files);
273 if(count($this->getBrowserPath()) > 0) {
274 array_unshift($elements, $this->browser_root_dir);
277 $this->prepareBrowserFiles($elements);
280 private function getElementaryBackup() {
282 if($this->OnlySelectedBackupSelection->Checked === true) {
283 $jobs = $this->getModule('api')->get(array('bvfs', 'getjobids', $this->BackupToRestore->SelectedValue));
284 $ids = is_object($jobs) ? $jobs->output : array();
285 foreach($ids as $jobid) {
286 if(preg_match('/^([\d\,]+)$/', $jobid, $match) == 1) {
295 $this->GroupBackupToRestore->SelectedValue,
297 $this->BackupClientName->SelectedValue,
299 $this->GroupBackupFileSet->SelectedValue
301 $jobs_recent = $this->getModule('api')->get($params);
302 if(count($jobs_recent->output) > 0) {
303 $ids = $jobs_recent->output;
304 $jobids = implode(',', $ids);
310 private function generateBvfsCacheByJobids($jobids) {
311 $this->getModule('api')->set(array('bvfs', 'update'), array('jobids' => $jobids));
314 private function setFileVersions($versions) {
315 $_SESSION['files_versions'] = $versions;
318 private function getFileVersions($fileid) {
320 foreach($_SESSION['files_versions'] as $file) {
321 if(array_key_exists('fileid', $file) && $file['fileid'] == $fileid) {
329 private function setBrowserFiles($files) {
330 $_SESSION['files_browser'] = $files;
333 private function getBrowserFile($fileid) {
335 foreach($_SESSION['files_browser'] as $file) {
336 if(array_key_exists('fileid', $file) && $file['fileid'] == $fileid) {
344 private function markFileToRestore($fileid, $file) {
345 if($fileid === null) {
346 $_SESSION['restore'] = array();
347 } elseif($file['name'] != $this->browser_root_dir['name'] && $file['name'] != $this->browser_up_dir['name']) {
348 $_SESSION['restore'][$fileid] = $file;
352 private function unmarkFileToRestore($fileid) {
353 if(array_key_exists($fileid, $_SESSION['restore'])) {
354 unset($_SESSION['restore'][$fileid]);
358 public function getFilesToRestore() {
359 return $_SESSION['restore'];
362 public function setFilesToRestore($files) {
363 $_SESSION['restore'] = $files;
366 public function getRestoreElements($asObject = false) {
369 foreach($this->getFilesToRestore() as $fileid => $properties) {
370 if($properties['type'] == 'dir') {
371 $dirids[] = $properties['pathid'];
372 } elseif($properties['type'] == 'file') {
373 $fileids[] = $fileid;
376 $ret = array('fileid' => $fileids, 'dirid' => $dirids);
377 if($asObject === true) {
383 public function wizardCompleted() {
384 $jobids = $this->getElementaryBackup();
385 $path = self::BVFS_PATH_PREFIX . getmypid();
386 $restore_elements = $this->getRestoreElements();
387 $cmd_props = array('jobids' => $jobids, 'path' => $path);
388 if(count($restore_elements['fileid']) > 0) {
389 $cmd_props['fileid'] = implode(',', $restore_elements['fileid']);
391 if(count($restore_elements['dirid']) > 0) {
392 $cmd_props['dirid'] = implode(',', $restore_elements['dirid']);
395 $this->getModule('api')->create(array('bvfs', 'restore'), $cmd_props);
396 $restore_props = array();
397 $restore_props['rpath'] = $path;
398 $restore_props['client'] = $this->RestoreClient->SelectedValue;
399 $restore_props['priority'] = intval($this->RestoreJobPriority->Text);
400 $restore_props['where'] = $this->RestorePath->Text;
401 $restore_props['replace'] = $this->ReplaceFiles->SelectedValue;
402 $restore_props['restorejob'] = $this->RestoreJob->SelectedValue;
404 $ret = $this->getModule('api')->create(array('jobs', 'restore'), $restore_props);
405 $jobid = $this->getModule('misc')->findJobIdStartedJob($ret->output);
406 $url_params = array('open' => 'Job');
407 if (is_numeric($jobid)) {
408 $url_params['id'] = $jobid;
410 $this->goToDefaultPage($url_params);
413 private function setRestoreJobs() {
414 $restore_job_tasks = $this->Application->getModule('api')->get(array('jobs', 'tasks', 'type', 'R'))->output;
416 foreach ($restore_job_tasks as $director => $restore_jobs) {
417 $jobs = array_merge($jobs, $restore_jobs);
419 $this->RestoreJob->DataSource = array_combine($jobs, $jobs);
420 $this->RestoreJob->dataBind();