Replaced java applet with jstree in bacula restore file selector

This commit is contained in:
Dario Corti
2021-03-29 00:24:59 +02:00
parent 638895ea67
commit 7259d0199d
16 changed files with 2461 additions and 105 deletions

View File

@@ -417,9 +417,10 @@ return @rv;
# bacula_file_button(filesfield, [jobfield], [volume])
# Pops up a window for selecting multiple files, using a tree-like view
# Brackets around "window" are to hijack Authentic Theme replacement of popups with modals ("undefined")
sub bacula_file_button
{
return "<input type=button onClick='ifield = form.$_[0]; jfield = form.$_[1]; chooser = window.open(\"treechooser.cgi?volume=".&urlize($_[2])."&files=\"+escape(ifield.value)+\"&job=\"+escape(jfield.value), \"chooser\", \"toolbar=no,menubar=no,scrollbar=no,width=500,height=400\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\">\n";
return "<input type=button onClick='ifield = form.$_[0]; jfield = form.$_[1]; chooser = (window).open(\"treechooser.cgi?volume=".&urlize($_[2])."&files=\"+escape(ifield.value)+\"&job=\"+escape(jfield.value), \"chooser\", \"toolbar=no,menubar=no,scrollbar=no,width=500,height=400\"); chooser.ifield = ifield; window.ifield = ifield' value=\"...\">\n";
}
sub tape_select

File diff suppressed because one or more lines are too long

