<com:TWizard ID="RestoreWizard"
CssClass="wizard"
StepStyle.CssClass="steps"
- HeaderStyle.CssClass="header"
+ HeaderStyle.CssClass="wizard-body"
NavigationStyle.CssClass="navigation"
UseDefaultLayout="false"
ShowSideBar="false"
+ OnPreviousButtonClick="wizardPrev"
+ OnNextButtonClick="wizardNext"
OnCancelButtonClick="wizardStop"
OnCompleteButtonClick="wizardCompleted"
>
<div class="button-cancel"><com:BButton CommandName="Cancel" Text="<%[ Cancel ]%>" /></div>
<div class="button-prev-next">
<com:BButton CausesValidation="False" CssClass="bbutton" CommandName="PreviousStep" Text="<%[ « Previous ]%>" />
- <com:BButton ID="NextButton" CommandName="NextStep" Text="<%[ Next » ]%>" Visible="<%=($this->getPage()->RestoreWizard->ActiveStepIndex != 1 || ($this->getPage()->RestoreWizard->ActiveStepIndex == 1 && $this->getPage()->BackupToRestore->ItemCount > 0))%>" />
+ <com:BButton ID="NextButton" CommandName="NextStep" Text="<%[ Next » ]%>" Visible="<%=($this->getPage()->RestoreWizard->ActiveStepIndex != 1 || ($this->getPage()->RestoreWizard->ActiveStepIndex == 1 && $this->getPage()->BackupsToRestore->ItemCount > 0))%>" />
</div>
</prop:StepNavigationTemplate>
</div>
</prop:FinishNavigationTemplate>
<com:TWizardStep ID="Step1" Title="<%[ Step 1 - select source backup client ]%>" StepType="Auto">
+ <h2><%[ Restore wizard ]%></h2>
+ <p>
+ <%[ 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. ]%>
+ </p>
<div class="line">
<div class="text"><com:TLabel ForControl="BackupClientName" Text="<%[ Backup from client: ]%>" /></div>
<div class="field">
- <com:TActiveDropDownList ID="BackupClientName" CssClass="textbox" Width="350px" OnLoad="setBackupClients" OnTextChanged="setJobs" CausesValidation="false" />
- <com:TCompareValidator CssClass="validator" Display="Dynamic" ControlToValidate="BackupClientName" DataType="String" ValueToCompare="none" Operator="NotEqual" Text="<%[ Please select language. ]%>" />
+ <com:TDropDownList ID="BackupClientName" CssClass="textbox" Width="350px" CausesValidation="false" />
</div>
</div>
</com:TWizardStep>
<com:TWizardStep ID="Step2" Title="<%[ Step 2 - select backup to restore ]%>" StepType="Auto">
- <com:TLabel Text="<%[ There is no backup for restore. Please go to previous step and select another client for restore or proceed backups for the client selected in previous step. ]%>" CssClass="validation-error-summary" Visible="<%=($this->BackupToRestore->ItemCount == 0 && $this->GroupBackupToRestore->ItemCount == 0)%>"/>
+ <p>
+ <%[ 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. ]%>
+ </p>
<div class="line" style="margin-top: 15px">
<div class="text"><com:TLabel Text="<%[ Backup selection method: ]%>" /></div>
<div class="field">
- <com:TActiveRadioButton ID="OnlySelectedBackupSelection" GroupName="BackupSelection" Checked="true" OnCheckedChanged="setBackupSelection" /> <com:TLabel ForControl="OnlySelectedBackupSelection" Text="<%[ Only selected backup ]%>" />
- <com:TActiveRadioButton ID="GroupBackupSelection" GroupName="BackupSelection" OnCheckedChanged="setBackupSelection" /> <com:TLabel ForControl="GroupBackupSelection" Text="<%[ Group most recent backups ]%>" /></div>
+ <com:TRadioButton
+ ID="OnlySelectedBackupSelection"
+ GroupName="BackupSelection"
+ Checked="true"
+ Attributes.onclick="$('#group-backup-to-restore-field').hide(); $('#backup-to-restore-field').show();"
+ />
+ <com:TLabel ForControl="OnlySelectedBackupSelection" Text="<%[ Only selected backup ]%>" />
+ <com:TRadioButton
+ ID="GroupBackupSelection"
+ GroupName="BackupSelection"
+ Attributes.onclick="$('#backup-to-restore-field').hide(); $('#group-backup-to-restore-field').show();"
+ />
+ <com:TLabel ForControl="GroupBackupSelection" Text="<%[ Group most recent backups ]%>" /></div>
</div>
- <com:TActivePanel ID="BackupToRestoreField">
- <div class="line">
- <div class="text"><com:TLabel ForControl="BackupToRestore" Text="<%[ Backup for restore: ]%>" /></div>
- <div class="field">
- <com:TActiveDropDownList ID="BackupToRestore" CssClass="textbox" Width="350px" CausesValidation="false" OnTextChanged="resetFileBrowser" />
- </div>
+ <div id="backup-to-restore-field" style="display: <%=!$this->OnlySelectedBackupSelection->Checked ? 'none' : 'block'%>">
+ <div>
+ <p><%[ Backup for restore: ]%></p>
+ <com:TDataGrid
+ ID="BackupsToRestore"
+ EnableViewState="false"
+ AutoGenerateColumns="false"
+ AllowSorting="false"
+ CellPadding="5px"
+ CssClass="window-section-detail"
+ ItemStyle.CssClass="slide-window-element"
+ AlternatingItemStyle.CssClass="slide-window-element-alternating"
+ >
+ <com:TBoundColumn HeaderText="ID" DataField="jobid" />
+ <com:TTemplateColumn HeaderText="<%[ Job name ]%>" SortExpression="name">
+ <prop:ItemTemplate>
+ <div title="<%=$this->getParent()->Data['name']%>"><%=$this->getParent()->Data['name']%></div>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TTemplateColumn>
+ <prop:HeaderText>
+ <span title="<%=Prado::localize('Type')%>" style="cursor: help">T</span>
+ </prop:HeaderText>
+ <prop:ItemTemplate>
+ <%=$this->getParent()->Data['type']%>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TTemplateColumn>
+ <prop:HeaderText>
+ <span title="<%=Prado::localize('Level')%>" style="cursor: help">L</span>
+ </prop:HeaderText>
+ <prop:ItemTemplate>
+ <%=$this->getParent()->Data['level']%>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TTemplateColumn HeaderText="<%[ Job status ]%>" SortExpression="jobstatus">
+ <prop:ItemTemplate>
+ <div class="job-status-<%=$this->getParent()->Data['jobstatus'] == 'T' && $this->getParent()->Data['joberrors'] > 0 ? 'W' : $this->getParent()->Data['jobstatus']%>" title="<%=$this->getPage()->getModule('misc')->jobStates[$this->getParent()->Data['jobstatus'] == 'T' && $this->getParent()->Data['joberrors'] > 0 ? 'W' : $this->getParent()->Data['jobstatus']]['description']%>"><%=$this->getPage()->getModule('misc')->jobStates[$this->getParent()->Data['jobstatus']]['value']%></div>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TTemplateColumn HeaderText="<%[ Size ]%>" SortExpression="jobbytes">
+ <prop:ItemTemplate>
+ <div class="size" rel="<%=$this->getParent()->Data['jobbytes']%>"><%=$this->getParent()->Data['jobbytes']%></div>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TBoundColumn SortExpression="jobfiles" HeaderText="<%[ Files ]%>" DataField="jobfiles" />
+ <com:TTemplateColumn HeaderText="<%[ End time ]%>" SortExpression="endtime">
+ <prop:ItemTemplate>
+ <%=$this->getParent()->Data['endtime']%>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ <com:TTemplateColumn HeaderText="<%[ Select ]%>">
+ <prop:ItemTemplate>
+ <div class="center"><input type="radio" name="backup_to_restore" value="<%=$this->getParent()->Data['jobid']%>" /></div>
+ </prop:ItemTemplate>
+ </com:TTemplateColumn>
+ </com:TDataGrid>
+ <script type="text/javascript">
+ var is_list = <%=$this->BackupsToRestore->ItemCount%> > 0;
+ if (is_list) {
+ SlideWindow.makeSortable('<%=$this->BackupsToRestore->ClientID%>');
+ SlideWindow.sortTable('<%=$this->BackupsToRestore->ClientID%>', 0, true);
+ Formatters.set_formatters();
+ var grid_id = '<%=$this->BackupsToRestore->ClientID%>';
+ var restore_single_jobid = '<%=$this->restore_single_jobid%>';
+ var rows = (
+ '#' + grid_id + ' tr.' + SlideWindow.elements.contentItems + ', ' +
+ '#' + grid_id + ' tr.' + SlideWindow.elements.contentAlternatingItems
+ );
+ $(rows).each(function(index, el) {
+ $(el).on('click', function(e) {
+ var el = $(e.srcElement||e.target);
+ if (el.length === 1) {
+ el = el.parents('tr').find('input[type=radio]');
+ if (el.length === 1) {
+ el[0].checked = true;
+ }
+ }
+ });
+ var radio = $(el).find('input[type=radio]')[0];
+ if (restore_single_jobid === radio.value) {
+ radio.checked = true;
+ }
+ }.bind(this));
+ } else {
+ document.getElementById('backup-to-restore-field').style.display = 'none';
+ }
+ </script>
</div>
- </com:TActivePanel>
- <com:TActivePanel ID="GroupBackupToRestoreField" Display="None">
+ </div>
+ <div id="group-backup-to-restore-field" style="display: <%=!$this->GroupBackupSelection->Checked ? 'none' : 'block'%>">
<div class="line">
<div class="text"><com:TLabel ForControl="GroupBackupToRestore" Text="<%[ Backup for restore: ]%>" /></div>
<div class="field">
- <com:TActiveDropDownList ID="GroupBackupToRestore" CssClass="textbox" Width="350px" CausesValidation="false" />
+ <com:TDropDownList ID="GroupBackupToRestore" CssClass="textbox" Width="350px" CausesValidation="false" />
</div>
</div>
<div class="line">
<div class="text"><com:TLabel ForControl="GroupBackupFileSet" Text="<%[ FileSet resource: ]%>" /></div>
<div class="field">
- <com:TActiveDropDownList ID="GroupBackupFileSet" CssClass="textbox" Width="350px" CausesValidation="false" />
+ <com:TDropDownList ID="GroupBackupFileSet" CssClass="textbox" Width="350px" CausesValidation="false" />
</div>
</div>
- </com:TActivePanel>
+ </div>
+ <com:TLabel Text="<%[ There is no backup for restore. Please go to previous step and select another client for restore or proceed backups for the client selected in previous step. ]%>" CssClass="validation-error-summary" Visible="<%=($this->BackupsToRestore->ItemCount == 0 && $this->GroupBackupToRestore->ItemCount == 0)%>"/>
+
</com:TWizardStep>
<com:TWizardStep ID="Step3" Title="<%[ Step 3 - select files to restore ]%>" StepType="Auto">
+ <%[ Path: ]%> <com:TActiveTextBox
+ ID="PathField"
+ CssClass="textbox-auto"
+ Attributes.onkeydown="var keycode = (event.keyCode ? event.keyCode : event.which); if (keycode === 13) {$('<%=$this->LoadPathBtn->ClientID%>').click(); return false; } return true;"
+ Width="895px" />
+ <com:TActiveLinkButton ID="LoadPathBtn" OnClick="loadPath" Text="<%[ OK ]%>">
+ <prop:ClientSide.OnLoading>
+ document.getElementById('restore-browser-files-loading').style.display = 'block';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('restore-browser-files-loading').style.display = 'none';
+ </prop:ClientSide.OnComplete>
+ </com:TActiveLinkButton>
<table id="restore-browser">
<tr>
<td rowspan="2">
<div id="restore-browser-files-loading" style="display: none"></div>
<div id="restore-browser-files">
- <com:TActiveDataGrid
- ID="DataGridFiles"
- AutoGenerateColumns="false"
- CellPadding="2px"
- HeaderStyle.CssClass="file-browser-header"
- CssClass="file-browser-detail"
- ItemStyle.CssClass="file-browser-element"
- DataKeyField="fileid">
- <prop:EmptyTemplate>
- <div class="file-browser-watermark" style="padding: 150px 10px">
- <%[ It seems that there is no files for choosing or file records in database for this job has been purged (file retention period expired) ]%>
+ <com:TActiveDataGrid
+ ID="DataGridFiles"
+ AutoGenerateColumns="false"
+ CellPadding="2px"
+ ShowHeader="false"
+ CssClass="file-browser-detail"
+ ItemStyle.CssClass="file-browser-element"
+ DataKeyField="fileid">
+ <prop:EmptyTemplate>
+ <div class="file-browser-watermark" style="padding: 200px 10px">
+ <%[ It seems that there is no files for choosing or file records in database for this job has been purged (file retention period expired) ]%>
+ </div>
+ </prop:EmptyTemplate>
+ <com:TActiveTemplateColumn>
+ <prop:ItemTemplate>
+ <com:TDraggable ID="FileElementLeft" Revert="false" Handle="<%=$this->FileElementLeft->ClientID%>" Ghosting="true" CssClass="draggable">
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/<%=($this->getParent()->Data['type'] == 'dir' ? 'directory-icon' : 'file-icon')%>.png" alt="<%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file' %>" /> <%=($this->getParent()->Data['name'] != '/') ? preg_replace('/\/$/', '', $this->getParent()->Data['name']) : '/'%>
+ <div class="restore-browser-element-size">
+ <com:TActiveLinkButton
+ CssClass="link"
+ Style="float: right;"
+ OnCallback="Page.addFileToRestore"
+ ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : null%>"
+ ActiveControl.EnableUpdate="false"
+ Visible="<%=!in_array($this->getParent()->Data['name'], $this->getPage()->excluded_elements_from_add)%>"
+ Text="<%[ Add ]%>"
+ />
+ <span class="size" style=""><%=isset($this->getParent()->Data['lstat']['size']) ? $this->getParent()->Data['lstat']['size'] : '0'%></span>
</div>
- </prop:EmptyTemplate>
- <com:TActiveTemplateColumn ID="PathField">
- <prop:ItemTemplate>
- <com:TDraggable ID="FileElementLeft" Revert="false" Handle="<%=$this->FileElementLeft->ClientID%>" Ghosting="true" CssClass="draggable">
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/<%=($this->getParent()->Data['type'] == 'dir' ? 'directory-icon' : 'file-icon')%>.png" alt="<%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file' %>" /> <%=($this->getParent()->Data['name'] != '/') ? preg_replace('/\/$/', '', $this->getParent()->Data['name']) : '/'%>
- <com:TActiveLinkButton CssClass="link" Style="float: right;" OnCallback="Page.addFileToRestore" ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : null%>" ActiveControl.EnableUpdate="false" Visible="<%=!in_array($this->getParent()->Data['name'], $this->getPage()->excluded_elements_from_add)%>" Text="<%[ Add ]%>" />
- </com:TDraggable>
- <com:TCallback ID="FileElementCall" OnCallback="Page.getVersions" ActiveControl.CallbackParameter="<%=$this->getParent()->Data['name'] . '|' . @$this->getParent()->Data['pathid'] . '|' . @$this->getParent()->Data['filenameid'] . '|' . @$this->getParent()->Data['jobid']%>">
- <prop:ClientSide.OnLoading>
- document.getElementById('restore-browser-files-loading').style.display = 'block';
- </prop:ClientSide.OnLoading>
- <prop:ClientSide.OnComplete>
- document.getElementById('restore-browser-files-loading').style.display = 'none';
- </prop:ClientSide.OnComplete>
- </com:TCallback>
- <script type="text/javascript">
- document.getElementById('<%=$this->FileElementLeft->ClientID%>').addEventListener('mouseup', function(event) {
- var el = event.target || event.srcElement;
- if (el) {
- el.style.position = 'static';
- el.style.top = '-1px';
- el.style.left = '-1px';
- }
- var fel = document.getElementById('<%=$this->FileElementLeft->ClientID%>');
- fel.style.position = 'static';
- fel.style.top = '-1px';
- fel.style.left = '-1px';
- });
-
- document.getElementById('<%=$this->FileElementLeft->ClientID%>').addEventListener('click', function(event) {
- var el = event.target || event.srcElement;
- var vposition = el.style.left;
- if(vposition == null || vposition == '0px'){
- event.stop();
- } else {
- var request = <%=$this->FileElementCall->ActiveControl->Javascript %>;
- request.dispatch();
- }
- });
- </script>
- </prop:ItemTemplate>
- </com:TActiveTemplateColumn>
- </com:TActiveDataGrid>
+ </com:TDraggable>
+ <com:TCallback ID="FileElementCall" OnCallback="Page.getVersions" ActiveControl.CallbackParameter="<%=$this->getParent()->Data['name'] . '|' . @$this->getParent()->Data['pathid'] . '|' . @$this->getParent()->Data['filenameid'] . '|' . @$this->getParent()->Data['jobid']%>">
+ <prop:ClientSide.OnLoading>
+ document.getElementById('restore-browser-files-loading').style.display = 'block';
+ </prop:ClientSide.OnLoading>
+ <prop:ClientSide.OnComplete>
+ document.getElementById('restore-browser-files-loading').style.display = 'none';
+ </prop:ClientSide.OnComplete>
+ </com:TCallback>
+ <script type="text/javascript">
+ Formatters.set_formatters();
+ document.getElementById('<%=$this->FileElementLeft->ClientID%>').addEventListener('mouseup', function(event) {
+ var el = event.target || event.srcElement;
+ if (el) {
+ el.style.position = 'static';
+ el.style.top = '-1px';
+ el.style.left = '-1px';
+ }
+ var fel = document.getElementById('<%=$this->FileElementLeft->ClientID%>');
+ fel.style.position = 'static';
+ fel.style.top = '-1px';
+ fel.style.left = '-1px';
+ });
+ document.getElementById('<%=$this->FileElementLeft->ClientID%>').addEventListener('click', function(event) {
+ var el = event.target || event.srcElement;
+ if (el.className == 'link') {
+ // 'Add' link doesn't cause sending request
+ return;
+ }
+ var vposition = el.style.left;
+ if(vposition == null || vposition == '0px'){
+ event.stop();
+ } else {
+ var request = <%=$this->FileElementCall->ActiveControl->Javascript %>;
+ request.dispatch();
+ }
+ });
+ </script>
+ </prop:ItemTemplate>
+ </com:TActiveTemplateColumn>
+ </com:TActiveDataGrid>
</div>
</td>
<td>
<prop:ItemTemplate>
<com:TDraggable ID="FileElementToRestore" Revert="false" Handle="<%=$this->FileElementToRestore->ClientID%>" Ghosting="true" CssClass="draggable" Style="float: left">
<img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/<%=($this->getParent()->Data['type'] == 'dir' ? 'directory-icon' : 'file-icon')%>.png" alt="<%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%>" />
- <com:TLabel Text="<%=$this->getParent()->Data['name']%>" Width="50%" /> <%=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']) : ''%>
+ <span><%=$this->getParent()->Data['name']%></span> » MTIME:<%=date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime'])%> » <%[ Size: ]%><span class="size"><%=$this->getParent()->Data['lstat']['size']%></span>
</com:TDraggable>
- <com:TActiveLinkButton CssClass="link" Style="float: right;" OnCallback="Page.addFileToRestore" ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : false%>" ActiveControl.EnableUpdate="false" Text="<%[ Add ]%>"/>
+ <com:TActiveLinkButton
+ CssClass="link"
+ Style="float: right;"
+ OnCallback="Page.addFileToRestore"
+ ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : false%>"
+ ActiveControl.EnableUpdate="false"
+ Text="<%[ Add ]%>"
+ />
<script type="text/javascript">
+ Formatters.set_formatters();
+ document.getElementById('<%=$this->FileElementToRestore->ClientID%>').addEventListener('mouseover', function(event) {
+ var tip_fields = [
+ '<%[ JobId: ]%> <%=$this->getParent()->Data['jobid']%>',
+ '<%[ Volume: ]%> <%=$this->getParent()->Data['volname']%>',
+ '<%[ InChanger: ]%> <%=$this->getParent()->Data['inchanger'] ? Prado::localize('Yes') : Prado::localize('No')%>',
+ '<%[ Sum: ]%> <%=$this->getParent()->Data['md5']%>'
+ ];
+ showTip(this, '<%=$this->getParent()->Data['name']%>', tip_fields.join('<br />'));
+ });
document.getElementById('<%=$this->FileElementToRestore->ClientID%>').addEventListener('mouseup', function(event) {
var el = event.target || event.srcElement;
if (el) {
DataKeyField="fileid"
>
<prop:EmptyTemplate>
- <div class="file-browser-watermark" style="padding: 50px 10px">
+ <div class="file-browser-watermark" style="padding: 110px 10px">
<%[ For add a file to restore please click 'Add' link or drag a file from frame on left or from above frame and drop it here ]%>
</div>
</prop:EmptyTemplate>
- <com:TActiveTemplateColumn>
- <prop:ItemTemplate>
- <com:TPanel ID="FileElement" Style="clear: both">
- <com:TActiveImageButton ImageUrl="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_close.png" Style="float: right; vertical-align: text-bottom;margin: 3px;" OnCallback="Page.removeSelectedFile" ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : ''%>"/>
- <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/<%=($this->getParent()->Data['type'] == 'dir' ? 'directory-icon' : 'file-icon')%>.png" alt="<%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%>" />
- <com:TLabel Text="<%=$this->getParent()->Data['name']%>" /> <%=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']) : ''%>
- </com:TPanel>
- </prop:ItemTemplate>
- </com:TActiveTemplateColumn>
+ <com:TActiveTemplateColumn>
+ <prop:ItemTemplate>
+ <com:TPanel ID="FileElement" Style="clear: both">
+ <com:TActiveImageButton ImageUrl="<%=$this->getPage()->getTheme()->getBaseUrl()%>/icon_close.png" Style="float: right; vertical-align: text-bottom;margin: 3px;" OnCallback="Page.removeSelectedFile" ActiveControl.CallbackParameter="<%=isset($this->getParent()->Data['fileid']) ? $this->getParent()->Data['fileid'] : ''%>"/>
+ <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/<%=($this->getParent()->Data['type'] == 'dir' ? 'directory-icon' : 'file-icon')%>.png" alt="<%=$this->getParent()->Data['type'] == 'dir' ? 'directory' : 'file'%>" />
+ <span><%=$this->getParent()->Data['name']%></span> » MTIME:<%=date("Y-m-d H:i:s", $this->getParent()->Data['lstat']['mtime'])%> » <%[ Size: ]%><span class="size"><%=$this->getParent()->Data['lstat']['size']%></span>
+ </com:TPanel>
+ <script type="text/javascript">
+ Formatters.set_formatters();
+ </script>
+ </prop:ItemTemplate>
+ </com:TActiveTemplateColumn>
</com:TActiveDataGrid>
</com:TDropContainer>
</div>
<div class="line">
<div class="text"><com:TLabel ForControl="RestoreClient" Text="<%[ Restore to client: ]%>" /></div>
<div class="field">
- <com:TActiveDropDownList ID="RestoreClient" CssClass="textbox" OnLoad="setRestoreClients" CausesValidation="false" />
+ <com:TDropDownList ID="RestoreClient" CssClass="textbox" CausesValidation="false" />
</div>
</div>
<div class="line">
<div class="text"><com:TLabel ForControl="RestorePath" Text="<%[ Restore to directory: ]%>" /></div>
<div class="field"><com:TTextBox ID="RestorePath" CssClass="textbox" Width="350px" Text="/tmp/restore" /></div>
</div>
+ <hr style="margin-top: 50px" />
+ <p><%[ During restore there will be used following volumes: ]%></p>
+ <com:TRepeater ID="RestoreVolumes">
+ <prop:HeaderTemplate>
+ <table style="min-width: 450px">
+ <tr>
+ <th><%[ In Changer ]%></th>
+ <th><%[ Volume ]%></th>
+ </tr>
+ </prop:HeaderTemplate>
+ <prop:ItemTemplate>
+ <tr>
+ <td class="center"><com:TLabel Text="<%#($this->DataItem['inchanger'] == 1 ? Prado::localize('Yes') : Prado::localize('No'))%>" /></td>
+ <td> <img src="<%=$this->getPage()->getTheme()->getBaseUrl()%>/tape_tools_icon.png" alt="" /> <com:TLabel Text="<%#$this->DataItem['volume']%>" /></td>
+ </tr>
+ </prop:ItemTemplate>
+ <prop:FooterTemplate>
+ </table>
+ </prop:FooterTemplate>
+ </com:TRepeater>
</com:TWizardStep>
<com:TWizardStep ID="Step5" Title="<%[ Step 5 - options for restore ]%>" StepType="Auto">
<div class="line">
<div class="text"><com:TLabel ForControl="RestoreJob" Text="<%[ Restore job: ]%>" /></div>
<div class="field">
- <com:TActiveDropDownList ID="RestoreJob" CssClass="textbox" Width="350px" CausesValidation="false" />
+ <com:TDropDownList ID="RestoreJob" CssClass="textbox" Width="350px" CausesValidation="false" />
</div>
</div>
<div class="line">
<com:TTextBox ID="RestoreJobPriority" CssClass="textbox" CausesValidation="false" Width="60px" MaxLength="5" Text="10" />
</div>
</div>
+ <div class="line" style="margin-bottom: 10px">
+ <div class="text" style="vertical-align: top"><%[ File relocation option: ]%></div>
+ <div class="field">
+ <input type="radio" name="file_relocation" id="file_relocation_1" value="1" onclick="switch_file_relocation_mode(this.value)" /> <label for="file_relocation_1"><%[ Do not relocate files ]%></label><br />
+ <input type="radio" name="file_relocation" id="file_relocation_2" value="2" onclick="switch_file_relocation_mode(this.value)" /> <label for="file_relocation_2"><%[ Relocate files with prefix and/or suffix ]%></label><br />
+ <input type="radio" name="file_relocation" id="file_relocation_3" value="3" onclick="switch_file_relocation_mode(this.value)" /> <label for="file_relocation_3"><%[ Relocate files with regular expression ]%></label>
+ </div>
+ </div>
+ <div id="file_relocation_prefix_suffix" style="display: none">
+ <div class="line">
+ <div class="text"><com:TLabel ForControl="RestoreStripPrefix" Text="<%[ Strip prefix: ]%>" /></div>
+ <div class="field">
+ <com:TTextBox ID="RestoreStripPrefix" CssClass="textbox" Width="350px" CausesValidation="false" />
+ </div>
+ </div>
+ <div class="line">
+ <div class="text"><com:TLabel ForControl="RestoreAddPrefix" Text="<%[ Add prefix: ]%>" /></div>
+ <div class="field">
+ <com:TTextBox ID="RestoreAddPrefix" CssClass="textbox" Width="350px" CausesValidation="false" />
+ </div>
+ </div>
+ <div class="line">
+ <div class="text"><com:TLabel ForControl="RestoreAddSuffix" Text="<%[ Add suffix: ]%>" /></div>
+ <div class="field">
+ <com:TTextBox ID="RestoreAddSuffix" CssClass="textbox" Width="350px" CausesValidation="false" />
+ </div>
+ </div>
+ </div>
+ <div id="file_relocation_regex_where" style="display: none">
+ <div class="line">
+ <div class="text"><com:TLabel ForControl="RestoreRegexWhere" Text="<%[ RegexWhere: ]%>" /></div>
+ <div class="field">
+ <com:TTextBox ID="RestoreRegexWhere" CssClass="textbox" Width="350px" CausesValidation="false" />
+ </div>
+ </div>
+ </div>
+ <script type="text/javascript">
+ function switch_file_relocation_mode(opt) {
+ var prefix_suffix_opts = document.getElementById('file_relocation_prefix_suffix');
+ var regex_where_opts = document.getElementById('file_relocation_regex_where');
+ var radio = document.getElementById('file_relocation_' + opt);
+ if (opt == 1) { // don't use file relocation
+ prefix_suffix_opts.style.display = 'none';
+ regex_where_opts.style.display = 'none';
+ radio.checked = true;
+ } else if (opt == 2) { // use prefix/suffix relocation
+ prefix_suffix_opts.style.display = '';
+ regex_where_opts.style.display = 'none';
+ radio.checked = true;
+ } else if (opt == 3) { // use regex_where relocation
+ prefix_suffix_opts.style.display = 'none';
+ regex_where_opts.style.display = '';
+ radio.checked = true;
+ }
+ }
+ var relocation_opt = '<%=$this->file_relocation_opt%>';
+ if (relocation_opt) {
+ switch_file_relocation_mode(relocation_opt);
+ } else {
+ switch_file_relocation_mode(1); // default setting
+ }
+ </script>
</com:TWizardStep>
<com:TWizardStep ID="Step6" Title="<%[ Step 6 - Finish ]%>" StepType="Finish">
<fieldset>
<div class="line">
<div class="text"><%[ Backup for restore: ]%></div>
<div class="field bold">
- <com:TLabel Text="<%=@$this->getModule('api')->get(array('jobs', $this->BackupToRestore->SelectedValue))->output->job%>" Visible="<%=$this->OnlySelectedBackupSelection->Checked%>" />
<com:TLabel Text="<%=$this->GroupBackupToRestore->SelectedValue%>" Visible="<%=$this->GroupBackupSelection->Checked%>" />
</div>
</div>
<div class="text"><%[ Restore job priority: ]%></div>
<div class="field bold"><%=$this->RestoreJobPriority->Text%></div>
</div>
+ <div class="line">
+ <div class="text"><%[ File relocation option: ]%></div>
+ <div class="field bold">
+ <com:TLabel Visible="<%=$this->file_relocation_opt == 1%>" Text="<%[ Do not relocate files ]%>"/>
+ <com:TLabel Visible="<%=$this->file_relocation_opt == 2%>" Text="<%[ Relocate files with prefix and/or suffix ]%>"/>
+ <com:TLabel Visible="<%=$this->file_relocation_opt == 3%>" Text="<%[ Relocate files with regular expression ]%>"/>
+ </div>
+ </div>
+ <div style="display: <%=$this->file_relocation_opt == 2 ? 'block' : 'none'%>">
+ <div class="line">
+ <div class="text"><%[ Strip prefix: ]%></div>
+ <div class="field bold"><%=$this->RestoreStripPrefix->Text%></div>
+ </div>
+ <div class="line">
+ <div class="text"><%[ Add prefix: ]%></div>
+ <div class="field bold"><%=$this->RestoreAddPrefix->Text%></div>
+ </div>
+ <div class="line">
+ <div class="text"><%[ Add suffix: ]%></div>
+ <div class="field bold"><%=$this->RestoreAddSuffix->Text%></div>
+ </div>
+ </div>
+ <div style="display: <%=$this->file_relocation_opt == 3 ? 'block' : 'none'%>">
+ <div class="line">
+ <div class="text"><%[ RegexWhere: ]%></div>
+ <div class="field bold"><%=$this->RestoreRegexWhere->Text%></div>
+ </div>
+ </div>
</fieldset>
</com:TWizardStep>
</com:TWizard>
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;
$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);
}
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) {
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) {
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();
}
}
+ /**
+ * 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;
$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);
$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']);
+ }
}
?>