]> git.sur5r.net Git - bacula/bacula/commitdiff
bweb: Add sqlite support
authorEric Bollengier <eric@eb.homelinux.org>
Mon, 11 Apr 2011 14:48:06 +0000 (16:48 +0200)
committerEric Bollengier <eric@eb.homelinux.org>
Mon, 11 Apr 2011 14:48:06 +0000 (16:48 +0200)
25 files changed:
gui/bweb/INSTALL
gui/bweb/cgi/bfileview.pl
gui/bweb/cgi/bgraph.pl
gui/bweb/cgi/btime.pl
gui/bweb/cgi/bweb.pl
gui/bweb/html/bweb.js
gui/bweb/lib/Bweb.pm
gui/bweb/lib/GTime.pm
gui/bweb/script/bweb-mysql.sql
gui/bweb/script/bweb-postgresql.sql
gui/bweb/script/httpd.conf
gui/bweb/script/regress.pl
gui/bweb/script/tpl_generate.pl
gui/bweb/tpl/ach_content.tpl
gui/bweb/tpl/add_media.tpl
gui/bweb/tpl/begin.tpl
gui/bweb/tpl/display_job.tpl
gui/bweb/tpl/display_job_zoom.tpl
gui/bweb/tpl/display_media.tpl
gui/bweb/tpl/display_media_zoom.tpl
gui/bweb/tpl/help_extern_compute.tpl
gui/bweb/tpl/help_intern_compute.tpl
gui/bweb/tpl/run_job_mod.tpl
gui/bweb/tpl/running_job.tpl
gui/bweb/tpl/scheduled_job.tpl

index 7c1ae65f13a98172c865ce3ff6f6af5b815e195b..bd7124a4dafadf7e3753bf28e4388017738fdcaa 100644 (file)
@@ -106,7 +106,9 @@ Click: Main -- you should see the charts
 
 ################ FILE COPY (Full Apache methode) ###############
  # you must get bweb svn files
- svn checkout https://bacula.svn.sourceforge.net/svnroot/bacula/trunk/gui/bweb bweb
+ git clone git://bacula.git.sourceforge.net/gitroot/bacula/bacula
+ git checkout origin/master
+ cd bacula/gui/
 
  # or get them from the released tar files or from the apt or rpms.
 
@@ -145,8 +147,8 @@ Click: Main -- you should see the charts
 
  # done !
 
- WARNING : Your www-data (or wwwrun on SuSE) user must be able to execute bconsole and able 
-           to read the bconsole.conf file!
+ WARNING : Your www-data (or wwwrun on SuSE) user must be able to execute
+           bconsole and able to read the bconsole.conf file!
            You can create an bconsole group for that.
 
 ################ USE FRENCH/SPANISH VERSION ####################
@@ -324,8 +326,8 @@ You MUST use brestore.pl -b or bresto.pl action=batch to initialize the
 database, and you CAN use bfileview.pl mode=batch jobid=xxx where=/ to compute
 tree size.
 
-At this time, it's a good idea to schedule brestore.pl -b after your 
-BackupCatalog job.
+At this time, it's a good idea to schedule brestore.pl -b or bresto.pl after
+your BackupCatalog job.
 
 Job {
   Name = "BackupCatalog"
@@ -383,6 +385,7 @@ For postgresql, it will be done with bweb/script/bweb-postgresql.sql (already do
 It will do some basics things on a working bweb/brestore setup.
 
 1) Go to http://extjs.com and download their toolkit (use the 2.2 release)
+   You can also get it from http://www.bacula.org/downloads/extjs-2.2.zip
 
 2) Install files in /bweb/ext web root 
  example on debian : 
@@ -390,15 +393,7 @@ It will do some basics things on a working bweb/brestore setup.
 
 3) Make sure that brestore cache tables are in your catalog (bweb-xxx.sql files)
 
-4) Enable bresto.pl cgi. 
-  edit the bweb/cgi/bresto.pl script and change $bresto_enable=0; to $bresto_enable=1;
-  on the top of the file.
-
-5) Use the last Bweb.pm
-  If you are trying bresto in a working bweb/brestore setup, you must make sure that you use 
-  the last Bweb.pm git version.
-
-6) Go on http://you-director/bweb/bresto.html
+4) Go on http://you-director/bweb/bresto.html
 
 ################################################################
 
index 23bd23016c77bdd40dcbcbe4fc8fb5364b858a53..08816c0932cb194f35a05fc130e8d2ab23c48505 100755 (executable)
@@ -67,7 +67,6 @@ if ($jobid and $batch eq 'batch') {
     exit 1;
 }
 
-print CGI::header('text/html');
 $bweb->display_begin();
 $bweb->display_job_zoom($jobid);
 
index 7407322dba4a5c3925973299c55d9a6b020ac373..e75e7941952f94221f2a565e716a0bd1562fcb4f 100755 (executable)
@@ -6,7 +6,7 @@ use strict;
    Bweb - A Bacula web interface
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 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
@@ -31,10 +31,6 @@ use strict;
    (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
    Switzerland, email:ftf@fsfeurope.org.
 
-=head1 VERSION
-
-    $Id$
-
 =cut
 
 use Bweb;
@@ -148,9 +144,8 @@ if ($gtype eq 'balloon') {
     $b->set_legend_axis(%legend);
 
     my $all = $dbh->selectall_arrayref("
-SELECT $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)
-                                  - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime))
-         AS duration, $order, JobId, Job.Name
+SELECT $bweb->{sql}->{JOB_DURATION} AS duration,
+       $order, JobId, Job.Name
        
  FROM $jobt AS Job, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
@@ -184,7 +179,7 @@ $limitq
     exit 0;
 }
 