6
bacula-backup/jstree/jstree.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -4,100 +4,149 @@
$trust_unknown_referers = 1;
require './bacula-backup-lib.pl';
&ReadParse();
print "Content-type: text/plain\n\n";
use JSON::PP;
# Input sanitization
die "Illegal input" if ($in{'job'} and $in{'job'} !~ /^\d+\z/);
# Output the appropriate content-type
if ($in{'fmt'} eq "json") {
print "Content-type: application/json\n\n";
}
else {
print "Content-type: text/plain\n\n";
}
# Format the parent with ending slash if missing
$d = ($in{'dir'} =~ /\/\z/) ? $in{'dir'} : $in{'dir'}."/";
$wind = &unix_to_dos($d);
# Get the parent directory ID
$dbh = &connect_to_database();
$cmd = $dbh->prepare("select PathId from Path where Path = ?");
$d = $in{'dir'} eq "/" ? "/" : $in{'dir'}."/";
$wind = &unix_to_dos($d);
$cmd = $dbh->prepare("SELECT PathId FROM Path WHERE Path = ?");
$cmd->execute($wind);
($pid) = $cmd->fetchrow();
$cmd->finish();
if ($in{'job'} ne "") {
$jobsql = "and Job.JobId = $in{'job'}";
}
if ($in{'job'}) {
$jobsql = "AND Job.JobId = $in{'job'}";
}
@nodes = ();
if ($in{'volume'}) {
# Search just within one volume
# Subdirectories of directory, that are on this volume
$cmd1 = $dbh->prepare("
select Path.Path
from Path, File, Job, JobMedia, Media
where File.PathId = Path.PathId
and File.JobId = Job.JobId
and Job.JobId = JobMedia.JobId
and JobMedia.MediaId = Media.MediaId
and Media.VolumeName = ?
$jobsql
");
$cmd1->execute($in{'volume'}) || die "db error : ".$dbh->errstr;
while(($f) = $cmd1->fetchrow()) {
$f = &dos_to_unix($f);
if ($f =~ /^(\Q$d\E[^\/]+\/)/) {
push(@rv, $1);
}
}
$cmd1->finish();
$cmd = $dbh->prepare("
SELECT DISTINCT Path.Path
FROM Job, File, Path, JobMedia, Media
WHERE Job.JobId = File.JobId
AND Job.JobId = JobMedia.JobId
AND File.PathId = Path.PathId
AND JobMedia.MediaId = Media.MediaId
AND Media.VolumeName = ?
$jobsql
ORDER BY Path.Path
");
# Files in directory, that are on this volume
$cmd2 = $dbh->prepare("
select Filename.Name
from File, Filename, Job, JobMedia, Media
where File.FilenameId = Filename.FilenameId
and File.PathId = ?
and File.JobId = Job.JobId
and Job.JobId = JobMedia.JobId
and JobMedia.MediaId = Media.MediaId
and Media.VolumeName = ?
$jobsql
");
$cmd2->execute($pid, $in{'volume'}) || die "db error : ".$dbh->errstr;
while(($f) = $cmd2->fetchrow()) {
push(@rv, "$d$f") if ($f =~ /\S/);
}
$cmd2->finish();
}
$cmd->execute($in{'volume'}) || die "db error: ".$dbh->errstr;
}
else {
# Search all files
# Subdirectories of directory
$cmd1 = $dbh->prepare("
select Path
from Path, File, Job
where File.PathId = Path.PathId
and File.JobId = Job.JobId
$jobsql
");
$cmd1->execute() || die "db error : ".$dbh->errstr;
while(($f) = $cmd1->fetchrow()) {
$f = &dos_to_unix($f);
if ($f =~ /^(\Q$d\E[^\/]+\/)/) {
push(@rv, $1);
}
}
$cmd1->finish();
$cmd = $dbh->prepare("
SELECT DISTINCT Path.Path
FROM Job, File, Path
WHERE Job.JobId = File.JobId
AND File.PathId = Path.PathId
$jobsql
ORDER BY Path.Path
");
# Files in directory
$cmd2 = $dbh->prepare("
select Filename.Name
from File, Filename, Job
where File.FilenameId = Filename.FilenameId
and File.PathId = ?
and File.JobId = Job.JobId
$jobsql
");
$cmd2->execute($pid) || die "db error : ".$dbh->errstr;
while(($f) = $cmd2->fetchrow()) {
push(@rv, "$d$f") if ($f =~ /\S/);
}
$cmd2->finish();
$cmd->execute() || die "db error: ".$dbh->errstr;
}
# Push all folders direcly under the starting path
while(($f) = $cmd->fetchrow()) {
$f = &dos_to_unix($f);
if ($f =~ /^(\Q$d\E([^\/]+)\/)/) {
push(@rv, $1);
}
}
$cmd->finish();
@rv = &unique(@rv);
# Build the nodes structure for folders
foreach $f (@rv) {
$f =~ /([^\/]+)\/\Z/;
push @nodes, {
text => $1,
fullpath => $f,
children => JSON::PP::true,
icon => "jstree-folder"
};
}
if ($in{'volume'}) {
# Files in directory, that are on this volume
$cmd = $dbh->prepare("
SELECT Filename.Name
FROM File, Filename, Job, JobMedia, Media
WHERE File.FilenameId = Filename.FilenameId
AND File.JobId = Job.JobId
AND Job.JobId = JobMedia.JobId
AND JobMedia.MediaId = Media.MediaId
AND File.PathId = ?
AND Media.VolumeName = ?
$jobsql
ORDER BY Filename.Name
");
$cmd->execute($pid, $in{'volume'}) || die "db error: ".$dbh->errstr;
}
else {
# Files in directory
$cmd = $dbh->prepare("
SELECT Filename.Name
FROM Job, File, Filename
WHERE Job.JobId = File.JobId
AND File.FilenameId = Filename.FilenameId
AND File.PathId = ?
$jobsql
ORDER BY Filename.Name
");
$cmd->execute($pid) || die "db error: ".$dbh->errstr;
}
# Push all the files in the starting path
while(($f) = $cmd->fetchrow()) {
if ($f =~ /\S/) {
push(@rv, "$d$f");
# Build the nodes structure for files
push @nodes, {
text => $f,
fullpath => "$d$f",
children => JSON::PP::false,
icon => "jstree-file"
};
}
}
$cmd->finish();
# Return output
@rv = &unique(@rv);
print "\n";
foreach $f (@rv) {
print $f,"\n";
}
if($in{'fmt'} eq "json") {
print JSON::PP->new->utf8->encode(\@nodes);
}
else {
print "\n";
foreach $f (@rv) {
print $f,"\n";
}
}

View File

@@ -1,43 +1,91 @@
#!/usr/local/bin/perl
# treechooser.cgi
# Outputs HTML for a java file-chooser tree
# Outputs HTML for a javascript file-chooser tree
require './bacula-backup-lib.pl';
&PrintHeader();
&ReadParse();
$shortest = "/";
if ($main::session_id) {
$session = "<param name=session value=\"sid=$main::session_id\">";
}
$in{'job'} =~ s/^(.*)_(\d+)$/$2/g;
print <<EOF;
<html><head><title>$text{'tree_title'}</title><body>
<html>
<head>
<title>$text{'tree_title'}</title>
<link rel="stylesheet" href="jstree/themes/default/style.min.css" />
<style>
body {
margin: 0;
font-family: Arial, Helvetica, sans-serif;
font-size: 0.8em;
}
.buttons {
display: flex;
flex-flow: row nowrap;
justify-content: center;
padding: 5px;
}
.main-container {
display: flex;
flex-flow: column nowrap;
height: 100%;
}
.spaced-button {
margin: 5px;
}
#jstree {
flex: 1;
margin: 10px;
margin-bottom: 0px;
border: thin solid gray;
overflow: auto;
}
</style>
</head>
<body>
<div class="main-container">
<div id="jstree"></div>
<div class="buttons">
<button id="confirm" class="ui-button ui-widget ui-corner-all spaced-button">OK</button>
<button id="cancel" class="ui-button ui-widget ui-corner-all spaced-button">Cancel</button>
</div>
</div>
<script src="jstree/jquery-3.6.0.min.js"></script>
<script src="jstree/jstree.min.js"></script>
<script>
function clear_files()
{
top.ifield.value = "";
}
\$("#cancel").click(function() {
window.close();
});
function add_file(file)
{
top.ifield.value = top.ifield.value + file + "\\n";
}
\$("#confirm").click(function() {
let list = \$('#jstree').jstree(true).get_selected(true).map(n => n.original.fullpath).sort().reduce((a, v) => (a + '\\n' + v));
\$(top.ifield).val(list);
window.close();
});
function finished()
{
window.close();
}
\$(function () {
\$('#jstree').jstree({
'plugins' : [ 'checkbox' ],
'core' : {
'data' : {
'url' : function (node) {
const r = [];
r.push('fmt=' + 'json');
r.push('job=' + encodeURIComponent('$in{'job'}'));
r.push('volume=' + encodeURIComponent('$in{'volume'}'));
r.push('dir=' + ((node.id == '#') ? encodeURIComponent('$shortest') : encodeURIComponent(node.original.fullpath)));
return 'list.cgi?' + r.join('&');
}
}
}
});
});
</script>
<applet code=TreeChooser name=TreeChooser width=100% height=100% MAYSCRIPT>
<param name=volume value="$in{'volume'}">
<param name=root value="$shortest">
<param name=job value="$in{'job'}">
$session
</applet>
</body></html>
</body>
</html>
EOF

View File

@@ -958,7 +958,7 @@ if (!$gconfig{'no_frame_options'}) {
print "X-Frame-Options: SAMEORIGIN\n";
}
if (!$gconfig{'no_content_security_policy'}) {
print "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'; frame-src 'self'; child-src 'self'\n";
print "Content-Security-Policy: script-src 'self' 'unsafe-inline' 'unsafe-eval'; frame-src 'self'; child-src 'self' blob:\n";
}
print "X-Content-Type-Options: nosniff\n";
if (defined($cs)) {