<?php

/**
 *
 * bareos-webui - Bareos Web-Frontend
 *
 * @link      https://github.com/bareos/bareos for the canonical source repository
 * @copyright Copyright (C) 2013-2024 Bareos GmbH & Co. KG (http://www.bareos.org/)
 * @license   GNU Affero General Public License (http://www.gnu.org/licenses/)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

$title = _('Restore');
$this->headTitle($title);

?>

<ul class="nav nav-tabs">
   <li><a href="<?php echo $this->url('restore', array('action' => 'index'), array('query' => $this->restore_params_generic)); ?>"><?php echo $this->translate('Restore multiple files'); ?></a></li>
   <li class="active"><a href="<?php echo $this->url('restore', array('action' => 'versions'), array('query' => $this->restore_params_generic)); ?>"><?php echo $this->translate('Restore specific file version'); ?></a></li>
</ul>

<br />

<?php if($this->acl_alert) : echo $this->ACLAlert($this->invalid_commands);
else : ?>

<!-- modal-001 start -->
<div id="modal-001" class="modal fade modal-001" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel1">
  <div class="modal-dialog modal-md">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel"><?php echo $this->translate("Failed to retrieve data from Bareos director"); ?></h4>
      </div>
      <div class="modal-body">
         <p><?php echo $this->translate("Error message received:"); ?></p>
         <p class="text-danger"><?php echo $this->errors; ?></p>
      </div>
      <div class="modal-footer">
         <button type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $this->translate("Close"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-001 end -->

<!-- modal-002 start (confirmation about started restore job) -->
<div id="modal-002" class="modal fade modal-002" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel2">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel"><?php echo $this->translate("Director message"); ?></h4>
      </div>
      <div class="modal-body">
         <pre><code>
<?php echo $this->result; ?>
         </code></pre>
      </div>
      <div class="modal-footer">
         <?php if (isset($this->restore_jobid)): ?>
            <a class="btn btn-primary" href="<?php echo $this->basePath() . '/job/details/' . $this->restore_jobid; ?>"><?php echo $this->translate("Job report"); ?></a>.
         <?php endif; ?>
         <button type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $this->translate("Close"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-002 end -->

<!-- modal-003 start (confirm to start restore job) -->
<div id="modal-003" class="modal fade modal-002" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel2">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel"><?php echo $this->translate("Confirm restore job submit"); ?></h4>
      </div>
      <div class="modal-body">
         <p>A restore job with the parameters given below will be scheduled.</p>
         <table id="restore-params-table" style="border-collapse: separate; border-spacing: 10px;">
         </table>
      </div>
      <div class="modal-footer">
         <button id="modal-003-btn-cancel" type="button" class="btn btn-default" data-dismiss="modal"><?php echo $this->translate("Cancel"); ?></button>
         <button id="modal-003-btn-ok" type="button" class="btn btn-primary"><?php echo $this->translate("OK"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-003 end -->

<!-- modal-004 start -->
<div id="modal-004" class="modal fade modal-002" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel2">
  <div class="modal-dialog modal-lg">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel"></h4>
      </div>
      <div class="modal-body">
         <p><?php echo $this->translate("No file selected to be restored!"); ?></p>
      </div>
      <div class="modal-footer">
         <button id="modal-004-btn-ok" type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $this->translate("OK"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-004 end -->

<!-- modal-005 start -->
<div id="modal-005" class="modal fade modal-005" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel2">
  <div class="modal-dialog modal-md">
    <div class="modal-content">
   <div class="modal-header">
   <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
   <h4 class="modal-title" id="myModalLabel"><?php echo $this->translate("Notification"); ?></h4>
      </div>
      <div class="modal-body">
         <p><?php echo $this->translate("Restoring NDMP backups is currently not supported by Bareos WebUI. Please use the bconsole instead."); ?></p>
      </div>
      <div class="modal-footer">
         <button id="modal-005-btn-ok" type="button" class="btn btn-primary" data-dismiss="modal"><?php echo $this->translate("OK"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-005 end -->

<div class="panel panel-default">
  <div class="panel-heading">
    <h3 class="panel-title">Restore</h3>
  </div>
  <div class="panel-body">
    <div class="container-fluid">

<?php
$form->prepare();
$form->setAttribute('class', 'form-horizontal');
echo $this->form()->openTag($form);
?>
      <div class="row">
        <div class="col-md-3">

<?php
echo '<p>'.$this->formRow($form->get('client')).'</p>';
echo '<p>'.$this->formRow($form->get('backups')).'</p>';
echo '<p>'.$this->formRow($form->get('mergefilesets')).'</p>';
echo '<p>'.$this->formRow($form->get('mergejobs')).'</p>';
echo '<p>'.$this->formRow($form->get('restorejob')).'</p>';
echo '<p>'.$this->formRow($form->get('restoreclient')).'</p>';
echo '<p>'.$this->formRow($form->get('replace')).'</p>';
echo '<p>'.$this->formRow($form->get('where')).'</p>';

if($restore_params['mergefilesets'] == 1) {
    foreach($form->backups as $backup) {
        if($backup['jobid'] === $this->restore_params['jobid'] && $backup['pluginjob']) {
            echo '<p>'.$this->formRow($form->get('pluginoptions')).'</p>';
        }
    }
} else {
    $pluginjobs = false;
    foreach($form->backups as $backup) {
        if($backup['pluginjob']) {
            $pluginjobs = true;
        }
    }
    if($pluginjobs) {
        echo '<p>'.$this->formRow($form->get('pluginoptions')).'</p>';
    }
}

echo '<p>'.$this->formRow($form->get('form-submit')).'</p>';

// Hidden fields
echo $this->formRow($form->get('checked_files'));
echo $this->formRow($form->get('checked_directories'));
echo $this->formRow($form->get('jobids_hidden'));
?>

        </div>

	 <div class="col-md-5">

<?php
echo '<strong>';
echo $this->translate("File selection");
echo '</strong>';
?>

          <div class="panel panel-default">
            <div id="filebrowser" style="width: 100%;">
              <div style="height: 60vH;"></div>
              </div>
            </div>
         </div>

         <div class="col-md-4">
            <strong>
               <?php echo $this->translate('Selected file'); ?>
            </strong>
            <div class="panel panel-default">
               <div class="panel-body">
                  <span id="selectedfile-name">&nbsp;</span>
               </div>
            </div>
            <strong>
               <?php echo $this->translate('Available versions'); ?>
            </strong>
            <div class="panel panel-default">
               <div class="panel-body" style="padding: 0">
                  <div class="scrollpane">
                     <table class="table table-no-bordered table-striped" id="job-totals">
                     <col width="10">
                     <col>
                     <col>
                     <col width="1*">
                     <thead class="bg-primary">
                        <tr>
                           <th><span class="glyphicon glyphicon-check"></span></th>
                           <th><?php echo $this->translate("Last modification"); ?></th>
                           <th><?php echo $this->translate("Size"); ?> </th>
                           <th><?php echo $this->translate("Checksum"); ?></th>
                        </tr>
                     </thead>
                     <tbody id="file-versions-list"></tbody>
                     </table>
                  </div>
               </div>
            </div>
         </div>

      </div>

<?php
   echo $this->form()->closeTag($form);
?>

    </div>
  </div>
</div>

<?php
echo $this->headScript()->prependFile($this->basePath() . '/js/jstreegrid-helper.js');
echo $this->headScript()->prependFile($this->basePath() . '/js/jstreegrid.js');
echo $this->headScript()->prependFile($this->basePath() . '/js/jstree.min.js');
echo $this->headLink()->prependStylesheet($this->basePath() . '/css/jstree.min.css');
?>

<style>

.jsTreeGridHeader {
   background-color: #eeeeee;
   color: #000000;
}

.fixed_header tbody {
   display: block;
   overflow: auto;
   height: 200px;
   width: 100%;
}

.fixed_header thead tr {
   display: block;
}

.scrollpane {
   max-height: 53vh;
   overflow: auto;
   overflow-y: scroll;
}

</style>

<script type="text/javascript">

function clearFileVersions() {
   $('#selectedfile-name').text('');
   $('#file-versions-list').empty()
}

function getFileVersions(pathid, filename) {
   $.ajax({
      url: '<?php echo $this->url('restore', array('action' => 'getData'), null) . '?data=fileversions&clientname=' . $this->restore_params['client']; ?>&pathid=' + pathid  + '&filename=' + filename,
      dataType: 'json',
      timeout: 10000,
      success: function(data) {
         $('#selectedfile-name').text(filename);
         $('#file-versions-list').empty();
         $(Object.keys(data)).each(function(key, id) {
            const fileId = data[id].fileid;
            const rowId = `fileVersion_${fileId}`;
            const row = $("<tr>");
            const col1 = $("<td>").append(`<input type="radio" id="${rowId}" name="fileVersion" value="${fileId}">`);
            const col2 = $("<td>").append(
            $(`<label for="${rowId}">`).text(moment.unix(data[id].stat.mtime).format("YYYY-MM-DD HH:mm:ss")));
            const col3 = $("<td>").append(
            $('<span>').text(format_bibytes(data[id].stat.size)));
            const col4 = $("<td>").append(
            $('<span>').addClass('code').text(data[id].md5));
            row.tooltip({
               title: "<table>" +
               "<col width='1*'>" +
               "<col width='5'>" +
               "<col width='1*'>" +
               "<tr>" +
               "<td class='text-left'>Job ID </td>" +
               "<td></td>" +
               `<td class='text-left'>${data[id].jobid}</td>` +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>Volume </td>" +
               "<td></td>" +
               `<td class='text-left'>${data[id].volumename}</td>` +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>Mode </td>" +
               "<td></td>" +
               "<td class='text-left'>" + formatJSTreeGridModeItem(data[id].stat.mode) + "</td>" +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>UID:GID </td>" +
               "<td></td>" +
               `<td class='text-left'>${data[id].stat.uid}:${data[id].stat.gid}</td>` +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>User:Group </td>" +
               "<td></td>" +
               `<td class='text-left'>${data[id].stat.user}:${data[id].stat.group}</td>` +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>Last access</td>" +
               "<td></td>" +
               `<td class='text-left'>${moment.unix(data[id].stat.atime).format("YYYY-MM-DD HH:mm:ss")}</td>` +
               "</tr>" +
               "<tr>" +
               "<td class='text-left'>Last status change</td>" +
               "<td></td>" +
               `<td class='text-left'>${moment.unix(data[id].stat.ctime).format("YYYY-MM-DD HH:mm:ss")}</td>` +
               "</tr>" +
               "</table>",
               html: true,
               trigger: 'hover',
               placement: 'left',
               delay: { "show": 200, "hide": 0 }
            });
            row.append(col1, col2, col3, col4);
            $('#file-versions-list').append(row)
            if(data.length == 1) {
               document.getElementById(rowId).checked = true;
            }
         });
      },
      error: function() {
         $('.revisions-container').empty();
         $('.revisions-container').append('Error fetching data.');
      },
      timeout: function() {
         $('.revisions-container').empty();
         $('.revisions-container').append('Timeout fetching data.');
      },
      parsererror: function() {
         $('.revisions-container').empty();
         $('.revisions-container').append('Error parsing data.');
      }
   });
}

function treeItemSelectedHandler(event, _data) {
   if (!_data.node.id || _data.node.id < 0) {
      _data.instance.deselect_node(_data.node);
      clearFileVersions()
   } else {
      const node = _data.node;
      getFileVersions(node.data.pathid, node.data.name)
   }
}

function initTreeHandler() {
   registerClickHandler(treeItemSelectedHandler)
}

function format_bibytes(v) {
   if (v === 0) {
      return "0.00 B";
   } else {
      const units = ["B", "kiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB"];
      const i = Math.floor(Math.log2(v) / 10);
      return parseFloat((v / (1 << (i * 10))).toFixed(2)) + " " + units[i];
   }
}

var modalConfirm = function(callback) {
   $("#modal-003-btn-ok").on("click", function() {
      callback(true);
      $("#modal-003").modal('hide');
   });

   $("#modal-003-btn-cancel").on("click", function() {
      callback(false);
      $("#modal-003").modal('hide');
   });
}

modalConfirm(function(confirm) {
   if(confirm) {
      submitRestoreFormData();
   } else {
      return false;
   }
});

function verifyRestoreFormDataFileSelection() {
   const checked_files = [];
   const fileId = $("input[name='fileVersion']:checked").val();

   if (fileId) {
      checked_files.push(fileId);
      document.getElementById('checked_files').value = checked_files.join(",");
      let client = document.getElementById('client').value;
      let restoreclient = document.getElementById('restoreclient').value;
      let replace = document.getElementById('replace').value;
      let where = document.getElementById('where').value;
      let pluginoptions = '';
      if(document.getElementById('pluginoptions') !== null) {
         pluginoptions = document.getElementById('pluginoptions').value;
      }
      let selectedFile = document.getElementById('selectedfile-name').innerHTML;
      updateModalRestoreParamsTable(client, restoreclient, replace, where, pluginoptions, selectedFile);
      $("#modal-003").modal();
   } else {
      $("#modal-004").modal();
   }

   return false
}

function submitRestoreFormData() {
   document.getElementById('restore').submit();
}

function updateModalRestoreParamsTable(client, restoreclient, replace, where, pluginoptions, selectedFile) {
   $('#restore-params-table').empty();
   $('#restore-params-table').append("<tr><td><b>Client:</b></td><td>" + client + "</td></tr>");
   $('#restore-params-table').append("<tr><td><b>Restore to client:</b></td><td>" + restoreclient + "</td></tr>");
   $('#restore-params-table').append("<tr><td><b>Replace files on client:</b></td><td>" + replace + "</td></tr>");
   $('#restore-params-table').append("<tr><td><b>Restore location on client:</b></td><td>" + where + "</td></tr>");
   $('#restore-params-table').append("<tr><td><b>Plugin Options:</b></td><td>" + pluginoptions + "</td></tr>");
   $('#restore-params-table').append("<tr><td><b>Selected file:</b></td><td>" + selectedFile + "</td></tr>");
}

function registerClickHandler(handler) {
   $('#filebrowser').on('select_node.jstree', handler)
}

function updateRestoreParams(k, v) {
   var p = [];
   var params = [];

   if (k === 'client') {
      p['jobid'] = '';
   } else {
      p['jobid'] = '<?php echo $this->restore_params['jobid']; ?>';
   }

   p['client'] = '<?php echo $this->restore_params['client']; ?>';

   if (k === 'client') {
      p['restoreclient'] = '';
   } else {
      p['restoreclient'] = '<?php echo $this->restore_params['restoreclient']; ?>';
   }

   p['restorejob'] = '<?php echo $this->restore_params['restorejob']; ?>';
   p['where'] = '<?php echo $this->restore_params['where']; ?>';
   p['pluginoptions'] = '<?php echo $this->restore_params['pluginoptions']; ?>';
   p['mergefilesets'] = '<?php echo $this->restore_params['mergefilesets']; ?>';
   p['mergejobs'] = '<?php echo $this->restore_params['mergejobs']; ?>';
   p['limit'] = '<?php echo $this->restore_params['limit']; ?>';

   p[k] = v;

   var key;

   for (key in p) {
      params.push(key + "=" + p[key]);
   }

   return params.join('&');
}

   function hasValue(array) {
      for (let i = 0; i < array.length; i++) {
         let id = array[i];
         if(!document.getElementById(id).value) {
            return false;
         }
      }
      return true;
   }

   function isShowFileTreeReady()
   {
      const required_ids = ["client", "jobid"];
      return hasValue(required_ids);
   }

   function isRestoreFormReady()
   {
      if(!isShowFileTreeReady()) {
         return false;
      }
      const required_ids = ["restorejob", "restoreclient"];
      return hasValue(required_ids);
   }

   function updateSubmitButtonStatus() {
      if (isRestoreFormReady()) {
         document.getElementById('btn-form-submit').disabled = false;
      } else {
         document.getElementById('btn-form-submit').disabled = true;
      }
   }

function displayBvfsCacheUpdateInfo() {
   $('#filebrowser').append("<br>");
   $('#filebrowser').append("<div id='alert-bvfs' class='alert alert-info' role='alert'>");
   $('#alert-bvfs').append("Update the Bvfs cache frequently to avoid timeouts.<br>");
   $('#alert-bvfs').append("Please read the Bareos Documentation for further details.");
   $('#filebrowser').append("</div>");
}

$(".search-input").keyup(function () {
   var searchString = $(this).val();
   $('#filebrowser').jstree('search', searchString);
});

$("#filebrowser").bind("loading.jstree", function() {
   displayBvfsCacheUpdateInfo();
});

function showFileTree() {
   $('#filebrowser').jstree({
      'plugins': ["grid", "state", "sort", "search", "types"],
      'core': {
         'animation': false,
         'force_text': true,
         'error': function (e) {
            $('#filebrowser').html(
               "<h4>There was an error while loading data for this tree.</h4>" +
               "<p><b>Error: </b>" + e.error + "</p>" +
               "<p><b>Plugin: </b>" + e.plugin + "</p>" +
               "<p><b>Reason: </b> " + e.reason + "</p>" +
               "<p><b>Data:</b></p>" +
               "<pre><code>" + e.data + "</code></pre>"
            );
            displayBvfsCacheUpdateInfo();
         },
         'multiple': false,
         'data': {
            'url': '<?php echo $this->basePath() . "/restore/filebrowser?jobid=" . $this->restore_params['jobid'] . "&client=" . $this->restore_params['client'] . "&mergefilesets=" . $this->restore_params['mergefilesets'] . "&mergejobs=" . $this->restore_params['mergejobs'] . "&limit=" . $this->restore_params['limit']; ?>',
            'dataType': 'json',
            'data': function (node) {
               return {
                  'id': node.id,
                  'state': {'checkbox_disabled': true}
               };
            },
            'timeout': <?php echo $_SESSION['bareos']['filetree_refresh_timeout']; ?>,
         },
      },
      'state' : {
         'key': 'versions'
      },
      'grid': {
         width: '100%',
         height: '60vh',
         fixedHeader: true,
         resizable: false,
         columns: [
            {
               width: '100%',
               height: '100%',
               header: '<?php echo $this->translate("Name"); ?>',
               headerClass: 'jsTreeGridHeader',
               title: "_DATA_"
            },
            {
               width: 120,
               header: '<?php echo $this->translate("Size"); ?>',
               headerClass: 'jsTreeGridHeader',
               value: function (node) {
                  if(node.data === null) return null;
                  return formatJSTreeGridSizeItem(node);
               }
            },
            {
               width: 150,
               header: '<?php echo $this->translate("Last modification"); ?>',
               headerClass: 'jsTreeGridHeader',
               value: function (node) {
                  if(node.data === null) return null;
                  return formatJSTreeGridLastModItem(node.data.stat.mtime);
               }
            }
         ],
      },
      'search': {
         "case_sensitive": false,
         "show_only_matches": false
      }
   });
}

$('#jobid').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('jobid', this.value);
});

$('#client').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('client', this.value);
});

   $('#restoreclient').change(function(event) {
      updateSubmitButtonStatus();
   });

$('#restorejob').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('restorejob', this.value);
});

$('#mergefilesets').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('mergefilesets', this.value);
});

$('#mergejobs').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('mergejobs', this.value);
});

$('#limit').change(function (event) {
   window.location.href = window.location.pathname + '?' + updateRestoreParams('limit', this.value);
});

$("#btn-form-submit").on("click", function() {
   verifyRestoreFormDataFileSelection();
});

initTreeHandler();

$(document).ready(function () {

   var show_file_tree = true;

   var errors = '<?php echo str_replace(array("\n", "\r"), "", $this->errors); ?>';
   if (errors.length > 0) {
      $("#modal-001").modal();
      show_file_tree = false;
   }

   var result = '<?php echo str_replace(array("\n", "\r"), "", $this->result); ?>';
   if (result.length > 0) {
      $("#modal-002").modal();
   }

   var ndmp_advice_note = '<?php echo $this->ndmp_advice_note; ?>';

   if(ndmp_advice_note) {
      $("#modal-005").modal();
      show_file_tree = false;
   }

   $('#checked_files').val('');
   $('#checked_directories').val('')

   if(show_file_tree && isShowFileTreeReady()) {
      showFileTree();
   }

   updateSubmitButtonStatus();

});

</script>

<?php endif; ?>