-print CGI::header('image/png');
+$bweb->send_content_type(-type => 'image/png');
 
 sub get_graph
 {
@@ -291,7 +286,7 @@ if ($graph eq 'job_size') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
        Client.Name                    AS clientname,
        Job.Name                       AS jobname,
        Job.JobBytes                   AS jobbytes,
@@ -330,7 +325,7 @@ if ($graph eq 'job_file') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
        Client.Name                    AS clientname,
        Job.Name                       AS jobname,
        Job.JobFiles                   AS jobfiles,
@@ -372,7 +367,7 @@ elsif ($graph eq 'file_histo' and $arg->{where}) {
     my $file = $dbh->quote(basename($arg->{where}));
 
     my $query = "
-SELECT UNIX_TIMESTAMP(Job.StartTime)    AS starttime,
+SELECT $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
        Client.Name                      AS client,
        Job.Name                         AS jobname,
        base64_decode_lstat(8,LStat)     AS lstat,
@@ -423,7 +418,7 @@ elsif ($graph eq 'rep_histo' and $arg->{where}) {
     $dir = $dbh->quote($dir);
 
     my $query = "
-SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+SELECT $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
        Client.Name                   AS client,
        Job.Name                      AS jobname,
        brestore_pathvisibility.size  AS size,
@@ -467,14 +462,11 @@ elsif ($graph eq 'job_rate') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)  AS starttime,
+       $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
        Client.Name                    AS clientname,
        Job.Name                       AS jobname,
        Job.JobBytes /
-       ($bweb->{sql}->{SEC_TO_INT}(
-                          $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
-                        - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) + 0.01) 
-         AS rate,
+               ($bweb->{sql}->{JOB_DURATION} + 0.01) AS rate,
        Job.Level                      AS joblevel
 
 FROM $jobt AS Job, FileSet, Client $filter $groupf
@@ -513,12 +505,10 @@ elsif ($graph eq 'job_duration') {
 
     my $query = "
 SELECT 
-       UNIX_TIMESTAMP(Job.StartTime)       AS starttime,
-       Client.Name                         AS clientname,
-       Job.Name                            AS jobname,
-  $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
-                             - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
-         AS duration,
+       $bweb->{sql}->{STARTTIME_SEC}  AS starttime,
+       Client.Name                    AS clientname,
+       Job.Name                       AS jobname,
+       $bweb->{sql}->{JOB_DURATION}   AS duration,
        Job.Level                      AS joblevel
 
 FROM $jobt AS Job, FileSet, Client $filter $groupf
@@ -574,12 +564,16 @@ $limitq
     if ($t eq 'sum' or $t eq 'avg') {
        push @arg, ('y_number_format' => \&Bweb::human_size);
     }
-    
-    my $stime = $bweb->{sql}->{"STARTTIME_$d"};
+    my $stime;
+    if ($per_t) {
+        $stime = $bweb->{sql}->{"STARTTIME_$d"};
+    } else {
+        $stime = $bweb->{sql}->{STARTTIME_SEC};
+    }
 
     my $query = "
 SELECT
-     " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
+     $stime AS A,
      $t(JobBytes)                  AS nb
 FROM $jobt AS Job, FileSet, Client $filter $groupf
 WHERE Job.ClientId = Client.ClientId
@@ -593,7 +587,6 @@ WHERE Job.ClientId = Client.ClientId
   $groupq
 $limit
 ";
-
     print STDERR $query  if ($debug);
 
     my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
index f264828f79834b8f22e929b6f18d010bbb311101..479a8bf5b797bb08a82c75d999694a5506759f1a 100755 (executable)
@@ -39,7 +39,6 @@ use strict;
 use GTime;
 use Getopt::Long ;
 use Bweb;
-use Time::ParseDate qw/parsedate/;
 use POSIX qw/strftime/;
 use CGI;
 
@@ -48,7 +47,6 @@ $conf->load();
 
 my $bweb = new Bweb(info => $conf);
 
-print CGI::header('text/html');
 $bweb->display_begin();
 $bweb->can_do('r_view_stat');
 
@@ -216,25 +214,25 @@ foreach my $elt (@$all)
 #              l => $elt->[2],
                type  => "waiting",
                begin => $begin,
-               end   => parsedate($elt->[1]) - $t,
+               end   => $elt->[1] - $t,
            };
 
            push @$data, {
 #              l => $elt->[2],
                type  => "despool",
-               begin => parsedate($elt->[1]) - $t,
+               begin => $elt->[1] - $t,
                end   => $elt->[1],
            };
 
            push @{$write->{$drive}}, { # display only write time
                type  => "despool",
-               begin => parsedate($elt->[1]) - $t,
+               begin => $elt->[1] - $t,
                end   => $elt->[1],
            };
 
            push @{$pool->{"$drive: $elt->[4]"}}, {
                type  => "despool",
-               begin => parsedate($elt->[1]) - $t,
+               begin => $elt->[1] - $t,
                end   => $elt->[1],
            };
        } else {
index 251011151697af6d93fa9be256c7ea15ec82db49..08a652e7dc04b5603abaf5306fd2b5f40fc3d8f2 100755 (executable)
@@ -41,27 +41,20 @@ my $client_re = qr/^([\w\d\.-]+)$/;
 
 my $action = CGI::param('action') || 'begin';
 
-if ($action eq 'restore') {
-    print CGI::header('text/brestore');        # specialy to run brestore.pl
-
-} else {
-    print CGI::header('text/html');
-}
-
 # loading config file
 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
 $conf->load();
 
 my $bweb = new Bweb(info => $conf);
+my $arg = $bweb->get_form('jobid', 'limit', 'offset', 'age', 'new_dir');
 
 # just send data with text/brestore content
 if ($action eq 'restore') {
+    print CGI::header('text/brestore');        # specialy to run brestore.pl
     $bweb->restore();
     exit 0;
 }
 
-my $arg = $bweb->get_form('jobid', 'limit', 'offset', 'age');
-
 $bweb->display_begin();
 
 # if no configuration, we send edit_conf
index de8f5b615c8cbdd899d84e78777ba56663e94e2d..0bcd098148d23967f773f74a51f575e66a5d8f5f 100644 (file)
@@ -1,5 +1,5 @@
 // Bweb - A Bacula web interface
-// Bacula® - The Network Backup Solution
+// Bacula® - The Network Backup Solution
 //
 // Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
 //
@@ -22,7 +22,7 @@
 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 // 02110-1301, USA.
 //
-// Bacula® is a registered trademark of Kern Sibbald.
+// Bacula® is a registered trademark of Kern Sibbald.
 // The licensor of Bacula is the Free Software Foundation Europe
 // (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
 // Switzerland, email:ftf@fsfeurope.org.
@@ -77,6 +77,7 @@ var joblevelname = {
  'F': 'Full',
  'I': 'Incremental',
  'D': 'Differential',
+ 'B': 'Base'
 };
 
 
@@ -131,24 +132,84 @@ function human_sec(val)
    val /= 60;                  // sec -> min
    
    if ((val / 60) <= 1) {
-      return val.toFixed(0) + ' mins';
+      return val.toFixed(0) + ' min' + add_s(val) ;
    }
 
    val /= 60;                  // min -> hour
 
    if ((val / 24) <= 1) { 
-      return val.toFixed(0) + ' hours';
+      return val.toFixed(0) + ' hour' + add_s(val) ;
    }
 
    val /= 24;                   // hour -> day
 
    if ((val / 365) < 2) { 
-      return val.toFixed(0) + ' days';
+      return val.toFixed(0) + ' day' + add_s(val);
    }
 
    val /= 365;
 
-   return val.toFixed(0) + ' years';
+   return val.toFixed(0) + ' year' + add_s(val);
+}
+
+function add_s(val)
+{
+    if (val >= 2) {
+        return "s ";
+    } else {
+        return " ";
+    }
+}
+
+function human_sec2(val)
+{
+   if (!val) {
+      val = 0;
+   }
+   val = parseInt(val);
+   if (val < 60) {
+       return val.toFixed(0) + ' sec' + add_s(val);
+   }
+
+   val /= 60;                  // sec -> min
+   if ((val / 60) <= 1) {
+       return val.toFixed(0) + ' min' + add_s(val);
+   }
+
+   var prev = val % 60;
+   val /= 60;                  // min -> hour
+
+   if ((val / 24) <= 1) { 
+       return val.toFixed(0) + ' hour' + add_s(val) 
+           + prev.toFixed(0) + ' min' + add_s(prev);
+   }
+   prev = val % 24;
+   val /= 24;                   // hour -> day
+
+   if ((val / 365) < 2) { 
+       return val.toFixed(0) + ' day' + add_s(val) 
+           + prev.toFixed(0) + ' hour' + add_s(prev);
+   }
+
+   prev = val % 365;
+   val /= 365;
+
+    return val.toFixed(0) + ' year' + add_s(val) 
+        + prev.toFixed(0) + ' day' + add_s(prev);
+}
+
+function human_duration(val)
+{
+    if (!val) {
+        val = 0;
+    }
+    val = parseInt(val);
+    var sec = val % 60;
+    val = val / 60;
+    var min = val % 60;
+    val = val / 60;
+    return pad(val.toFixed(0)) + ':' + pad(min.toFixed(0)) + ':' + pad(sec.toFixed(0));
 }
 
 
@@ -308,6 +369,18 @@ function percent_usage(value, parent)
    return parent;
 }
 
+function pad(n){return n<10 ? '0'+n : n}
+
+function timestamp_to_iso(ts)
+{
+    if(ts < 1000000000000){
+        ts=(ts*1000);
+    }
+    var datum = new Date(ts);
+    
+    return datum.getFullYear() + '-' + pad(datum.getMonth()+1) + '-' + pad(datum.getDay()+1) + ' ' + pad(datum.getHours()) + ':' + pad(datum.getMinutes()) + ':' + pad(datum.getSeconds());
+}
+
 function bweb_get_job_img(status, errors, type)
 {
   var ret;
index 7e5178163673b3b815f9698df37fcfb3503b5211..174d3e434b7c71b6f576fc8a4c0b71488328645d 100644 (file)
@@ -6,7 +6,7 @@ use strict;
    Bweb - A Bacula web interface
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2006-2010 Free Software Foundation Europe e.V.
+   Copyright (C) 2006-2011 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
@@ -68,6 +68,7 @@ sub new
     my ($class, %arg) = @_;
     my $self = bless {
         name => undef,
+        info => undef,
     }, $class;
 
     map { $self->{lc($_)} = $arg{$_} } keys %arg ;
@@ -88,6 +89,13 @@ sub debug
     }
 }
 
