10) accessing to bweb
 11) setting mysql read-only account
 12) get more statistics
+13) use groups with bweb
 
 ################ FILE COPY #####################################
  # you must get bweb svn files
 
  That should show you if any of the pre-requisites are missing.
  On SuSE 10.2, I was able to load all the appropriate modules via rpms,
-  with the exception of Expect and Time::ParseDate, which I loaded manually
-  using perl.
+ with the exception of Expect and Time::ParseDate, which I loaded manually
+ using perl.
 
-  If you experience problems, always consult the Apache error_log
-  file.
+ If you experience problems, always consult the Apache error_log
+ file.
 
 ################ APACHE CONFIGURATION ##########################
 
 You have to remove "<!-- Remove this to activate bfileview" and "-->" from
 tpl/display_job_zoom.tpl.
 
-You must use brestore.pl -b to initialize the database, and
-you can use bfileview.pl mode=batch jobid=xxx where=/ to compute tree size.
+You MUST use brestore.pl -b 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.
 INSERT INTO job_old 
   (SELECT * FROM Job WHERE JobId NOT IN (SELECT JobId FROM job_old) );
 
+################ USE GROUPS WITH BWEB ##########################
+
+It works with postgresql and mysql5 (4 not tested).
+To enable group, load bweb/script/bweb_group.sql into your catalog
+
 ################################################################
 
 Enjoy !
 
           Release Notes for bweb 2.2
 
+2007/05/31
+ - add group management (see INSTALL to enable it)
+
 2007/05/23
  - put Bconsole error to stderr (error.log)
 
 
 $legend = ($legend eq 'on')?1:0;
 
 my $arg = $bweb->get_form(qw/width height limit offset age where jobid
-                            jfilesets level status jjobnames jclients/);
+                            jfilesets level status jjobnames jclients jclient_groups/);
 
 my ($limitq, $label) = $bweb->get_limit(age   => $arg->{age},
                                        limit => $arg->{limit},
     $arg->{jclients} = 'all';  # skip warning
 }
 
+my $groupf='';                 # from clause
+my $groupq='';                 # whre clause
+if ($arg->{jclient_groups}) {
+    $groupf = " JOIN client_group_member ON (Client.ClientId = client_group_member.clientid) 
+                JOIN client_group USING (client_group_id)";
+    $groupq = " AND client_group_name IN ($arg->{jclient_groups}) ";
+}
+
 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, Client, FileSet
+FROM $jobt, FileSet, Client $groupf
 WHERE $jobt.ClientId = Client.ClientId
   AND $jobt.FileSetId = FileSet.FileSetId
   AND $jobt.Type = 'B'
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
        Client.Name                      AS clientname,
        $jobt.Name                       AS jobname,
        $jobt.JobFiles                   AS jobfiles
-FROM $jobt, Client, FileSet
+FROM $jobt, FileSet, Client $groupf
 WHERE $jobt.ClientId = Client.ClientId
   AND $jobt.FileSetId = FileSet.FileSetId
   AND $jobt.Type = 'B'
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
        Job.Name                         AS jobname,
        base64_decode_lstat(8,LStat)     AS lstat
 
-FROM Job, Client, FileSet, Filename, Path, File
+FROM Job, FileSet, Filename, Path, File, Client
 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, Client, FileSet
+FROM $jobt, FileSet, Client $groupf
 WHERE $jobt.ClientId = Client.ClientId
   AND $jobt.FileSetId = FileSet.FileSetId
   AND $jobt.Type = 'B'
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
   $bweb->{sql}->{SEC_TO_INT}(  $bweb->{sql}->{UNIX_TIMESTAMP}(EndTime)  
                              - $bweb->{sql}->{UNIX_TIMESTAMP}(StartTime)) 
          AS duration
