<?php

/**
 *
 * bareos-webui - Bareos Web-Frontend
 *
 * @link   https://github.com/bareos/bareos for the canonical source repository
 * @copyright   Copyright (C) 2013-2023 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 = 'Job details';
$this->headTitle($title);

?>

<ul class="nav nav-tabs">
   <li class="active"><a href="<?php echo $this->url('job', array('action'=>'index')); ?>"><?php echo $this->translate('Show'); ?></a></li>
   <li><a href="<?php echo $this->url('job', array('action'=>'actions')); ?>"><?php echo $this->translate('Actions'); ?></a></li>
   <li><a href="<?php echo $this->url('job', array('action'=>'run')); ?>"><?php echo $this->translate('Run'); ?></a></li>
   <li><a href="<?php echo $this->url('job', array('action'=>'rerun')); ?>"><?php echo $this->translate('Rerun'); ?></a></li>
   <li><a href="<?php echo $this->url('job', array('action'=>'timeline')); ?>"><?php echo $this->translate('Timeline'); ?></a></li>
</ul>

<br />

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

<!-- Live progress (hidden unless the job is currently running) -->
<div class="row" id="live-progress-row" style="display: none;">
   <div class="col-md-12">
      <div class="panel panel-primary">
         <div class="panel-heading">
            <h3 class="panel-title">
               <span class="glyphicon glyphicon-refresh" style="animation: spin 1.6s linear infinite;"></span>
               <?php echo $this->translate("Live progress"); ?>
               <span id="live-progress-status" class="badge" style="margin-left: 8px;"></span>
            </h3>
         </div>
         <div class="panel-body">
            <div class="row">
               <div class="col-md-6">
                  <dl class="dl-horizontal">
                     <dt><?php echo $this->translate("Client bytes"); ?></dt>
                     <dd id="lp-fd-bytes">&mdash;</dd>
                     <dt><?php echo $this->translate("Client files"); ?></dt>
                     <dd id="lp-fd-files">&mdash;</dd>
                     <dt><?php echo $this->translate("Files examined"); ?></dt>
                     <dd id="lp-fd-examined">&mdash;</dd>
                     <dt><?php echo $this->translate("Processing file"); ?></dt>
                     <dd id="lp-fd-processing" style="word-break: break-all;">&mdash;</dd>
                  </dl>
               </div>
               <div class="col-md-6">
                  <dl class="dl-horizontal">
                     <dt><?php echo $this->translate("Storage bytes"); ?></dt>
                     <dd id="lp-sd-bytes">&mdash;</dd>
                     <dt><?php echo $this->translate("Average rate"); ?></dt>
                     <dd id="lp-sd-avgrate">&mdash;</dd>
                     <dt><?php echo $this->translate("Current rate"); ?></dt>
                     <dd id="lp-sd-lastrate">&mdash;</dd>
                     <dt><?php echo $this->translate("Volume"); ?></dt>
                     <dd id="lp-sd-volume">&mdash;</dd>
                     <dt><?php echo $this->translate("Device"); ?></dt>
                     <dd id="lp-sd-device" style="word-break: break-all;">&mdash;</dd>
                  </dl>
               </div>
            </div>
            <div id="live-progress-warning" class="alert alert-warning" style="display: none;"></div>
         </div>
      </div>
   </div>
</div>
<style>@keyframes spin { from{transform:rotate(0);} to{transform:rotate(360deg);} }</style>

<!-- Job -->
<div class="row">

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

<div class="panel panel-default">

<div class="panel-heading">
<h3 class="panel-title"><?php echo $this->translate("Job"); ?></h3>
</div>

<div class="panel-body">

<table class="table table-no-bordered table-hover" id="jobdetails">

<thead class="bg-primary">
   <th><?php echo $this->translate("Job ID"); ?></th>
   <th><?php echo $this->translate("Job name"); ?></th>
   <th><?php echo $this->translate("Client"); ?></th>
   <th><?php echo $this->translate("Type"); ?></th>
   <th><?php echo $this->translate("Level"); ?></th>
   <th><?php echo $this->translate("Files"); ?></th>
   <th><?php echo $this->translate("Bytes"); ?></th>
   <th><?php echo $this->translate("Errors"); ?></th>
   <th><?php echo $this->translate("Status"); ?></th>
   <th><?php echo $this->translate("Actions"); ?></th>
</thead>

</table>

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

<div class="row">

   <div class="col-md-8">
      <div class="panel panel-default">
         <div class="panel-heading">
            <h3 class="panel-title"><?php echo $this->translate("Messages"); ?></h3>
         </div>
         <div class="panel-body">
            <table class="table table-no-bordered table-hover" id="joblog">
               <thead class="bg-primary">
                  <th>#</th>
                  <th><?php echo $this->translate("Timestamp"); ?></th>
                  <th><?php echo $this->translate("Message"); ?></th>
               </thead>
            </table>
         </div>
      </div>
   </div>

   <div class="col-md-4">
      <div class="panel panel-default">
         <div class="panel-heading">
            <h3 class="panel-title"><?php echo $this->translate("Used Volumes"); ?></h3>
         </div>
         <div class="panel-body">
            <table class="table table-no-bordered table-hover" id="jobmedia">
               <thead class="bg-primary">
                  <th><?php echo $this->translate("Volume"); ?></th>
               </thead>
            </table>
         </div>
      </div>
   </div>

</div>


<?php
   echo $this->headScript()->prependFile($this->basePath() . '/js/custom-functions.js');
   echo $this->headLink()->prependStylesheet($this->basePath() . '/css/bootstrap-table.min.css');
   echo $this->headScript()->prependFile($this->basePath() . '/js/bootstrap-table-formatter.js');
   echo $this->headScript()->prependFile($this->basePath() . '/js/bootstrap-table-locale-all.min.js');
   echo $this->headScript()->prependFile($this->basePath() . '/js/bootstrap-table-cookie.min.js');
   echo $this->headScript()->prependFile($this->basePath() . '/js/bootstrap-table.min.js');
?>

<!-- modal-001 start -->
<div id="modal-001" class="modal fade modal-001" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel">
  <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 from director:"); ?></p>
   <p class="text-danger"><?php echo $this->translate("Failed to send result as json. Maybe the result message is too long?"); ?></p>
      </div>
      <div class="modal-footer">
         <button type="button" class="btn btn-default" data-dismiss="modal"><?php echo $this->translate("Close"); ?></button>
      </div>
    </div>
  </div>
</div>
<!-- modal-001 end -->

<script>

   var basePath = "<?php echo $this->basePath(); ?>";

   function detailFormatterJobDetails(index, row) {
      var html = [];
      html.push('<div class="container-fluid">');
      html.push('<table class="table table-bordered">');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("Fileset")); ?></th>');
      html.push('<td><a href="<?php echo $this->basePath() . '/fileset/details/'; ?>' + row.filesetid + '">' + row.fileset +'</a></td>');
      html.push('</tr>');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("Pool")); ?></th>');
      html.push('<td><a href="<?php echo $this->basePath() . '/pool/details/?pool='; ?>' + row.poolname + '">' + row.poolname + '</a></td>');
      html.push('</tr>');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("Scheduled")); ?></th>');
      html.push('<td>' + row.schedtime + '</td>');
      html.push('</tr>');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("Start")); ?></th>');
      html.push('<td>' + row.starttime + '</td>');
      html.push('</tr>');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("End")); ?></th>');
      html.push('<td>' + row.endtime + '</td>');
      html.push('</tr>');
      html.push('<tr>');
      html.push('<th><?php echo addslashes($this->translate("Duration")); ?></th>');
      html.push('<td>' + row.duration + '</td>');
      html.push('</tr>');
      html.push('</table>');
      html.push('</div>');
      return html.join('');
   }

   function attachJobDetailsTable() {
      $('#jobdetails').bootstrapTable({
         locale: '<?php echo str_replace('_','-', $_SESSION['bareos']['locale']); ?>',
         url: '<?php echo $this->url("api/job", array("id" => $this->jobid), array()); ?>',
         method: 'get',
         dataType: 'json',
         detailView: true,
         detailFormatter: 'detailFormatterJobDetails',
         columns: [
            {
               field: 'jobid',
            },
            {
               field: 'name',
               formatter: function(value) {
                  return formatJobName(value, basePath);
               }
            },
            {
               field: 'client',
               formatter: function(value) {
                  return formatClientName(value, basePath);
               }
            },
            {
               field: 'type',
               formatter: function(value) {
                  return formatJobType(value);
               }
            },
            {
               field: 'level',
               formatter: function(value) {
                  return formatJobLevel(value);
               }
            },
            {
               field: 'jobfiles',
            },
            {
               field: 'jobbytes',
               formatter: function(value) {
                  return formatBytes(value);
               }
            },
            {
               field: 'joberrors',
            },
            {
               field: 'jobstatus',
               formatter: function(value) {
                  return formatJobStatus(value);
               }
            },
            {
               field: 'action',
               formatter: function(value, row, index) {
                  return jobActionButtonsFormatter(value, row, index, basePath);
               }
            }
         ]
      });
   }

   function attachJobLogTable() {
      $('#joblog').bootstrapTable({
         locale: '<?php echo str_replace('_','-', $_SESSION['bareos']['locale']); ?>',
         cookie: <?php echo $_SESSION['bareos']['dt_statesave']; ?>,
         cookieIdTable: 'job_table_job_log',
         url: '<?php echo $this->url("api/joblog", array("id" => $this->jobid), array()); ?>',
         method: 'get',
         dataType: 'json',
         pagination : true,
         sidePagination: 'client',
         pageList: [ <?php echo $_SESSION['bareos']['dt_lengthmenu']; ?> ],
         pageSize: <?php echo $_SESSION['bareos']['dt_pagelength']; ?>,
         search: true,
         sortName: 'id',
         sortOrder: 'desc',
         columns: [
            {
               field: 'id',
               sortable: true,
            },
            {
               field: 'time'
            },
            {
               field: 'logtext',
               formatter: function(value) {
                  return formatLogMessage(value);
               }
            }
         ]
      });
   }

   function attachJobMediaTable() {
      $('#jobmedia').bootstrapTable({
         locale: '<?php echo str_replace('_','-', $_SESSION['bareos']['locale']); ?>',
         cookie: <?php echo $_SESSION['bareos']['dt_statesave']; ?>,
         cookieIdTable: 'job_table_job_media',
         url: '<?php echo $this->url('api/media', array(), array("query" => array("filter" => "jobid", "jobid" => $this->jobid))); ?>',
         method: 'get',
         dataType: 'json',
         pagination : true,
         sidePagination: 'client',
         pageList: [ <?php echo $_SESSION['bareos']['dt_lengthmenu']; ?> ],
         pageSize: <?php echo $_SESSION['bareos']['dt_pagelength']; ?>,
         search: true,
         sortName: 'volumename',
         sortOrder: 'asc',
         columns: [
            {
               field: 'volumename',
               sortable: true,
               formatter: function(value) {
                  return formatVolumeName(value, basePath);
               }
            }
         ]
      });
   }

   // Live progress panel: polls /api/jobstatus/<id> every 5s while the
   // job is running, hides the panel (and stops polling) once the job
   // is no longer in the director's live JCR list. Fail-soft: if the
   // director or either daemon returns a structured error (too-old,
   // oversize reply, parse failure) the panel stays visible with a
   // warning banner but continues polling -- the error may be transient.
   var liveProgressTimer = null;
   var liveProgressJobid = <?php echo (int)$this->jobid; ?>;

   function fmtBytes(n) {
      if (n === null || n === undefined) return '\u2014';
      var u = ['B','KB','MB','GB','TB','PB'];
      var i = 0; n = Number(n);
      while (n >= 1024 && i < u.length - 1) { n /= 1024; i++; }
      return n.toFixed(i === 0 ? 0 : 2) + ' ' + u[i];
   }

   function fmtRate(bps) {
      if (!bps) return '\u2014';
      return fmtBytes(bps) + '/s';
   }

   function renderLiveProgress(data) {
      // The endpoint returns the job_status object directly (Laminas
      // JsonModel serializes $this->result, which is the inner subtree).
      var warn = $('#live-progress-warning');
      if (!data || !data.jobid) { return false; }
      var js = data;

      if (js.error === 'job_not_found') {
         // Job is no longer running. Hide panel and stop polling.
         $('#live-progress-row').hide();
         return false;
      }

      var fdData = (js.fd && js.fd.data) ? js.fd.data : null;
      var sdData = (js.sd && js.sd.data) ? js.sd.data : null;
      var fdErr = (js.fd && js.fd.error) ? js.fd.error : null;
      var sdErr = (js.sd && js.sd.error) ? js.sd.error : null;

      $('#live-progress-row').show();
      if (js.dir && js.dir.status) {
         $('#live-progress-status').text(js.dir.status);
      }

      var fdJob = (fdData && fdData.running_jobs)
         ? fdData.running_jobs.find(function(j) { return j.jobid === liveProgressJobid; })
         : null;
      if (fdJob) {
         $('#lp-fd-bytes').text(fmtBytes(fdJob.bytes));
         $('#lp-fd-files').text((fdJob.files || 0).toLocaleString());
         $('#lp-fd-examined').text((fdJob.files_examined || 0).toLocaleString());
         $('#lp-fd-processing').text(fdJob.processing_file || '\u2014');
      }

      var sdJob = (sdData && sdData.running_jobs)
         ? sdData.running_jobs.find(function(j) { return j.jobid === liveProgressJobid; })
         : null;
      if (sdJob) {
         $('#lp-sd-bytes').text(fmtBytes(sdJob.bytes));
         $('#lp-sd-avgrate').text(fmtRate(sdJob.avg_bytes_per_sec));
         $('#lp-sd-lastrate').text(fmtRate(sdJob.last_bytes_per_sec));
         if (sdJob.writing) {
            $('#lp-sd-volume').text(sdJob.writing.volume || '\u2014');
            $('#lp-sd-device').text(sdJob.writing.device || '\u2014');
         }
      }

      var warnings = [];
      if (fdErr) { warnings.push('<?php echo $this->translate("File Daemon JSON status unavailable"); ?>: ' + fdErr); }
      if (sdErr) { warnings.push('<?php echo $this->translate("Storage Daemon JSON status unavailable"); ?>: ' + sdErr); }
      if (warnings.length) {
         warn.html(warnings.join('<br>')).show();
      } else {
         warn.hide();
      }

      return true;
   }

   function pollLiveProgress() {
      $.ajax({
         url: '<?php echo $this->url('api/jobstatus', array('id' => $this->jobid)); ?>',
         method: 'GET',
         dataType: 'json',
         timeout: 10000
      }).done(function(data) {
         var stillRunning = renderLiveProgress(data);
         if (!stillRunning) {
            clearInterval(liveProgressTimer);
            liveProgressTimer = null;
         }
      }).fail(function() {
         // Transient -- leave panel in its current state, keep polling.
      });
   }

   function attachLiveProgress() {
      pollLiveProgress();
      liveProgressTimer = setInterval(pollLiveProgress, 5000);
   }

   $(document).ready(function() {

      setDtTextDomain('<?php echo $this->basePath() . '/js/locale'; ?>');
      setDtLocale('<?php echo $_SESSION['bareos']['locale']; ?>');

      attachJobDetailsTable();
      attachJobLogTable();
      attachJobMediaTable();
      attachLiveProgress();

   });

   $('#joblog').on('load-error.bs.table', function(status, res) {
      $("#modal-001").modal();
   });

</script>

<?php endif; ?>