+sub ldebug
+{
+    open(FP, ">>/tmp/log");
+    print FP Data::Dumper::Dumper(\@_);
+    close(FP);
+}
+
 sub fdebug
 {
     my ($self, $what) = @_;
@@ -123,6 +131,22 @@ sub error
     return 0;
 }
 
+# send content type the first time, see man CGI to overwrite
+# values
+my $send_content_type_done=0;
+sub send_content_type
+{
+    my ($self, %arg) = @_;
+    my $info = $self->{info} || $self;
+
+    if (!$send_content_type_done) { # display it once
+        $send_content_type_done = 1;
+
+        %arg = (-type => 'text/html', %arg);
+        print CGI::header(%arg);
+    }
+}
+
 =head1 FUNCTION
 
     display - display an html page with HTML::Template
@@ -138,6 +162,8 @@ sub error
     hash keys are not sensitive. See HTML::Template for more
     explanations about the hash ref. (it's can be quiet hard to understand) 
 
+    It uses the following variables: template_dir lang director
+
 =head2 EXAMPLE
 
     $ref = { name => 'me', age => 26 };
@@ -148,8 +174,10 @@ sub error
 sub display
 {
     my ($self, $hash, $tpl) = @_ ;
-    my $dir = $self->{template_dir} || $template_dir;
-    my $lang = $self->{lang} || 'en';
+    my $info = $self->{info} || $self;
+
+    my $dir = $info->{template_dir} || $template_dir;
+    my $lang = $self->{current_lang} || $info->{lang} || 'en';
     my $template = HTML::Template->new(filename => $tpl,
                                        path =>["$dir/$lang",
                                                "$dir/$lang/tpl",
@@ -172,6 +200,7 @@ sub display
     $template->param('loginname', CGI::remote_user());
 
     $template->param($hash);
+    $self->send_content_type();
     print $template->output();
 }
 1;
@@ -213,7 +242,7 @@ use CGI;
 
 =cut
 
-our %k_re = ( dbi      => qr/^(dbi:(Pg|mysql):(?:\w+=[\w\d\.-]+;?)+)$/i,
+our %k_re = ( dbi      => qr/^(dbi:(Pg|mysql|SQLite):(?:\w+=[\w\d\.\/\-]+;?)+)$/i,
               user     => qr/^([\w\d\.-]+)$/i,
               password => qr/^(.*)$/,
               fv_write_path => qr!^([/\w\d\.-]*)$!,
@@ -223,14 +252,19 @@ our %k_re = ( dbi      => qr/^(dbi:(Pg|mysql):(?:\w+=[\w\d\.-]+;?)+)$/i,
               email_media => qr/^([\w\d\.-]+@[\d\w\.-]+)$/,
               graph_font  => qr!^([/\w\d\.-]+.ttf)?$!,
               bconsole    => qr!^(.+)?$!,
-              syslog_file => qr!^(.+)?$!,
-              log_dir     => qr!^(.+)?$!,
               wiki_url    => qr!(.*)$!,
               stat_job_table => qr!^(\w*)$!,
               display_log_time => qr!^(on)?$!,
               enable_security => qr/^(on)?$/,
               enable_security_acl => qr/^(on)?$/,
               default_age => qr/^((?:\d+(?:[ywdhms]\s*?)?)+)\s*$/,
+              name => qr/^([\w\s\d\.\-]+)$/,
+              dir_ver => qr/^(\d+(\.\d+)?)$/,
+              # can be subitem
+              subconf => qr/^$/,
+              achs => qr/^$/,
+              ach_list => qr/^$/,
+              url => qr!^(https?://[\w\.\d/@?;]+)$!,
               );
 
 =head1 FUNCTION
@@ -265,46 +299,15 @@ sub load
     use strict;
 
     if ($f and $@) {
-        $self->load_old();
-        $self->save();
-        return $self->error("If you update from an old bweb install, your must reload this page and if it's fail again, you have to configure bweb again...") ;
+        return $self->error("Something is wrong with your configuration file...") ;
     }
 
-    # set default values
-    $self->{default_age} = '7d';
-
+    # keep a backup of the original config
     foreach my $k (keys %$VAR1) {
-        $self->{$k} = $VAR1->{$k};
-    }
-
-    return 1;
-}
-
-=head1 FUNCTION
-
-    load_old - load old configuration format
-
-=cut
-
-sub load_old
-{
-    my ($self) = @_ ;
-
-    unless (open(FP, $self->{config_file}))
-    {
-        return $self->error("$self->{config_file} : $!");
-    }
-
-    while (my $line = <FP>)
-    {
-        chomp($line);
-        my ($k, $v) = split(/\s*=\s*/, $line, 2);
-        if ($k_re{$k}) {
-            $self->{$k} = $v;
+        if (exists $k_re{$k} and defined $VAR1->{$k}) {
+            $self->{main_conf}->{$k} = $VAR1->{$k};
         }
     }
-
-    close(FP);
     return 1;
 }
 
@@ -972,10 +975,7 @@ SELECT Media.VolumeName  AS volumename,
        Media.Slot        AS slot,
        Media.InChanger   AS inchanger,
        Pool.Name         AS name,
-       $bweb->{sql}->{FROM_UNIXTIME}(
-          $bweb->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-        + $bweb->{sql}->{TO_SEC}(Media.VolRetention)
-       ) AS expire
+       $self->{sql}->{MEDIA_EXPIRE} AS expire
 FROM Media 
  INNER JOIN Pool USING (PoolId) 
 
@@ -1089,6 +1089,7 @@ sub new
         my $sel = $self->{name}?"=\"$self->{name}\"":'';
         my $b = $self->{bconsole};
         my $out = $b->send_cmd("show schedule$sel");
+        $self->{show_output}=$out;
         $self->parse_scheds(split(/\r?\n/, $out));
         undef $self->{bconsole}; # useless now
     }
@@ -1296,7 +1297,10 @@ our %sql_func = (
               SEC_TO_INT => "SEC_TO_INT",
               SEC_TO_TIME => '',
               MATCH => " ~* ",
-              STARTTIME_SEC  => " date_trunc('sec', Job.StartTime) ",
+              MEDIA_EXPIRE => "date_part('epoch', Media.LastWritten) + Media.VolRetention",
+              ENDTIME_SEC => " date_part('epoch', EndTime) ",
+              JOB_DURATION => " date_part('epoch', EndTime) -  date_part('epoch', StartTime) ",
+              STARTTIME_SEC  => " date_part('epoch', Job.StartTime) ",
               STARTTIME_DAY  => " date_trunc('day', Job.StartTime) ",
               STARTTIME_HOUR => " date_trunc('hour', Job.StartTime) ",
               STARTTIME_MONTH  => " date_trunc('month', Job.StartTime) ",
@@ -1318,7 +1322,10 @@ our %sql_func = (
               TO_SEC => '',
               SEC_TO_TIME => 'SEC_TO_TIME',
               MATCH => " REGEXP ",
-              STARTTIME_SEC => " DATE_FORMAT(Job.StartTime, '%Y-%m-%d %T') ",
+              MEDIA_EXPIRE => 'UNIX_TIMESTAMP(Media.LastWritten)+Media.VolRetention',
+              ENDTIME_SEC => " UNIX_TIMESTAMP(EndTime) ",
+              JOB_DURATION => " UNIX_TIMESTAMP(EndTime) - UNIX_TIMESTAMP(StartTime) ",
+              STARTTIME_SEC => " UNIX_TIMESTAMP(Job.StartTime) ",
               STARTTIME_DAY  => " DATE_FORMAT(Job.StartTime, '%Y-%m-%d') ",
               STARTTIME_HOUR => " DATE_FORMAT(Job.StartTime, '%Y-%m-%d %H') ",
               STARTTIME_MONTH => " DATE_FORMAT(Job.StartTime, '%Y-%m') ",
@@ -1335,6 +1342,31 @@ our %sql_func = (
               CONCAT_SEP => " SEPARATOR '' ",
               NOW => "NOW()",
           },
+          SQLite => {
+              UNIX_TIMESTAMP => '',
+              FROM_UNIXTIME => '',
+              SEC_TO_INT => '',
+              TO_SEC => '',
+              SEC_TO_TIME => '',
+              MATCH => " REGEXP ",
+              MEDIA_EXPIRE => "strftime('%s', Media.LastWritten) + Media.VolRetention",
+              ENDTIME_SEC => " strftime('%s', EndTime) ",
+              STARTTIME_SEC =>  " strftime('%s', Job.StartTime) ",
+              JOB_DURATION => " strftime('%s', EndTime) -  strftime('%s', StartTime)",
+
+              STARTTIME_DAY  => " strftime('%Y-%m-%d', Job.StartTime) ",
+              STARTTIME_HOUR => " strftime('%Y-%m-%d %H', Job.StartTime) ",
+              STARTTIME_MONTH => " strftime('%Y-%m', Job.StartTime) ",
+              STARTTIME_WEEK => " strftime('%Y-%W', Job.StartTime) ",
+              STARTTIME_PHOUR=> " strftime('%H', Job.StartTime) ",
+              STARTTIME_PDAY => " strftime('%d', Job.StartTime) ",
+              STARTTIME_PMONTH => " strftime('%m', Job.StartTime) ",
+              STARTTIME_PWEEK => " strftime('%W', Job.StartTime) ",
+              DB_SIZE => " SELECT 0 ",
+              CAT_POOL_TYPE => " MediaType || Pool.Name ",
+              CONCAT_SEP => "",
+              NOW => "strftime('%Y-%m-%d %H:%M:%S', 'now')",
+          },
          );
 
 use Exporter 'import';
@@ -1349,6 +1381,18 @@ sub dbh_is_mysql
     return $self->{info}->{dbi} =~ /dbi:mysql/i;
 }
 
+sub dbh_is_sqlite
+{
+    my ($self) = @_;
+    return $self->{info}->{dbi} =~ /dbi:sqlite/i;
+}
+
+sub dbh_is_pg
+{
+    my ($self) = @_;
+    return $self->{info}->{dbi} =~ /dbi:pg/i;
+}
+
 sub dbh_disconnect
 {
     my ($self) = @_;
@@ -1392,6 +1436,16 @@ sub dbh_do
     return $self->{dbh}->do($query);
 }
 
+# For sqlite, convert UNIX_TIMESTAMP(a) to strftime('%s', a)
+sub dbh_convert
+{
+    my ($self, $query) = @_ ; 
+    if ($self->dbh_is_sqlite()) {
+        $query =~ s/UNIX_TIMESTAMP\(([^)]+)\)/strftime('%s', $1)/gs;
+    }
+    return $query;
+}
+
 sub dbh_selectall_hashref
 {
     my ($self, $query, $join) = @_;
@@ -1558,14 +1612,14 @@ sub connect_db
                                     $self->{info}->{user},
                                     $self->{info}->{password});
 
-        $self->error("Can't connect to your database:\n$DBI::errstr\n")
+        return $self->error("Can't connect to your database:\n$DBI::errstr\n")
             unless ($self->{dbh});
 
         $self->{dbh}->{FetchHashKeyName} = 'NAME_lc';
 
         if ($self->dbh_is_mysql()) {
             $self->{dbh}->do("SET group_concat_max_len=1000000");
-        } else {
+        } elsif ($self->dbh_is_pg()) {
             $self->{dbh}->do("SET datestyle TO 'ISO, YMD'");
         }
     }
@@ -1591,9 +1645,11 @@ sub new
 
     $self->{loginname} = CGI::remote_user();
     $self->{debug} = $self->{info}->{debug};
-    $self->{lang} = $self->{info}->{lang};
     $self->{template_dir} = $self->{info}->{template_dir};
 
+    my $args = $self->get_form('dir', 'lang');
+    $self->set_lang($args->{lang});
+
     return $self;
 }
 
@@ -1603,6 +1659,7 @@ sub display_begin
     if ($self->{info}->{enable_security}) {
         $self->get_roles();     # get lang
     }
+
     $self->display($self->{info}, "begin.tpl");
 }
 
@@ -1695,17 +1752,16 @@ sub get_limit
     my $sql = $self->{sql};
 
     if ($arg{since} and $arg{age}) {
-        my $now = "$self->{sql}->{UNIX_TIMESTAMP}(TIMESTAMP '$arg{since}')";
         my $d = strftime('%Y-%m-%d %H:%M:%S', localtime($btime + $arg{age}));
         $limit .= "
  AND StartTime > '$arg{since}'  
- AND $self->{sql}->{UNIX_TIMESTAMP}(EndTime) < ($now + $self->{sql}->{TO_SEC}($arg{age}))";
+ AND EndTime < '$d' ";
 
         $label .= "since $arg{since} and during " . human_sec($arg{age});
 
     } elsif ($arg{age}) {
-        my $d = strftime('%Y-%m-%d %H:%M:%S', localtime($btime - $arg{age}));
-        $limit .=  "AND EndTime > '$d' " ;
+        my $when = $btime - $arg{age};
+        $limit .= "AND JobTDate > $when";
 
         $label = "last " . human_sec($arg{age});
     }
@@ -1735,6 +1791,22 @@ sub get_limit
     return ($limit, $label);
 }
 