-FROM $jobt, Client, FileSet
+FROM $jobt, FileSet, Client $groupf
 WHERE $jobt.ClientId = Client.ClientId
   AND $jobt.FileSetId = FileSet.FileSetId
   AND $jobt.Type = 'B'
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limitq
 ";
 
 SELECT
      " . ($per_t?"":"UNIX_TIMESTAMP") . "($stime) AS A,
      $t(JobBytes)                  AS nb
-FROM $jobt, Client, FileSet
+FROM $jobt, FileSet, Client $groupf
 WHERE $jobt.ClientId = Client.ClientId
   AND $jobt.FileSetId = FileSet.FileSetId
   AND $jobt.Type = 'B'
   $filesetq
   $levelq
   $jobnameq
+  $groupq
 $limit
 ";
 
 
     print "<div><table border='0'><tr><td valign='top'>\n";
     my $fields = $bweb->get_form(qw/status level db_clients db_filesets
                                    limit age offset qclients qfilesets
-#                                  db_client_groups qclient_groups
-                                   jobtype qpools db_pools/);
+                                   jobtype qpools db_pools
+                                   db_client_groups qclient_groups/); # drop this to hide 
+
     $bweb->display($fields, "display_form_job.tpl");
 
     print "</td><td valign='top'>";
        }
     }
 
+} elsif ($action eq 'group_stats') {
+
+    $bweb->display_group_stats(age => $arg->{age});
+
 } elsif ($action eq 'running') {
     $bweb->display_running_jobs(1);
 
 
 
 <ul id="menu">
  <li><a href="bweb.pl?">Principal</a> </li>
- <li><a href="bweb.pl?action=client">Clientes</a></li>
+ <li><a href="bweb.pl?action=client">Clientes</a>
+     <ul>
+       <li><a href="bweb.pl?action=client">Clientes</a> </li>
+       <li><a href="bweb.pl?action=groups">Groups</a> </li>
+     </ul>
+ </li>
  <li><a href="bweb.pl?action=run_job">Jobs</a>
    <ul> 
      <li><a href="bweb.pl?action=run_job">Jobs Definidos</a>
 
 <br/>
  <div class='titlediv'>
-  <h1 class='newstitle'> Cliente : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=label>)</h1>
+  <h1 class='newstitle'> Cliente : <TMPL_VAR clientname> (<TMPL_VAR label>)</h1>
  </div>
  <div class='bodydiv'>
 <form action='?'
