mirror of
https://github.com/webmin/webmin.git
synced 2026-02-03 06:03:28 +00:00
Replaced java applet with jstree in bacula restore file selector
This commit is contained in:
@@ -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
|
||||
|
||||
2
bacula-backup/jstree/jquery-3.6.0.min.js
vendored
Normal file
2
bacula-backup/jstree/jquery-3.6.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
6
bacula-backup/jstree/jstree.min.js
vendored
Normal file
6
bacula-backup/jstree/jstree.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
bacula-backup/jstree/themes/default-dark/32px.png
Normal file
BIN
bacula-backup/jstree/themes/default-dark/32px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
bacula-backup/jstree/themes/default-dark/40px.png
Normal file
BIN
bacula-backup/jstree/themes/default-dark/40px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
1146
bacula-backup/jstree/themes/default-dark/style.css
Normal file
1146
bacula-backup/jstree/themes/default-dark/style.css
Normal file
File diff suppressed because it is too large
Load Diff
1
bacula-backup/jstree/themes/default-dark/style.min.css
vendored
Normal file
1
bacula-backup/jstree/themes/default-dark/style.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
bacula-backup/jstree/themes/default-dark/throbber.gif
Normal file
BIN
bacula-backup/jstree/themes/default-dark/throbber.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
bacula-backup/jstree/themes/default/32px.png
Normal file
BIN
bacula-backup/jstree/themes/default/32px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.5 KiB |
BIN
bacula-backup/jstree/themes/default/40px.png
Normal file
BIN
bacula-backup/jstree/themes/default/40px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.2 KiB |
1102
bacula-backup/jstree/themes/default/style.css
Normal file
1102
bacula-backup/jstree/themes/default/style.css
Normal file
File diff suppressed because it is too large
Load Diff
1
bacula-backup/jstree/themes/default/style.min.css
vendored
Normal file
1
bacula-backup/jstree/themes/default/style.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
bacula-backup/jstree/themes/default/throbber.gif
Normal file
BIN
bacula-backup/jstree/themes/default/throbber.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
Reference in New Issue
Block a user