+sub get_item
+{
+    my ($what, $default) = @_;
+    my %opt_cookies = ( dir => 1 );
+
+    my $ret = CGI::param($what);
+
+    if ($opt_cookies{$what} && !$ret) {
+        $ret = CGI::cookie($what);
+    }
+    
+    $ret = $ret || $default;
+    return $ret;
+    
+}
+
 =head1 FUNCTION
 
     $bweb->get_form(...) - Get useful stuff
@@ -1786,6 +1858,9 @@ sub get_form
                  );
 
     my %opt_ss =(               # string with space
+                 name    => 1,
+                 dir     => 1,
+                 new_dir => 1,
                  job     => 1,
                  storage => 1,
                  );
@@ -1830,7 +1905,7 @@ sub get_form
 
     foreach my $i (@what) {
         if (exists $opt_i{$i}) {# integer param
-            my $value = CGI::param($i) || $opt_i{$i} ;
+            my $value = get_item($i, $opt_i{$i}) ;
             if ($value =~ /^(\d+)$/) {
                 $ret{$i} = $1;
             } elsif ($i eq 'age' &&  # can have unit
@@ -1839,12 +1914,12 @@ sub get_form
                 $ret{$i} = human_sec_unit($value);
             }
         } elsif ($opt_s{$i}) {  # simple string param
-            my $value = CGI::param($i) || '';
+            my $value = get_item($i, '');
             if ($value =~ /^([\w\d\.-]+)$/) {
                 $ret{$i} = $1;
             }
         } elsif ($opt_ss{$i}) { # simple string param (with space)
-            my $value = CGI::param($i) || '';
+            my $value = get_item($i, '');
             if ($value =~ /^([\w\d\.\-\s]+)$/) {
                 $ret{$i} = $1;
             }
@@ -1864,22 +1939,22 @@ sub get_form
             $ret{$i} = [ map { { name => $self->dbh_quote($_) } } 
                                            grep { ! /^\s*$/ } CGI::param($1) ];
         } elsif (exists $opt_p{$i}) {
-            my $value = CGI::param($i) || '';
+            my $value = get_item($i, '');
             if ($value =~ /^([\w\d\.\/\s:\@\-]+)$/) {
                 $ret{$i} = $1;
             }
         } elsif (exists $opt_r{$i}) {
-            my $value = CGI::param($i) || '';
+            my $value = get_item($i, '');
             if ($value =~ /^([^'"']+)$/) {
                 $ret{$i} = $1;
             }
         } elsif (exists $opt_d{$i}) {
-            my $value = CGI::param($i) || '';
+            my $value = get_item($i, '');
             if ($value =~ /^\s*(\d+\s+\w+)$/) {
                 $ret{$i} = $1;
             }
         } elsif (exists $opt_t{$i}) { # 1: hh:min optionnal, 2: hh:min required
-            my $when = CGI::param($i) || '';
+            my $when = get_item($i, '');
             if ($when =~ /(\d{4}-\d{2}-\d{2}( \d{2}:\d{2}(:\d{2})?)?)/) {
                 if ($opt_t{$i} == 1 or defined $2) {
                     $ret{$i} = $1;
@@ -1888,6 +1963,14 @@ sub get_form
         }
     }
 
+    if ($what{comment}) {
+        my $s = CGI::param('comment');
+        if ($s) {
+            $s =~ s/["\\'<>]/ /g; # strip some characters
+            $ret{comment}=$s;
+        }
+    }
+
     if ($what{storage_cmd}) {
         if (!grep {/^\Q$ret{storage_cmd}\E$/} ('mount', 'umount', 'release','status')) {
             delete $ret{storage_cmd};
@@ -1911,7 +1994,7 @@ sub get_form
     }
 
     if ($what{lang}) {
-        my $lang = CGI::param('lang') || 'en';
+        my $lang = get_item('lang', 'en');
         if ($lang =~ /^(\w\w)$/) {
             $ret{lang} = $1;
         }
@@ -2144,10 +2227,7 @@ SELECT Media.VolumeName  AS volumename,
        Media.VolMounts   AS volmounts,
        Pool.Name         AS name,
        Media.Recycle     AS recycle,
-       $self->{sql}->{FROM_UNIXTIME}(
-          $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-        + $self->{sql}->{TO_SEC}(Media.VolRetention)
-       ) AS expire
+       $self->{sql}->{MEDIA_EXPIRE} AS expire
 FROM Media 
  INNER JOIN Pool     ON (Pool.PoolId = Media.PoolId)
  LEFT  JOIN Location ON (Media.LocationId = Location.LocationId)
@@ -2190,9 +2270,7 @@ sub help_intern_compute
         # we take only expired volumes or purged/recycle ones
         $sql = "
 AND (
- (  ($self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-      + $self->{sql}->{TO_SEC}(Media.VolRetention)
-    ) < $self->{sql}->{NOW}
+ ( ($self->{sql}->{MEDIA_EXPIRE}) < $btime
  ) OR ( 
   Media.VolStatus IN ('Purged', 'Recycle')
  )
@@ -2207,10 +2285,7 @@ SELECT Media.VolumeName  AS volumename,
        Media.MediaType   AS mediatype,
        Media.VolMounts   AS volmounts,
        Pool.Name         AS name,
-       $self->{sql}->{FROM_UNIXTIME}(
-          $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-        + $self->{sql}->{TO_SEC}(Media.VolRetention)
-       ) AS expire
+       $self->{sql}->{MEDIA_EXPIRE} AS expire
 FROM Media 
  INNER JOIN Pool ON (Pool.PoolId = Media.PoolId) 
  LEFT  JOIN Location ON (Location.LocationId = Media.LocationId)
@@ -2432,7 +2507,8 @@ JOIN client_group USING (client_group_id)
 ";
     }
     my $filter = $self->get_client_filter();
-
+    my $comment = $self->get_db_field('Comment');
+    my $rb = $self->get_db_field('ReadBytes');
     my $query="
 SELECT  Job.JobId       AS jobid,
         Client.Name     AS client,
@@ -2446,10 +2522,9 @@ SELECT  Job.JobId       AS jobid,
         JobBytes        AS jobbytes,
         JobStatus       AS jobstatus,
         Type            AS jobtype,
-     $self->{sql}->{SEC_TO_TIME}(  $self->{sql}->{UNIX_TIMESTAMP}(EndTime)  
-                                 - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
-                        AS duration,
-
+        $rb             AS readbytes,
+        $comment        AS comment,
+        $self->{sql}->{JOB_DURATION} AS duration,
         JobErrors       AS joberrors
 
  FROM Client $filter $cgq, 
@@ -2474,6 +2549,24 @@ SELECT  Job.JobId       AS jobid,
                    "display_job.tpl");
 }
 
+# Adapt the code to the Schema version
+# TODO: can use the Version field
+sub get_db_field
+{
+    my ($self, $what) = @_ ;
+
+    my %feature = ('Comment' => 4, 'ReadBytes' => 4);
+    my %replacement = ('Comment' => "''", 'ReadBytes' => 'JobBytes');
+
+    if (!$self->{info}->{dir_ver} or 
+        $self->{info}->{dir_ver} >= $feature{$what})
+    {
+        return $what;
+    } else {
+        return $replacement{$what};
+    }
+}
+
 # display job informations
 sub display_job_zoom
 {
@@ -2484,7 +2577,8 @@ sub display_job_zoom
 
     # get security filter
     my $filter = $self->get_client_filter();
-
+    my $comment = $self->get_db_field('Comment');
+    my $rb = $self->get_db_field('ReadBytes');
     my $query="
 SELECT DISTINCT Job.JobId       AS jobid,
                 Client.Name     AS client,
@@ -2498,9 +2592,9 @@ SELECT DISTINCT Job.JobId       AS jobid,
                 JobStatus       AS jobstatus,
                 JobErrors       AS joberrors,
                 Type            AS jobtype,
-                $self->{sql}->{SEC_TO_TIME}(  $self->{sql}->{UNIX_TIMESTAMP}(EndTime)  
-                                            - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) AS duration
-
+                $rb             AS readbytes,
+                $comment        AS comment,
+                $self->{sql}->{JOB_DURATION} AS duration
  FROM Client $filter,
       Job LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
           LEFT JOIN Pool    ON (Job.PoolId    = Pool.PoolId)
@@ -2546,16 +2640,13 @@ SELECT client_group_name AS client_group_name,
        COALESCE(jobok.joberrors,0) + COALESCE(joberr.joberrors,0) AS joberrors,
        COALESCE(jobok.nbjobs,0)  AS nbjobok,
        COALESCE(joberr.nbjobs,0) AS nbjoberr,
-       COALESCE(jobok.duration, '0:0:0') AS duration
+       COALESCE(jobok.duration, '0') AS duration
 
 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,
-           SUM($self->{sql}->{SEC_TO_TIME}(  $self->{sql}->{UNIX_TIMESTAMP}(EndTime)  
-                              - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)))
-                        AS duration
-
+           $self->{sql}->{JOB_DURATION} AS duration
     FROM Job JOIN client_group_member ON (Job.ClientId = client_group_member.ClientId)
              JOIN client_group USING (client_group_id)
     
@@ -2608,9 +2699,7 @@ sub display_media
     if ($arg->{expired}) {
         $where = " 
         AND VolStatus IN ('Full', 'Used')
-        AND (    $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-               + $self->{sql}->{TO_SEC}(Media.VolRetention)
-            ) < $self->{sql}->{NOW}  " . $where ;
+        AND ( $self->{sql}->{MEDIA_EXPIRE} ) < $btime  " . $where ;
     }
 
     my $query="
@@ -2623,10 +2712,7 @@ SELECT Media.VolumeName  AS volumename,
        Location.Location AS location,
        (volbytes*100/COALESCE(media_avg_size.size,-1))  AS volusage,
        Pool.Name         AS poolname,
-       $self->{sql}->{FROM_UNIXTIME}(
-          $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-        + $self->{sql}->{TO_SEC}(Media.VolRetention)
-       ) AS expire
+       $self->{sql}->{MEDIA_EXPIRE} AS expire
 FROM      Pool, Media 
 LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
 LEFT JOIN (SELECT avg(Media.VolBytes) AS size,
@@ -2640,7 +2726,6 @@ WHERE Media.PoolId=Pool.PoolId
 $where
 $limit
 ";
-
     my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
 
     $self->display({ ID => $cur_id++,
@@ -2694,10 +2779,7 @@ SELECT InChanger     AS online,
        Media.VolWriteTime/1000000 AS volwritetime,
        Media.RecycleCount AS recyclecount,
        Media.Comment      AS comment,
-       $self->{sql}->{FROM_UNIXTIME}(
-          $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten) 
-        + $self->{sql}->{TO_SEC}(Media.VolRetention)
-       ) AS expire
+       $self->{sql}->{MEDIA_EXPIRE} AS expire
  FROM Pool,
       Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
  WHERE Pool.PoolId = Media.PoolId
@@ -2878,7 +2960,6 @@ FROM Location
 ";
 
     my $location = $self->dbh_selectall_hashref($query, 'location');
-
     $self->display({ ID => $cur_id++,
                      Locations => [ values %$location ] },
                    "display_location.tpl");
@@ -3141,7 +3222,7 @@ sub get_roles
     }
     $self->{security}->{use_acl} = $rows->[0]->[0];
     if ($rows->[0]->[2] =~ /^(\w\w)$/) {
-        $self->{lang} = $1;
+        $self->set_lang($1);
     }
     return 1;
 }
@@ -3827,7 +3908,7 @@ sub display_overview
 
     my $q = "
 SELECT name, $stime1 AS num, 
-       JobStatus AS value, joberrors, nb_job, date
+       Status.JobStatus AS value, joberrors, nb_job, date
 FROM (
   SELECT $stime2        AS date, 
          client_group_name AS name,
@@ -3838,7 +3919,7 @@ FROM (
     JOIN client_group_member USING (ClientId)
     JOIN client_group        USING (client_group_id) $filter3
     JOIN Status              USING (JobStatus)
-   WHERE JobStatus IN ('T', 'W', 'f', 'A', 'e', 'E')
+   WHERE Job.JobStatus IN ('T', 'W', 'f', 'A', 'e', 'E')
        $filter1 $filter2
    GROUP BY client_group_name, date
 ) AS sub JOIN Status USING (severity)
@@ -4098,9 +4179,7 @@ SELECT Job.JobId AS jobid,
        Job.JobFiles  AS jobfiles,
        Job.JobBytes  AS jobbytes,
        Job.JobStatus AS jobstatus,
-$self->{sql}->{SEC_TO_TIME}($self->{sql}->{UNIX_TIMESTAMP}($self->{sql}->{NOW})
-                          - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
-         AS duration,
+       $btime - Job.JobTDate AS duration,
        Client.Name AS clientname
 FROM Job INNER JOIN Client USING (ClientId) $filter
 WHERE 
@@ -4250,7 +4329,6 @@ sub restore
 # TODO : make this internal to not eject tape ?
 use Bconsole;
 
-
 sub display_files
 {
     my ($self) = @_ ;
@@ -4620,7 +4698,7 @@ SELECT count(1) AS nbline,
 your 'Messages' resources include 'catalog = all' and you loaded Bweb SQL
 functions in your Catalog.");
     }
-    $log->{logtxt} =~ s/\0//g;
+    $log->{logtxt} =~ s/(\0|\\,)//g;
     $self->display({ lines=> $log->{logtxt},
                      nbline => $log->{nbline},
                      jobid => $arg->{jobid},
@@ -4684,7 +4762,7 @@ INSERT INTO $jobtable
                             pool => $arg->{pool},
                             level => $arg->{level},
                             starttime => $arg->{when},
-                            duration => '00:00:00',
+                            duration => 0,
                             jobfiles => 0,
                             jobbytes => 0,
                             joberrors => 0,
@@ -4720,8 +4798,10 @@ sub add_media
         $b->send("0\n");
         $b->send("$arg->{media}\n");
     }
+    $b->close();
 
-    $b->expect_it('-re','^[*]');
+    sleep(2);
+    #$b->expect_it('-re','^[*]');
 
     CGI::param('media', '');
     CGI::param('re_media', $arg->{media});
@@ -4791,7 +4871,7 @@ sub purge
         return $self->error("Can't get media selection");
     }
 
-    my $b = new Bconsole(pref => $self->{info}, timeout => 60);
+    my $b = $self->get_bconsole(timeout => 60);
 
     foreach my $v (@volume) {
         $self->display({
@@ -4814,7 +4894,7 @@ sub prune
         return $self->error("Can't get media selection");
     }
 
-    my $b = new Bconsole(pref => $self->{info}, timeout => 60);
+    my $b = $self->get_bconsole(timeout => 60);
 
     foreach my $v (@volume) {
         $self->display({
@@ -4906,10 +4986,16 @@ sub enable_disable_job
     }, "command.tpl");  
 }
 
+sub set_lang
+{
+    my ($self, $lang) = @_;
+    $self->{current_lang} = $lang;
+}
+
 sub get_bconsole
 {
-    my ($self) = @_;
-    return new Bconsole(pref => $self->{info});
+    my ($self, @opts) = @_;
+    return new Bconsole(pref => $self->{info}, @opts);
 }
 
 sub cmd_storage
@@ -4980,7 +5066,7 @@ sub run_job_mod
     $self->can_do('r_run_job');
 
     my $b = $self->get_bconsole();
-    my $arg = $self->get_form(qw/pool level client fileset storage media job/);
+    my $arg = $self->get_form(qw/pool level client fileset storage media job comment/);
 
     if (!$arg->{job}) {
         return $self->error("Can't get job name");
@@ -5004,7 +5090,7 @@ SELECT Pool.Name AS name
 
     my %job_opt = (%$attr, %$arg);
     
-    my $jobs   = [ map {{ name => $_ }} $b->list_job() ];
+    my $jobs   = [ map {{ name => $_ }} $b->list_backup() ];
 
     my $pools  = [ map { { name => $_ } } $b->list_pool() ];
     my $clients = [ map { { name => $_ } }$b->list_client()];
@@ -5028,8 +5114,10 @@ sub run_job
 
     my $b = $self->get_bconsole();
     
-    my $jobs   = [ map {{ name => $_ }} $b->list_job() ];
-
+    my $jobs   = [ map {{ name => $_ }} $b->list_backup() ];
+    if ($b->{error}) {
+        return $self->error("Bconsole returns an error, check your setup. ERR=$b->{error}");
+    }
     $self->display({
         jobs     => $jobs,
     }, "run_job.tpl");
@@ -5045,7 +5133,7 @@ sub run_job_now
     # TODO: check input (don't use pool, level)
 
     my $arg = $self->get_form(qw/pool level client priority when 
-                                 fileset job storage/);
+                                 fileset job storage comment/);
     if (!$arg->{job}) {
         return $self->error("Can't get your job name");
     }
@@ -5058,6 +5146,7 @@ sub run_job_now
                         pool => $arg->{pool},
                         fileset => $arg->{fileset},
                         when => $arg->{when},
+                        comment => $arg->{comment}
                         );
 
     print $b->{error};    
@@ -5122,6 +5211,8 @@ sub check_job
     my ($self, $sched, $schedname, $job, $job_pool, $client, $type) = @_;
     return undef if (!$self->can_view_client($client));
 
+    $self->debug("checking $job, $job_pool, $client, $type, $schedname");
+
     my $sch = $sched->get_scheds($schedname);    
     return undef if (!$sch);
 
@@ -5152,7 +5243,7 @@ sub check_job
  LIMIT 1
 ");             
             if ($all) {
-#               print "ok $job ";
+                $self->debug("found job record for $job on $client");
             } else {
                 push @{$self->{tmp}}, {date => $evt, level => $level,
                                        type => 'Backup', name => $job,
@@ -5182,7 +5273,7 @@ sub display_missing_job
     my $sched = new Bweb::Sched(bconsole => $bconsole,
                                 begin => $arg->{begin},
                                 end => $arg->{end});
-
+    $self->debug($sched);
     my $job = $bconsole->send_cmd("show job");
     my ($jname, $jsched, $jclient, $jpool, $jtype);
     foreach my $j (split(/\r?\n/, $job)) {
index c06745c4ccc533ed8ea700c20b7b4ff819c897af..8fc818cbd42cae215cfbf1cab97cd03cddb74dfa 100644 (file)
@@ -84,10 +84,10 @@ sub new
     map { $self->{$_} = $arg{$_} } keys %arg ;
 
     if ($self->{begin}) {
-        $self->{begin} = parsedate($self->{begin});
+        $self->{begin} = $self->{begin};
     }
     if ($self->{end}) {
-        $self->{end} = parsedate($self->{end});
+        $self->{end} = $self->{end};
     }
 
     bless $self ;
@@ -349,20 +349,20 @@ $top->add_job(label => "label",
               data => [
                        {
                            type  => "init",
-                           begin => $begin1,
-                           end   => $end1,
+                           begin => parsedate($begin1),
+                           end   => parsedate($end1),
                        },
 
                        {
                            type  => "write",
-                           begin => $end1,
-                           end   => $begin2,
+                           begin => parsedate($end1),
+                           end   => parsedate($begin2),
                        },
 
                        {
                            type  => "commit",
-                           begin => $begin2,
-                           end   => $end2,
+                           begin => parsedate($begin2),
+                           end   => parsedate($end2),
                        },
                        ]);
 
@@ -370,20 +370,20 @@ $top->add_job(label => "label2",
               data => [
                        {
                            type  => "init",
-                           begin => $begin1,
-                           end   => $end1,
+                           begin => parsedate($begin1),
+                           end   => parsedate($end1),
                        },
 
                        {
                            type  => "write",
-                           begin => $end1,
-                           end   => $begin2,
+                           begin => parsedate($end1),
+                           end   => parsedate($begin2),
                        },
 
                        {
                            type  => "commit",
-                           begin => $begin2,
-                           end   => $end2,
+                           begin => parsedate($begin2),
+                           end   => parsedate($end2),
                        },
                        ]);
 
index 90e8d1d4bab0dba7e329b3ae20dd732a9ad87df3..afbabb8c7ec3d32a4b336f8a86d5db4234cc4855 100644 (file)
@@ -1,3 +1,9 @@
+-- --------------------------------------------------
+-- Upgrade from 5.0
+-- --------------------------------------------------
+
+CREATE UNIQUE INDEX location_idx ON Location (Location(255));
+
 -- --------------------------------------------------
 -- Upgrade from 2.4
 -- --------------------------------------------------
index 50f57297c96c31bce5692999b978df0ec2c0934d..3c9aa630274f8c7300fc3d0327933e417466b066 100644 (file)
@@ -1,6 +1,22 @@
 -- Require > 7.4, else use createlang command
 CREATE PROCEDURAL LANGUAGE plpgsql;
 
+-- --------------------------------------------------
+-- Upgrade from 5.0
+-- --------------------------------------------------
+
+BEGIN;
+
+-- PG 8.4 drops implicit cast from double to bigint
+CREATE FUNCTION SEC_TO_TIME(double precision)
+RETURNS interval AS $$
+    select date_trunc('second', $1 * interval '1 second');
+$$ LANGUAGE SQL;
+
+COMMIT;
+
+CREATE UNIQUE INDEX location_idx ON Location (Location);
+
 -- --------------------------------------------------
 -- Upgrade from 2.2
 -- --------------------------------------------------
index 81793bc1269edb857c94dae90072deadc8f78977..d42ddd877f705f701a7383dcfff93537f2106331 100644 (file)
@@ -11,9 +11,10 @@ server.port = 9180
 
 var.basedir = env.BWEBBASE
 
-server.modules = ("mod_cgi", "mod_alias", "mod_setenv")
+server.modules = ("mod_cgi", "mod_alias", "mod_setenv", "mod_accesslog")
 server.document-root = basedir + "/html/" 
-
+server.errorlog = basedir + "/error.log"
+accesslog.filename = basedir + "/access.log"
 cgi.assign = ( ".pl" => "/usr/bin/perl" )
 alias.url = ( "/cgi-bin/bweb/" => basedir + "/cgi/", 
               "/bweb/fv/" => "/tmp/",
index a42c791c85c8e1a19b28230a01976b04244bd77f..f0206185c17e6e68627762e79f72e41e3048b0da 100755 (executable)
@@ -6,7 +6,7 @@ use strict;
    Bweb - A Bacula web interface
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 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
@@ -34,13 +34,14 @@ use strict;
 =head1 USAGE
 
     Get it working with a regress environment:
-     * get regress module from SVN
+     * get regress module from git
      * use postgresql or mysql in config
      * make setup
      * add catalog = all, !skipped, !saved into Messages Standard (scripts/bacula-dir.conf)
      * add exit 0 to scripts/cleanup
      * run bacula-backup-test
-     * uncomment job schedule in bacula-dir.conf
+     * uncomment job schedule in bacula-dir.conf 
+       $ sed -i 's/# Sc/  Sc/' bacula-dir.conf
      * load bweb-(mysql|postgresql).sql
      * ./bin/bacula start
      * configure bweb to point to bconsole and the catalog
index bc3f221a002a568b1cc29fed24a697a4cbd10b30..1765c975a0f25a781af433a5cf89d34986d321ce 100755 (executable)
@@ -6,7 +6,7 @@ use strict;
    Bweb - A Bacula web interface
    Bacula® - The Network Backup Solution
 
-   Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
+   Copyright (C) 2000-2010 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
@@ -37,10 +37,6 @@ use strict;
     rm -f lang/fr/tpl/*.tpl
     LANGUAGE=fr ./script/tpl_generate.pl tpl/*.pl
 
-=head1 VERSION
-
-    $Id$
-
 =cut
 
 my $debug=0;
@@ -76,6 +72,9 @@ foreach my $f (@ARGV)
     print "Converting $f -> $out/$file ";
     while (my $l = <FP>)
     {
+#        $l =~ s:bresto.html:bresto.html<TMPL_IF cur_name>?dir=<TMPL_VAR cur_name></TMPL_IF>:;
+#        $l =~ s:(href=['"](\w+.pl)?\?):${1}<TMPL_IF cur_name>dir=<TMPL_VAR cur_name>;</TMPL_IF>:g;
+#        $l =~ s:(<form [^>]+>):$1<TMPL_IF cur_name><input type='hidden' name='dir' value='<TMPL_VAR cur_name>'></TMPL_IF>:g;
        my (@str) = ($l =~ m/__(.+?)__/g);
         
         while (my $s = shift @str) {
index 70ede3a6895ade1bf6c71b516fedc0e9d6f28fb6..59f8afe19243e3c8377f4aecfbf01f68fc36ddb6 100644 (file)
@@ -72,7 +72,7 @@ human_size(<TMPL_VAR volbytes>),
 "<TMPL_VAR mediatype>",
 "<TMPL_VAR name>",
 "<TMPL_VAR lastwritten>",
-"<TMPL_VAR expire>",
+timestamp_to_iso("<TMPL_VAR expire>"),
 chkbox
  )
 );
index 15102e3537e50c2f93b424bf3d6fbea451ceeddb..99e8562a6c0569f38e99a724c7da66ba7ed18d9e 100644 (file)
@@ -34,7 +34,7 @@
      </tr>
     </table>
     <button type="submit" class="bp" name='action' value='add_media'>
-     <img src='/bweb/add.png' alt=''>__Add__<button>
+     <img src='/bweb/add.png' alt=''>__Add__</button>
    </form>
 </div>
 
index a7399eaf17028b13d22d1b21bd1ce3d69392c44f..8b73fb23f79125b310fefd2654b069f2ca547dd5 100644 (file)
@@ -76,9 +76,12 @@ if ('__Main__' == ('_' + '_Main_' + '_')) {
 </TMPL_IF>
 </li>
  <li> <a href="bweb.pl?action=about"> __About__ </a> </li>
- <li style="padding: 0.25em 2em;float: right;">&nbsp;__Logged as__ <TMPL_VAR NAME=loginname> </li>
+ <li style="padding: 0.25em 2em;float: right;">&nbsp;
+<TMPL_IF loginname>__Logged as__ <TMPL_VAR loginname></TMPL_IF>
+<TMPL_IF cur_name>__on__ <TMPL_VAR cur_name></TMPL_IF>
+</li>
  <li style="float: right;white-space: nowrap;">
-<button type="submit" class="bp" class="button" title="__Search media__" onclick="search_media();"><img src="/bweb/tape.png" alt=''></button><button type="submit" title="__Search client__" onclick="search_client();" class='bp'><img src="/bweb/client.png" alt=''></button><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__"></button></li>
+<button type="submit" class="bp" class="button" title="__Search media__" onclick="search_media();"><img src="/bweb/tape.png" alt=''></button><button type="submit" title="__Search client__" onclick="search_client();" class='bp'><img src="/bweb/client.png" alt=''></button><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'>
index ef0c25fa0e25c7e8a0f36501f4c2a56bbcc58b25..8530023dbacb0f374893bf3bdf5a6bd40dd5dc0f 100644 (file)
@@ -14,14 +14,15 @@ document.getElementById('status_<TMPL_VAR status>').checked = true;
 
 var header = new Array("JobId",
                       "__Client__",
-                      "__Job Name__", 
+                      "__Job Name__",
+                       "__Comment__",
                       "__FileSet__",
 //                     "__Pool__",
                        "__Level__",
                        "__StartTime__",
                       "__Duration__",
                        "__JobFiles__",
-                       "__JobBytes__", 
+                       "__JobBytes__",
                        "__Errors__",
                       "__Status__");
 
@@ -42,12 +43,13 @@ a.appendChild(img);
 data.push( new Array(
 "<TMPL_VAR JobId>",
 "<TMPL_VAR Client>",     
-"<TMPL_VAR JobName>",    
+"<TMPL_VAR JobName>",
+"<TMPL_VAR Comment>",
 "<TMPL_VAR FileSet>",    
 //"<TMPL_VAR Pool>",
 "<TMPL_VAR Level>",      
 "<TMPL_VAR StartTime>",
-"<TMPL_VAR Duration>",
+human_duration("<TMPL_VAR Duration>"),
 "<TMPL_VAR JobFiles>",   
 human_size(<TMPL_VAR JobBytes>),
 "<TMPL_VAR joberrors>",   
index 69ec1f9b8a2d6b618edac7fb8fc29952fb0246fb..f8eafd0b6eb54bb6a555cfb4da0bc9b832dbbf50 100644 (file)
@@ -1,5 +1,6 @@
  <div class='titlediv'>
-  <h1 class='newstitle'>__Information about job__</h1>
+  <h1 class='newstitle'>__Information about job__ <i><TMPL_VAR JobName></i>
+       <TMPL_IF comment>(<TMPL_VAR comment>)</TMPL_IF></h1>
  </div>
  <div class="bodydiv">
  <table id='id0'></table>
@@ -88,6 +89,7 @@ var header = new Array("JobId",
                       "__Duration__",
                        "__JobFiles__",
                        "__JobBytes__",
+//                       "__Comp__",
                        "__Errors__",
                       "__Pool__",
                        "__Volume Name__",
@@ -108,9 +110,10 @@ data.push( new Array(
 "<TMPL_VAR FileSet>",    
 "<TMPL_VAR Level>",      
 "<TMPL_VAR StartTime>",
-"<TMPL_VAR duration>",
+human_duration("<TMPL_VAR duration>"),
 "<TMPL_VAR JobFiles>",   
 human_size(<TMPL_VAR JobBytes>),
+//parseInt(100-100*<TMPL_VAR JobBytes>/(<TMPL_VAR ReadBytes>+0.00001), 10) + "%",
 "<TMPL_VAR joberrors>",
 "<TMPL_VAR poolname>",
 "<TMPL_LOOP volumes><TMPL_VAR VolumeName>\n</TMPL_LOOP>",   
index 89adcec886c42f518cf0b0c68ac489fa83d940ee..7487343516dfcc7fc922bcccd9a2cb8a8f59c9fd 100644 (file)
@@ -62,7 +62,7 @@ d,
 "<TMPL_VAR poolname>",
 "<TMPL_VAR mediatype>",
 "<TMPL_VAR lastwritten>",
-"<TMPL_VAR expire>",
+timestamp_to_iso("<TMPL_VAR expire>"),
 chkbox
  )
 );
index 655da33a55e1631ffc7954dc7b535bedbaa37482..1f676bfb5fceb6be81990de15fca43d6232708c5 100644 (file)
@@ -56,7 +56,7 @@ human_enabled("<TMPL_VAR enabled>"),
 "<TMPL_VAR location>",
 "<TMPL_VAR volstatus>",
 human_size(<TMPL_VAR nb_bytes>),
-"<TMPL_VAR expire>",
+timestamp_to_iso("<TMPL_VAR expire>"),
 human_sec(<TMPL_VAR volretention>),
 human_sec(<TMPL_VAR voluseduration>),
 "<TMPL_VAR maxvoljobs>"
index dc9be23886d68f5d0b56cd902f9a7ad7c8705d34..f679201f5f79e3017fb363d6562cd483bf25e22b 100644 (file)
@@ -37,7 +37,7 @@ data.push( new Array(
 "<TMPL_VAR mediatype>",
 "<TMPL_VAR name>",
 "<TMPL_VAR lastwritten>",
-"<TMPL_VAR expire>",
+timestamp_to_iso("<TMPL_VAR expire>"),
 chkbox
  )
 );
index 1ad308c53017efb5ba65b4f0c790e332f4645e49..41cc9f228d007988ad854aee71afd65138621682 100644 (file)
@@ -39,7 +39,7 @@ data.push( new Array(
 "<TMPL_VAR mediatype>",
 "<TMPL_VAR name>",
 "<TMPL_VAR lastwritten>",
-"<TMPL_VAR expire>",
+timestamp_to_iso("<TMPL_VAR expire>"),
 chkbox
  )
 );
index 8ba90c7a7c1df8f54731f48f0ee374342c7c3ed7..8a42aa40535e338bcac85428bd990da98bf4309e 100644 (file)
      <option id='level_Incremental' value='Incremental'>__Incremental__</option>
      <option id='level_Full' value='Full'>__Full__</option>
      <option id='level_Differential' value='Differential'>__Differential__</option>
+     <option id='level_Base' value='Base'>__Base__</option>
    </select>
+   </td></tr><tr><td>__Comment:__ </td><td>
+   <input type='text' title='Comment about this job'
+          size='17' name='comment' value='<TMPL_VAR comment>'>
 
    </td></tr><tr id='more1' style="visibility:hidden"><td>__Start Time:__ </td><td>
    <input type='text' title='YYYY-MM-DD HH:MM:SS'
index 60967e59b5671dc9f4d8e0e6f35d3584fae4b89e..34c0f935324fcd8e81b2e37d4027cfc7005b1cd4 100644 (file)
@@ -54,7 +54,7 @@ data.push( new Array(
 "<TMPL_VAR JobName>",    
 joblevel['<TMPL_VAR Level>'],      
 "<TMPL_VAR StartTime>",
-"<TMPL_VAR duration>",
+human_sec2("<TMPL_VAR duration>"),
 //"<TMPL_VAR JobFiles>",   
 //"<TMPL_VAR JobBytes>",
 a,
index c76ab7703d717c1d292ed59b97691bf0266bad75..ed8f5b79628974127fcf3f6b10131517f022db0e 100644 (file)
@@ -3,7 +3,7 @@
   <h1 class='newstitle'> <TMPL_IF title><TMPL_VAR title><TMPL_ELSE>__Next Jobs__ </TMPL_IF></h1>
  </div>
  <div class='bodydiv'>
-    <form name='form1' action='<TMPL_VAR cginame>?' method='GET'>
+    <form name='form1' action='?' method='GET'>
      <table id='id<TMPL_VAR ID>'></table>
      <button type="submit" class="bp" name='action' title='__Run now__' value='run_job_mod'>
        <img src='/bweb/R.png' alt=''>  __Run now__ </button>