-     <table id='id<TMPL_VAR NAME=ID>'></table>
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Not enough data' >  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Not enough data'>  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Not enough data'>  
+     <table id='id<TMPL_VAR ID>'></table>
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Not enough data' >  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Not enough data'>  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Not enough data'>  
 <!--   <div class="otherboxtitle">
           Actions  
         </div>
 var data = new Array();
 
 data.push( 
-  new Array( "<TMPL_VAR NAME=clientname>", 
-            "<TMPL_VAR NAME=nb_jobs>",
-            human_size(<TMPL_VAR NAME=nb_bytes>),
-            "<TMPL_VAR NAME=nb_files>",
-            "<TMPL_VAR NAME=nb_err>"
+  new Array( "<TMPL_VAR clientname>", 
+            "<TMPL_VAR nb_jobs>",
+            human_size(<TMPL_VAR nb_bytes>),
+            "<TMPL_VAR nb_files>",
+            "<TMPL_VAR nb_err>"
              )
 ) ; 
 
 nrsTable.setup(
 {
- table_name:     "id<TMPL_VAR NAME=ID>",
+ table_name:     "id<TMPL_VAR ID>",
  table_header: header,
  table_data: data,
  up_icon: up_icon,
 
      title='Supprimer' src='/bweb/remove.png'> 
     <input type="image" name='action' value='groups_edit' title='Modify' src='/bweb/edit.png'> 
 
-    <input type="image" name='action' value='groups' title='View'
-     src='/bweb/zoom.png'>
+    <input type="image" name='action' value='client' title='View members'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='job' title='View jobs'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='group_stats' title='Estadísticas del groups' src='/bweb/chart.png'> 
    </form>
  </div>
 
 var data = new Array();
 var chkbox;
 
-<TMPL_LOOP groups>
+<TMPL_LOOP db_client_groups>
 
 chkbox = document.createElement('INPUT');
 chkbox.type  = 'radio';
 chkbox.name  = 'client_group';
-chkbox.value = '<TMPL_VAR client_group>';
+chkbox.value = '<TMPL_VAR name>';
 
 data.push( new Array(
-"<TMPL_VAR client_group>",
+"<TMPL_VAR name>",
 chkbox
  )
 );
 
 
 <ul id="menu">
  <li><a href="bweb.pl?">Accueil</a> </li>
- <li><a href="bweb.pl?action=client">Clients</a></li>
+ <li><a href="bweb.pl?action=client">Clients</a>
+     <ul>
+       <li><a href="bweb.pl?action=client">Clients</a> </li>
+       <li><a href="bweb.pl?action=groups">Groupes</a> </li>
+     </ul>
+ </li>
  <li><a href="bweb.pl?action=run_job">Jobs</a>
    <ul> 
      <li><a href="bweb.pl?action=run_job">Jobs définis</a>
 
 <br/>
  <div class='titlediv'>
-  <h1 class='newstitle'> Client : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=label>)</h1>
+  <h1 class='newstitle'> Client : <TMPL_VAR clientname> (<TMPL_VAR label>)</h1>
  </div>
  <div class='bodydiv'>
 <form action='?'
-     <table id='id<TMPL_VAR NAME=ID>'></table>
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Données insufisantes' >  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Données insufisantes'>  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Données insufisantes'>  
+     <table id='id<TMPL_VAR ID>'></table>
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Données insufisantes' >  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Données insufisantes'>  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Données insufisantes'>  
 <!--   <div class="otherboxtitle">
           Actions  
         </div>
 var data = new Array();
 
 data.push( 
-  new Array( "<TMPL_VAR NAME=clientname>", 
-            "<TMPL_VAR NAME=nb_jobs>",
-            human_size(<TMPL_VAR NAME=nb_bytes>),
-            "<TMPL_VAR NAME=nb_files>",
-            "<TMPL_VAR NAME=nb_err>"
+  new Array( "<TMPL_VAR clientname>", 
+            "<TMPL_VAR nb_jobs>",
+            human_size(<TMPL_VAR nb_bytes>),
+            "<TMPL_VAR nb_files>",
+            "<TMPL_VAR nb_err>"
              )
 ) ; 
 
 nrsTable.setup(
 {
- table_name:     "id<TMPL_VAR NAME=ID>",
+ table_name:     "id<TMPL_VAR ID>",
  table_header: header,
  table_data: data,
  up_icon: up_icon,
 
     <select name='level' class='formulaire'>
       <option id='level_Any' value='Any'>Tous</option>
       <option id='level_F' value='F'>Full</option>
-      <option id='level_D' value='D'>Différentielle</option>
-      <option id='level_I' value='I'>Incrémentale</option>
+      <option id='level_D' value='D'>Différentielle</option>
+      <option id='level_I' value='I'>Incrémentale</option>
     </select>     
   </td>
 </tr>
       <option id='status_T'   value='T'>Ok</option>
       <option id='status_W'   value='W'>Warning</option>
       <option id='status_f'   value='f'>Erreur</option>
-      <option id='status_A'   value='A'>Annulé</option>
+      <option id='status_A'   value='A'>Annulé</option>
     </select>     
   </td>
 </tr>
 </tr>
 <tr>
   <td valign='top'>
-    <h2>Période</h2>
+    <h2>Période</h2>
     <select name='age' class='formulaire'>
       <option id='age_86400'   value='86400'>Hier</option>
       <option id='age_604800'   value='604800'>Cette semaine</option>
  </tr>
  <tr>
   <td valign='bottom'> 
-    <h2>Nombre d'éléments</h2>
+    <h2>Nombre d'éléments</h2>
     <input type='text' name='limit' value='<TMPL_VAR limit>' 
        class='formulaire' size='4'>
   </td>
 
      title='Supprimer' src='/bweb/remove.png'> 
     <input type="image" name='action' value='groups_edit' title='Modifier' src='/bweb/edit.png'> 
 
-    <input type="image" name='action' value='groups' title='Voir les groupes'
-     src='/bweb/zoom.png'>
+    <input type="image" name='action' value='client' title='Voir les membres'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='job' title='Voir les jobs'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='group_stats' title='Statistiques sur le groupe' src='/bweb/chart.png'> 
    </form>
  </div>
 
 var data = new Array();
 var chkbox;
 
-<TMPL_LOOP groups>
+<TMPL_LOOP db_client_groups>
 
 chkbox = document.createElement('INPUT');
 chkbox.type  = 'radio';
 chkbox.name  = 'client_group';
-chkbox.value = '<TMPL_VAR client_group>';
+chkbox.value = '<TMPL_VAR name>';
 
 data.push( new Array(
-"<TMPL_VAR client_group>",
+"<TMPL_VAR name>",
 chkbox
  )
 );
 
     my ($self) = @_;
 
     my $where='';
-    my $arg = $self->get_form("client", "qre_client");
+    my $arg = $self->get_form("client", "qre_client", "jclient_groups");
 
     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})";
     }
 
     my $query = "
 {
     my ($self) = @_;
 
-    my $query = "
-SELECT client_group_name AS client_group 
-FROM client_group ORDER BY client_group_name;
-";
+    my $arg = $self->get_form(qw/db_client_groups/) ;
 
-    my $groups = $self->dbh_selectall_hashref($query, 'client_group');
+    if ($self->{dbh}->errstr) {
+       return $self->error("Can't use groups with bweb, read INSTALL to enable them");
+    }
 
-    $self->debug($groups);
+    $self->debug($arg);
 
     $self->display({ ID => $cur_id++,
-                    groups => [ values %$groups ] },
+                    %$arg},
                   "display_groups.tpl");
 }
 
     my ($self, %arg) = @_ ;
 
     my $client = $self->dbh_quote($arg{clientname});
+
     my ($limit, $label) = $self->get_limit(%arg);
 
     my $query = "
     sum(Job.JobErrors)   AS nb_err,
     sum(Job.JobFiles)    AS nb_files,
     Client.Name          AS clientname
-FROM Job INNER JOIN Client USING (ClientId)
+FROM Job JOIN Client USING (ClientId)
 WHERE 
     Client.Name = $client
     $limit 
 
     $row->{ID} = $cur_id++;
     $row->{label} = $label;
+    $row->{grapharg} = "client";
+
+    $self->display($row, "display_client_stats.tpl");
+}
+
+
+sub display_group_stats
+{
+    my ($self, %arg) = @_ ;
+
+    my $carg = $self->get_form(qw/qclient_group/);
+
+    my ($limit, $label) = $self->get_limit(%arg);
+
+    my $query = "
+SELECT 
+    count(Job.JobId)     AS nb_jobs,
+    sum(Job.JobBytes)    AS nb_bytes,
+    sum(Job.JobErrors)   AS nb_err,
+    sum(Job.JobFiles)    AS nb_files,
+    client_group.client_group_name  AS clientname
+FROM Job JOIN Client USING (ClientId) 
+         JOIN client_group_member ON (Client.ClientId = client_group_member.clientid) 
+         JOIN client_group USING (client_group_id)
+WHERE 
+    client_group.client_group_name = $carg->{qclient_group}
+    $limit 
+GROUP BY client_group.client_group_name
+";
+
+    my $row = $self->dbh_selectrow_hashref($query);
+
+    $row->{ID} = $cur_id++;
+    $row->{label} = $label;
+    $row->{grapharg} = "client_group";
 
     $self->display($row, "display_client_stats.tpl");
 }
     my $whereW = '';
 
     my $arg = $self->get_form('jmediatypes', 'qmediatypes');
