Release Notes for brestore 2.2.0
+Version 2.2.5-2:
+ - Use 2 transactions in brestore.pl -b
+ (you can use Ctrl-C in cleanup)
+
+Version 2.2.5-1:
+ - Fix warning, thanks to Tuomas Jormola
+
Version 2.2.0-2:
- cleanup brestore
- update brestore_xxx only when job is in (T, f, A)
$self->update_brestore_table(map { $_->[0] } @$jobs);
+ $self->{conf}->{dbh}->commit();
+ $self->{conf}->{dbh}->begin_work();
+
print STDERR "Cleaning path visibility\n";
my $nb = $self->dbh_do("
exit 1;
}
-my $file_conf = "$ENV{HOME}/.brestore.conf" ;
+my $file_conf = (exists $ENV{HOME})? "$ENV{HOME}/.brestore.conf" : undef ;
my $batch_mod;
GetOptions("conf=s" => \$file_conf,
"debug" => \$debug,
"help" => \&HELP_MESSAGE) ;
+if (! defined $file_conf) {
+ print STDERR "Could not detect default config and no config file specified\n";
+ HELP_MESSAGE();
+}
+
my $p = new Pref($file_conf);
if (! -f $file_conf) {
exit (0);
}
-$glade_file = $p->{glade_file};
+$glade_file = $p->{glade_file} || $glade_file;
foreach my $path ('','.','/usr/share/brestore','/usr/local/share/brestore') {
if (-f "$path/$glade_file") {
Release Notes for bweb 2.2
+2007/11/08
+ - Add Prev/Next on job log output
+
+2007/10/31
+ - Add a error filter to Job zoom view
+
+2007/10/30
+ - Replace VolStatus by Enabled in volume location
+ - Add Enabled in volume update and zoom
+
+2007/10/26
+ - add an option to view only expired media
+
+2007/09/26
+ - add bresto.pl, bresto.html and bresto.js
2007/08/20
- fix update of locationid field during label barcodes
$groupq = " AND client_group_name IN ($arg->{jclient_groups}) ";
}
+$bweb->can_do('r_view_job');
+my $filter = $bweb->get_client_filter();
+
my $gtype = CGI::param('gtype') || 'bars';
print CGI::header('image/png');
Client.Name AS clientname,
$jobt.Name AS jobname,
$jobt.JobBytes AS jobbytes
-FROM $jobt, FileSet, Client $groupf
+FROM $jobt, FileSet, Client $filter $groupf
WHERE $jobt.ClientId = Client.ClientId
AND $jobt.FileSetId = FileSet.FileSetId
AND $jobt.Type = 'B'
Client.Name AS clientname,
$jobt.Name AS jobname,
$jobt.JobFiles AS jobfiles
-FROM $jobt, FileSet, Client $groupf
+FROM $jobt, FileSet, Client $filter $groupf
WHERE $jobt.ClientId = Client.ClientId
AND $jobt.FileSetId = FileSet.FileSetId
AND $jobt.Type = 'B'
Job.Name AS jobname,
base64_decode_lstat(8,LStat) AS lstat
-FROM Job, FileSet, Filename, Path, File, Client
+FROM Job, FileSet, Filename, Path, File, Client $filter
WHERE Job.ClientId = Client.ClientId
AND Job.FileSetId = FileSet.FileSetId
AND Job.Type = 'B'
Job.Name AS jobname,
brestore_pathvisibility.size AS size
-FROM Job, Client, FileSet, Path, brestore_pathvisibility
+FROM Job, Client $filter, FileSet, Path, brestore_pathvisibility
WHERE Job.ClientId = Client.ClientId
AND Job.FileSetId = FileSet.FileSetId
AND Job.Type = 'B'
- $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01)
AS rate
-FROM $jobt, FileSet, Client $groupf
+FROM $jobt, FileSet, Client $filter $groupf
WHERE $jobt.ClientId = Client.ClientId
AND $jobt.FileSetId = FileSet.FileSetId
AND $jobt.Type = 'B'
$bweb->{sql}->{SEC_TO_INT}( $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
- $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
AS duration
-FROM $jobt, FileSet, Client $groupf
+FROM $jobt, FileSet, Client $filter $groupf
WHERE $jobt.ClientId = Client.ClientId
AND $jobt.FileSetId = FileSet.FileSetId
AND $jobt.Type = 'B'
SELECT
" . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
$t(JobBytes) AS nb
-FROM $jobt, FileSet, Client $groupf
+FROM $jobt, FileSet, Client $filter $groupf
WHERE $jobt.ClientId = Client.ClientId
AND $jobt.FileSetId = FileSet.FileSetId
AND $jobt.Type = 'B'
);
my $all = $dbh->selectall_arrayref($query) ;
- print STDERR Data::Dumper::Dumper($all);
+# print STDERR Data::Dumper::Dumper($all);
my ($ret) = make_tab_sum($all);
print $obj->plot([$ret->{date}, $ret->{nb}])->png;
$bweb->display_job(limit => 10);
} elsif ($action eq 'view_conf') {
+ $bweb->can_do('r_configure');
$conf->view()
} elsif ($action eq 'edit_conf') {
+ $bweb->can_do('r_configure');
$conf->edit();
} elsif ($action eq 'apply_conf') {
+ $bweb->can_do('r_configure');
$conf->modify();
+} elsif ($action eq 'user_del') {
+ $bweb->users_del();
+
+} elsif ($action eq 'user_add') {
+ $bweb->users_add();
+
+} elsif ($action eq 'user_edit') {
+ $bweb->display_user();
+
+} elsif ($action eq 'user_save') {
+ $bweb->users_add();
+
+} elsif ($action eq 'users') {
+ $bweb->display_users();
+
} elsif ($action eq 'client') {
$bweb->display_clients();
} elsif ($action eq 'media') {
print "<div><table border='0'><tr><td valign='top'>\n";
- my $fields = $bweb->get_form(qw/db_locations db_pools
+ my $fields = $bweb->get_form(qw/db_locations db_pools expired
qlocations qpools volstatus qre_media
limit qmediatypes db_mediatypes/);
$bweb->display($fields, "display_form_media.tpl");
limit => $arg->{limit});
print "</td></tr></table></div>";
-} elsif ($action eq 'medias') {
- $bweb->display_medias();
+} elsif ($action eq 'allmedia') {
+ $bweb->display_allmedia();
} elsif ($action eq 'eject') {
+ $bweb->can_do('r_autochanger_mgnt');
+
my $arg = $bweb->get_form("ach");
my $a = $bweb->ach_get($arg->{ach});
$bweb->eject_media();
} elsif ($action eq 'clear_io') {
+ $bweb->can_do('r_autochanger_mgnt');
+
my $arg = $bweb->get_form('ach');
my $a = $bweb->ach_get($arg->{ach});
$bweb->ach_del();
} elsif ($action eq 'ach_view') {
+ $bweb->can_do('r_autochanger_mgnt');
+
# TODO : get autochanger name and create it
$bweb->connect_db();
my $arg = $bweb->get_form('ach');
$bweb->ach_add();
} elsif ($action eq 'ach_load') {
+ $bweb->can_do('r_autochanger_mgnt');
+
my $arg = $bweb->get_form('ach', 'drive', 'slot');
my $a = $bweb->ach_get($arg->{ach});
}
} elsif ($action eq 'ach_unload') {
+ $bweb->can_do('r_autochanger_mgnt');
+
my $arg = $bweb->get_form('drive', 'slot', 'ach');
my $a = $bweb->ach_get($arg->{ach});
$bweb->help_extern_compute();
} elsif ($action eq 'extern') {
+ $bweb->can_do('r_media_mgnt');
+ $bweb->can_do('r_autochanger_mgnt');
+
print "<div style='float: left;'>";
my @achs = $bweb->eject_media();
for my $ach (@achs) {
$bweb->update_slots();
}
print "</div><div style='float: left;margin-left: 20px;'>";
- $bweb->move_media();
+ $bweb->move_media('no'); # enabled = no
print "</div>";
} elsif ($action eq 'move_email') {
$bweb->display($bweb, 'about.tpl');
} elsif ($action eq 'intern') {
- $bweb->move_media(); # TODO : remove that
+ $bweb->move_media('yes'); # TODO : remove that
} elsif ($action eq 'move_media') {
- $bweb->move_media();
+ my $a = $bweb->get_form('enabled');
+ $bweb->move_media($a->{enabled});
} elsif ($action eq 'save_location') {
$bweb->save_location();
$bweb->groups_del();
} elsif ($action eq 'job') {
-
+ $bweb->can_do('r_view_job');
print "<div><table border='0'><tr><td valign='top'>\n";
- my $fields = $bweb->get_form(qw/status level db_clients db_filesets
+ my $fields = $bweb->get_form(qw/status level filter db_clients
+ db_filesets
limit age offset qclients qfilesets
jobtype qpools db_pools
db_client_groups qclient_groups/); # drop this to hide
limit => $arg->{limit});
print "</td></tr></table></div>";
} elsif ($action eq 'job_group') {
-
+ $bweb->can_do('r_view_job');
print "<div><table border='0'><tr><td valign='top'>\n";
- my $fields = $bweb->get_form(qw/limit level age
+ my $fields = $bweb->get_form(qw/limit level age filter
db_client_groups qclient_groups/); # drop this to hide
$fields->{hide_status} = 1;
}
} elsif ($action eq 'group_stats') {
-
$bweb->display_group_stats(age => $arg->{age});
} elsif ($action eq 'running') {
$bweb->display_running_job();
} elsif ($action eq 'update_from_pool') {
+ $bweb->can_do('r_media_mgnt');
my $elt = $bweb->get_form(qw/media pool/);
unless ($elt->{media} || $elt->{pool}) {
$bweb->error("Can't get media or pool param");
$bweb->update_media();
} elsif ($action eq 'client_status') {
+ $bweb->can_do('r_client_status');
my $b;
foreach my $client (CGI::param('client')) {
if ($client =~ m/$client_re/) {
$bweb->fileset_view();
} else {
- $bweb->error("Sorry, this action don't exist");
+ $bweb->error("Sorry, this action doesn't exist");
}
$bweb->display_end();
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <title>Bweb - restore</title>
+
+ <link rel="stylesheet" type="text/css" href="/bweb/ext/resources/css/ext-all.css" />
+
+ <script type="text/javascript" src="/bweb/ext/adapter/ext/ext-base.js"></script> <!-- ENDLIBS -->
+
+<!-- <script type="text/javascript" src="/bweb/ext/ext-all.js"></script> -->
+ <script type="text/javascript" src="/bweb/ext/ext-all-debug.js"></script>
+ <script type="text/javascript" src="bweb.js"></script>
+ <script type="text/javascript" src="bresto.js"></script>
+
+ </head>
+ <body>
+<div id="div-container" >
+ <div id="div-main-menu" >
+ <div id="div-toolbar" ></div>
+ <div id="div-tb-sel" ></div>
+ <div id="div-files" ></div>
+ <div id="div-file-versions" ></div>
+ <div id="div-file-selection" ></div>
+ <div id="div-tree" ></div>
+</div>
+
+<div id="div-resto-dlg" style="visibility:hidden;">
+<div id="div-resto-form" >
+<div id="div-resto-form-bp1"></div> <div id="div-resto-form-bp2"></div>
+</div>
+</div>
+ </body>
+</html>
--- /dev/null
+
+// Bweb - A Bacula web interface
+// Bacula® - The Network Backup Solution
+//
+// Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+//
+// The main author of Bweb is Eric Bollengier.
+// The main author of Bacula is Kern Sibbald, with contributions from
+// many others, a complete list can be found in the file AUTHORS.
+//
+// This program is Free Software; you can redistribute it and/or
+// modify it under the terms of version two of the GNU General Public
+// License as published by the Free Software Foundation plus additions
+// that are listed in the file LICENSE.
+//
+// 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
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+// 02110-1301, USA.
+//
+// Bacula® is a registered trademark of John Walker.
+// The licensor of Bacula is the Free Software Foundation Europe
+// (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
+// Switzerland, email:ftf@fsfeurope.org.
+
+// render if vol is online/offline
+function rd_vol_is_online(val)
+{
+ return '<img src="/bweb/inflag' + val + '.png">';
+}
+
+// TODO: fichier ou rep
+function rd_file_or_dir(val)
+{
+ if (val == 'F') {
+ return '<img src="/bweb/A.png">';
+ } else {
+ return '<img src="/bweb/R.png">';
+ }
+}
+
+Ext.namespace('Ext.brestore');
+
+Ext.brestore.jobid=0; // selected jobid
+Ext.brestore.jobdate=''; // selected date
+Ext.brestore.client=''; // selected client
+Ext.brestore.path=''; // current path (without user location)
+Ext.brestore.root_path=''; // user location
+
+Ext.brestore.option_vosb = false;
+Ext.brestore.option_vafv = false;
+
+
+function get_node_path(node)
+{
+ var temp='';
+ for (var p = node; p; p = p.parentNode) {
+ if (p.parentNode) {
+ if (p.text == '/') {
+ temp = p.text + temp;
+ } else {
+ temp = p.text + '/' + temp;
+ }
+ }
+ }
+ return Ext.brestore.root_path + temp;
+}
+
+
+function init_params(baseParams)
+{
+ baseParams['client']= Ext.brestore.client;
+
+ if (Ext.brestore.option_vosb) {
+ baseParams['jobid'] = Ext.brestore.jobid;
+ } else {
+ baseParams['date'] = Ext.brestore.jobdate;
+ }
+ return baseParams;
+}
+
+
+function ext_init()
+{
+//////////////////////////////////////////////////////////////:
+ var Tree = Ext.tree;
+ var tree_loader = new Ext.tree.TreeLoader({
+ baseParams:{},
+ dataUrl:'/cgi-bin/bweb/bresto.pl'
+ });
+
+ var tree = new Ext.tree.TreePanel('div-tree', {
+ animate:true,
+ loader: tree_loader,
+ enableDD:true,
+ enableDragDrop: true,
+ containerScroll: true
+ });
+
+ // set the root node
+ var root = new Ext.tree.AsyncTreeNode({
+ text: 'Select a job',
+ draggable:false,
+ id:'source'
+ });
+ tree.setRootNode(root);
+
+ // render the tree
+ tree.render();
+// root.expand();
+
+ tree.on('click', function(node, event) {
+ Ext.brestore.path = get_node_path(node);
+
+ file_store.removeAll();
+ file_versions_store.removeAll();
+ file_store.load({params:init_params({action: 'list_files',
+ node:node.id})
+ });
+ return true;
+ });
+
+ tree.on('beforeload', function(e) {
+ file_store.removeAll();
+ return true;
+ });
+
+
+////////////////////////////////////////////////////////////////
+
+ var file_store = new Ext.data.Store({
+ proxy: new Ext.data.HttpProxy({
+ url: '/cgi-bin/bweb/bresto.pl',
+ method: 'GET',
+ params:{}
+ }),
+
+ reader: new Ext.data.ArrayReader({
+ }, Ext.data.Record.create([
+ {name: 'fileid' },
+ {name: 'filenameid'},
+ {name: 'pathid' },
+ {name: 'name' },
+ {name: 'size', type: 'int' },
+ {name: 'mtime', type: 'date', dateFormat: 'Y-m-d h:i:s'}
+ ]))
+ });
+
+ var cm = new Ext.grid.ColumnModel([{
+ id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 })
+ header: 'File',
+ dataIndex: 'name',
+ width: 100,
+ css: 'white-space:normal;'
+ },{
+ header: "Size",
+ dataIndex: 'size',
+ renderer: human_size,
+ width: 50
+ },{
+ header: "Date",
+ dataIndex: 'mtime',
+ width: 100
+ },{
+ dataIndex: 'pathid',
+ hidden: true
+ },{
+ dataIndex: 'filenameid',
+ hidden: true
+ },{
+ dataIndex: 'fileid',
+ hidden: true
+ }
+ ]);
+
+ // by default columns are sortable
+ cm.defaultSortable = true;
+
+ // create the grid
+ var files_grid = new Ext.grid.Grid('div-files', {
+ ds: file_store,
+ cm: cm,
+ ddGroup : 'TreeDD',
+ enableDrag: true,
+ enableDragDrop: true,
+ selModel: new Ext.grid.RowSelectionModel(),
+ loadMask: true,
+ enableColLock:false
+
+ });
+
+ // when we reload the view,
+ // we clear the file version box
+ file_store.on('beforeload', function(e) {
+ file_versions_store.removeAll();
+ return true;
+ });
+
+ // TODO: selection only when using dblclick
+ files_grid.selModel.on('rowselect', function(e,i,r) {
+ Ext.brestore.filename = r.json[3];
+ file_versions_store.load({params:init_params({action: 'list_versions',
+ vafv: Ext.brestore.option_vafv,
+ pathid: r.json[2],
+ filenameid: r.json[1]
+ })
+ });
+ return true;
+ });
+ files_grid.render();
+
+//////////////////////////////////////////////////////////////:
+
+ var file_selection_store = new Ext.data.Store({
+ proxy: new Ext.data.MemoryProxy(),
+
+ reader: new Ext.data.ArrayReader({
+ }, Ext.data.Record.create([
+ {name: 'jobid' },
+ {name: 'fileid' },
+ {name: 'filenameid'},
+ {name: 'pathid' },
+ {name: 'name' },
+ {name: 'size', type: 'int' },
+ {name: 'mtime', type: 'date', dateFormat: 'Y-m-d h:i:s'}
+ ]))
+ });
+
+ var file_selection_cm = new Ext.grid.ColumnModel([{
+ id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 })
+ header: "Name",
+ dataIndex: 'name',
+ width: 250
+ },{
+ header: "JobId",
+ width: 50,
+ dataIndex: 'jobid'
+ },{
+ header: "Size",
+ dataIndex: 'size',
+ renderer: human_size,
+ width: 50
+ },{
+ header: "Date",
+ dataIndex: 'mtime',
+ width: 100
+ },{
+ dataIndex: 'pathid',
+ hidden: true
+ },{
+ dataIndex: 'filenameid',
+ hidden: true
+ },{
+ dataIndex: 'fileid',
+ hidden: true
+ }
+ ]);
+
+
+ // create the grid
+ var file_selection_grid = new Ext.grid.Grid('div-file-selection', {
+ cm: file_selection_cm,
+ ds: file_selection_store,
+ ddGroup : 'TreeDD',
+ enableDrag: false,
+ enableDrop: true,
+ selModel: new Ext.grid.RowSelectionModel(),
+ loadMask: true,
+ enableColLock:false
+
+ });
+
+ var file_selection_record = Ext.data.Record.create(
+ {name: 'jobid'},
+ {name: 'fileid'},
+ {name: 'filenameid'},
+ {name: 'pathid'},
+ {name: 'size'},
+ {name: 'mtime'}
+ );
+// data.selections[0].json[]
+// data.node.id
+// http://extjs.com/forum/showthread.php?t=12582&highlight=drag+drop
+ var ddrow = new Ext.dd.DropTarget(file_selection_grid.container, {
+ ddGroup : 'TreeDD',
+ copy:false,
+ notifyDrop : function(dd, e, data){
+ var r;
+ if (data.selections) {
+ if (data.grid.id == 'div-files') {
+ for(var i=0;i<data.selections.length;i++) {
+ r = new file_selection_record({
+ jobid: Ext.brestore.jobid,
+ fileid: data.selections[i].json[0],
+ filenameid:data.selections[i].json[1],
+ pathid: data.selections[i].json[2],
+ name: Ext.brestore.path + data.selections[i].json[3],
+ size: data.selections[i].json[4],
+ mtime: data.selections[i].json[5]
+ });
+ file_selection_store.add(r)
+ }
+ }
+
+ if (data.grid.id == 'div-file-versions') {
+ r = new file_selection_record({
+ jobid: data.selections[0].json[3],
+ fileid: data.selections[0].json[0],
+ filenameid:data.selections[0].json[1],
+ pathid: data.selections[0].json[2],
+ name: Ext.brestore.path + Ext.brestore.filename,
+ size: data.selections[0].json[7],
+ mtime: data.selections[0].json[8]
+ });
+ file_selection_store.add(r)
+ }
+ }
+
+ if (data.node) {
+ var path= get_node_path(data.node);
+ r = new file_selection_record({
+ jobid: Ext.brestore.jobid,
+ fileid: 0,
+ filenameid:0,
+ pathid: data.node.id,
+ name: path,
+ size: 4096,
+ mtime: 0
+ });
+ file_selection_store.add(r)
+ }
+
+ return true;
+ }});
+
+
+ file_selection_grid.on('enddrag', function(dd,e) {
+ alert(e) ; return true;
+ });
+ file_selection_grid.on('notifyDrop', function(dd,e) {
+ alert(e) ; return true;
+ });
+ file_selection_grid.render();
+
+///////////////////////////////////////////////////////
+
+ var file_versions_store = new Ext.data.Store({
+ proxy: new Ext.data.HttpProxy({
+ url: '/cgi-bin/bweb/bresto.pl',
+ method: 'GET',
+ params:{offset:0, limit:50 }
+ }),
+
+ reader: new Ext.data.ArrayReader({
+ }, Ext.data.Record.create([
+ {name: 'fileid' },
+ {name: 'filenameid'},
+ {name: 'pathid' },
+ {name: 'jobid' },
+ {name: 'volume' },
+ {name: 'inchanger' },
+ {name: 'md5' },
+ {name: 'size', type: 'int' },
+ {name: 'mtime'} //, type: 'date', dateFormat: 'Y-m-d h:i:s'}
+ ]))
+ });
+
+ var file_versions_cm = new Ext.grid.ColumnModel([{
+ id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 })
+ dataIndex: 'name',
+ hidden: true
+ },{
+ header: "InChanger",
+ dataIndex: 'inchanger',
+ width: 60,
+ renderer: rd_vol_is_online
+ },{
+ header: "Volume",
+ dataIndex: 'volume'
+ },{
+ header: "JobId",
+ width: 50,
+ dataIndex: 'jobid'
+ },{
+ header: "Size",
+ dataIndex: 'size',
+ renderer: human_size,
+ width: 50
+ },{
+ header: "Date",
+ dataIndex: 'mtime',
+ width: 100
+ },{
+ header: "MD5",
+ dataIndex: 'md5',
+ width: 160
+ },{
+ dataIndex: 'pathid',
+ hidden: true
+ },{
+ dataIndex: 'filenameid',
+ hidden: true
+ },{
+ dataIndex: 'fileid',
+ hidden: true
+ }
+ ]);
+
+ // by default columns are sortable
+ file_versions_cm.defaultSortable = true;
+
+ // create the grid
+ var file_versions_grid = new Ext.grid.Grid('div-file-versions', {
+ ds: file_versions_store,
+ cm: file_versions_cm,
+ ddGroup : 'TreeDD',
+ enableDrag: true,
+ enableDrop: false,
+ selModel: new Ext.grid.RowSelectionModel(),
+ loadMask: true,
+ enableColLock:false
+
+ });
+
+ file_versions_grid.on('rowdblclick', function(e) {
+ alert(e) ; file_versions_store.removeAll(); return true;
+ });
+ file_versions_grid.render();
+
+//////////////////////////////////////////////////////////////:
+
+
+ var client_store = new Ext.data.Store({
+ proxy: new Ext.data.HttpProxy({
+ url: '/cgi-bin/bweb/bresto.pl',
+ method: 'GET',
+ params:{action:'list_client'}
+ }),
+
+ reader: new Ext.data.ArrayReader({
+ }, Ext.data.Record.create([
+ {name: 'name' }
+ ]))
+ });
+
+ var client_combo = new Ext.form.ComboBox({
+ fieldLabel: 'Clients',
+ store: client_store,
+ displayField:'name',
+ typeAhead: true,
+ mode: 'local',
+ triggerAction: 'all',
+ emptyText:'Select a client...',
+ selectOnFocus:true,
+ forceSelection: true,
+ width:135
+ });
+
+ client_combo.on('valid', function(e) {
+ Ext.brestore.client = e.getValue();
+ job_store.load( {params:{action: 'list_job',
+ client:Ext.brestore.client}});
+ return true;
+ });
+
+//////////////////////////////////////////////////////////////:
+
+ var job_store = new Ext.data.Store({
+ proxy: new Ext.data.HttpProxy({
+ url: '/cgi-bin/bweb/bresto.pl',
+ method: 'GET',
+ params:{offset:0, limit:50 }
+ }),
+
+ reader: new Ext.data.ArrayReader({
+ }, Ext.data.Record.create([
+ {name: 'jobid' },
+ {name: 'date' },
+ {name: 'jobname' }
+ ]))
+ });
+
+ var job_combo = new Ext.form.ComboBox({
+ fieldLabel: 'Jobs',
+ store: job_store,
+ displayField:'jobname',
+ typeAhead: true,
+ mode: 'local',
+ triggerAction: 'all',
+ emptyText:'Select a job...',
+ selectOnFocus:true,
+ forceSelection: true,
+ width:350
+ });
+
+ job_combo.on('select', function(e,c) {
+ Ext.brestore.jobid = c.json[0];
+ Ext.brestore.jobdate = c.json[1];
+ Ext.brestore.root_path='';
+ root.setText("Root");
+ tree_loader.baseParams = init_params({init:1, action: 'list_dirs'});
+ root.reload();
+ });
+
+////////////////////////////////////////////////////////////////
+
+ function sel_option(item, check)
+ {
+ if (item.id == 'id_vosb') {
+ Ext.brestore.option_vosb = check;
+ }
+ if (item.id == 'id_vafv') {
+ Ext.brestore.option_vafv = check;
+ }
+ }
+
+ var menu = new Ext.menu.Menu({
+ id: 'div-main-menu',
+ items: [
+ new Ext.menu.CheckItem({
+ id: 'id_vosb',
+ text: 'View only selected backup',
+ checked: Ext.brestore.option_vosb,
+ checkHandler: sel_option
+ }),
+ new Ext.menu.CheckItem({
+ id: 'id_vafv',
+ text: 'View all file versions',
+ checked: Ext.brestore.option_vafv,
+ checkHandler: sel_option
+ })
+ ]
+ });
+////////////////////////////////////////////////////////////////:
+
+ // create the primary toolbar
+ var tb2 = new Ext.Toolbar('div-tb-sel');
+ tb2.add({
+ id:'save',
+ text:'Save',
+ disabled:true,
+// handler:save,
+ cls:'x-btn-text-icon save',
+ tooltip:'Saves all components to the server'
+ },'-', {
+ id:'add',
+ text:'Component',
+// handler:addComponent,
+ cls:'x-btn-text-icon add-cmp',
+ tooltip:'Add a new Component to the dependency builder'
+ }, {
+ id:'option',
+ text:'Option',
+ disabled:true,
+// handler:addOption,
+ cls:'x-btn-text-icon add-opt',
+ tooltip:'Add a new optional dependency to the selected component'
+ },'-',{
+ id:'remove',
+ text:'Remove',
+ disabled:true,
+// handler:removeNode,
+ cls:'x-btn-text-icon remove',
+ tooltip:'Remove the selected item'
+ });
+
+ var where_field = new Ext.form.TextField({
+ fieldLabel: 'Location',
+ name: 'where',
+ width:175,
+ allowBlank:false
+ });
+
+ var tb = new Ext.Toolbar('div-toolbar', [
+ client_combo,
+ job_combo,
+ '-',
+ {
+ id: 'tb_home',
+// icon: '/bweb/up.gif',
+ text: 'Change location',
+ cls:'x-btn-text-icon',
+ handler: function() {
+ var where = where_field.getValue();
+ Ext.brestore.root_path=where;
+ root.setText(where);
+ tree_loader.baseParams = init_params({ action:'list_dirs', path: where });
+ root.reload();
+ }
+ },
+ where_field,
+ '-',
+ {
+ cls: 'x-btn-text-icon bmenu', // icon and text class
+ text:'Options',
+ menu: menu // assign menu by instance
+ },
+ {
+ icon: '/bweb/remove.png', // icons can also be specified inline
+ cls: 'x-btn-icon',
+ text: 'restore',
+ handler: function() {
+ var dialog = new Ext.LayoutDialog("div-resto-dlg", {
+// modal:true,
+ width:600,
+ height:400,
+ shadow:true,
+ minWidth:300,
+ minHeight:300,
+ proxyDrag: true,
+// west: {
+// split:true,
+// initialSize: 150,
+// minSize: 100,
+// maxSize: 250,
+// titlebar: true,
+// collapsible: true,
+// animate: true
+// },
+ center: {
+ autoScroll:true,
+// tabPosition: 'top',
+// closeOnTab: true,
+// alwaysShowTabs: true
+ }
+ });
+ dialog.addKeyListener(27, dialog.hide, dialog);
+ dialog.addButton('Submit', dialog.hide, dialog);
+ dialog.addButton('Close', dialog.hide, dialog);
+
+ var fs = new Ext.form.Form({
+ labelAlign: 'right',
+ labelWidth: 80
+ });
+
+ fs.fieldset(
+ {legend:'Restore job'},
+ new Ext.form.ComboBox({
+ fieldLabel: 'Replace',
+ hiddenName:'replace',
+ store: new Ext.data.SimpleStore({
+ fields: ['replace'],
+ data : [['always'],['never'],['if newer']]
+ }),
+ displayField:'replace',
+ typeAhead: true,
+ mode: 'local',
+ triggerAction: 'all',
+ emptyText:'never',
+ selectOnFocus:true,
+ width:190
+ }),
+
+ new Ext.form.ComboBox({
+ fieldLabel: 'job',
+ hiddenName:'job',
+ store: client_store,
+ displayField:'name',
+ typeAhead: true,
+ mode: 'local',
+ triggerAction: 'all',
+ emptyText:'Select a job...',
+ selectOnFocus:true,
+ width:190
+ })
+// ,
+// new Ext.form.TextField({
+// fieldLabel: 'Where',
+// name: 'where',
+// width:190
+// }),
+//
+// new Ext.form.ComboBox({
+// fieldLabel: 'client',
+// hiddenName:'client',
+// store: client_store,
+// displayField:'name',
+// typeAhead: true,
+// mode: 'local',
+// triggerAction: 'all',
+// emptyText:'Select a client...',
+// selectOnFocus:true,
+// width:190
+// }),
+// new Ext.form.ComboBox({
+// fieldLabel: 'storage',
+// hiddenName:'storage',
+// store: client_store,
+// displayField:'name',
+// typeAhead: true,
+// mode: 'local',
+// triggerAction: 'all',
+// emptyText:'Select a storage...',
+// selectOnFocus:true,
+// width:190
+// })
+ );
+
+ fs.render('div-resto-form');
+
+// var f = new Ext.form.BasicForm('div-resto-form', {url: '/bweb/test', method: 'GET',
+// baseParams: {init: 1}
+// }
+// );
+
+ var layout = dialog.getLayout();
+ layout.beginUpdate();
+ layout.add('center', new Ext.ContentPanel('div-resto-form', {
+ autoCreate:true, title: 'Third Tab', closable:true, background:true}));
+ layout.endUpdate();
+ dialog.show();
+ }
+ }
+ ]);
+
+////////////////////////////////////////////////////////////////
+
+ var layout = new Ext.BorderLayout(document.body, {
+ north: {
+// split: true
+ },
+ south: {
+ split: true, initialSize: 300
+ },
+ east: {
+ split: true, initialSize: 550
+ },
+ west: {
+ split: true, initialSize: 300
+ },
+ center: {
+ initialSize: 450
+ }
+
+ });
+
+layout.beginUpdate();
+ layout.add('north', new Ext.ContentPanel('div-toolbar', {
+ fitToFrame: true, autoCreate:true,closable: false
+ }));
+ layout.add('south', new Ext.ContentPanel('div-file-selection', {
+ toolbar: tb2,resizeEl:'div-file-selection',
+ fitToFrame: true, autoCreate:true,closable: false
+ }));
+ layout.add('east', new Ext.ContentPanel('div-file-versions', {
+ fitToFrame: true, autoCreate:true,closable: false
+ }));
+ layout.add('west', new Ext.ContentPanel('div-tree', {
+ autoScroll:true, fitToFrame: true,
+ autoCreate:true,closable: false
+ }));
+ layout.add('center', new Ext.ContentPanel('div-files', {
+ autoScroll:true,autoCreate:true,fitToFrame: true
+ }));
+layout.endUpdate();
+
+
+////////////////////////////////////////////////////////////////
+
+// job_store.load();
+ client_store.load({params:{action: 'list_client'}});
+// file_store.load({params:{offset:0, limit:50}});
+// file_versions_store.load({params:{offset:0, limit:50}});
+// file_selection_store.load();
+
+}
+Ext.onReady( ext_init );
</li>
</TMPL_IF>
<li><a href="bweb.pl?action=graph"> EstadÃsticas </a></li>
- <li> <a href="bweb.pl?action=view_conf"> Configuración </a> </li>
+ <li> <a href="bweb.pl?action=view_conf"> Configuración </a>
+<TMPL_IF enable_security>
+ <ul> <li> <a href="bweb.pl?action=view_conf"> Configuration </a>
+ <li> <a href="bweb.pl?action=users"> Manage users </a>
+ </ul>
+</TMPL_IF>
+</li>
<li> <a href="bweb.pl?action=about"> Acerca </a> </li>
<li style="padding: 0.25em 2em;float: right;"> Usuario <TMPL_VAR NAME=loginname> </li>
<li style="float: right;white-space: nowrap;">
Puede mover este medio a <TMPL_VAR newlocation>
Medio :
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
- <TMPL_VAR VolumeName> (<TMPL_VAR location>)
</TMPL_LOOP>
<tr><td>display_log_time :</td>
<td> <input class="formulaire" title="display log timestamp" type='checkbox' name='display_log_time' <TMPL_IF display_log_time> checked='checked' value='on' </TMPL_IF> >
</td></tr>
+ <tr><td>security :</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security' title='Use user managment in bweb. Read INSTALL first' <TMPL_IF enable_security> checked='checked' value='on' </TMPL_IF> >
+ <tr><td>security acl:</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security_acl' title='Use user acl in bweb. Read INSTALL first' <TMPL_IF enable_security_acl> checked='checked' value='on' </TMPL_IF> >
<tr><td>debug :</td>
<td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
</td></tr>
<tr><td title="You can choose the Job table that you want to use to get statistics">stat_job_table :</td> <td> <TMPL_IF stat_job_table><TMPL_VAR stat_job_table><TMPL_ELSE>Job</TMPL_IF> </td></tr>
<tr><td title="/path/a/bconsole -n -c /path/to/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
<tr><td title="display timestamp in job log">display_log_time :</td> <td> <TMPL_VAR display_log_time> </td></tr>
+ <tr><td>security :</td> <td> <TMPL_VAR enable_security> </td></tr>
+ <tr><td title="user filter">security acl :</td> <td> <TMPL_VAR enable_security_acl> </td></tr>
<tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
<TMPL_IF achs>
<tr> <td><b>Libreria</b></td> <td/></tr>
-<br/>
- <div class='titlediv'>
- <h1 class='newstitle'> ültimos jobs de <TMPL_VAR clientname> (<TMPL_VAR Filter>)
- </h1>
- </div>
- <div class='bodydiv'>
-
- <table id='id<TMPL_VAR ID>'></table>
-
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_size;status=T">
- <img src="/bweb/chart.png" alt="backup size" title="backup size evolution"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_duration;status=T">
- <img src="/bweb/chart.png" alt="backup duration" title="backup time evolution"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_rate;status=T">
- <img src="/bweb/chart.png" alt="backup rate" title="backup rate evolution"/>
- </a>
- </div>
-
-
-<script type="text/javascript" language="JavaScript">
-var header = new Array("IdJob", "Nombre Job", "File Set", "Nivel", "Tiempo Inicio",
- "Archivos Job", "Bytes Job", "Errors");
-
-var data = new Array();
-
-<TMPL_LOOP Jobs>
-data.push( new Array(
-"<TMPL_VAR JobId>",
-"<TMPL_VAR JobName>",
-"<TMPL_VAR FileSet>",
-"<TMPL_VAR Level>",
-"<TMPL_VAR StartTime>",
-"<TMPL_VAR JobFiles>",
-human_size(<TMPL_VAR JobBytes>),
-"<TMPL_VAR JobErrors>"
- )
-);
-</TMPL_LOOP>
-
-nrsTable.setup(
-{
- table_name: "id<TMPL_VAR ID>",
- table_header: header,
- table_data: data,
- up_icon: up_icon,
- down_icon: down_icon,
- prev_icon: prev_icon,
- next_icon: next_icon,
- rew_icon: rew_icon,
- fwd_icon: fwd_icon,
-// natural_compare: true,
- even_cell_color: even_cell_color,
- odd_cell_color: odd_cell_color,
- header_color: header_color,
- page_nav: true,
- rows_per_page: rows_per_page,
- disable_sorting: new Array(5,6)
-}
-);
-
-// get newest job first
-nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
-</script>
<TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
class='formulaire' size='8'>
</td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Expired media</h2>
+ <input type='checkbox' name='expired' <TMPL_IF expired> checked </TMPL_IF>
+ class='formulaire'>
+ </td>
</tr>
<tr>
<td valign='bottom'>
</label>
</form>
</td>
+<TMPL_IF joberrors>
+ <td>
+ <a href="<TMPL_VAR thisurl>;error=1"
+ title="View only errors">
+ <img src='/bweb/doc.png' alt="view errors"></a> View only errors
+ </td>
+</TMPL_IF>
</table>
</div>
<pre id='log'>
<TMPL_VAR lines>
</pre>
+
+<a id='prev'><img border='0' src='/bweb/prev.png'></a>
+<a id='next'><img border='0' src='/bweb/next.png'></a>
</div>
+<script type="text/javascript" language='JavaScript'>
+
+var url='<TMPL_VAR thisurl>';
+var urlprev=url;
+var urlnext=url;
+
+var reoff = new RegExp('offset=[0-9]+', "");
+var relim = new RegExp('limit=[0-9]+', "");
+
+var offset=0;
+var limit=1000;
+var nbline=0;
+<TMPL_IF offset>
+offset = <TMPL_VAR offset>;
+</TMPL_IF>
+<TMPL_IF limit>
+limit = <TMPL_VAR limit>;
+</TMPL_IF>
+<TMPL_IF nbline>
+nbline=<TMPL_VAR nbline>;
+</TMPL_IF>
+
+if (nbline == limit) {
+ var offset_next = offset + limit;
+
+ if (url.match(reoff)) {
+ urlnext = urlnext.replace(reoff, 'offset=' + offset_next);
+ } else {
+ urlnext = urlnext + ';offset=' + offset_next;
+ }
+
+ if (url.match(relim)) {
+ urlnext = urlnext.replace(relim, 'limit=' + limit);
+ } else {
+ urlnext = urlnext + ';limit=' + limit;
+ }
+
+ document.getElementById('next').href = urlnext;
+
+} else {
+ document.getElementById('next').style.visibility="hidden";
+}
+
+if (offset > 0) {
+ var offset_prev = offset - limit;
+ if (offset_prev < 0) {
+ offset_prev=0;
+ }
+ if (url.match(reoff)) {
+ urlprev = urlprev.replace(reoff, 'offset=' + offset_prev);
+ } else {
+ urlprev = urlprev + ';offset=' + offset_prev ;
+ }
+ if (url.match(relim)) {
+ urlprev = urlprev.replace(relim, 'limit=' + limit);
+ } else {
+ urlprev = urlprev + ';limit=' + limit;
+ }
+
+ if (offset_prev >= 0) {
+ document.getElementById('prev').href = urlprev;
+ } else {
+ document.getElementById('prev').style.visibility="hidden";
+ }
+} else {
+ document.getElementById('prev').style.visibility="hidden";
+}
+</script>
var chkbox;
var d;
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
d = percent_usage(<TMPL_VAR volusage>);
img = document.createElement('IMG');
</table>
<script type="text/javascript" language="JavaScript">
-var header = new Array("Pool","Online","Location","Vol Status", "Vol Bytes", "Expire",
+var header = new Array("Pool","Online","Enabled","Location","Vol Status", "Vol Bytes", "Expire",
"Retention","Max use duration", "Max jobs" );
var data = new Array();
data.push( new Array(
"<TMPL_VAR poolname>",
img,
+human_enabled("<TMPL_VAR enabled>"),
"<TMPL_VAR location>",
"<TMPL_VAR volstatus>",
human_size(<TMPL_VAR nb_bytes>),
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR NAME=volumename>';
<td style='align: left;'>
<input type="image" onclick='javascript:window.history.go(-2);' title='Volver' src='/bweb/prev.png'>
</td><td style='align: right;'>
+ <input type="hidden" name='enabled' value="yes">
<input type="image" name='action' value='move_media' title='Cargar selección' src='/bweb/intern.png'>
</td></tr>
</form>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.name = 'media';
<h1 class='newstitle'>Mover Medio</h1>
</div>
<div class="bodydiv">
- <form action='?' method='get'>
+ <form name='form1' action='?' method='get'>
<table id='id<TMPL_VAR NAME=ID>'></table>
<table border='0'>
<tr><td> Nueva Ubicación: </td><td>
<option value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
</TMPL_LOOP>
</select>
- </td></tr><tr><td> Estado: </td><td>
-<select name='volstatus' class='formulaire'>
- <option value=''>No Actualizar</option>
- <option value='Append'>Listo</option>
- <option value='Archive'>Archivado</option>
- <option value='Disabled'>Desactivado</option>
- <option value='Cleaning'>Limpieza</option>
- <option value='Error'>Error</option>
- <option value='Full'>Lleno</option>
- <option value='Purged'>Purgado</option>
- <option value='Read-Only'>ectura</option>
- <option value='Recycle'>Reciclado</option>
- <option value='Used'>Usado</option>
+ </td></tr><tr><td> Enabled: </td><td>
+<select name='enabled' class='formulaire'>
+ <option value='no'>no</option>
+ <option value='yes'>yes</option>
+ <option value='archived'>archived</option>
</select>
</td><tr><td> Usuario: </td><td>
<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
rows_per_page: rows_per_page
}
);
+<TMPL_IF enabled>
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
+</TMPL_IF>
</script>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
</td>
</tr>
+ <tr><td>Enabled:</td>
+ <td> <select name='enabled' class='formulaire'>
+ <option value='yes'>yes</option>
+ <option value='no'>no</option>
+ <option value='archived'>archived</option>
+ </select>
+ </td>
+ </tr>
+
<tr><td> Ubicación : </td>
<td><select name='location' class='formulaire'>
<option value=''></option>
ok=0;
}
}
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
</script>
</li>
</TMPL_IF>
<li><a href="bweb.pl?action=graph"> Statistiques </a></li>
- <li> <a href="bweb.pl?action=view_conf"> Configuration </a> </li>
+ <li> <a href="bweb.pl?action=view_conf"> Configuration </a>
+<TMPL_IF enable_security>
+ <ul> <li> <a href="bweb.pl?action=view_conf"> Configuration </a>
+ <li> <a href="bweb.pl?action=users"> Manage users </a>
+ </ul>
+</TMPL_IF>
+ </li>
<li> <a href="bweb.pl?action=about"> A propos </a> </li>
<li style="padding: 0.25em 2em;float: right;"> Logged as <TMPL_VAR NAME=loginname> </li>
<li style="float: right;white-space: nowrap;">
Pouvez vous déplacer ces médias vers <TMPL_VAR newlocation> ?
Média :
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
- <TMPL_VAR VolumeName> (<TMPL_VAR location>)
</TMPL_LOOP>
<tr><td>display_log_time :</td>
<td> <input class="formulaire" title="affiche les heures dans les logs" type='checkbox' name='display_log_time' <TMPL_IF display_log_time> checked='checked' value='on' </TMPL_IF> >
</td></tr>
+ <tr><td>security :</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security' title='Active la gestion des utilisateurs dans bweb. Lire le manuel avant.' <TMPL_IF enable_security> checked='checked' value='on' </TMPL_IF> >
+ <tr><td>security acl:</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security_acl' title='Use user acl in bweb. Read INSTALL first' <TMPL_IF enable_security_acl> checked='checked' value='on' </TMPL_IF> >
<tr><td>debug :</td>
<td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
</td></tr>
<tr><td title="Vous pouvez utiliser une autre table que Job pour vos statistiques">stat_job_table :</td> <td> <TMPL_IF stat_job_table><TMPL_VAR stat_job_table><TMPL_ELSE>Job</TMPL_IF> </td></tr>
<tr><td title="/chemin/vers/bconsole -n -c /chemin/vers/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
<tr><td title="affiche les heures dans le log des jobs">display_log_time :</td> <td> <TMPL_VAR display_log_time> </td></tr>
+ <tr><td>security :</td> <td> <TMPL_VAR enable_security> </td></tr>
+ <tr><td title="user filter">security acl :</td> <td> <TMPL_VAR enable_security_acl> </td></tr>
<tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
<TMPL_IF achs>
<tr> <td><b>Robotique (Autochanger)</b></td> <td/></tr>
-<br/>
- <div class='titlediv'>
- <h1 class='newstitle'> Historique de <TMPL_VAR clientname> (<TMPL_VAR Filter>)
- </h1>
- </div>
- <div class='bodydiv'>
-
- <table id='id<TMPL_VAR ID>'></table>
-
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_size;status=T">
- <img src="/bweb/chart.png" alt="taille des sauvegardes" title="évolution de la taille des sauvegardes"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_duration;status=T">
- <img src="/bweb/chart.png" alt="durée des sauvegardes" title="évolution de la durée des sauvegardes"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_rate;status=T">
- <img src="/bweb/chart.png" alt="vitesse des sauvegardes" title="évolution de la vitesse des sauvegardes"/>
- </a>
- </div>
-
-
-<script type="text/javascript" language="JavaScript">
-var header = new Array("JobId", "Nom du Job", "File Set", "Niveau", "Début",
- "Nb fichiers", "Taille", "Erreurs");
-
-var data = new Array();
-
-<TMPL_LOOP Jobs>
-data.push( new Array(
-"<TMPL_VAR JobId>",
-"<TMPL_VAR JobName>",
-"<TMPL_VAR FileSet>",
-"<TMPL_VAR Level>",
-"<TMPL_VAR StartTime>",
-"<TMPL_VAR JobFiles>",
-human_size(<TMPL_VAR JobBytes>),
-"<TMPL_VAR JobErrors>"
- )
-);
-</TMPL_LOOP>
-
-nrsTable.setup(
-{
- table_name: "id<TMPL_VAR ID>",
- table_header: header,
- table_data: data,
- up_icon: up_icon,
- down_icon: down_icon,
- prev_icon: prev_icon,
- next_icon: next_icon,
- rew_icon: rew_icon,
- fwd_icon: fwd_icon,
-// natural_compare: true,
- even_cell_color: even_cell_color,
- odd_cell_color: odd_cell_color,
- header_color: header_color,
- page_nav: true,
- rows_per_page: rows_per_page,
- disable_sorting: new Array(5,6)
-}
-);
-
-// get newest job first
-nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
-</script>
<TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
class='formulaire' size='8'>
</td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Médias expirés</h2>
+ <input type='checkbox' name='expired' <TMPL_IF expired> checked </TMPL_IF>
+ class='formulaire'>
+ </td>
</tr>
<tr>
<td valign='bottom'>
</label>
</form>
</td>
+<TMPL_IF joberrors>
+ <td>
+ <a href="<TMPL_VAR thisurl>;error=1"
+ title="Voir seulement les erreurs">
+ <img src='/bweb/doc.png' alt="Voir les erreurs"></a> Voir les erreurs
+ </td>
+</TMPL_IF>
</table>
</div>
<pre id='log'>
<TMPL_VAR lines>
</pre>
+
+<a id='prev'><img border='0' src='/bweb/prev.png'></a>
+<a id='next'><img border='0' src='/bweb/next.png'></a>
</div>
+<script type="text/javascript" language='JavaScript'>
+
+var url='<TMPL_VAR thisurl>';
+var urlprev=url;
+var urlnext=url;
+
+var reoff = new RegExp('offset=[0-9]+', "");
+var relim = new RegExp('limit=[0-9]+', "");
+
+var offset=0;
+var limit=1000;
+var nbline=0;
+<TMPL_IF offset>
+offset = <TMPL_VAR offset>;
+</TMPL_IF>
+<TMPL_IF limit>
+limit = <TMPL_VAR limit>;
+</TMPL_IF>
+<TMPL_IF nbline>
+nbline=<TMPL_VAR nbline>;
+</TMPL_IF>
+
+if (nbline == limit) {
+ var offset_next = offset + limit;
+
+ if (url.match(reoff)) {
+ urlnext = urlnext.replace(reoff, 'offset=' + offset_next);
+ } else {
+ urlnext = urlnext + ';offset=' + offset_next;
+ }
+
+ if (url.match(relim)) {
+ urlnext = urlnext.replace(relim, 'limit=' + limit);
+ } else {
+ urlnext = urlnext + ';limit=' + limit;
+ }
+
+ document.getElementById('next').href = urlnext;
+
+} else {
+ document.getElementById('next').style.visibility="hidden";
+}
+
+if (offset > 0) {
+ var offset_prev = offset - limit;
+ if (offset_prev < 0) {
+ offset_prev=0;
+ }
+ if (url.match(reoff)) {
+ urlprev = urlprev.replace(reoff, 'offset=' + offset_prev);
+ } else {
+ urlprev = urlprev + ';offset=' + offset_prev ;
+ }
+ if (url.match(relim)) {
+ urlprev = urlprev.replace(relim, 'limit=' + limit);
+ } else {
+ urlprev = urlprev + ';limit=' + limit;
+ }
+
+ if (offset_prev >= 0) {
+ document.getElementById('prev').href = urlprev;
+ } else {
+ document.getElementById('prev').style.visibility="hidden";
+ }
+} else {
+ document.getElementById('prev').style.visibility="hidden";
+}
+</script>
var chkbox;
var d;
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
d = percent_usage(<TMPL_VAR volusage>);
img = document.createElement('IMG');
</table>
<script type="text/javascript" language="JavaScript">
-var header = new Array("Pool","En ligne","Localisation","Statut", "Taille", "Expiration",
+var header = new Array("Pool","En ligne","Enabled","Localisation","Statut", "Taille", "Expiration",
"Rétention","Temps maxi d'utilisation", "Nb de job maxi" );
var data = new Array();
data.push( new Array(
"<TMPL_VAR poolname>",
img,
+human_enabled("<TMPL_VAR enabled>"),
"<TMPL_VAR location>",
"<TMPL_VAR volstatus>",
human_size(<TMPL_VAR nb_bytes>),
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR NAME=volumename>';
<td style='align: left;'>
<input type="image" onclick='javascript:window.history.go(-2);' title='Précédent' src='/bweb/prev.png'>
</td><td style='align: right;'>
+ <input type="hidden" name='enabled' value="yes">
<input type="image" name='action' value='move_media'
src='/bweb/intern.png'>
</td></tr>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.name = 'media';
<h1 class='newstitle'>Déplacer un média</h1>
</div>
<div class="bodydiv">
- <form action='?' method='get'>
+ <form name='form1' action='?' method='get'>
<table id='id<TMPL_VAR NAME=ID>'></table>
<table border='0'>
<tr><td> Nouvelle localisation : </td><td>
<option value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
</TMPL_LOOP>
</select>
- </td></tr><tr><td> Statut : </td><td>
-<select name='volstatus' class='formulaire'>
- <option value=''>Ne pas modifier</option>
- <option value='Append'>Append</option>
- <option value='Archive'>Archive</option>
- <option value='Disabled'>Disabled</option>
- <option value='Cleaning'>Cleaning</option>
- <option value='Error'>Error</option>
- <option value='Full'>Full</option>
- <option value='Purged'>Purged</option>
- <option value='Read-Only'>Read-Only</option>
- <option value='Recycle'>Recycle</option>
- <option value='Used'>Used</option>
+ </td></tr><tr><td> Enabled: </td><td>
+<select name='enabled' class='formulaire'>
+ <option value='no'>non</option>
+ <option value='yes'>oui</option>
+ <option value='archived'>archive</option>
</select>
</td><tr><td> Utilisateur : </td><td>
<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
rows_per_page: rows_per_page
}
);
+
+<TMPL_IF enabled>
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
+</TMPL_IF>
</script>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
</td>
</tr>
+ <tr><td>Enabled :</td>
+ <td> <select name='enabled' class='formulaire'>
+ <option value='yes'>oui</option>
+ <option value='no'>non</option>
+ <option value='archived'>archive</option>
+ </select>
+ </td>
+ </tr>
+
<tr><td> Localisation : </td>
<td><select name='location' class='formulaire'>
<option value=''></option>
ok=0;
}
}
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
</script>
sub purge_volume
{
- my ($self, @volume) = @_;
+ my ($self, $volume) = @_;
- my $sel = $self->_get_volume(@volume);
+ my $sel = $self->_get_volume($volume);
my $ret;
if ($sel) {
$ret = $self->send_cmd("purge $sel");
sub prune_volume
{
- my ($self, @volume) = @_;
+ my ($self, $volume) = @_;
- my $sel = $self->_get_volume(@volume);
+ my $sel = $self->_get_volume($volume);
my $ret;
if ($sel) {
$ret = $self->send_cmd("prune $sel yes");
Bweb - A Bacula web interface
Bacula® - The Network Backup Solution
- Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+ Copyright (C) 2000-2007 Free Software Foundation Europe e.V.
The main author of Bweb is Eric Bollengier.
The main author of Bacula is Kern Sibbald, with contributions from
log_dir => qr!^(.+)?$!,
stat_job_table => qr!^(\w*)$!,
display_log_time => qr!^(on)?$!,
+ enable_security => qr/^(on)?$/,
+ enable_security_acl => qr/^(on)?$/,
);
=head1 FUNCTION
my ($self) = @_;
$self->{error} = '';
+ # we need to reset checkbox first
$self->{debug} = 0;
+ $self->{display_log_time} = 0;
+ $self->{enable_security} = 0;
+ $self->{enable_security_acl} = 0;
foreach my $k (CGI::param())
{
STARTTIME_PHOUR=> " date_part('hour', Job.StartTime) ",
STARTTIME_PDAY => " date_part('day', Job.StartTime) ",
STARTTIME_PMONTH => " date_part('month', Job.StartTime) ",
+ STARTTIME_PWEEK => " date_part('week', Job.StartTime) ",
DB_SIZE => " SELECT pg_database_size(current_database()) ",
CAT_POOL_TYPE => " MediaType || '_' || Pool.Name ",
+ CONCAT_SEP => "",
},
mysql => {
UNIX_TIMESTAMP => 'UNIX_TIMESTAMP',
STARTTIME_PHOUR=> " DATE_FORMAT(StartTime, '%H') ",
STARTTIME_PDAY => " DATE_FORMAT(StartTime, '%d') ",
STARTTIME_PMONTH => " DATE_FORMAT(StartTime, '%m') ",
+ STARTTIME_PWEEK => " DATE_FORMAT(StartTime, '%v') ",
# with mysql < 5, you have to play with the ugly SHOW command
DB_SIZE => " SELECT 0 ",
# works only with mysql 5
# DB_SIZE => " SELECT sum(DATA_LENGTH) FROM INFORMATION_SCHEMA.TABLES ",
CAT_POOL_TYPE => " CONCAT(MediaType,'_',Pool.Name) ",
+ CONCAT_SEP => " SEPARATOR '' ",
},
);
return $self->{dbh}->selectrow_hashref($query) ;
}
+sub dbh_strcat
+{
+ my ($self, @what) = @_;
+ if ($self->{conf}->{connection_string} =~ /dbi:mysql/i) {
+ return 'CONCAT(' . join(',', @what) . ')' ;
+ } else {
+ return join(' || ', @what);
+ }
+}
+
+sub dbh_prepare
+{
+ my ($self, $query) = @_;
+ $self->debug($query, up => 1);
+ return $self->{dbh}->prepare($query);
+}
+
# display Mb/Gb/Kb
sub human_size
{
return "$val years";
}
+# display Enabled
+sub human_enabled
+{
+ my $val = shift || 0;
+
+ if ($val == 1 or $val eq "yes") {
+ return "yes";
+ } elsif ($val == 2 or $val eq "archived") {
+ return "archived";
+ } else {
+ return "no";
+ }
+}
+
# get Day, Hour, Year
sub from_human_sec
{
sub new
{
my ($class, %arg) = @_;
- my $self = bless {
+ my $self = bless ({
dbh => undef, # connect_db();
info => {
dbi => '', # DBI:Pg:database=bacula;host=127.0.0.1
user => 'bacula',
password => 'test',
},
- } ;
+ },$class) ;
map { $self->{lc($_)} = $arg{$_} } keys %arg ;
$self->{sql} = $sql_func{$1};
}
+ $self->{loginname} = CGI::remote_user();
$self->{debug} = $self->{info}->{debug};
$Bweb::Gui::template_dir = $self->{info}->{template_dir};
sub display_clients
{
my ($self) = @_;
+ my $where=''; # by default
- my $where='';
- my $arg = $self->get_form("client", "qre_client", "jclient_groups", "qnotingroup");
+ my $arg = $self->get_form("client", "qre_client",
+ "jclient_groups", "qnotingroup");
if ($arg->{qre_client}) {
$where = "WHERE Name $self->{sql}->{MATCH} $arg->{qre_client} ";
} elsif ($arg->{client}) {
$where = "WHERE Name = '$arg->{client}' ";
} elsif ($arg->{jclient_groups}) {
- $where = "JOIN client_group_member ON (Client.ClientId = client_group_member.clientid)
- JOIN client_group USING (client_group_id)
- WHERE client_group_name IN ($arg->{jclient_groups})";
+ # $filter could already contains client_group_member
+ $where = "
+ JOIN client_group_member USING (ClientId)
+ JOIN client_group USING (client_group_id)
+ WHERE client_group_name IN ($arg->{jclient_groups}) ";
} elsif ($arg->{qnotingroup}) {
$where = "
WHERE NOT EXISTS
WHERE Client.ClientId = client_group_member.ClientId
)
";
-
}
my $query = "
AutoPrune AS autoprune,
FileRetention AS fileretention,
JobRetention AS jobretention
-FROM Client
-$where
-";
+FROM Client " . $self->get_client_filter() .
+$where ;
my $all = $self->dbh_selectall_hashref($query, 'name') ;
type => 1,
poolrecycle => 1,
replace => 1,
- );
+ expired => 1,
+ enabled => 1,
+ username => 1,
+ rolename => 1,
+ );
my %opt_p = ( # option with path
fileset=> 1,
mtxcmd => 1,
}
if ($what{db_clients}) {
+ my $filter='';
+ if ($what{filter}) {
+ # get security filter only if asked
+ $filter = $self->get_client_filter();
+ }
+
my $query = "
SELECT Client.Name as clientname
- FROM Client
+ FROM Client $filter
";
my $clients = $self->dbh_selectall_hashref($query, 'clientname');
}
if ($what{db_client_groups}) {
+ my $filter='';
+ if ($what{filter}) {
+ # get security filter only if asked
+ $filter = $self->get_client_group_filter();
+ }
+
my $query = "
SELECT client_group_name AS name
- FROM client_group
+ FROM client_group $filter
";
my $grps = $self->dbh_selectall_hashref($query, 'name');
values %$grps] ;
}
+ if ($what{db_usernames}) {
+ my $query = "
+SELECT username
+ FROM bweb_user
+";
+
+ my $users = $self->dbh_selectall_hashref($query, 'username');
+ $ret{db_usernames} = [sort {$a->{username} cmp $b->{username} }
+ values %$users] ;
+ }
+
+ if ($what{db_roles}) {
+ my $query = "
+SELECT rolename
+ FROM bweb_role
+";
+
+ my $r = $self->dbh_selectall_hashref($query, 'rolename');
+ $ret{db_roles} = [sort {$a->{rolename} cmp $b->{rolename} }
+ values %$r] ;
+ }
+
if ($what{db_mediatypes}) {
my $query = "
SELECT MediaType as mediatype
FROM MediaType
";
- my $medias = $self->dbh_selectall_hashref($query, 'mediatype');
+ my $media = $self->dbh_selectall_hashref($query, 'mediatype');
$ret{db_mediatypes} = [sort {$a->{mediatype} cmp $b->{mediatype} }
- values %$medias] ;
+ values %$media] ;
}
if ($what{db_locations}) {
}
if ($what{db_jobnames}) {
+ my $filter='';
+ if ($what{filter}) {
+ $filter = " JOIN Client USING (ClientId) " . $self->get_client_filter();
+ }
my $query = "
SELECT DISTINCT Job.Name AS jobname
- FROM Job
+ FROM Job $filter
";
my $jobnames = $self->dbh_selectall_hashref($query, 'jobname');
my ($self) = @_;
my $fields = $self->get_form(qw/age level status clients filesets
- graph gtype type
- db_clients limit db_filesets width height
+ graph gtype type filter db_clients
+ limit db_filesets width height
qclients qfilesets qjobnames db_jobnames/);
}
-sub display_client_job
-{
- my ($self, %arg) = @_ ;
-
- $arg{order} = ' Job.JobId DESC ';
- my ($limit, $label) = $self->get_limit(%arg);
-
- my $clientname = $self->dbh_quote($arg{clientname});
-
- my $query="
-SELECT DISTINCT Job.JobId AS jobid,
- Job.Name AS jobname,
- FileSet.FileSet AS fileset,
- Level AS level,
- StartTime AS starttime,
- JobFiles AS jobfiles,
- JobBytes AS jobbytes,
- JobStatus AS jobstatus,
- JobErrors AS joberrors
-
- FROM Client,Job,FileSet
- WHERE Client.Name=$clientname
- AND Client.ClientId=Job.ClientId
- AND Job.FileSetId=FileSet.FileSetId
- $limit
-";
-
- my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
-
- $self->display({ clientname => $arg{clientname},
- Filter => $label,
- ID => $cur_id++,
- Jobs => [ values %$all ],
- },
- "display_client_job.tpl") ;
-}
-
sub get_selected_media_location
{
my ($self) = @_ ;
- my $medias = $self->get_form('jmedias');
+ my $media = $self->get_form('jmedias');
- unless ($medias->{jmedias}) {
+ unless ($media->{jmedias}) {
return undef;
}
my $query = "
SELECT Media.VolumeName AS volumename, Location.Location AS location
FROM Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
-WHERE Media.VolumeName IN ($medias->{jmedias})
+WHERE Media.VolumeName IN ($media->{jmedias})
";
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
sub move_media
{
- my ($self) = @_ ;
+ my ($self, $in) = @_ ;
- my $medias = $self->get_selected_media_location();
+ my $media = $self->get_selected_media_location();
- unless ($medias) {
+ unless ($media) {
return ;
}
-
+
my $elt = $self->get_form('db_locations');
$self->display({ ID => $cur_id++,
+ enabled => human_enabled($in),
%$elt, # db_locations
- medias => [
- sort { $a->{volumename} cmp $b->{volumename} } values %$medias
+ media => [
+ sort { $a->{volumename} cmp $b->{volumename} } values %$media
],
},
"move_media.tpl");
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
- $self->display({ Medias => [ values %$all ] },
+ $self->display({ Media => [ values %$all ] },
"help_extern_compute.tpl");
}
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
- $self->display({ Medias => [ values %$all ] },
+ $self->display({ Media => [ values %$all ] },
"help_intern_compute.tpl");
}
}
if ($elt{mediatypes}) {
- my @medias = grep { ! /^\s*$/ } CGI::param('mediatype');
- if (@medias) {
- $ret{mediatypes} = \@medias;
- my $str = $self->dbh_join(@medias);
+ my @media = grep { ! /^\s*$/ } CGI::param('mediatype');
+ if (@media) {
+ $ret{mediatypes} = \@media;
+ my $str = $self->dbh_join(@media);
$limit .= "AND Media.MediaType IN ($str) ";
}
}
sub display_job
{
my ($self, %arg) = @_ ;
+ $self->can_do('r_view_job');
$arg{order} = ' Job.JobId DESC ';
'pools',
'jobid',
'status');
-
- my $cgq = '';
+ my $cgq='';
if (CGI::param('client_group')) {
- $cgq = "
-LEFT JOIN client_group_member ON (Job.ClientId = client_group_member.ClientId)
-LEFT JOIN client_group USING (client_group_id)
+ $cgq .= "
+JOIN client_group_member USING (ClientId)
+JOIN client_group USING (client_group_id)
";
}
+ my $filter = $self->get_client_filter();
my $query="
SELECT Job.JobId AS jobid,
JobErrors AS joberrors
- FROM Client,
+ FROM Client $filter $cgq,
Job LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
- $cgq
WHERE Client.ClientId=Job.ClientId
AND Job.JobStatus NOT IN ('R', 'C')
$where
sub display_job_zoom
{
my ($self, $jobid) = @_ ;
+ $self->can_do('r_view_job');
$jobid = $self->dbh_quote($jobid);
-
+
+ # get security filter
+ my $filter = $self->get_client_filter();
+
my $query="
SELECT DISTINCT Job.JobId AS jobid,
Client.Name AS client,
$self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
- $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) AS duration
- FROM Client,
+ FROM Client $filter,
Job LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
WHERE Client.ClientId=Job.ClientId
sub display_job_group
{
my ($self, %arg) = @_;
+ $self->can_do('r_view_job');
my ($limit, $label) = $self->get_limit(groupby => 'client_group_name', %arg);
my ($where, undef) = $self->get_param('client_groups',
'level',
'pools');
-
+ my $filter = $self->get_client_group_filter();
my $query =
"
SELECT client_group_name AS client_group_name,
COALESCE(joberr.nbjobs,0) AS nbjoberr,
COALESCE(jobok.duration, '0:0:0') AS duration
-FROM client_group LEFT JOIN (
+FROM client_group $filter LEFT JOIN (
SELECT client_group_name AS client_group_name, COUNT(1) AS nbjobs,
SUM(JobFiles) AS jobfiles, SUM(JobBytes) AS jobbytes,
SUM(JobErrors) AS joberrors,
'volstatus',
'locations');
- my $arg = $self->get_form('jmedias', 'qre_media');
+ my $arg = $self->get_form('jmedias', 'qre_media', 'expired');
if ($arg->{jmedias}) {
$where = "AND Media.VolumeName IN ($arg->{jmedias}) $where";
if ($arg->{qre_media}) {
$where = "AND Media.VolumeName $self->{sql}->{MATCH} $arg->{qre_media} $where";
}
+ if ($arg->{expired}) {
+ $where = "
+ AND VolStatus = 'Full'
+ AND ( $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten)
+ + $self->{sql}->{TO_SEC}(Media.VolRetention)
+ ) < NOW() " . $where ;
+ }
my $query="
SELECT Media.VolumeName AS volumename,
$self->display({ ID => $cur_id++,
Pool => $elt{pool},
Location => $elt{location},
- Medias => [ values %$all ]
+ Media => [ values %$all ],
},
"display_media.tpl");
}
-sub display_medias
+sub display_allmedia
{
my ($self) = @_ ;
{
my ($self) = @_ ;
- my $medias = $self->get_form('jmedias');
+ my $media = $self->get_form('jmedias');
- unless ($medias->{jmedias}) {
+ unless ($media->{jmedias}) {
return $self->error("Can't get media selection");
}
my $query="
SELECT InChanger AS online,
+ Media.Enabled AS enabled,
VolBytes AS nb_bytes,
VolumeName AS volumename,
VolStatus AS volstatus,
FROM Pool,
Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
WHERE Pool.PoolId = Media.PoolId
- AND VolumeName IN ($medias->{jmedias})
+ AND VolumeName IN ($media->{jmedias})
";
my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
sub location_edit
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
my $loc = $self->get_form('qlocation');
unless ($loc->{qlocation}) {
$self->display({ ID => $cur_id++,
%$row }, "location_edit.tpl") ;
-
}
sub location_save
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
my $arg = $self->get_form(qw/qlocation qnewlocation cost/) ;
unless ($arg->{qlocation}) {
sub location_del
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
+
my $arg = $self->get_form(qw/qlocation/) ;
unless ($arg->{qlocation}) {
$self->location_display();
}
-
sub location_add
{
my ($self) = @_ ;
+ $self->can_do('r_location_mgnt');
+
my $arg = $self->get_form(qw/qlocation cost/) ;
unless ($arg->{qlocation}) {
{
my ($self) = @_ ;
- my $medias = $self->get_selected_media_location();
- unless ($medias) {
+ my $media = $self->get_selected_media_location();
+ unless ($media) {
return ;
}
$self->display({ email => $self->{info}->{email_media},
%$arg,
- medias => [ values %$medias ],
+ media => [ values %$media ],
},
"update_location.tpl");
}
sub groups_edit
{
my ($self) = @_;
+ $self->can_do('r_group_mgnt');
my $grp = $self->get_form(qw/qclient_group db_clients/);
- $self->debug($grp);
unless ($grp->{qclient_group}) {
return $self->error("Can't get group");
sub groups_save
{
my ($self) = @_;
+ $self->can_do('r_group_mgnt');
my $arg = $self->get_form(qw/qclient_group jclients qnewgroup/);
unless ($arg->{qclient_group}) {
sub groups_del
{
my ($self) = @_;
+ $self->can_do('r_group_mgnt');
+
my $arg = $self->get_form(qw/qclient_group/);
unless ($arg->{qclient_group}) {
FROM client_group
WHERE client_group_name = $arg->{qclient_group});
+DELETE FROM bweb_client_group_acl
+ WHERE client_group_id IN
+ (SELECT client_group_id
+ FROM client_group
+ WHERE client_group_name = $arg->{qclient_group});
+
DELETE FROM client_group
WHERE client_group_name = $arg->{qclient_group};
";
sub groups_add
{
my ($self) = @_;
+ $self->can_do('r_group_mgnt');
+
my $arg = $self->get_form(qw/qclient_group/) ;
unless ($arg->{qclient_group}) {
"display_groups.tpl");
}
+###########################################################
+
+sub get_roles
+{
+ my ($self) = @_;
+ if (not $self->{info}->{enable_security}) {
+ return 1;
+ }
+ # admin is a special user that can do everything
+ if ($self->{loginname} eq 'admin') {
+ return 1;
+ }
+ if (!$self->{loginname}) {
+ return 0;
+ }
+ # already fill
+ if (defined $self->{security}) {
+ return 1;
+ }
+ $self->{security} = {};
+ my $u = $self->dbh_quote($self->{loginname});
+
+ my $query = "
+ SELECT use_acl, rolename
+ FROM bweb_user
+ JOIN bweb_role_member USING (userid)
+ JOIN bweb_role USING (roleid)
+ WHERE username = $u
+";
+ my $rows = $self->dbh_selectall_arrayref($query);
+ # do cache with this role
+ if (!$rows) {
+ return 0;
+ }
+ foreach my $r (@$rows) {
+ $self->{security}->{$r->[1]}=1;
+ }
+
+ $self->{security}->{use_acl} = $rows->[0]->[0];
+ return 1;
+}
+
+# TODO: avoir un mode qui coupe le programme avec une page d'erreur
+# we can also get all security and fill {security} hash
+sub can_do
+{
+ my ($self, $action) = @_;
+ # is security enabled in configuration ?
+ if (not $self->{info}->{enable_security}) {
+ return 1;
+ }
+ # admin is a special user that can do everything
+ if ($self->{loginname} eq 'admin') {
+ return 1;
+ }
+ # must be logged
+ if (!$self->{loginname}) {
+ $self->error("Can't do $action, your are not logged. " .
+ "Check security with your administrator");
+ $self->display_end();
+ exit (0);
+ }
+ $self->get_roles();
+ if (!$self->{security}->{$action}) {
+ $self->error("$self->{loginname} sorry, but this action ($action) " .
+ "is not permited. " .
+ "Check security with your administrator");
+ $self->display_end();
+ exit (0);
+ }
+ return 1;
+}
+
+sub use_filter
+{
+ my ($self) = @_;
+
+ if (!$self->{info}->{enable_security} or
+ !$self->{info}->{enable_security_acl})
+ {
+ return 0 ;
+ }
+
+ if ($self->get_roles()) {
+ return $self->{security}->{use_acl};
+ } else {
+ return 0;
+ }
+}
+
+# JOIN Client USING (ClientId) " . $b->get_client_filter() . "
+sub get_client_filter
+{
+ my ($self) = @_;
+ if ($self->use_filter()) {
+ my $u = $self->dbh_quote($self->{loginname});
+ return "
+ JOIN (SELECT ClientId FROM client_group_member
+ JOIN client_group USING (client_group_id)
+ JOIN bweb_client_group_acl USING (client_group_id)
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $u
+ ) AS filter USING (ClientId)";
+ } else {
+ return '';
+ }
+}
+
+#JOIN client_group USING (client_group_id)" . $b->get_client_group_filter()
+sub get_client_group_filter
+{
+ my ($self) = @_;
+ if ($self->use_filter()) {
+ my $u = $self->dbh_quote($self->{loginname});
+ return "
+ JOIN (SELECT client_group_id
+ FROM bweb_client_group_acl
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $u
+ ) AS filter USING (client_group_id)";
+ } else {
+ return '';
+ }
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub revoke
+{
+ my ($self, $role, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ DELETE FROM bweb_role_member
+ WHERE roleid = (SELECT roleid FROM bweb_role
+ WHERE rolename IN ($role))
+ AND userid = (SELECT userid FROM bweb_user
+ WHERE username IN ($username))");
+ return $nb;
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub grant
+{
+ my ($self, $role, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ INSERT INTO bweb_role_member (roleid, userid)
+ SELECT roleid, userid FROM bweb_role, bweb_user
+ WHERE rolename IN ($role)
+ AND username IN ($username)
+ ");
+ return $nb;
+}
+
+# role and username have to be quoted before
+# role and username can be a quoted list
+sub grant_like
+{
+ my ($self, $copy, $user) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $nb = $self->dbh_do("
+ INSERT INTO bweb_role_member (roleid, userid)
+ SELECT roleid, a.userid
+ FROM bweb_user AS a, bweb_role_member
+ JOIN bweb_user USING (userid)
+ WHERE bweb_user.username = $copy
+ AND a.username = $user");
+ return $nb;
+}
+
+# username can be a join quoted list of usernames
+sub revoke_all
+{
+ my ($self, $username) = @_;
+ $self->can_do("r_user_mgnt");
+
+ $self->dbh_do("
+ DELETE FROM bweb_role_member
+ WHERE userid IN (
+ SELECT userid
+ FROM bweb_user
+ WHERE username in ($username))");
+ $self->dbh_do("
+DELETE FROM bweb_client_group_acl
+ WHERE userid IN (
+ SELECT userid
+ FROM bweb_user
+ WHERE username IN ($username))");
+
+}
+
+sub users_del
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form(qw/jusernames/);
+
+ unless ($arg->{jusernames}) {
+ return $self->error("Can't get user");
+ }
+
+ $self->{dbh}->begin_work();
+ {
+ $self->revoke_all($arg->{jusernames});
+ $self->dbh_do("
+DELETE FROM bweb_user WHERE username IN ($arg->{jusernames})");
+ }
+ $self->{dbh}->commit();
+
+ $self->display_users();
+}
+
+sub users_add
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ # we don't quote username directly to check that it is conform
+ my $arg = $self->get_form(qw/username qpasswd qcomment jrolenames qcreate qcopy_username jclient_groups/) ;
+
+ if (not $arg->{qcreate}) {
+ $arg = $self->get_form(qw/db_roles db_usernames db_client_groups/);
+ $self->display($arg, "display_user.tpl");
+ return 1;
+ }
+
+ my $u = $self->dbh_quote($arg->{username});
+
+ $arg->{use_acl}=(CGI::param('use_acl')?'true':'false');
+
+ if (!$arg->{qpasswd}) {
+ $arg->{qpasswd} = "''";
+ }
+ if (!$arg->{qcomment}) {
+ $arg->{qcomment} = "''";
+ }
+
+ # will fail if user already exists
+ $self->dbh_do("
+ UPDATE bweb_user
+ SET passwd=$arg->{qpasswd}, comment=$arg->{qcomment},
+ use_acl=$arg->{use_acl}
+ WHERE username = $u")
+ or
+ $self->dbh_do("
+ INSERT INTO bweb_user (username, passwd, use_acl, comment)
+ VALUES ($u, $arg->{qpasswd}, $arg->{use_acl}, $arg->{qcomment})");
+
+ $self->{dbh}->begin_work();
+ {
+ $self->revoke_all($u);
+
+ if ($arg->{qcopy_username}) {
+ $self->grant_like($arg->{qcopy_username}, $u);
+ } else {
+ $self->grant($arg->{jrolenames}, $u);
+ }
+
+ $self->dbh_do("
+INSERT INTO bweb_client_group_acl (client_group_id, userid)
+ SELECT client_group_id, userid
+ FROM client_group, bweb_user
+ WHERE client_group_name IN ($arg->{jclient_groups})
+ AND username = $u
+");
+
+ }
+ $self->{dbh}->commit();
+
+ $self->display_users();
+}
+
+# TODO: we miss a matrix with all user/roles
+sub display_users
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form(qw/db_usernames/) ;
+
+ if ($self->{dbh}->errstr) {
+ return $self->error("Can't use users with bweb, read INSTALL to enable them");
+ }
+
+ $self->display({ ID => $cur_id++,
+ %$arg},
+ "display_users.tpl");
+}
+
+sub display_user
+{
+ my ($self) = @_;
+ $self->can_do("r_user_mgnt");
+
+ my $arg = $self->get_form('username');
+ my $user = $self->dbh_quote($arg->{username});
+
+ my $userp = $self->dbh_selectrow_hashref("
+ SELECT username, passwd, comment, use_acl
+ FROM bweb_user
+ WHERE username = $user
+");
+
+ if (!$userp) {
+ return $self->error("Can't find $user in catalog");
+ }
+ $arg = $self->get_form(qw/db_usernames db_client_groups/);
+ my $arg2 = $self->get_form(qw/filter db_client_groups/);
+
+# rolename | userid
+#------------+--------
+# cancel_job |
+# restore |
+# run_job | 1
+
+ my $role = $self->dbh_selectall_hashref("
+SELECT rolename, temp.userid
+ FROM bweb_role
+ LEFT JOIN (SELECT roleid, userid
+ FROM bweb_user JOIN bweb_role_member USING (userid)
+ WHERE username = $user) AS temp USING (roleid)
+ORDER BY rolename
+", 'rolename');
+
+ $self->display({
+ db_usernames => $arg->{db_usernames},
+ username => $userp->{username},
+ comment => $userp->{comment},
+ passwd => $userp->{passwd},
+ use_acl => $userp->{use_acl},
+ db_client_groups => $arg->{db_client_groups},
+ client_group => $arg2->{db_client_groups},
+ db_roles => [ values %$role],
+ }, "display_user.tpl");
+}
+
+
###########################################################
sub get_media_max_size
Media.VolUseDuration AS voluseduration,
Media.VolRetention AS volretention,
Media.Comment AS comment,
- PoolRecycle.Name AS poolrecycle
+ PoolRecycle.Name AS poolrecycle,
+ Media.Enabled AS enabled
FROM Media INNER JOIN Pool AS PoolMedia ON (Media.PoolId = PoolMedia.PoolId)
LEFT JOIN Pool AS PoolRecycle ON (Media.RecyclePoolId = PoolRecycle.PoolId)
my $row = $self->dbh_selectrow_hashref($query);
$row->{volretention} = human_sec($row->{volretention});
$row->{voluseduration} = human_sec($row->{voluseduration});
+ $row->{enabled} = human_enabled($row->{enabled});
my $elt = $self->get_form(qw/db_pools db_locations/);
sub save_location
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
my $arg = $self->get_form('jmedias', 'qnewlocation') ;
sub location_change
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
- my $medias = $self->get_selected_media_location();
- unless ($medias) {
+ my $media = $self->get_selected_media_location();
+ unless ($media) {
return $self->error("Can't get media selection");
}
my $newloc = CGI::param('newlocation');
my $comm = CGI::param('comment') || '';
$comm = $self->dbh_quote("$user: $comm");
- my $query;
+ my $arg = $self->get_form('enabled');
+ my $en = human_enabled($arg->{enabled});
+ my $b = $self->get_bconsole();
- foreach my $media (keys %$medias) {
+ my $query;
+ foreach my $vol (keys %$media) {
$query = "
INSERT LocationLog (Date, Comment, MediaId, LocationId, NewVolStatus)
VALUES(
- NOW(), $comm, (SELECT MediaId FROM Media WHERE VolumeName = '$media'),
- (SELECT LocationId FROM Location WHERE Location = '$medias->{$media}->{location}'),
- (SELECT VolStatus FROM Media WHERE VolumeName = '$media')
+ NOW(), $comm, (SELECT MediaId FROM Media WHERE VolumeName = '$vol'),
+ (SELECT LocationId FROM Location WHERE Location = '$media->{$vol}->{location}'),
+ (SELECT VolStatus FROM Media WHERE VolumeName = '$vol')
)
";
$self->dbh_do($query);
$self->debug($query);
+ $b->send_cmd("update volume=\"$vol\" enabled=$en");
}
+ $b->close();
my $q = new CGI;
$q->param('action', 'update_location');
$self->display({ email => $self->{info}->{email_media},
url => $url,
newlocation => $newloc,
- # [ { volumename => 'vol1' }, { volumename => 'vol2'\81\81Â },..]
- medias => [ values %$medias ],
+ # [ { volumename => 'vol1' }, { volumename => 'vol2'},..]
+ media => [ values %$media ],
},
"change_location.tpl");
sub display_client_stats
{
my ($self, %arg) = @_ ;
+ $self->can_do('r_view_stats');
my $client = $self->dbh_quote($arg{clientname});
+ # get security filter
+ my $filter = $self->get_client_filter();
my ($limit, $label) = $self->get_limit(%arg);
-
my $query = "
SELECT
count(Job.JobId) AS nb_jobs,
sum(Job.JobErrors) AS nb_err,
sum(Job.JobFiles) AS nb_files,
Client.Name AS clientname
-FROM Job JOIN Client USING (ClientId)
+FROM Job JOIN Client USING (ClientId) $filter
WHERE
Client.Name = $client
$limit
sub display_running_job
{
my ($self) = @_;
+ $self->can_do('r_view_running_job');
my $arg = $self->get_form('client', 'jobid');
if (!$arg->{client} and $arg->{jobid}) {
+ # get security filter
+ my $filter = $self->get_client_filter();
my $query = "
SELECT Client.Name AS name
-FROM Job INNER JOIN Client USING (ClientId)
+FROM Job INNER JOIN Client USING (ClientId) $filter
WHERE Job.JobId = $arg->{jobid}
";
sub display_running_jobs
{
my ($self, $display_action) = @_;
-
+ $self->can_do('r_view_running_job');
+
+ # get security filter
+ my $filter = $self->get_client_filter();
+
my $query = "
SELECT Job.JobId AS jobid,
Job.Name AS jobname,
- $self->{sql}->{UNIX_TIMESTAMP}(StartTime))
AS duration,
Client.Name AS clientname
-FROM Job INNER JOIN Client USING (ClientId)
-WHERE JobStatus IN ('C','R','B','e','D','F','S','m','M','s','j','c','d','t','p')
+FROM Job INNER JOIN Client USING (ClientId) $filter
+WHERE
+ JobStatus IN ('C','R','B','e','D','F','S','m','M','s','j','c','d','t','p')
";
my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
sub eject_media
{
my ($self) = @_;
+ $self->can_do('r_media_mgnt');
+
my %ret;
my $arg = $self->get_form('jmedias');
$a->status();
$a->{have_status} = 1;
}
-
+ # TODO: set enabled
print "eject $vol->{volumename} from $vol->{storage} : ";
if ($a->send_to_io($vol->{slot})) {
print "<img src='/bweb/T.png' alt='ok'><br/>";
sub ach_register
{
my ($self, $ach) = @_;
+ $self->can_do('r_configure');
$self->{info}->{ach_list}->{$ach->{name}} = $ach;
sub ach_edit
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach');
if (!$arg->{ach}
or !$self->{info}->{ach_list}
sub ach_del
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach');
if (!$arg->{ach}
sub ach_add
{
my ($self) = @_;
+ $self->can_do('r_configure');
+
my $arg = $self->get_form('ach', 'mtxcmd', 'device', 'precmd');
my $b = $self->get_bconsole();
sub delete
{
my ($self) = @_;
+ $self->can_do('r_delete_job');
+
my $arg = $self->get_form('jobid');
if ($arg->{jobid}) {
sub do_update_media
{
my ($self) = @_ ;
+ $self->can_do('r_media_mgnt');
my $arg = $self->get_form(qw/media volstatus inchanger pool
slot volretention voluseduration
maxvoljobs maxvolfiles maxvolbytes
- qcomment poolrecycle
+ qcomment poolrecycle enabled
/);
unless ($arg->{media}) {
$update .= " slot=0 inchanger=no ";
}
+ if ($arg->{enabled}) {
+ $update .= " enabled=$arg->{enabled} ";
+ }
+
if ($arg->{pool}) {
$update .= " pool=$arg->{pool} " ;
}
sub update_slots
{
my ($self) = @_;
+ $self->can_do('r_autochanger_mgnt');
my $ach = CGI::param('ach') ;
$ach = $self->ach_get($ach);
sub get_job_log
{
my ($self) = @_;
+ $self->can_do('r_view_log');
my $arg = $self->get_form('jobid', 'limit', 'offset');
unless ($arg->{jobid}) {
if ($arg->{limit} == 100) {
$arg->{limit} = 1000;
}
-
- my $t = CGI::param('time') || $self->{info}->{display_log_time} || '';
+ # get security filter
+ my $filter = $self->get_client_filter();
my $query = "
SELECT Job.Name as name, Client.Name as clientname
- FROM Job INNER JOIN Client ON (Job.ClientId = Client.ClientId)
+ FROM Job INNER JOIN Client USING (ClientId) $filter
WHERE JobId = $arg->{jobid}
";
return $self->error("Can't find $arg->{jobid} in catalog");
}
+ # display only Error and Warning messages
+ $filter = '';
+ if (CGI::param('error')) {
+ $filter = " AND LogText $self->{sql}->{MATCH} 'Error|Warning' ";
+ }
+
+ my $logtext;
+ if (CGI::param('time') || $self->{info}->{display_log_time}) {
+ $logtext = 'LogText';
+ } else {
+ $logtext = $self->dbh_strcat('Time', ' ', 'LogText')
+ }
+
$query = "
-SELECT Time AS time, LogText AS log
- FROM Log
- WHERE Log.JobId = $arg->{jobid}
- OR (Log.JobId = 0 AND Time >= (SELECT StartTime FROM Job WHERE JobId=$arg->{jobid})
- AND Time <= (SELECT COALESCE(EndTime,NOW()) FROM Job WHERE JobId=$arg->{jobid})
- )
+SELECT count(1) AS nbline, JobId AS jobid,
+ GROUP_CONCAT($logtext $self->{sql}->{CONCAT_SEP}) AS logtxt
+ FROM (
+ SELECT JobId, Time, LogText
+ FROM Log
+ WHERE ( Log.JobId = $arg->{jobid}
+ OR (Log.JobId = 0
+ AND Time >= (SELECT StartTime FROM Job WHERE JobId=$arg->{jobid})
+ AND Time <= (SELECT COALESCE(EndTime,NOW()) FROM Job WHERE JobId=$arg->{jobid})
+ ) ) $filter
ORDER BY LogId
LIMIT $arg->{limit}
OFFSET $arg->{offset}
+ ) AS temp
+ GROUP BY JobId
+
";
- my $log = $self->dbh_selectall_arrayref($query);
+ my $log = $self->dbh_selectrow_hashref($query);
unless ($log) {
return $self->error("Can't get log for jobid $arg->{jobid}");
}
- my $logtxt;
- if ($t) {
- # log contains \n
- $logtxt = join("", map { ($_->[0] . ' ' . $_->[1]) } @$log ) ;
- } else {
- $logtxt = join("", map { $_->[1] } @$log ) ;
- }
-
- $self->display({ lines=> $logtxt,
+ $self->display({ lines=> $log->{logtxt},
+ nbline => $log->{nbline},
jobid => $arg->{jobid},
name => $row->{name},
client => $row->{clientname},
}, 'display_log.tpl');
}
-
sub label_barcodes
{
my ($self) = @_ ;
+ $self->can_do('r_autochanger_mgnt');
my $arg = $self->get_form('ach', 'slots', 'drive');
sub purge
{
my ($self) = @_;
+ $self->can_do('r_purge');
my @volume = CGI::param('media');
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
- $self->display({
- content => $b->purge_volume(@volume),
- title => "Purge media",
- name => "purge volume=" . join(' volume=', @volume),
- }, "command.tpl");
+ foreach my $v (@volume) {
+ $self->display({
+ content => $b->purge_volume($v),
+ title => "Purge media",
+ name => "purge volume=$v",
+ }, "command.tpl");
+ }
$b->close();
}
sub prune
{
my ($self) = @_;
+ $self->can_do('r_prune');
my @volume = CGI::param('media');
unless (@volume) {
my $b = new Bconsole(pref => $self->{info}, timeout => 60);
- $self->display({
- content => $b->prune_volume(@volume),
- title => "Prune media",
- name => "prune volume=" . join(' volume=', @volume),
- }, "command.tpl");
-
+ foreach my $v (@volume) {
+ $self->display({
+ content => $b->prune_volume($v),
+ title => "Prune volume",
+ name => "prune volume=$v",
+ }, "command.tpl");
+ }
$b->close();
}
sub cancel_job
{
my ($self) = @_;
+ $self->can_do('r_cancel_job');
my $arg = $self->get_form('jobid');
unless ($arg->{jobid}) {
sub enable_disable_job
{
my ($self, $what) = @_ ;
+ $self->can_do('r_run_job');
my $name = CGI::param('job') || '';
unless ($name =~ /^[\w\d\.\-\s]+$/) {
sub run_job_select
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $joblist = [ map { { name => $_ } } $b->list_job() ];
sub run_job_mod
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $job = CGI::param('job') || '';
sub run_job
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
my $jobs = [ map {{ name => $_ }} $b->list_job() ];
sub run_job_now
{
my ($self) = @_;
+ $self->can_do('r_run_job');
+
my $b = $self->get_bconsole();
# TODO: check input (don't use pool, level)
+
+-- --------------------------------------------------
+-- Upgrade from 2.2
+-- --------------------------------------------------
+
+CREATE FUNCTION concat (text, text) RETURNS text AS '
+DECLARE
+result text;
+BEGIN
+IF $1 is not null THEN
+result := $1 || $2;
+END IF;
+
+RETURN result;
+END;
+' LANGUAGE plpgsql;
+
+CREATE AGGREGATE group_concat(
+sfunc = concat,
+basetype = text,
+stype = text,
+initcond = ''
+);
+
+BEGIN;
+CREATE TABLE bweb_user
+(
+ userid serial not null,
+ username text not null,
+ use_acl boolean default false,
+ comment text default '',
+ passwd text default '',
+ primary key (userid)
+);
+CREATE UNIQUE INDEX bweb_user_idx on bweb_user (username);
+
+CREATE TABLE bweb_role
+(
+ roleid serial not null,
+ rolename text not null,
+-- comment text default '',
+ primary key (roleid)
+);
+CREATE UNIQUE INDEX bweb_role_idx on bweb_role (rolename);
+
+INSERT INTO bweb_role (rolename) VALUES ('r_user_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_delete_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_prune');
+INSERT INTO bweb_role (rolename) VALUES ('r_purge');
+INSERT INTO bweb_role (rolename) VALUES ('r_group_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_location_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_cancel_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_run_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_configure');
+INSERT INTO bweb_role (rolename) VALUES ('r_client_status');
+INSERT INTO bweb_role (rolename) VALUES ('r_view_job');
+
+CREATE TABLE bweb_role_member
+(
+ roleid integer not null,
+ userid integer not null,
+ primary key (roleid, userid)
+);
+
+CREATE TABLE bweb_client_group_acl
+(
+ client_group_id integer not null,
+ userid integer not null,
+ primary key (client_group_id, userid)
+);
+COMMIT;
-- --------------------------------------------------
-- Upgrade from 2.0
-- --------------------------------------------------
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+
+=head1 LICENSE
+
+ Bweb - A Bacula web interface
+ Bacula® - The Network Backup Solution
+
+ Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+
+ The main author of Bweb is Eric Bollengier.
+ The main author of Bacula is Kern Sibbald, with contributions from
+ many others, a complete list can be found in the file AUTHORS.
+
+ This program is Free Software; you can redistribute it and/or
+ modify it under the terms of version two of the GNU General Public
+ License as published by the Free Software Foundation plus additions
+ that are listed in the file LICENSE.
+
+ 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ 02110-1301, USA.
+
+ Bacula® is a registered trademark of John Walker.
+ The licensor of Bacula is the Free Software Foundation Europe
+ (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
+ Switzerland, email:ftf@fsfeurope.org.
+
+=head1 INFORMATIONS
+
+ This script is used to extract strings from tpl files
+
+=head1 VERSION
+
+ $Id$
+
+=cut
+
+package TmplParser;
+use base "HTML::Parser";
+use HTML::Parser;
+use Locale::PO;
+my $in_script;
+
+my %dict;
+sub print_it
+{
+ foreach my $w (@_) {
+ next if ($w eq ' ');
+ next if ($w !~ /\w/);
+ next if ($w =~ />/);
+
+ next if (exists $dict{$w});
+ $dict{$w}=1;
+
+ # we add " at the begining and the end of the string
+ $w =~ s/(^|$)/"/gm; # "
+ print "msgid ", $w, "\n";
+ print 'msgstr ""', "\n\n";
+ }
+}
+
+sub text {
+ my ($self, $text) = @_;
+
+ # between <script>..</script> we take only "words"
+ if ($in_script) {
+ my @words = ($text =~ /"([\w\s]+)"/gs);
+ print_it(@words);
+ return;
+ }
+
+ # just print out the original text
+# $text =~ s/<\/?TMPL[^>]+>//gs;
+
+ # strip extra spaces
+ $text =~ s/(^\s+)|(\s+$)//gs;
+
+ # skip some special cases
+ return if ($text eq 'selected');
+ print_it($text);
+}
+
+sub start {
+ my ($self, $tag, $attr, $attrseq, $origtext) = @_;
+
+ # On note qu'on se trouve dans un script
+ # pour prendre que les chaines entre ""
+ if ($tag eq 'script') {
+ $in_script = 1;
+ }
+
+ #return unless ($tag =~ /^(a|input|img)$/);
+
+ # liste des attributs a traduire
+ if (defined $attr->{'title'}) {
+ print_it($attr->{'title'});
+ }
+
+ if (defined $attr->{'alt'}) {
+ print_it($attr->{'alt'});
+ }
+}
+
+sub end {
+ if ($in_script) {
+ $in_script=0;
+ }
+}
+
+package main;
+
+my $p = new TmplParser;
+for (my $f = shift ; $f and -f $f ; $f = shift) {
+ #$TmplParser::nb=0;
+ print "#============ $f ==========\n";
+ $p->parse_file($f);
+}
+
--- /dev/null
+-- --------------------------------------------------
+-- Upgrade from 2.2
+-- --------------------------------------------------
+
+CREATE FUNCTION concat (text, text) RETURNS text AS '
+DECLARE
+result text;
+BEGIN
+IF $1 is not null THEN
+result := $1 || $2;
+END IF;
+
+RETURN result;
+END;
+' LANGUAGE plpgsql;
+
+CREATE AGGREGATE group_concat(
+sfunc = concat,
+basetype = text,
+stype = text,
+initcond = ''
+);
+
+BEGIN;
+CREATE TABLE bweb_user
+(
+ userid serial not null,
+ username text not null,
+ use_acl boolean default false,
+ comment text default '',
+ passwd text default '',
+ primary key (userid)
+);
+CREATE UNIQUE INDEX bweb_user_idx on bweb_user (username);
+
+CREATE TABLE bweb_role
+(
+ roleid serial not null,
+ rolename text not null,
+-- comment text default '',
+ primary key (roleid)
+);
+CREATE UNIQUE INDEX bweb_role_idx on bweb_role (rolename);
+
+INSERT INTO bweb_role (rolename) VALUES ('r_user_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_delete_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_prune');
+INSERT INTO bweb_role (rolename) VALUES ('r_purge');
+INSERT INTO bweb_role (rolename) VALUES ('r_group_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_location_mgnt');
+INSERT INTO bweb_role (rolename) VALUES ('r_cancel_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_run_job');
+INSERT INTO bweb_role (rolename) VALUES ('r_configure');
+INSERT INTO bweb_role (rolename) VALUES ('r_client_status');
+INSERT INTO bweb_role (rolename) VALUES ('r_view_job');
+
+CREATE TABLE bweb_role_member
+(
+ roleid integer not null,
+ userid integer not null,
+ primary key (roleid, userid)
+);
+
+CREATE TABLE bweb_client_group_acl
+(
+ client_group_id integer not null,
+ userid integer not null,
+ primary key (client_group_id, userid)
+);
+COMMIT;
<li><a href="bweb.pl?action=groups">Groups</a> </li>
</ul>
</li>
- <li>Jobs
+ <li style="padding: 0.25em 2em;">Jobs
<ul>
<li><a href="bweb.pl?action=run_job">Defined Jobs</a>
<li><a href="bweb.pl?action=job_group">Jobs by group</a>
</li>
</TMPL_IF>
<li><a href="bweb.pl?action=graph"> Statistics </a></li>
- <li> <a href="bweb.pl?action=view_conf"> Configuration </a> </li>
+ <li> <a href="bweb.pl?action=view_conf"> Configuration </a>
+<TMPL_IF enable_security>
+ <ul> <li> <a href="bweb.pl?action=view_conf"> Configuration </a>
+ <li> <a href="bweb.pl?action=users"> Manage users </a>
+ </ul>
+</TMPL_IF>
+</li>
<li> <a href="bweb.pl?action=about"> About </a> </li>
<li style="padding: 0.25em 2em;float: right;"> Logged as <TMPL_VAR NAME=loginname> </li>
<li style="float: right;white-space: nowrap;">
-<input type="image" class="button" title="search media" onclick="search_media();" src="/bweb/tape.png"><input type="image" title="search client" onclick="search_client();" src="/bweb/client.png"> <input class='formulaire' style="margin: 0 2px 0 2px; padding: 0 0 0 0;" id='searchbox' type='text' size='8' value="search..." onclick="this.value='';" title="search media or client"></li>
+<input type="image" class="button" title="Search media" onclick="search_media();" src="/bweb/tape.png"><input type="image" title="Search client" onclick="search_client();" src="/bweb/client.png"> <input class='formulaire' style="margin: 0 2px 0 2px; padding: 0 0 0 0;" id='searchbox' type='text' size='8' value="search..." onclick="this.value='';" title="Search media or client"></li>
</ul>
<form name="search" action="bweb.pl?" method='GET'>
Could you move these media to <TMPL_VAR newlocation>
Media :
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
- <TMPL_VAR VolumeName> (<TMPL_VAR location>)
</TMPL_LOOP>
<tr><td>display_log_time :</td>
<td> <input class="formulaire" title="display log timestamp" type='checkbox' name='display_log_time' <TMPL_IF display_log_time> checked='checked' value='on' </TMPL_IF> >
</td></tr>
+ <tr><td>security :</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security' title='Use user managment in bweb. Read INSTALL first' <TMPL_IF enable_security> checked='checked' value='on' </TMPL_IF> >
+ <tr><td>security acl:</td>
+ <td> <input class="formulaire" type='checkbox' name='enable_security_acl' title='Use user acl in bweb. Read INSTALL first' <TMPL_IF enable_security_acl> checked='checked' value='on' </TMPL_IF> >
<tr><td>debug :</td>
<td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
</td></tr>
<tr><td title="You can choose the Job table that you want to use to get statistics">stat_job_table :</td> <td> <TMPL_IF stat_job_table><TMPL_VAR stat_job_table><TMPL_ELSE>Job</TMPL_IF> </td></tr>
<tr><td title="/path/to/bconsole -n -c /path/to/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
<tr><td title="display timestamp in job log">display_log_time :</td> <td> <TMPL_VAR display_log_time> </td></tr>
+ <tr><td title="user managment">security :</td> <td> <TMPL_VAR enable_security> </td></tr>
+ <tr><td title="user filter">security acl :</td> <td> <TMPL_VAR enable_security_acl> </td></tr>
<tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
<TMPL_IF achs>
<tr> <td><b>Autochanger</b></td> <td/></tr>
-<br/>
- <div class='titlediv'>
- <h1 class='newstitle'> Last jobs for <TMPL_VAR clientname> (<TMPL_VAR Filter>)
- </h1>
- </div>
- <div class='bodydiv'>
-
- <table id='id<TMPL_VAR ID>'></table>
-
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_size;status=T">
- <img src="/bweb/chart.png" alt="backup size" title="backup size evolution"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_duration;status=T">
- <img src="/bweb/chart.png" alt="backup duration" title="backup time evolution"/>
- </a>
-<a href="bgraph.pl?client=<TMPL_VAR clientname>;action=job_rate;status=T">
- <img src="/bweb/chart.png" alt="backup rate" title="backup rate evolution"/>
- </a>
- </div>
-
-
-<script type="text/javascript" language="JavaScript">
-var header = new Array("JobId", "Job Name", "File Set", "Level", "Start Time",
- "Job Files", "Job Bytes", "Errors");
-
-var data = new Array();
-
-<TMPL_LOOP Jobs>
-data.push( new Array(
-"<TMPL_VAR JobId>",
-"<TMPL_VAR JobName>",
-"<TMPL_VAR FileSet>",
-"<TMPL_VAR Level>",
-"<TMPL_VAR StartTime>",
-"<TMPL_VAR JobFiles>",
-human_size(<TMPL_VAR JobBytes>),
-"<TMPL_VAR JobErrors>"
- )
-);
-</TMPL_LOOP>
-
-nrsTable.setup(
-{
- table_name: "id<TMPL_VAR ID>",
- table_header: header,
- table_data: data,
- up_icon: up_icon,
- down_icon: down_icon,
- prev_icon: prev_icon,
- next_icon: next_icon,
- rew_icon: rew_icon,
- fwd_icon: fwd_icon,
-// natural_compare: true,
- even_cell_color: even_cell_color,
- odd_cell_color: odd_cell_color,
- header_color: header_color,
- page_nav: true,
- rows_per_page: rows_per_page,
- disable_sorting: new Array(5,6)
-}
-);
-
-// get newest job first
-nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
-</script>
document.getElementById('client_' + <TMPL_VAR name>).selected = true;
</TMPL_LOOP>
+ <TMPL_LOOP qclient_groups>
+ document.getElementById('group_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
<TMPL_IF status>
document.getElementById('status_<TMPL_VAR status>').selected=true;
</TMPL_IF>
<TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
class='formulaire' size='8'>
</td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Expired media</h2>
+ <input type='checkbox' name='expired' <TMPL_IF expired> checked </TMPL_IF>
+ class='formulaire'>
+ </td>
+</tr>
</tr>
<tr>
<td valign='bottom'>
</label>
</form>
</td>
+<TMPL_IF joberrors>
+ <td>
+ <a href="<TMPL_VAR thisurl>;error=1"
+ title="View only errors">
+ <img src='/bweb/doc.png' alt="view errors"></a> View only errors
+ </td>
+</TMPL_IF>
</table>
</div>
<pre id='log'>
<TMPL_VAR lines>
</pre>
+
+<a id='prev'><img border='0' src='/bweb/prev.png'></a>
+<a id='next'><img border='0' src='/bweb/next.png'></a>
</div>
+<script type="text/javascript" language='JavaScript'>
+
+var url='<TMPL_VAR thisurl>';
+var urlprev=url;
+var urlnext=url;
+
+var reoff = new RegExp('offset=[0-9]+', "");
+var relim = new RegExp('limit=[0-9]+', "");
+
+var offset=0;
+var limit=1000;
+var nbline=0;
+<TMPL_IF offset>
+offset = <TMPL_VAR offset>;
+</TMPL_IF>
+<TMPL_IF limit>
+limit = <TMPL_VAR limit>;
+</TMPL_IF>
+<TMPL_IF nbline>
+nbline=<TMPL_VAR nbline>;
+</TMPL_IF>
+
+if (nbline == limit) {
+ var offset_next = offset + limit;
+
+ if (url.match(reoff)) {
+ urlnext = urlnext.replace(reoff, 'offset=' + offset_next);
+ } else {
+ urlnext = urlnext + ';offset=' + offset_next;
+ }
+
+ if (url.match(relim)) {
+ urlnext = urlnext.replace(relim, 'limit=' + limit);
+ } else {
+ urlnext = urlnext + ';limit=' + limit;
+ }
+
+ document.getElementById('next').href = urlnext;
+
+} else {
+ document.getElementById('next').style.visibility="hidden";
+}
+
+if (offset > 0) {
+ var offset_prev = offset - limit;
+ if (offset_prev < 0) {
+ offset_prev=0;
+ }
+ if (url.match(reoff)) {
+ urlprev = urlprev.replace(reoff, 'offset=' + offset_prev);
+ } else {
+ urlprev = urlprev + ';offset=' + offset_prev ;
+ }
+ if (url.match(relim)) {
+ urlprev = urlprev.replace(relim, 'limit=' + limit);
+ } else {
+ urlprev = urlprev + ';limit=' + limit;
+ }
+
+ if (offset_prev >= 0) {
+ document.getElementById('prev').href = urlprev;
+ } else {
+ document.getElementById('prev').style.visibility="hidden";
+ }
+} else {
+ document.getElementById('prev').style.visibility="hidden";
+}
+</script>
var chkbox;
var d;
-<TMPL_LOOP Medias>
+<TMPL_LOOP media>
d = percent_usage(<TMPL_VAR volusage>);
img = document.createElement('IMG');
</table>
<script type="text/javascript" language="JavaScript">
-var header = new Array("Pool","Online","Location","Vol Status", "Vol Bytes", "Expire",
+var header = new Array("Pool","Online","Enabled", "Location","Vol Status", "Vol Bytes", "Expire",
"Retention","Max use duration", "Max jobs" );
var data = new Array();
data.push( new Array(
"<TMPL_VAR poolname>",
img,
+human_enabled("<TMPL_VAR enabled>"),
"<TMPL_VAR location>",
"<TMPL_VAR volstatus>",
human_size(<TMPL_VAR nb_bytes>),
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> User: <TMPL_VAR username></h1>
+</div>
+<div class='bodydiv'>
+
+<form name="form1" action="?">
+<input type="hidden" value="1" name="create">
+ <table>
+ <tr>
+ <td>Username:</td> <td> <input class="formulaire" type="text" name="username" value="<TMPL_VAR username>"> </td>
+<!-- </tr><tr>
+ <td>Password:</td> <td> <input class="formulaire" type="password" name="passwd" value="<TMPL_VAR passwd>"> </td>
+-->
+ </tr><tr>
+ <td>Comment:</td> <td> <input class="formulaire" type="text" name="comment" value="<TMPL_VAR comment>"> </td>
+ </tr><tr>
+<td> Profile:</td><td>
+ <select name="profile" id='profile' class="formulaire">
+ <option onclick='set_role("")'></option>
+ <option onclick='set_role("administrator")'>Administrator</option>
+ <option onclick='set_role("customer")'>Customer</option>
+ </select>
+</td><td>Or like an existing user: </td><td>
+ <select name="copy_username" class="formulaire">
+ <option onclick="disable_sel(false)"></option>
+ <TMPL_LOOP db_usernames>
+ <option title="<TMPL_VAR comment>" onclick="disable_sel(true)" value="<TMPL_VAR username>"><TMPL_VAR username></option>
+ </TMPL_LOOP>
+ </select>
+</td>
+ </tr><tr>
+ </tr><tr>
+<td> Roles:</td><td>
+ <select name="rolename" id='rolename' multiple class="formulaire" size=15>
+ <TMPL_LOOP db_roles>
+ <option title="<TMPL_VAR comment>" value="<TMPL_VAR rolename>" <TMPL_IF userid>selected</TMPL_IF> ><TMPL_VAR rolename></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+</tr><tr>
+<td> Use groups filter:</td><td>
+<input class="formulaire" onclick="disable_group(this.checked == false)" type="checkbox" name="use_acl" <TMPL_IF use_acl> checked </TMPL_IF> > </td>
+</tr><tr>
+<td> Groups:</td><td>
+ <select name="client_group" id='client_group' multiple class="formulaire" size=15>
+<TMPL_LOOP db_client_groups>
+ <option id='group_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+</table>
+ <input type="image" name='action' value='user_save'
+ src='/bweb/save.png'>
+</form>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+
+<TMPL_LOOP client_group>
+ document.getElementById('group_<TMPL_VAR name>').selected = true;
+</TMPL_LOOP>
+
+<TMPL_UNLESS use_acl>
+disable_group(true);
+</TMPL_UNLESS>
+
+function disable_sel(val)
+{
+ document.form1.profile.disabled = val;
+ document.form1.rolename.disabled = val;
+}
+function disable_group(val)
+{
+ document.form1.client_group.disabled = val;
+}
+function set_role(val)
+{
+ if (val == "administrator") {
+ for (var i=0; i < document.form1.rolename.length; ++i) {
+ document.form1.rolename[i].selected = true;
+ }
+ } else if (val == "production") {
+ for (var i=0; i < document.form1.rolename.length; ++i) {
+ if (document.form1.rolename[i].value != 'r_configure' &&
+ document.form1.rolename[i].value != 'r_user_mgnt' &&
+ document.form1.rolename[i].value != 'r_group_mgnt'
+ )
+ {
+ document.form1.rolename[i].selected = true;
+ }
+ }
+ } else if (val == "customer") {
+ for (var i=0; i < document.form1.rolename.length; ++i) {
+ if (document.form1.rolename[i].value == 'r_view_stats' ||
+ document.form1.rolename[i].value == 'r_view_history' ||
+ document.form1.rolename[i].value == 'r_view_log'
+ )
+ {
+ document.form1.rolename[i].selected = true;
+ } else {
+ document.form1.rolename[i].selected = false;
+ }
+ }
+ }
+
+}
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Users</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='user_add' title='Add' src='/bweb/add.png'>
+ <input type="image" name='action' value='user_del'
+ onclick="return confirm('Do you want to delete this user ?');"
+ title='Supprimer' src='/bweb/remove.png'>
+ <input type="image" name='action' value='user_edit' title='Modify' src='/bweb/edit.png'>
+
+ <input type="image" name='action' value='client' title='View clients'
+ src='/bweb/zoom.png'>
+ <input type="image" name='action' value='job' title='View jobs'
+ src='/bweb/zoom.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Username","Selection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP db_usernames>
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'username';
+chkbox.value = '<TMPL_VAR username>';
+
+data.push( new Array(
+"<TMPL_VAR username>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR ID>",
+ table_header: header,
+ table_data: data,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+// natural_compare: false,
+ even_cell_color: even_cell_color,
+ odd_cell_color: odd_cell_color,
+ header_color: header_color,
+ page_nav: true,
+ padding: 3,
+// disable_sorting: new Array(5,6),
+ rows_per_page: rows_per_page
+}
+);
+</script>
+
+
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR NAME=volumename>';
<td style='align: left;'>
<input type="image" onclick='javascript:window.history.go(-2);' title='Back' src='/bweb/prev.png'>
</td><td style='align: right;'>
+ <input type="hidden" name='enabled' value="yes">
<input type="image" name='action' value='move_media'
src='/bweb/intern.png'>
</td></tr>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=Medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.name = 'media';
<h1 class='newstitle'>Move media</h1>
</div>
<div class="bodydiv">
- <form action='?' method='get'>
+ <form name='form1' action='?' method='get'>
<table id='id<TMPL_VAR NAME=ID>'></table>
<table border='0'>
<tr><td> New location: </td><td>
<option value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
</TMPL_LOOP>
</select>
- </td></tr><tr><td> Status: </td><td>
-<select name='volstatus' class='formulaire'>
- <option value=''>Don't update</option>
- <option value='Append'>Append</option>
- <option value='Archive'>Archive</option>
- <option value='Disabled'>Disabled</option>
- <option value='Cleaning'>Cleaning</option>
- <option value='Error'>Error</option>
- <option value='Full'>Full</option>
- <option value='Purged'>Purged</option>
- <option value='Read-Only'>Read-Only</option>
- <option value='Recycle'>Recycle</option>
- <option value='Used'>Used</option>
+ </td></tr><tr><td> Enabled: </td><td>
+<select name='enabled' class='formulaire'>
+ <option value='no'>no</option>
+ <option value='yes'>yes</option>
+ <option value='archived'>archived</option>
</select>
</td><tr><td> User: </td><td>
<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
rows_per_page: rows_per_page
}
);
+<TMPL_IF enabled>
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
+</TMPL_IF>
</script>
var data = new Array();
var chkbox;
-<TMPL_LOOP NAME=medias>
+<TMPL_LOOP NAME=media>
chkbox = document.createElement('INPUT');
chkbox.type = 'checkbox';
chkbox.value = '<TMPL_VAR name=volumename>';
</td>
</tr>
+ <tr><td>Enabled:</td>
+ <td> <select name='enabled' class='formulaire'>
+ <option value='yes'>yes</option>
+ <option value='no'>no</option>
+ <option value='archived'>archived</option>
+ </select>
+ </td>
+ </tr>
+
<tr><td> Location : </td>
<td><select name='location' class='formulaire'>
<option value=''></option>
ok=0;
}
}
+ok=1;
+for (var i=0; ok && i < document.form1.enabled.length; ++i) {
+ if (document.form1.enabled[i].value == '<TMPL_VAR enabled>') {
+ document.form1.enabled[i].selected = true;
+ ok=0;
+ }
+}
</script>
echo
echo "If you are using postgresql, you have to load /usr/share/bweb/bweb-postgresql.sql in your database"
- echo "postgres@localhost:~$ psql bacula < /usr/share/bweb/bweb-postgresql.sql"
+ echo "postgres@localhost:~$ createlang plpgsql -Upostgres -d bacula"
+ echo "postgres@localhost:~$ psql -U bacula bacula < /usr/share/bweb/bweb-postgresql.sql"
echo
echo "If you are using mysql, you have to load /usr/share/bweb/bweb-mysql.sql in your database"
echo "root@localhost:~$ mysql bacula < /usr/share/bweb/bweb-mysql.sql"
if [ "$1" = "upgrade" ] ; then
echo "If you are using postgresql, you have to load /usr/share/bweb/upgrade-2.0_2.2_postgresql.sql in your database"
- echo "postgres@localhost:~$ psql bacula < /usr/share/bweb/upgrade-2.0_2.2_postgresql.sql"
+ echo "postgres@localhost:~$ psql -U bacula bacula < /usr/share/bweb/upgrade-2.0_2.2_postgresql.sql"
echo
fi
+bweb (2.2.6-1) stable; urgency=low
+ * Replace VolStatus by Enabled in volume location
+ * Add Enabled in volume update and zoom
+ * Add Prev/Next on job log output
+ * Add a error filter to Job zoom view
+
+ -- Eric Bollengier <eric@eb.homelinux.org> Tue, 30 Oct 2007 22:15:47 +0000
+
bweb (2.2.1-1) stable; urgency=low
* WARNING: 2.2.1-1 version don't support bacula 2.0.X anymore
Architecture: all
Depends: libtime-modules-perl, bweb-common, libgd-graph-perl, libdbd-pg-perl|libdbd-mysql-perl,libhtml-template-perl
Description: Bacula restore interface
- Bweb is a bacula web interface
+ Bweb is a Bacula web interface
Package: bweb-common
Section: base
Architecture: all
Depends: libtime-modules-perl, libexpect-perl
Description: Bacula restore interface
- Bweb is a bacula web interface
-
+ Bweb is a Bacula web interface