-    if ($arg->{jmediatypes}) {
+    if ($arg->{jmediatypes}) { 
        $whereW = "WHERE MediaType IN ($arg->{jmediatypes}) ";
        $whereA = "AND   MediaType IN ($arg->{jmediatypes}) ";
     }
 
 
 <ul id="menu">
  <li><a href="bweb.pl?">Main</a> </li>
- <li><a href="bweb.pl?action=client">Clients</a></li>
+ <li><a href="bweb.pl?action=client">Clients</a>
+     <ul>
+       <li><a href="bweb.pl?action=client">Clients</a> </li>
+       <li><a href="bweb.pl?action=groups">Groups</a> </li>
+     </ul>
+ </li>
  <li><a href="bweb.pl?action=run_job">Jobs</a>
    <ul> 
      <li><a href="bweb.pl?action=run_job">Defined Jobs</a>
 
 <br/>
  <div class='titlediv'>
-  <h1 class='newstitle'> Client : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=label>)</h1>
+  <h1 class='newstitle'> Client : <TMPL_VAR clientname> (<TMPL_VAR label>)</h1>
  </div>
  <div class='bodydiv'>
-<form action='?'
-     <table id='id<TMPL_VAR NAME=ID>'></table>
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Not enough data' >  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Not enough data'>  
-     <img src="bgraph.pl?client=<TMPL_VAR NAME=clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Not enough data'>  
+<form action='?'>
+     <table id='id<TMPL_VAR ID>'></table>
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_duration;age=2592000;width=420;height=200" alt='Not enough data' >  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_rate;age=2592000;width=420;height=200" alt='Not enough data'>  
+     <img src="bgraph.pl?<TMPL_VAR grapharg>=<TMPL_VAR clientname>;graph=job_size;age=2592000;width=420;height=200" alt='Not enough data'>  
 <!--   <div class="otherboxtitle">
           Actions  
         </div>
 var data = new Array();
 
 data.push( 
-  new Array( "<TMPL_VAR NAME=clientname>", 
-            "<TMPL_VAR NAME=nb_jobs>",
-            human_size(<TMPL_VAR NAME=nb_bytes>),
-            "<TMPL_VAR NAME=nb_files>",
-            "<TMPL_VAR NAME=nb_err>"
+  new Array( "<TMPL_VAR clientname>", 
+            "<TMPL_VAR nb_jobs>",
+            human_size(<TMPL_VAR nb_bytes>),
+            "<TMPL_VAR nb_files>",
+            "<TMPL_VAR nb_err>"
              )
 ) ; 
 
 nrsTable.setup(
 {
- table_name:     "id<TMPL_VAR NAME=ID>",
+ table_name:     "id<TMPL_VAR ID>",
  table_header: header,
  table_data: data,
  up_icon: up_icon,
 
      title='Supprimer' src='/bweb/remove.png'> 
     <input type="image" name='action' value='groups_edit' title='Modify' src='/bweb/edit.png'> 
 
-    <input type="image" name='action' value='groups' title='View'
-     src='/bweb/zoom.png'>
+    <input type="image" name='action' value='client' title='View members'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='job' title='View jobs'
+     src='/bweb/zoom.png'> 
+    <input type="image" name='action' value='group_stats' title='Statistics' src='/bweb/chart.png'> 
    </form>
  </div>
 
 var data = new Array();
 var chkbox;
 
-<TMPL_LOOP groups>
+<TMPL_LOOP db_client_groups>
 
 chkbox = document.createElement('INPUT');
 chkbox.type  = 'radio';
 chkbox.name  = 'client_group';
-chkbox.value = '<TMPL_VAR client_group>';
+chkbox.value = '<TMPL_VAR name>';
 
 data.push( new Array(
-"<TMPL_VAR client_group>",
+"<TMPL_VAR name>",
 chkbox
  )
 );