--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
--- /dev/null
+################################################################
+# INSTALL NOTES #
+################################################################
+
+Bweb works well with 1.39 release or later.
+
+1) install Perl lib
+2) copy your files
+3) initialize your configuration file
+4) do some sql stuff (for postgresql users)
+5) use the -n option on bconsole so that it works with Expect
+6) get bacula log more useful
+7) bweb limitation
+8) using sudo with autochanger
+9) using bfileview.pl
+10) accessing to bweb
+
+################ FILE COPY #####################################
+ # you must get bweb cvs files
+ cvs -d:pserver:anonymous@bacula.cvs.sourceforge.net:/cvsroot/bacula login
+ cvs -z3 -d:pserver:anonymous@bacula.cvs.sourceforge.net:/cvsroot/bacula co -P gui
+ cd gui
+
+ # or get them from the released tar files or from the apt or rpms.
+
+ #
+ # Once you have the gui directory loaded, follow the instructions below
+ # or edit the environment variables in the file install_bweb
+ # then execute it. install_web does everything to the next
+ # set of #######'s.
+ #
+
+ # first, copy the bweb Perl library into your PERL5 INC path
+ perl Makefile.PL
+ make install
+ # or
+ install -m 644 -o root -g root bweb/lib/*.pm /usr/share/perl5
+
+ # copy the bweb perl program to your cgi location
+ mkdir -m 755 /usr/lib/cgi-bin/bweb
+ install -m 755 -o root -g root bweb/cgi/*.pl /usr/lib/cgi-bin/bweb
+
+ # get a config file
+ mkdir -m 755 /etc/bacula
+ chown root:bacula /etc/bacula
+ echo '$VAR1 = { template_dir => "/usr/share/bweb/tpl" };' > /etc/bacula/bweb.conf
+ chown www-data /etc/bacula/bweb.conf
+
+ # copy the bweb template file
+ mkdir -p /usr/share/bweb/tpl
+ install -m 644 -o root -g root bweb/tpl/*.tpl /usr/share/bweb/tpl
+
+ # copy the bweb graphics elements (bweb elements must reside in /bweb)
+ mkdir /var/www/bweb
+ install -m 644 -o root -g root bweb/html/*.{js,png,css,gif,ico,html} /var/www/bweb
+
+ # done !
+
+ 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 VERSION ############################
+
+Simply use lang/fr/tpl/*.tpl files instead of tpl/*.tpl
+
+################ INSTALL PERL LIBRARY ##########################
+
+ - perl modules
+ - DBI (with mysql or postgresql support DBD::Pg and DBD::mysql)
+ - GD::Graph
+ - GD
+ - HTML::Template
+ - CGI
+ - Expect
+ - Time::ParseDate
+
+ You can install perl modules with CPAN
+ perl -e shell -MCPAN
+ > install Expect
+
+ Or use your distribution
+ apt-get install libgd-graph-perl libhtml-template-perl libexpect-perl
+ apt-get install libdbd-mysql-perl libdbd-pg-perl libdbi-perl libtime-modules-perl
+
+ Note: the best way to test if you have all the dependencies
+ fullfilled for Perl is to:
+
+ cd /var/www/cgi-bin/bweb
+ ./bweb.pl
+
+ you might also want to try:
+
+ ./bgraph.pl
+
+ To make sure the graphing modules are loaded. However, running it
+ will spray binary graphics data on your terminal. Otherwise, you will
+ get an error.
+
+ 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.
+
+ If you experience problems, always consult the Apache error_log
+ file.
+
+################ APACHE CONFIGURATION ##########################
+
+It could be a good idea to protect your bweb installation (optional).
+
+Put this in your httpd.conf, and add user with htpasswd
+
+<Directory /usr/lib/cgi-bin/bweb>
+ Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
+ AuthType Basic
+ AuthName MyPrivateFile
+ AuthUserFile /etc/apache/htpasswd
+ AllowOverride None
+ Require valid-user
+</Directory>
+
+On SuSE 10.2, the following in /etc/apache2/default-server.conf works.
+However, it is not secure:
+
+Directory "/srv/www/cgi-bin/bweb">
+ AllowOverride None
+ Options +ExecCGI -Includes
+ Order allow,deny
+ Allow from all
+</Directory>
+
+
+################ CONFIGURATION #################################
+
+Note, this is automatically installed by the install_bweb script.
+
+/etc/bacula/bweb.conf looks like : (you can edit it inside bweb)
+$VAR1 = bless( {
+ 'graph_font' => '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf',
+ 'name' => undef,
+ 'config_file' => '/etc/bacula/bweb.conf',
+ 'bconsole' => '/usr/sbin/bconsole -n -c /etc/bacula/bconsole.conf',
+ 'ach_list' => {
+ 'S1_L80' => bless( {
+ 'info' => {
+ 'drive' => 0,
+ 'io' => 0,
+ 'slot' => 0
+ },
+ 'name' => 'S1_L80',
+ 'bweb' => undef,
+ 'device' => '/dev/changer',
+ 'drive' => [],
+ 'debug' => 0,
+ 'label' => {},
+ 'precmd' => 'sudo',
+ 'io' => [],
+ 'mtxcmd' => '/usr/sbin/mtx',
+ 'drive_name' => [
+ 'S1_L80_SDLT0',
+ 'S1_L80_SDLT1'
+ ],
+ 'slot' => []
+ }, 'Bweb::Autochanger' )
+ },
+ 'password' => 'xxx',
+ 'template_dir' => '/usr/share/bweb/tpl',
+ 'dbi' => 'DBI:mysql:database=bacula',
+ 'error' => '',
+ 'debug' => 0,
+ 'user' => 'bacula',
+ 'email_media' => 'eric@localhost'
+}, 'Bweb::Config' );
+
+
+################ BRESTORE ######################################
+
+If you want to use brestore with bweb, you must associate a mime type
+text/brestore with your brestore.pl, and you must install brestore.
+See gui/brestore/README for instructions on installing brestore.pl.
+
+################ POSTGRESQL NOTES ##############################
+
+If you want to use PostgreSQL instead of MySQL, you must add
+a function to the PostgreSQL Bacula database to get Bweb to work.
+
+psql -U bacula bacula < script/bweb-postgresql.sql
+
+Tips: PL must be enable in your cluster, you can do this with postgres user
+postgres:~$ echo 'CREATE PROCEDURAL LANGUAGE plpgsql;' | psql bacula
+
+################ BCONSOLE NOTES ################################
+
+You must use bconsole without conio/readline support. For that, use
+the bconsole -n option. This is done automatically with the standard
+configuration file. In addition, please ensure that the binary
+bconsole file can be executed by your web server. It is normally
+located in /usr/bin/bconsole (or some such file), and must have
+modes 755 or it will not be executable by your web server. The
+normal Bacula installation usually sets mode 750.
+
+################ BACULA LOG ####################################
+
+If you want Bweb to be able to show listings of the Job output,
+you must add a "catalog = all, !skipped, !saved" to your
+messages resource in bacula-dir.conf and restart your Bacula
+server. This is shown below:
+
+Messages {
+ Name = Standard
+
+ operator = root@localhost = mount
+ console = all, !skipped, !saved
+ append = "/tmp/bacula/var/bacula/working/log" = all, !skipped
+
+ catalog = all, !skipped, !saved
+}
+
+
+################ BWEB LIMITATION ###############################
+
+To get bweb working, you must follow these rules
+ - Media, Storage and Pool must have [A-Za-z_0-9\.-]+ (no space)
+ - AutoChanger name must be same as Device and Location name in bacula
+
+################ SUDO CONFIGURATION ############################
+
+If you use sudo, put this on you /etc/sudoers
+
+www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer transfer *
+www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer status
+www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer load *
+www-data ALL = (root) NOPASSWD: /usr/sbin/mtx -f /dev/changer unload *
+
+################ BFILEVIEW SETUP ###############################
+
+At this time, bfileview works only with PostgreSQL.
+
+Alias /bweb/fv /var/spool/bweb
+<Directory "/var/spool/bweb">
+ Options None
+ AllowOverride AuthConfig
+ Order allow,deny
+ Allow from all
+</Directory>
+
+mkdir /var/spool/bweb
+chmod 700 /var/spool/bweb
+chown www-data /var/spool/bweb
+
+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.
+
+At this time, it's a good idea to schedule brestore.pl -b after your
+BackupCatalog job.
+
+Job {
+ Name = "BackupCatalog"
+ ...
+ # This creates an ASCII copy of the catalog
+ RunBeforeJob = "/opt/bacula/etc/make_catalog_backup bacula bacula Pei0ahm9"
+ # This deletes the copy of the catalog
+ RunAfterJob = "/opt/bacula/etc/delete_catalog_backup"
+ RunAfterJob = "/usr/local/bin/brestore.pl -b"
+}
+
+To upgrade from an old installation, you can use :
+ALTER TABLE brestore_pathvisibility ADD Size int8;
+ALTER TABLE brestore_pathvisibility ADD Files int4;
+
+################ ACCESSING TO BWEB ###############################
+
+Now, you are able to launch firefox/mozilla and go on
+http://your-server/bweb
+
+Enjoy !
--- /dev/null
+lib/Bweb.pm
+lib/Bconsole.pm
+lib/CCircle.pm
+Makefile.PL
+INSTALL
+COPYING
+MANIFEST
--- /dev/null
+use Test::More tests => 7;
+
+require_ok('DBI');
+require_ok('GD::Graph');
+require_ok('GD');
+require_ok('HTML::Template');
+require_ok('CGI');
+require_ok('Expect');
+require_ok('Time::ParseDate');
+
+
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ ABSTRACT => "An other Bacula WEB interface",
+ AUTHOR => "Eric Bollengier <eric@eb.homelinux.org>",
+ NAME => 'Bweb',
+ VERSION => "lib/Bweb.pm",
+
+ #INST_BIN => "cgi",
+ # setup your cgi installation
+ # debian : /usr/lib/cgi-bin/bweb
+ # redhat/suse : /var/www/cgi-bin/bweb
+ #
+ #INSTALLSITEBIN => "/usr/lib/cgi-bin/bweb",
+);
--- /dev/null
+ Release Notes for bweb 2.0
+
+2007/01/25
+ - Add spanish translation, thanks to Santiago Alberch <caesarneron@gmail.com>
+
+2007/01/15
+ - fix do_update_media timeout bug
+
+2007/01/09
+ - fix stats bug on main page
+
+2007/01/01
+ - Add an index.html page on /bweb
+
+2006/12/31
+ - Move /etc/bweb/config to /etc/bacula/bweb.conf
+
+2006/12/29
+ - Add graphical backup view. See INSTALL to enable it. It's usefull
+ for tuning backup.
+ NOTES :
+ - You must use brestore -b to initialise database after BackupCatalog job
+ - If you have an old installation, you must alter your schema (see INSTALL)
+
+2006/12/14
+ - Add pool filter to job form
+
+2006/12/12
+ - Add More options button to run_job and Start Date field
+
+2006/12/09
+ - Update Copyright
+
+2006/12/05
+ - Add html form filter to media view
+
+2006/12/04
+ - Add mediatype filter to pool view
+
+2006/11/18
+ - Add system log to job log (JobId = 0)
+
+2006/11/18
+ - Add debian package
+ - Add cgi/bconsole.pl and BwebConsole package to keep bconsole installation and
+ scp usage on admin workstation.
+
+2006/11/16
+ - Add Comment and RecyclePool to Media Update
+
+2006/11/13:
+ - Use LogId instead of Time in get_log function
+
+2006/10/31:
+ - remove Purged and Recycle volume size from total pool size
+
+2006/10/26:
+ - Fix javascript sort bug
+ - Add french translation for bweb (template_dir must point to bweb/lang/fr/tpl)
+
+2006/10/14:
+ - Fix Mysql bug for pool display #639
+
+2006/09/20:
+ - Fix SQL query for pool display
+
+2006/09/08:
+ - Eject media takes Location as Autochanger Name
+ - Email is now sent when request to move media
+
+2006/09/06:
+ - Can delete empty location
+ - Drop Bconsole::send_cmd_with_drive and use drive= instead
+ - Use JavaScript to display Human size and time
+
+2006/08/31:
+ - Add FileSet view action
+ - Add view fileset to job_zoom
+
+2006/08/26:
+ - Add more graphics
+ o job avg size/days
+ o job avg size/hours
+ o ...
+
+2006/08/25:
+ - Cleanup
+ - Add more graphics
+ o job/days
+ o job/hours
+ o size/days
+ o size/hours
+ o ...
+
+2006/08/24:
+ - Add Location log to media zoom view
+
+2006/08/23:
+ - Add client/media search box (with regexp)
+ - Add menubar instead of plain menu
+ - Drop all <button> element (works now with IE)
+ - Fix big div
+ - Add favicon
+
+2006/08/22:
+ - Add autochanger module (add/del/mod)
+ - Add copyright notice
+
--- /dev/null
+#!/usr/bin/perl -w
+use strict;
+
+=head1 NAME
+
+ bconsole.pl - Interface between brestore and bweb to use bconsole.
+
+ You can also use Bconsole.pm and install bconsole on your workstation.
+
+=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 VERSION
+
+ $Id$
+
+=cut
+
+use Data::Dumper;
+use CGI;
+use POSIX qw/strftime/;
+use File::Temp qw/tempfile/;
+
+use Bweb;
+use Bconsole;
+
+my $conf = new Bweb::Config(config_file => $Bweb::config_file);
+$conf->load();
+
+my $bweb = new Bweb(info => $conf);
+my $bconsole = new Bconsole(pref => $conf);
+my $debug = $bweb->{debug};
+
+my $opts = $bweb->get_form('timeout');
+$bconsole->{timeout} = $opts->{timeout} || 40;
+
+# on doit utiliser du POST
+print CGI::header('text/plain');
+
+my @action = CGI::param('action') ;
+my %cmd = ( 'list_job' => '.job',
+ 'list_client' => '.client' ,
+ 'list_storage' => '.storage',
+ 'list_fileset' => '.fileset',
+ );
+
+
+my $have_run=0;
+for my $a (@action)
+{
+ if (defined $cmd{$a})
+ {
+ my $out = $bconsole->send_cmd($cmd{$a});
+ $out =~ s/\r\n/;/g ;
+ print "$a=$out\n";
+
+ } elsif ($a eq 'run' and $have_run==0) {
+ $have_run=1;
+
+ my $arg = $bweb->get_form(qw/job client storage fileset
+ where replace priority/);
+
+ my $bootstrap = CGI::param('bootstrap');
+
+ if (!$bootstrap or !$arg->{job} or !$arg->{client}) {
+ print "ERROR: missing bootstrap or job or client\n";
+ exit 1;
+ }
+
+ my ($fh, $filename) = tempfile();
+ while (my $l = <$bootstrap>) {
+ $fh->print($l);
+ }
+ close($fh);
+ chmod(0644, $filename);
+
+ my $jobid = $bconsole->run(job => $arg->{job},
+ client => $arg->{client},
+ storage => $arg->{storage},
+ fileset => $arg->{fileset},
+ where => $arg->{where},
+ replace => $arg->{replace},
+ priority => $arg->{priority},
+ bootstrap => $filename);
+
+ print "run=$jobid\n";
+
+ } else {
+ print STDERR "unknow argument $a\n";
+ }
+}
+
+exit 0;
--- /dev/null
+#!/usr/bin/perl -w
+
+=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 VERSION
+
+ $Id$
+
+=cut
+
+use strict;
+use POSIX qw/strftime/;
+use Bweb;
+use CCircle ;
+use Digest::MD5 qw(md5_hex);
+use File::Basename qw/basename dirname/;
+
+my $conf = new Bweb::Config(config_file => $Bweb::config_file);
+$conf->load();
+my $bweb = new Bweb(info => $conf);
+$bweb->connect_db();
+
+my $arg = $bweb->get_form('where', 'jobid');
+my $where = $arg->{where};
+my $jobid = $arg->{jobid};
+my $jobid_url = "jobid=$jobid";
+my $opt_level = 2 ;
+my $max_file = 20;
+my $batch = CGI::param("mode") || '';
+
+my $md5_rep = md5_hex("$where:$jobid") ;
+my $base_url = '/bweb/fv' ;
+my $base_fich = $conf->{fv_write_path};
+
+if ($where and $jobid and $batch eq 'batch') {
+ my $root = fv_get_root_pathid($where);
+ if ($root) {
+ fv_compute_size($jobid, $root);
+ exit 0;
+ }
+ exit 1;
+}
+
+print CGI::header('text/html');
+$bweb->display_begin();
+$bweb->display_job_zoom($jobid);
+
+unless ($where and $jobid) {
+ $bweb->error("Can't get where or jobid");
+ exit 0;
+}
+
+unless ($base_fich and -w $base_fich) {
+ $bweb->error("fv_write_path ($base_fich) is not writable." .
+ " See Bweb configuration.");
+ exit 0;
+}
+
+if (-f "$base_fich/$md5_rep.png" and -f "$base_fich/$md5_rep.tpl")
+{
+ $bweb->display({}, "$base_fich/$md5_rep.tpl");
+ $bweb->display_end();
+ exit 0;
+}
+
+my $attribs = fv_get_file_attribute($jobid, $where);
+if ($attribs->{found}) {
+ $bweb->display($attribs, 'fv_file_attribs.tpl');
+ $bweb->display_end();
+ exit 0;
+}
+
+if ($where !~ m!/$!) {
+ $where = $where . "/" ;
+}
+
+my $root = fv_get_root_pathid($where);
+if (!$root) {
+ $bweb->error("Can't find $where in catalog");
+ $bweb->display_end();
+ exit 0;
+}
+
+my $total = fv_compute_size($jobid, $root);
+
+my $url_action = "bfileview.pl?opt_level=$opt_level" ;
+my $top = new CCircle(
+ display_other => 1,
+ base_url => "$url_action;$jobid_url;where=$where",
+ ) ;
+
+fv_display_rep($top, $total, $root, $opt_level) ;
+
+$top->draw_labels() ;
+$top->set_title(Bweb::human_size($total)) ;
+
+open(OUT, ">$base_fich/$md5_rep.png") or die "$base_fich/$md5_rep.png $!";
+# make sure we are writing to a binary stream
+binmode OUT;
+# Convert the image to PNG and print it on standard output
+print OUT $CCircle::gd->png;
+close(OUT) ;
+
+open(OUT, ">$base_fich/$md5_rep.tpl") or die "$base_fich/$md5_rep.tpl $!";
+print OUT "
+ <form action='$url_action' method='get'>
+ <div align='right'>
+ <input title='jobids' type='hidden' name='jobid' value='$jobid'>
+ <input title='repertoire' type='text' name='where' value='$where'/>
+ <input type='submit' size='256' name='go' value='go'/>
+ </div>
+ </form>
+ <br/>
+" ;
+
+print OUT $top->get_imagemap($where, "$base_url/$md5_rep.png") ;
+close(OUT) ;
+
+$bweb->display({}, "$base_fich/$md5_rep.tpl");
+$bweb->display_end();
+
+sub fv_display_rep
+{
+ my ($ccircle, $max, $rep, $level) = @_ ;
+ return if ($max < 1);
+
+ my $sum = 0;
+ my $dirs = fv_list_dirs($jobid, $rep); # 0: pathid, 1: pathname
+
+ foreach my $dir (@{$dirs})
+ {
+ my $size = fv_compute_size($jobid, $dir->[0]);
+ $sum += $size;
+
+ my $per = $size * 100 / $max;
+ my $chld = $ccircle->add_part($per,
+ basename($dir->[1]) . '/',
+ basename($dir->[1])
+ . sprintf(' %.0f%% ', $per)
+ . Bweb::human_size($size)
+ ) ;
+
+ if ($chld and $level > 0) {
+ fv_display_rep($chld, $size, $dir->[0], $level - 1) ;
+ }
+ }
+
+ # 0: name, 1: size
+ my $files = fv_get_big_files($jobid, $rep, 3*100/$max, $max_file/($level+1));
+ foreach my $f (@{$files}) {
+ $ccircle->add_part($f->[1] * 100 / $max,
+ $f->[0],
+ $f->[0] . "\n" . Bweb::human_size($f->[1]));
+ $sum += $f->[1];
+ }
+
+ if ($sum < $max) {
+ $ccircle->add_part(($max - $sum) * 100 / $max,
+ "other files < 3%",
+ "other\n" . Bweb::human_size($max - $sum));
+ }
+
+ $ccircle->finalize() ;
+}
+
+sub fv_compute_size
+{
+ my ($jobid, $rep) = @_;
+
+ my $size = fv_get_size($jobid, $rep);
+ if ($size) {
+ return $size;
+ }
+
+ $size = fv_get_files_size($jobid, $rep);
+
+ my $dirs = fv_list_dirs($jobid, $rep);
+ foreach my $dir (@{$dirs}) {
+ $size += fv_compute_size($jobid, $dir->[0]);
+ }
+
+ fv_update_size($jobid, $rep, $size);
+ return $size;
+}
+
+sub fv_list_dirs
+{
+ my ($jobid, $rep) = @_;
+
+ my $ret = $bweb->dbh_selectall_arrayref("
+ SELECT P.PathId,
+ (
+ SELECT Path FROM Path WHERE PathId = P.PathId
+ UNION
+ SELECT Path FROM brestore_missing_path WHERE PathId = P.PathId
+ ) AS Path
+ FROM (
+ SELECT PathId
+ FROM brestore_pathvisibility
+ INNER JOIN brestore_pathhierarchy USING (PathId)
+ WHERE PPathId = $rep
+ AND JobId = $jobid
+ ) AS P
+");
+
+ return $ret;
+}
+
+sub fv_get_file_attribute
+{
+ my ($jobid, $full_name) = @_;
+
+ my $filename = $bweb->dbh_quote(basename($full_name));
+ my $path = $bweb->dbh_quote(dirname($full_name) . "/");
+
+ my $attr = $bweb->dbh_selectrow_hashref("
+ SELECT 1 AS found,
+ MD5 AS md5,
+ base64_decode_lstat(8, LStat) AS size,
+ base64_decode_lstat(11, LStat) AS atime,
+ base64_decode_lstat(12, LStat) AS mtime,
+ base64_decode_lstat(13, LStat) AS ctime
+
+ FROM File INNER JOIN Filename USING (FilenameId)
+ INNER JOIN Path USING (PathId)
+ WHERE Name = $filename
+ AND Path = $path
+ AND JobId = $jobid
+");
+
+ $attr->{filename} = $full_name;
+ $attr->{size} = Bweb::human_size($attr->{size});
+ foreach my $d (qw/atime ctime mtime/) {
+ $attr->{$d} = strftime('%F %H:%M', localtime($attr->{$d}));
+ }
+ return $attr;
+}
+
+sub fv_get_size
+{
+ my ($jobid, $rep) = @_;
+
+ my $ret = $bweb->dbh_selectrow_hashref("
+ SELECT Size AS size
+ FROM brestore_pathvisibility
+ WHERE PathId = $rep
+ AND JobId = $jobid
+");
+
+ return $ret->{size};
+}
+
+sub fv_get_files_size
+{
+ my ($jobid, $rep) = @_;
+
+ my $ret = $bweb->dbh_selectrow_hashref("
+ SELECT sum(base64_decode_lstat(8,LStat)) AS size
+ FROM File
+ WHERE PathId = $rep
+ AND JobId = $jobid
+");
+
+ return $ret->{size};
+}
+
+sub fv_get_big_files
+{
+ my ($jobid, $rep, $min, $limit) = @_;
+
+ my $ret = $bweb->dbh_selectall_arrayref("
+ SELECT Name, size
+ FROM (
+ SELECT FilenameId,base64_decode_lstat(8,LStat) AS size
+ FROM File
+ WHERE PathId = $rep
+ AND JobId = $jobid
+ ) AS S INNER JOIN Filename USING (FilenameId)
+ WHERE S.size > $min
+ ORDER BY S.size DESC
+ LIMIT $limit
+");
+
+ return $ret;
+}
+
+sub fv_update_size
+{
+ my ($jobid, $rep, $size) = @_;
+
+ my $nb = $bweb->dbh_do("
+ UPDATE brestore_pathvisibility SET Size = $size
+ WHERE JobId = $jobid
+ AND PathId = $rep
+");
+
+ return $nb;
+}
+
+sub fv_get_root_pathid
+{
+ my ($path) = @_;
+ $path = $bweb->dbh_quote($path);
+ my $ret = $bweb->dbh_selectrow_hashref("
+SELECT PathId FROM Path WHERE Path = $path
+ UNION
+SELECT PathId FROM brestore_missing_path WHERE PATH = $path
+");
+ return $ret->{pathid};
+}
+
+__END__
+
+CREATE OR REPLACE FUNCTION base64_decode_lstat(int4, varchar) RETURNS int8 AS $$
+DECLARE
+val int8;
+b64 varchar(64);
+size varchar(64);
+i int;
+BEGIN
+size := split_part($2, ' ', $1);
+b64 := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+val := 0;
+FOR i IN 1..length(size) LOOP
+val := val + (strpos(b64, substr(size, i, 1))-1) * (64^(length(size)-i));
+END LOOP;
+RETURN val;
+END;
+$$ language 'plpgsql';
+
+ALTER TABLE brestore_pathvisibility ADD Size int8;
+
+
+
+ALTER TABLE brestore_pathvisibility ADD Files int4;
--- /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 VERSION
+
+ $Id$
+
+=cut
+
+use Bweb;
+
+use Data::Dumper;
+use CGI;
+
+use POSIX qw/strftime/;
+use File::Basename qw/basename dirname/;
+
+my $conf = new Bweb::Config(config_file => $Bweb::config_file);
+$conf->load();
+
+my $bweb = new Bweb(info => $conf);
+$bweb->connect_db();
+my $dbh = $bweb->{dbh};
+my $debug = $bweb->{debug};
+
+my $graph = CGI::param('graph') || 'job_size';
+my $legend = CGI::param('legend') || 'on' ;
+$legend = ($legend eq 'on')?1:0;
+
+my $arg = $bweb->get_form(qw/width height limit offset age where jobid
+ jfilesets level status jjobnames jclients/);
+
+my ($limitq, $label) = $bweb->get_limit(age => $arg->{age},
+ limit => $arg->{limit},
+ offset=> $arg->{offset},
+ order => 'Job.StartTime ASC',
+ );
+
+my $statusq='';
+if ($arg->{status} and $arg->{status} ne 'Any') {
+ $statusq = " AND Job.JobStatus = '$arg->{status}' ";
+}
+
+my $levelq='';
+if ($arg->{level} and $arg->{level} ne 'Any') {
+ $levelq = " AND Job.Level = '$arg->{level}' ";
+}
+
+my $filesetq='';
+if ($arg->{jfilesets}) {
+ $filesetq = " AND FileSet.FileSet IN ($arg->{qfilesets}) ";
+}
+
+my $jobnameq='';
+if ($arg->{jjobnames}) {
+ $jobnameq = " AND Job.Name IN ($arg->{jjobnames}) ";
+} else {
+ $arg->{jjobnames} = 'all'; # skip warning
+}
+
+my $clientq='';
+if ($arg->{jclients}) {
+ $clientq = " AND Client.Name IN ($arg->{jclients}) ";
+} else {
+ $arg->{jclients} = 'all'; # skip warning
+}
+
+my $gtype = CGI::param('gtype') || 'bars';
+
+print CGI::header('image/png');
+
+sub get_graph
+{
+ my (@options) = @_;
+ my $graph;
+ if ($gtype eq 'lines') {
+ use GD::Graph::lines;
+ $graph = GD::Graph::lines->new ( $arg->{width}, $arg->{height} );
+
+ } elsif ($gtype eq 'bars') {
+ use GD::Graph::bars;
+ $graph = GD::Graph::bars->new ( $arg->{width}, $arg->{height} );
+
+ } elsif ($gtype eq 'linespoints') {
+ use GD::Graph::linespoints;
+ $graph = GD::Graph::linespoints->new ( $arg->{width}, $arg->{height} );
+
+# this doesnt works at this time
+# } elsif ($gtype eq 'bars3d') {
+# use GD::Graph::bars3d;
+# $graph = GD::Graph::bars3d->new ( $arg->{width}, $arg->{height} );
+
+ } else {
+ return undef;
+ }
+
+ $graph->set('x_label' => 'Time',
+ 'x_number_format' => sub { strftime('%D', localtime($_[0])) },
+ 'x_tick_number' => 1,
+ @options,
+ );
+
+ return $graph;
+}
+
+sub make_tab
+{
+ my ($all_row) = @_;
+
+ my $i=0;
+ my $last_date=0;
+
+ my $ret = {};
+
+ foreach my $row (@$all_row) {
+ my $label = $row->[1] . "/" . $row->[2] ; # client/backup name
+
+ $ret->{date}->[$i] = $row->[0];
+ $ret->{$label}->[$i] = $row->[3];
+ $i++;
+ $last_date = $row->[0];
+ }
+
+ # insert a fake element
+ foreach my $elt ( keys %{$ret}) {
+ $ret->{$elt}->[$i] = undef;
+ }
+
+ $ret->{date}->[$i] = $last_date + 1;
+
+ my $date = $ret->{date} ;
+ delete $ret->{date};
+
+ return ($date, $ret);
+}
+
+sub make_tab_sum
+{
+ my ($all_row) = @_;
+
+ my $i=0;
+ my $last_date=0;
+
+ my $ret = {};
+
+ foreach my $row (@$all_row) {
+ $ret->{date}->[$i] = $row->[0];
+ $ret->{nb}->[$i] = $row->[1];
+ $i++;
+ }
+
+ return ($ret);
+}
+
+if ($graph eq 'job_size') {
+
+ my $query = "
+SELECT
+ UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+ Client.Name AS clientname,
+ Job.Name AS jobname,
+ Job.JobBytes AS jobbytes
+FROM Job, Client, FileSet
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $obj = get_graph('title' => "Job Size : $arg->{jclients}/$arg->{jjobnames}",
+ 'y_label' => 'Size',
+ 'y_min_value' => 0,
+ 'y_number_format' => \&Bweb::human_size,
+ );
+
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+}
+
+if ($graph eq 'job_file') {
+
+ my $query = "
+SELECT
+ UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+ Client.Name AS clientname,
+ Job.Name AS jobname,
+ Job.JobFiles AS jobfiles
+FROM Job, Client, FileSet
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $obj = get_graph('title' => "Job Files : $arg->{jclients}/$arg->{jjobnames}",
+ 'y_label' => 'Number Files',
+ 'y_min_value' => 0,
+ );
+
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+}
+
+# it works only with postgresql at this time
+elsif ($graph eq 'file_histo' and $arg->{where}) {
+
+ my $dir = $dbh->quote(dirname($arg->{where}) . '/');
+ my $file = $dbh->quote(basename($arg->{where}));
+
+ my $query = "
+SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+ Client.Name AS client,
+ Job.Name AS jobname,
+ base64_decode_lstat(8,LStat) AS lstat
+
+FROM Job, Client, FileSet, Filename, Path, File
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ AND File.JobId = Job.JobId
+ AND File.FilenameId = Filename.FilenameId
+ AND File.PathId = Path.PathId
+ AND Path.Path = $dir
+ AND Filename.Name = $file
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my $obj = get_graph('title' => "File size : $arg->{where}",
+ 'y_label' => 'File size',
+ 'y_min_value' => 0,
+ 'y_min_value' => 0,
+ 'y_number_format' => \&Bweb::human_size,
+ );
+
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+}
+
+# it works only with postgresql at this time
+# TODO: use brestore_missing_path
+elsif ($graph eq 'rep_histo' and $arg->{where}) {
+
+ my $dir = $arg->{where};
+ $dir .= '/' if ($dir !~ m!/$!);
+ $dir = $dbh->quote($dir);
+
+ my $query = "
+SELECT UNIX_TIMESTAMP(Job.StartTime) AS starttime,
+ Client.Name AS client,
+ Job.Name AS jobname,
+ brestore_pathvisibility.size AS size
+
+FROM Job, Client, FileSet, Path, brestore_pathvisibility
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ AND Job.JobId = brestore_pathvisibility.JobId
+ AND Path.PathId = brestore_pathvisibility.PathId
+ AND Path.Path = $dir
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my $obj = get_graph('title' => "Directory size : $arg->{where}",
+ 'y_label' => 'Directory size',
+ 'y_min_value' => 0,
+ 'y_min_value' => 0,
+ 'y_number_format' => \&Bweb::human_size,
+ );
+
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+}
+
+elsif ($graph eq 'job_rate') {
+
+ my $query = "
+SELECT
+ UNIX_TIMESTAMP(Job.StartTime) 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
+
+FROM Job, Client, FileSet
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $obj = get_graph('title' => "Job Rate : $arg->{jclients}/$arg->{jjobnames}",
+ 'y_label' => 'Rate b/s',
+ 'y_min_value' => 0,
+ 'y_number_format' => \&Bweb::human_size,
+ );
+
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+}
+
+
+
+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
+FROM Job, Client, FileSet
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limitq
+";
+
+ print STDERR $query if ($debug);
+
+ my $obj = get_graph('title' => "Job Duration : $arg->{jclients}/$arg->{jjobnames}",
+ 'y_label' => 'Duration',
+ 'y_min_value' => 0,
+ 'y_number_format' => \&Bweb::human_sec,
+ );
+ my $all = $dbh->selectall_arrayref($query) ;
+
+ my ($d, $ret) = make_tab($all);
+ if ($legend) {
+ $obj->set_legend(keys %$ret);
+ }
+ print $obj->plot([$d, values %$ret])->png;
+
+
+# number of job per day/hour
+} elsif ($graph =~ /^job_(count|sum|avg)_((p?)(day|hour|month))$/) {
+ my $t = $1;
+ my $d = uc($2);
+ my $per_t = $3;
+ my ($limit, $label) = $bweb->get_limit(age => $arg->{age},
+ limit => $arg->{limit},
+ offset=> $arg->{offset},
+ groupby => "A",
+ );
+ my @arg; # arg for plotting
+
+ if (!$per_t) { # much better aspect
+ #$gtype = 'lines';
+ } else {
+ push @arg, ("x_number_format" => undef,
+ "x_min_value" => 0,
+ );
+ }
+
+ if ($t eq 'sum' or $t eq 'avg') {
+ push @arg, ('y_number_format' => \&Bweb::human_size);
+ }
+
+ my $query = "
+SELECT
+ " . ($per_t?"":"UNIX_TIMESTAMP") . "(" . $bweb->{sql}->{"STARTTIME_$d"} . ") AS A,
+ $t(JobBytes) AS nb
+FROM Job, Client, FileSet
+WHERE Job.ClientId = Client.ClientId
+ AND Job.FileSetId = FileSet.FileSetId
+ AND Job.Type = 'B'
+ $clientq
+ $statusq
+ $filesetq
+ $levelq
+ $jobnameq
+$limit
+";
+
+ print STDERR $query if ($debug);
+
+ my $obj = get_graph('title' => "Job $t : $arg->{jclients}/$arg->{jjobnames}",
+ 'y_label' => $t,
+ 'y_min_value' => 0,
+ @arg,
+ );
+
+ my $all = $dbh->selectall_arrayref($query) ;
+ my ($ret) = make_tab_sum($all);
+
+ print $obj->plot([$ret->{date}, $ret->{nb}])->png;
+}
+
--- /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 VERSION
+
+ $Id$
+
+=cut
+
+use Data::Dumper;
+use Bweb;
+use CGI;
+
+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);
+
+# just send data with text/brestore content
+if ($action eq 'restore') {
+ $bweb->restore();
+ exit 0;
+}
+
+my $arg = $bweb->get_form('jobid', 'limit', 'offset', 'age');
+
+$bweb->display_begin();
+
+# if no configuration, we send edit_conf
+if ($action ne 'apply_conf' and !$bweb->{info}->{dbi}) {
+ $action = 'edit_conf';
+}
+
+if ($action eq 'begin') { # main display
+ print "<div style='left=0;'><table border='0'><tr><td valign='top' width='100%'>\n";
+ $bweb->display_general(age => $arg->{age});
+ $bweb->display_running_jobs(0);
+ print "</td><td valign='top'>";
+ $bweb->display({}, "stats.tpl");
+ print "</td></tr></table></div>";
+ $bweb->display_job(limit => 10);
+
+} elsif ($action eq 'view_conf') {
+ $conf->view()
+
+} elsif ($action eq 'edit_conf') {
+ $conf->edit();
+
+} elsif ($action eq 'apply_conf') {
+ $conf->modify();
+
+} elsif ($action eq 'client') {
+ $bweb->display_clients();
+
+} elsif ($action eq 'pool') {
+ $bweb->display_pool();
+
+} elsif ($action eq 'location_edit') {
+ $bweb->location_edit();
+
+} elsif ($action eq 'location_save') {
+ $bweb->location_save();
+
+} elsif ($action eq 'location_add') {
+ $bweb->location_add();
+
+} elsif ($action eq 'location_del') {
+ $bweb->location_del();
+
+} elsif ($action eq 'media') {
+ print "<div><table border='0'><tr><td valign='top'>\n";
+ my $fields = $bweb->get_form(qw/db_locations db_pools
+ qlocations qpools volstatus qre_media
+ limit qmediatypes db_mediatypes/);
+ $bweb->display($fields, "display_form_media.tpl");
+
+ print "</td><td valign='top'>";
+ $bweb->display_media();
+ print "</td></tr></table></div>";
+
+} elsif ($action eq 'medias') {
+ $bweb->display_medias();
+
+} elsif ($action eq 'eject') {
+ my $arg = $bweb->get_form("ach");
+ my $a = $bweb->ach_get($arg->{ach});
+
+ if ($a) {
+ $a->status();
+ foreach my $slot (CGI::param('slot')) {
+ print $a->{error} unless $a->send_to_io($slot);
+ }
+
+ foreach my $media (CGI::param('media')) {
+ my $slot = $a->get_media_slot($media);
+ print $a->{error} unless $a->send_to_io($slot);
+ }
+
+ $a->display_content();
+ }
+
+} elsif ($action eq 'eject_media') {
+ $bweb->eject_media();
+
+} elsif ($action eq 'clear_io') {
+ my $arg = $bweb->get_form('ach');
+
+ my $a = $bweb->ach_get($arg->{ach});
+ if (defined $a) {
+ $a->status();
+ $a->clear_io();
+ $a->display_content();
+ }
+
+} elsif ($action eq 'ach_edit') {
+ $bweb->ach_edit();
+
+} elsif ($action eq 'ach_del') {
+ $bweb->ach_del();
+
+} elsif ($action eq 'ach_view') {
+ # TODO : get autochanger name and create it
+ $bweb->connect_db();
+ my $arg = $bweb->get_form('ach');
+
+ my $a = $bweb->ach_get($arg->{ach});
+ if ($a) {
+ $a->status();
+ $a->display_content();
+ }
+
+} elsif ($action eq 'ach_add') {
+ $bweb->ach_add();
+
+} elsif ($action eq 'ach_load') {
+ my $arg = $bweb->get_form('ach', 'drive', 'slot');
+
+ my $a = $bweb->ach_get($arg->{ach});
+
+ if (defined $a and defined $arg->{drive} and defined $arg->{slot})
+ {
+ my $b = new Bconsole(pref => $conf, timeout => 300, log_stdout => 1) ;
+ # TODO : use template here
+ print "<pre>\n";
+ $b->send_cmd("mount slot=$arg->{slot} drive=$arg->{drive} storage=\"" . $a->get_drive_name($arg->{drive}) . '"');
+ print "</pre>\n";
+ } else {
+ $bweb->error("Can't get drive, slot or ach");
+ }
+
+} elsif ($action eq 'ach_unload') {
+ my $arg = $bweb->get_form('drive', 'slot', 'ach');
+
+ my $a = $bweb->ach_get($arg->{ach});
+
+ if (defined $a and defined $arg->{drive} and defined $arg->{slot})
+ {
+ my $b = new Bconsole(pref => $conf, timeout => 300, log_stdout => 1) ;
+ # TODO : use template here
+ print "<pre>\n";
+ $b->send_cmd("umount drive=$arg->{drive} storage=\"" . $a->get_drive_name($arg->{drive}) . '"');
+ print "</pre>\n";
+
+ } else {
+ $bweb->error("Can't get drive, slot or ach");
+ }
+} elsif ($action eq 'intern_media') {
+ $bweb->help_intern();
+
+} elsif ($action eq 'compute_intern_media') {
+ $bweb->help_intern_compute();
+
+} elsif ($action eq 'extern_media') {
+ $bweb->help_extern();
+
+} elsif ($action eq 'compute_extern_media') {
+ $bweb->help_extern_compute();
+
+} elsif ($action eq 'extern') {
+ print "<div style='float: left;'>";
+ my @achs = $bweb->eject_media();
+ for my $ach (@achs) {
+ CGI::param('ach', $ach);
+ $bweb->update_slots();
+ }
+ print "</div><div style='float: left;margin-left: 20px;'>";
+ $bweb->move_media();
+ print "</div>";
+
+} elsif ($action eq 'move_email') {
+ $bweb->move_email();
+
+} elsif ($action eq 'change_location') {
+ $bweb->change_location();
+
+} elsif ($action eq 'location') {
+ $bweb->display_location();
+
+} elsif ($action eq 'about') {
+ $bweb->display($bweb, 'about.tpl');
+
+} elsif ($action eq 'intern') {
+ $bweb->move_media(); # TODO : remove that
+
+} elsif ($action eq 'move_media') {
+ $bweb->move_media();
+
+} elsif ($action eq 'save_location') {
+ $bweb->save_location();
+
+} elsif ($action eq 'update_location') {
+ $bweb->update_location();
+
+} elsif ($action eq 'update_media') {
+ $bweb->update_media();
+
+} elsif ($action eq 'do_update_media') {
+ $bweb->do_update_media();
+
+} elsif ($action eq 'update_slots') {
+ $bweb->update_slots();
+
+} elsif ($action eq 'graph') {
+ $bweb->display_graph();
+
+} elsif ($action eq 'next_job') {
+ $bweb->director_show_sched();
+
+} elsif ($action eq 'enable_job') {
+ $bweb->enable_disable_job(1);
+
+} elsif ($action eq 'disable_job') {
+ $bweb->enable_disable_job(0);
+
+} elsif ($action eq 'job') {
+
+ 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
+ jobtype qpools db_pools/);
+ $bweb->display($fields, "display_form_job.tpl");
+
+ print "</td><td valign='top'>";
+ $bweb->display_job(age => $arg->{age}, # last 7 days
+ offset => $arg->{offset},
+ limit => $arg->{limit});
+ print "</td></tr></table></div>";
+} elsif ($action eq 'client_stats') {
+
+ foreach my $client (CGI::param('client')) {
+ if ($client =~ m/$client_re/) {
+ $bweb->display_client_stats(clientname => $1,
+ age => $arg->{age});
+ }
+ }
+
+} elsif ($action eq 'running') {
+ $bweb->display_running_jobs(1);
+
+} elsif ($action eq 'dsp_cur_job') {
+ $bweb->display_running_job();
+
+} elsif ($action eq 'update_from_pool') {
+ my $elt = $bweb->get_form(qw/media pool/);
+ unless ($elt->{media} || $elt->{pool}) {
+ $bweb->error("Can't get media or pool param");
+ } else {
+ my $b = new Bconsole(pref => $conf) ;
+
+ $bweb->display({
+ content => $b->send_cmd("update volume=$elt->{media} fromPool=$elt->{pool}"),
+ title => "Update pool",
+ name => "update volume=$elt->{media} fromPool=$elt->{pool}",
+ }, "command.tpl");
+ }
+
+ $bweb->update_media();
+
+} elsif ($action eq 'client_status') {
+ my $b;
+ foreach my $client (CGI::param('client')) {
+ if ($client =~ m/$client_re/) {
+ $client = $1;
+ $b = new Bconsole(pref => $conf)
+ unless ($b) ;
+
+ $bweb->display({
+ content => $b->send_cmd("st client=$client"),
+ title => "Client status",
+ name => $client,
+ }, "command.tpl");
+
+ } else {
+ $bweb->error("Can't get client selection");
+ }
+ }
+
+} elsif ($action eq 'cancel_job') {
+ $bweb->cancel_job();
+
+} elsif ($action eq 'media_zoom') {
+ $bweb->display_media_zoom();
+
+} elsif ($action eq 'job_zoom') {
+ if ($arg->{jobid}) {
+ $bweb->display_job_zoom($arg->{jobid});
+ $bweb->get_job_log();
+ }
+} elsif ($action eq 'job_log') {
+ $bweb->get_job_log();
+
+} elsif ($action eq 'prune') {
+ $bweb->prune();
+
+} elsif ($action eq 'purge') {
+ $bweb->purge();
+
+} elsif ($action eq 'run_job') {
+ $bweb->run_job();
+
+} elsif ($action eq 'run_job_mod') {
+ $bweb->run_job_mod();
+
+} elsif ($action eq 'run_job_now') {
+ $bweb->run_job_now();
+
+} elsif ($action eq 'label_barcodes') {
+ $bweb->label_barcodes();
+
+} elsif ($action eq 'delete') {
+ $bweb->delete();
+
+} elsif ($action eq 'fileset_view') {
+ $bweb->fileset_view();
+
+} else {
+ $bweb->error("Sorry, this action don't exist");
+}
+
+$bweb->display_end();
+
+
+__END__
+
+TODO :
+
+ o Affichage des job en cours, termines
+ o Affichage du detail d'un job (status client)
+ o Acces aux log d'une sauvegarde
+ o Cancel d'un job
+ o Lancement d'un job
+
+ o Affichage des medias (pool, cf bacweb)
+ o Affichage de la liste des cartouches
+ o Affichage d'un autochangeur
+ o Mise a jour des slots
+ o Label barcodes
+ o Affichage des medias qui ont besoin d'etre change
+
+ o Affichage des stats sur les dernieres sauvegardes (cf bacula-web)
+ o Affichage des stats sur un type de job
+ o Affichage des infos de query.sql
+
+ - Affichage des du TapeAlert sur le site
+ - Recuperation des erreurs SCSI de /var/log/kern.log
+
+ o update d'un volume
+ o update d'un pool
+
+ o Configuration des autochanger a la main dans un hash dumper
--- /dev/null
+body { background-color: #ffffff; font-family: verdana,arial,helvetica; font-size: 8pt;}
+a { text-decoration: none;}
+.code_display { background-color: #b9b9ac;
+ border: 1px solid #9d9d94;
+}
+
+abutton.formulaire { font-size: 9pt; height: 48px; width: 80px; background-color: transparent; }
+
+button.formulaire { border: 0px; font-size: 9pt; background-color: transparent; }
+
+td.joberr { background-color: red; color: white;}
+
+.pSlice, .pSliceFull, .pSliceError, .pSliceAppend, .pSlicePurged, .pSliceRecycle,
+.pSliceArchive, .pSliceUsed, .pSliceRead_Only, .pSliceDisabled,
+.pSliceOk,.pSliceWarn,.pSliceCrit,.pSliceEmpty
+ {
+ width: 2px;
+ height: 9px;
+ margin-right: 1px;
+ margin-bottom: 1px;
+ border: solid 1px #CCCCCC;
+ background-color: #E6E6E6;
+}
+
+.pSliceFull, .pSliceRead_Only,.pSliceArchive, .pSliceUsed, .pSliceDisabled,
+.pSliceCrit {
+ border: solid 1px #BD0C10;
+ background-color: #FF3029;
+}
+
+.pSliceError {
+ border: solid 1px #000000;
+ background-color: #020202;
+}
+
+.pSliceAppend, .pSliceWarn {
+ border: solid 1px #CD6500;
+ background-color: #FFCE00;
+}
+
+.pSlicePurged, .pSliceRecycle, .pSliceOk {
+ border: solid 1px #009900;
+ background-color: #00FF00;
+}
+
+input.button {
+ border: 0px;
+ margin: 0 5px 0 5px;
+ padding: 0 0 0 0;
+}
+
+#menu, #menu ul {
+ float: left;
+ list-style: none;
+ line-height: 1;
+ background:#eeeae6 ;
+ font-weight: bold;
+ padding: 0;
+ border: solid #999;
+ border-width: 1px 0;
+ margin: 0 0 1em 0;
+ padding: 0.25em 2em;
+ width: 100%;
+ color: #7C6240;
+}
+
+#menu a {
+ display: block;
+ color: #7C6240;
+ text-decoration: none;
+ padding: 0.25em 2em;
+}
+
+#menu a.daddy {
+ background: url(right.gif) center right no-repeat;
+}
+
+#menu li {
+ float: left;
+ padding: 0;
+}
+
+#menu li ul {
+ position: absolute;
+ left: -999em;
+ height: auto;
+ width: 14.4em;
+ border-width: 1px;
+}
+
+#logged {
+ padding: 0.25em 2em;
+ float: right;
+ clear: left;
+ text-align: right;
+
+}
+
+#menu li li {
+ padding-right: 1em;
+}
+
+#menu li ul a {
+}
+
+#menu li ul ul {
+ margin: -1.75em 0 0 14em;
+}
+
+#menu li:hover ul ul, #menu li:hover ul ul ul,
+#menu li.sfhover ul ul, #menu li.sfhover ul ul ul {
+ left: -999em;
+}
+
+#menu li:hover ul, #menu li li:hover ul, #menu li li li:hover ul,
+#menu li.sfhover ul, #menu li li.sfhover ul, #menu li li li.sfhover ul {
+ left: auto;
+}
+
+#menu li:hover, #menu li.sfhover {
+ background: #eeeae6;
+}
+
--- /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.
+
+ var even_cell_color = "#FFFFFF";
+ var odd_cell_color = "#EEEEEE";
+ var header_color = "#E1E0DA";
+ var rows_per_page = 20;
+ var bweb_root = "/bweb/";
+ var up_icon = "/bweb/up.gif";
+ var down_icon = "/bweb/down.gif";
+ var prev_icon = "/bweb/left.gif";
+ var next_icon = "/bweb/right.gif";
+ var rew_icon = "/bweb/first.gif";
+ var fwd_icon = "/bweb/last.gif";
+
+ var jobstatus = {
+ 'C': 'created but not yet running',
+ 'R': 'running',
+ 'B': 'blocked',
+ 'T': 'terminated normally',
+ 'E': 'Job terminated in error',
+ 'e': 'Non-fatal error',
+ 'f': 'Fatal error',
+ 'D': 'Verify differences',
+ 'A': 'canceled by user',
+ 'F': 'waiting on File daemon',
+ 'S': 'waiting on the Storage daemon',
+ 'm': 'waiting for new media',
+ 'M': 'waiting for Mount',
+ 's': 'Waiting for storage resource',
+ 'j': 'Waiting for job resource',
+ 'c': 'Waiting for Client resource',
+ 'd': 'Waiting for maximum jobs',
+ 't': 'Waiting for start time',
+ 'p': 'Waiting for higher priority jobs to finish'
+};
+
+var joblevel = {
+ 'F': 'Full backup',
+ 'I': 'Incr (since last backup)',
+ 'D': 'Diff (since last full backup)',
+ 'C': 'verify from catalog',
+ 'V': 'verify save (init DB)',
+ 'O': 'verify Volume to catalog entries',
+ 'd': 'verify Disk attributes to catalog',
+ 'A': 'verify data on volume',
+ 'B': 'Base level job'
+};
+
+
+var refresh_time = 60000;
+
+function bweb_refresh() {
+ location.reload(true)
+}
+function bweb_add_refresh(){
+ window.setInterval("bweb_refresh()",refresh_time);
+}
+
+function human_size(val)
+{
+ if (!val) {
+ return '';
+ }
+ var unit = ['b', 'Kb', 'Mb', 'Gb', 'Tb'];
+ var i=0;
+ var format;
+ while (val /1024 > 1) {
+ i++;
+ val /= 1024;
+ }
+
+ var format = (i>0)?1:0;
+ return val.toFixed(format) + ' ' + unit[i];
+}
+
+function human_sec(val)
+{
+ if (!val) {
+ val = 0;
+ }
+ val /= 60; // sec -> min
+
+ if ((val / 60) <= 1) {
+ return val.toFixed(0) + ' mins';
+ }
+
+ val /= 60; // min -> hour
+
+ if ((val / 24) <= 1) {
+ return val.toFixed(0) + ' hours';
+ }
+
+ val /= 24; // hour -> day
+
+ if ((val / 365) < 2) {
+ return val.toFixed(0) + ' days';
+ }
+
+ val /= 365;
+
+ return val.toFixed(0) + ' years';
+}
+
+
+//
+// percent_display("row2", [ { nb: 1, name: "Full" },
+// { nb: 2, name: "Error" },
+// { nb: 5, name: "Append" },
+// { nb: 2, name: "Purged" },
+// {} # last element must be {}
+// ]);
+
+function percent_get_img(type)
+{
+ var img=document.createElement('img');
+ if (type) {
+ img.className="pSlice" + type ;
+ } else {
+ img.className="pSlice";
+ }
+ img.src="/bweb/pix.png";
+ img.alt="";
+ return img;
+}
+
+var percent_display_nb_slice = 20;
+var percent_usage_nb_slice = 5;
+
+function percent_display(hash_values, parent)
+{
+ var nb_elt=percent_display_nb_slice;
+ var tips= "";
+
+ if (!parent) {
+ parent = document.createElement('DIV');
+ }
+
+ if (typeof parent != "object") {
+ parent = document.getElementById(parent);
+ }
+
+ if (!parent) {
+ alert("E : display_percent(): Can't find parent " + parent);
+ return;
+ }
+
+ hash_values.pop(); // drop last element {}
+
+ var nb_displayed = 0;
+ var nb_max = 0;
+
+ for(var i=0;i<hash_values.length;i++) {
+ nb_max += hash_values[i]['nb'];
+ }
+
+ for(var i=0;i<hash_values.length;i++) {
+ var elt = hash_values[i];
+ var cur_nb = (elt['nb'] * nb_elt)/nb_max;
+ var cur_name = elt['name'];
+ cur_name.replace(/-/,"_");
+
+ tips = tips + " " + elt['nb'] + " " + cur_name;
+
+ while ((nb_displayed < nb_elt) && (cur_nb >=1)) {
+ nb_displayed++;
+ cur_nb--;
+
+ var img= percent_get_img(cur_name);
+ parent.appendChild(img);
+ }
+ }
+
+ while (nb_displayed < nb_elt) {
+ nb_displayed++;
+ var img= percent_get_img();
+ parent.appendChild(img);
+ }
+
+ parent.title = tips;
+
+ return parent;
+}
+
+function percent_usage(value, parent)
+{
+ var nb_elt=percent_usage_nb_slice;
+ var type;
+
+ if (!parent) {
+ parent = document.createElement('DIV');
+ }
+
+ if (typeof parent != "object") {
+ parent = document.getElementById(parent);
+ }
+
+ if (!parent) {
+ alert("E : display_percent(): Can't find parent " + parent);
+ return;
+ }
+
+ if (value <= 0.001) {
+ type = "Empty";
+ value = 0;
+ } else if (value <= 40) {
+ type = "Ok";
+ } else if (value <= 75) {
+ type = "Warn";
+ } else if (value <= 85) {
+ type = "Crit";
+ } else {
+ type = "Crit";
+ }
+
+ var nb = parseInt(value*nb_elt/100, 10);
+ parent.title = parseInt(value*100,10)/100 + "% used (approximate)";
+
+ for(var i=0; i<nb; i++) {
+ var img= percent_get_img(type);
+ parent.appendChild(img);
+ }
+
+ for(nb;nb < nb_elt;nb++) {
+ var img= percent_get_img("Empty");
+ parent.appendChild(img);
+ }
+
+ return parent;
+}
+
+function bweb_get_job_img(status, errors)
+{
+ var ret;
+
+ if (status == "T") {
+ if (errors > 0) {
+ ret = "W.png";
+
+ } else {
+ ret = "T.png";
+ }
+
+ } else {
+ ret = status + ".png";
+ }
+
+ return bweb_root + ret;
+}
+
+function search_media()
+{
+ var what = document.getElementById('searchbox').value;
+ if (what) {
+ document.search.action.value='media';
+ document.search.re_media.value=what;
+ document.search.submit();
+ }
+}
+
+function search_client()
+{
+ var what = document.getElementById('searchbox').value;
+ if (what) {
+ document.search.action.value='client';
+ document.search.re_client.value=what;
+ document.search.submit();
+ }
+}
+
+sfHover = function() {
+ var sfEls = document.getElementById("menu").getElementsByTagName("LI");
+ for (var i=0; i<sfEls.length; i++) {
+ sfEls[i].onmouseover=function() {
+ this.className+=" sfhover";
+ }
+ sfEls[i].onmouseout=function() {
+ this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
+ }
+ }
+}
+
+if (window.attachEvent) window.attachEvent("onload", sfHover);
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<HTML>
+<HEAD>
+<META HTTP-EQUIV=Refresh CONTENT="0; URL=/cgi-bin/bweb/bweb.pl">
+</HEAD>
+<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#0000EF" VLINK="#55188A" ALINK="#FF0000">
+</BODY>
+</HTML>
+
--- /dev/null
+/*
+* ----------------------------------------------------------------------------
+* "THE BEER-WARE LICENSE" (Revision 42):
+* Willy Morin (kaiska@ifrance.com) wrote this file. As long as you retain this
+* notice you can do whatever you want with this stuff. If we meet some day,
+* and you think this stuff is worth it, you can buy me a beer in return
+*
+* Willy Morin
+* http://kaiska.flinkserver.net
+*
+* Addendum Étienne Bersac ( commentsbrowser ), http://bersace03.free.fr
+* ----------------------------------------------------------------------------
+*/
+
+* { font-family: sans-serif, monospace; }
+
+body { background: #f1f1f1; }
+
+a:link {
+ color: #36598E;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #B64545;
+ text-decoration: none;
+}
+
+h1.rubrique_info {
+ color: #552 !important;
+ margin: 0px 0px 0px 0px;
+ padding: 0em;
+ border: 0px;
+ font-size: 12px;
+}
+h1.connexe {
+ font-size: 12px;
+ padding: 0em;
+ margin: 0px 0px 0px 0px;
+ color: #900;
+}
+
+a.rubrique_infolink { text-decoration: none; }
+ul.rubrique_infoul {
+ display: inline;
+ list-style-type: square;
+}
+ul.rubrique_infoul * { width: 100%; }
+li.rubrique_infoul { margin-left: 15px; }
+
+.leftbox ul.rubrique_infoul {
+ display:block;
+ padding: 0 10px 0 10px;
+ margin-right: -10px;
+}
+
+h1 { color: #990033; }
+
+div.main {
+ margin: 15px 10px 0px 10px;
+ border: 2px #ddd solid;
+ text-align: left;
+ font-size: 0.8em;
+}
+
+div.lsfnbanner {
+ position: absolute;
+ top: 0;
+ left: 4px;
+ width: 99%;
+ text-align: center;
+ font-size: .8em;
+ padding: 0;
+ margin: 0;
+ border: 0;
+}
+
+div.footer {
+ padding-top: 5px;
+ padding-bottom: 3px;
+ border-top: 1px #ccc solid;
+ border-left: 1px #ccc solid;
+ border-right: 1px #ccc solid;
+ text-align: left;
+ font-size: 9px;
+ background: #eeeae6;
+ margin-top: 40px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+div.footer p {
+ margin-left: 10px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+div.menubartop {
+ margin: 0;
+ padding:0;
+ background:#ddd;
+}
+
+div.smallmenubar {
+ padding: 4px;
+ padding-left: 10px;
+ padding-right: 10em;
+ margin-top: -4px;
+ font-weight: normal;
+ font-size: 0.8em;
+ text-align: left;
+ background: #e0ddd8;
+}
+
+div.smallmenubar a:hover{
+ margin-bottom:4px;
+}
+div.menuevent {
+ float: left;
+ width: 350px;
+ font-size: 11px;
+ text-align: left;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-left: 10px;
+ font-weight: bold;
+ margin: 0px;
+}
+div.menubar {
+ background: #eeeae6;
+ border-top: 1px #999 solid;
+ border-bottom: 1px #999 solid;
+ padding: 2px 10px 3px 10px;
+ font-weight:bold;
+ font-size: 1em;
+}
+div.menubar a {
+ text-decoration: none;
+}
+div.menubar p {
+ padding: 0px;
+ margin: 0px;
+}
+
+.menudate { visibility:hidden;height:0;margin:0;padding:0;}
+
+div.menusearch {
+ float: none;
+ position: absolute;
+ top: 19px;
+ right:22px;
+ margin:0;
+ text-align: left;
+ padding: 0;
+ width: auto;
+ background: transparent url('/bweb/add.png') 2px no-repeat;
+ padding-left:25px;
+}
+div.menusearch p {
+ margin: 0;
+}
+div.newsletter {
+ float: right;
+ text-align: right;
+ margin: 0px;
+ padding: 0px;
+ font-size: 8px;
+ font-weight: normal;
+}
+div.newsletter p { margin: 0px 0px 0px 0px; }
+.searchinput {
+ border: solid 1px #ccc;
+ padding-left: 5px;
+ padding-right: 5px;
+ height:1.1em;
+}
+.searchinput:focus{
+ border: solid 1px #777;
+ background: #f5f5e9;
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.newsletterinput {
+ border: solid 1px black;
+ font-weight: normal;
+}
+a#menulinkselect { color: #ed7e00; }
+
+div.leftbox {
+ width: 200px;
+ float: left;
+ padding-right: 5px;
+ padding-bottom: 5px;
+ border-right: 0px solid #ccc;
+ border-bottom: 0px solid #ccc;
+ background: white;
+ margin-bottom: 10px;
+ margin-right: 20px;
+ overflow: hidden;
+}
+div.leftbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 10px;
+ margin: 0px;
+}
+div.leftbox h2 {
+ font-weight: bold;
+ font-size: 12px;
+ margin: 0px;
+}
+div.leftbox ul {
+ list-style-type: square;
+ color: #552;
+ margin-left: 5px;
+ margin-right: 5px;
+ padding-left: 10px;
+ border: 1px solid #bbb;
+ background: #eeeae6;
+ -moz-border-radius: 10px;
+}
+div.leftbox li {
+ margin-left: 10px;
+ margin-top: 3px;
+ padding-bottom: 3px;
+ border-bottom: 1px solid #ddd;
+ font-size:0.8em;
+}
+
+div.leftbox p {
+ color: #552;
+ text-align:center;
+}
+
+div.rightbox {
+ width: 140px;
+ float: right;
+ padding-top: 10px;
+ padding-left: 5px;
+ padding-right: 15px;
+ padding-bottom: 5px;
+ border: 1px solid #ccc;
+ margin: 10px;
+ -moz-border-radius: 10px;
+ text-align: left;
+ background: #eeeae6;
+}
+div.rightbox h1{
+ color: #552;
+}
+
+div.rightbox ul {
+ list-style-type: square;
+ color: #552;
+ padding:0;
+}
+div.rightbox li {
+ margin-left: 10px;
+ margin-top: 3px;
+ padding-bottom: 3px;
+ border-bottom: 1px solid #ddd;
+ font-size:0.8em;
+}
+
+div.journaldiv {
+ margin-left: 0px;
+ margin-right: 0px;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ border: 1px solid #ccc;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ background-color: #eeeae6;
+ -moz-border-radius: 10px;
+}
+div.journaldiv p {
+ margin-top: 10px;
+ margin-bottom: 0px;
+}
+div.journaldiv h1 {
+ color: #552;
+ margin: 0;
+ border-bottom: 1px dotted #bbb;
+}
+div.journaldiv h2 {
+ font-size: 10px;
+ margin: 0;
+}
+div.tipdiv {
+ margin-right: 50px;
+ padding-top: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: justify;
+ background-color: #eee;
+ border: black solid 1px;
+ margin-left: 10px;
+}
+div.tipdiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ color: black;
+ margin-top: 0;
+ margin-bottom: 20px;
+}
+div.tipdiv h2 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 12px;
+ margin: 0;
+}
+
+div.newsdiv {
+ margin-left: 220px;
+ margin-right: 180px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ text-align: justify;
+}
+div.newsdiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ margin: 0px;
+}
+div.newsdiv h2 {
+ font-weight: normal;
+ font-size: 12px;
+ margin: 0px;
+}
+div.newsdiv h3 {
+ font-weight: normal;
+ font-size: 12px;
+ margin-bottom: 20px;
+}
+div.objdiv {
+ margin-left: 220px;
+ margin-right: 20px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+
+h1.newstitle {
+ text-align: left;
+ font-size: 14px;
+ margin: 0px 0px 0px 0px;
+ color: #552;
+}
+div.titlediv {
+ border-top: solid #cac2a8 0px;
+ margin-top: 20px;
+ padding-top: 5px;
+ background-color: #eeeae6;
+ padding-left: 10px;
+ font-size: 11px;
+ color:#666;
+ -moz-border-radius: 10px 10px 0 0;
+}
+div.bodydiv {
+ border-top: solid #ccc 0px;
+ border-bottom: solid #ccc 1px;
+ border-left: solid #ccc 1px;
+ border-right: solid #ccc 1px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 10px;
+ padding-bottom: 5px;
+ margin-top: 0;
+ text-align: justify;
+ background: #f5f5f5;
+ -moz-border-radius: 0 0 10px 10px;
+}
+
+div.comments {
+ padding: 10px;
+ border-top: solid 2px #552;
+ border-bottom: solid 2px #552;
+ border-left: 1px solid #ccc;
+ border-right: 1px solid #ccc;
+ margin-top: 20px;
+ margin-bottom: 10px;
+ background: #dad6d1;
+ font-size: 12px;
+ line-height: 1.3em;
+ -moz-border-radius: 10px;
+}
+
+.comment-vote { font-style: italic; color:#666; font-size:0.9em;}
+
+p.commentsbody {
+ border-left: 1px solid #aaa;
+ padding-left: 10px;
+ text-align: justify;
+ margin-right: 20px;
+}
+div.commentsreply {
+ text-align: center;
+ margin-top: 10px;
+ font-size:0.9em;
+ color: #666;
+}
+
+div.commentsreply .searchinput{ height:auto; }
+
+ul.commentsul {
+ list-style-type: none;
+ margin-bottom: 10px;
+ margin-left: 1.25em;
+ padding-left: 0;
+}
+ul.commentsli { margin: 10px; }
+div.comments li {
+ margin-top: 20px;
+ margin-left: 2px;
+}
+
+div.comments h1 {
+ font-size: 12px;
+ color: black;
+ margin: 0px 20px 3px 0px;
+ background:#ebe7e2;
+ padding-left: 1px;
+ font-weight: normal;
+ -moz-border-radius: 10px;
+}
+
+div.articlediv {
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-top: 10px;
+ padding-bottom: 20px;
+ margin-right: 180px;
+ margin-left: 220px;
+ margin-top: 10px;
+ text-align: justify;
+ background: #dad6d1;
+ -moz-border-radius: 10px;
+ border: 1px solid #ccc;
+}
+img { border: 0; }
+div.sectionimg {
+ float: left;
+ margin-right: 10px;
+ margin-top: 5px;
+}
+p.hautpage {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ margin-left: 10px;
+}
+div.leftcol {
+ width: 202px;
+ float: left;
+ padding: 0px;
+}
+div.logodiv {
+ border-right: 0px black solid;
+ border-bottom: 0px black solid;
+ padding: 0px;
+ line-height: 0px
+}
+div.loginbox {
+ margin-left: 4px;
+ border: solid #bbb 1px;
+ margin-top: 5px;
+ padding: 5px;
+ background-color: #E0DDD8;
+ font-size: 0.8em;
+ -moz-border-radius: 10px;
+}
+div.loginbox p {
+ margin: 0px;
+ padding: 0px;
+}
+div.loginbox ul {
+ margin-left: 2px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ padding: 0px;
+ list-style-type: none;
+ border-left: 1px solid #aaa;
+ padding-left: 8px;
+}
+div.loginbox h1 {
+ text-transform: none;
+ font-weight: bold;
+ color: #552;
+ font-size: 1.2em;
+ margin: 0px;
+ margin-top:10px;
+ border-bottom:1px solid #ccc;
+}
+div.loginbox h2 {
+ font-weight: bold;
+ font-size: 1.1em;
+ margin: 0px;
+}
+div.loginbox h3 {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ font-size: 12px;
+}
+#poll div.otherbox ul {
+ margin-left: 0px;
+ margin-top: 0px;
+ margin-bottom: 10px;
+ padding: 0px;
+ list-style-type: none;
+}
+div.otherboxtitle {
+ margin-bottom: 2px;
+ background-color: #e0ddd8;
+ margin-left: 4px;
+ margin-top: 5px;
+ margin-bottom: 0px;
+ padding-left: 5px;
+ padding-top: 2px;
+ font-size: 11px;
+ border-top: 1px solid #ccc;
+ border-bottom: 0px solid #ccc;
+ font-size: 1em;
+ font-weight: bold;
+ -moz-border-radius: 10px 10px 0px 0px;
+ text-transform: none;
+}
+div.otherbox {
+ margin-left: 4px;
+ border: 1px #ccc solid;
+ border-top:0px;
+ margin-top: 0px;
+ padding: 5px;
+ text-align: left;
+ background: #e0ddd8;
+ font-size: 10px;
+ -moz-border-radius: 0px 0px 10px 10px;
+}
+div.otherbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #552;
+ font-size: 1.2em;
+ margin: 0px;
+ text-transform: none;
+ border-bottom: 1px dotted #ccc;
+}
+div.otherbox h2 {
+ font-weight: bold;
+ font-size: 11px;
+ margin: 0px;
+}
+div.otherbox p {
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+div.rightlogo {
+ width: 90px;
+ float: right;
+ margin-right: 0px;
+ padding-top: 0px;
+ padding-left: 0px;
+ padding-bottom: 0px;
+}
+div.centraldiv {
+ margin-left: 220px;
+ margin-right: 10px;
+ margin-bottom: 20px;
+ margin-top: 10px;
+}
+div.centraldiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ margin: 0px;
+}
+div.centraldiv h2 {
+ font-weight: normal;
+ font-size: 12px;
+ margin: 0px;
+}
+div.centraldiv h3 {
+ font-weight: normal;
+ font-size: 12px;
+ margin-bottom: 20px;
+}
+div.lefttopbox {
+ padding: 5px;
+ border: 2px #ddd solid;
+ text-align: justify;
+ background: #f9f9f9;
+ font-size: 0.8em;
+ float: left;
+ -moz-border-radius: 10px;
+}
+div.lefttopbox p { margin-top: 5px; margin-bottom: 10px; }
+div.lefttopbox h1 {
+ font-size: 0px;
+ font-weight: bold;
+ margin: 0px;
+ text-align: right;
+}
+div.lefttopbox h2 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 13px;
+ margin: 0px;
+}
+div.lefttopbox h3 {
+ font-weight: bold;
+ font-size: 1.1em;
+ text-align:left;
+ margin: 0px;
+}
+div.righttopbox {
+ border: 1px #ccc solid;
+ background: white;
+ padding: 5px;
+ text-align: justify;
+ font-size: 0.8em;
+ width: 34%;
+ float: right;
+ -moz-border-radius: 10px;
+}
+div.righttopbox h1 {
+ text-transform:none;
+ font-weight: bold;
+ font-size: 10px;
+ margin: 0px;
+ color: #552;
+ border-bottom: 1px solid #ccc;
+}
+div.righttopbox h2 {
+ font-weight: bold;
+ font-size: 1.1em;
+ color: #666;
+ margin: 0px;
+}
+div.boardindex {
+ text-align: justify;
+ font-size: 11px;
+ padding: 10px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+a.boardindex:link,a.boardindex:visited,a.boardindex:active {
+ text-decoration:none;
+ color: red;
+}
+div.boardleftmsg {
+ float: left;
+ margin-top: 3px;
+}
+div.boardrightmsg {
+ margin-left: 130px;
+ margin-top: 3px;
+ padding-left: 5px;
+}
+div.journalbody {
+ margin-left: 40px;
+ margin-top: 40px;
+}
+div.journalbody h1 {
+ font-size: 15px;
+ font-weight: bold;
+}
+div.journalbody p {
+ margin-bottom: 20px;
+}
+.formulaire {
+ border: solid 1px black;
+font-size: 12px;
+background-color: #fffbf7;
+color: #000000;
+}
+
+.newcomments {
+ color: red;
+ font-weight: bold;
+}
+div.commentsreplythanks {
+ margin-left: 100px;
+ margin-top: 50px;
+ margin-right: 100px;
+ background-color: #eee;
+ border: black solid 1px;
+ padding: 10px;
+}
+div.archivediv { margin-right: 20px; }
+.archivedate { color:#f30; }
+.archivelink {
+ font-size: 14px;
+ font-weight: bold;
+ text-decoration: underline;
+}
+div.forumgroup {
+ text-align: left;
+ border: solid black 1px;
+ padding: 10px 20px 20px 10px;
+ background-color: #eeeae6;
+ border: 1px solid #ccc;
+ -moz-border-radius: 10px;
+}
+
+div.forumgroup b{
+ color: #552;
+ background: #e0ddd8;
+ font-size: 1.1em;
+ display:block;
+ -moz-border-radius: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ margin-left:-15px;
+ margin-right:-15px;
+ margin-top:-3px;
+}
+
+div.forumgroup td{
+ -moz-border-radius: 10px;
+}
+
+div.adminall {
+ font-size: 12px;
+ padding: 10px;
+}
+div.floatwin {
+ position:absolute;
+ top: 150px;
+ left: 450px;
+ width:200px;
+ visibility:hidden;
+ background: #dcdff4;
+ padding: 5px;
+ border: solid black 1px;
+}
+
+div.poll-result-bar {
+ background: #eeeae6;
+ color: black;
+ border: solid 1px #ccc;
+ font-size: 0.7em;
+ -moz-border-radius: 10px;
+}
+div.funbanner {
+ text-align: right;
+ border-top: #aaa solid 1px;
+ border-bottom: #aaa solid 1px;
+ padding-right: 20px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ font-size: 10px;
+ background-color: #fff2e8;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+div.main
+{
+ margin-bottom: 3em !important;
+}
+
+.replie
+{
+ display: none;
+}
+
+.deplie
+{
+ display: block;
+}
+
+#commentsnav
+{
+ display: none;
+ text-align: right;
+}
+
+#commentsbrowser
+{
+ border: 1px solid #baa !important;
+ background: #dfd6d1 !important;
+ color: #333333;
+ position: fixed;
+ bottom: 0.5ex;
+ max-height: 0.8ex;
+ padding: 2px 0 0;
+ overflow: hidden;
+ left: 19px !important;
+ right: 19px !important;
+ font-size: 10px;
+}
+
+#commentsbrowser:hover
+{
+ max-height: none;
+ padding: 0.5ex;
+ overflow: hidden;
+}
+
+div.comments h1.nouveau
+{
+ border: 1px solid rgb(211, 117, 55);
+ /*border: 1px solid #D58E64;*/
+ background-color: rgb(225, 220, 220);
+
+ color: #111111;
+}
+
+#pallierdiv, #csspersodiv
+{
+ position: fixed;
+ bottom: 5em;
+ left: 3%;
+ max-width: 17em;
+ border: 1px solid #333333;
+ background: #EEEEEE;
+ color: #333333;
+ padding: 0.6em;
+ display: none;
+}
+
+#csspersodiv
+{
+ left: 37%;
+ max-width: 35em;
+}
+
+#pallierdiv input, #csspersodiv input
+{
+ margin-left: 0.2em;
+}
+
+form
+{
+ margin: 0;
+}
+
+#cssurl
+{
+ width: 12em;
+}
+
+#pallierdiv h3, #csspersodiv h3
+{
+ font-size: 12px;
+ padding: 0;
+ margin: 0 0 0.5em 0;
+ color: #990033;
+}
+
+tt
+{
+ font-size: 1em;
+ background:#fffbec;
+ color: #330;
+}
+
+pre, code, blockquote,quote
+{
+ font-size: 1em;
+ display:block;
+ background:#fffbec;
+ margin: 5px 15px 5px 15px;
+ padding: 5px;
+ border-left: 1px solid #552;
+ color: #330;
+}
+
+tt, pre, code
+{
+ font-family: "Bitstream Vera Sans Mono", "Courier New", Courier, monospace;
+}
+
+blockquote { font-style:italic; }
+
+div.titledivmini {
+ border-top: solid #cac2a8 0px;
+ padding-top: 5px;
+ background-color: #eeeae6;
+ padding-left: 10px;
+ color:#666;
+ -moz-border-radius: 10px 10px 0 0;
+}
+h1.newstitlemini {
+ text-align: left;
+ margin: 0px 0px 0px 0px;
+ color: #552;
+ font-size: 12px !important;
+}
+div.subtitlemini {
+ border-top: solid #cac2a8 0px;
+ margin-top: 0px;
+ padding-top: 5px;
+ background-color: #eeeae6;
+ padding-left: 1px;
+ font-size: 11px;
+ color:#666;
+}
+div.bodydivmini {
+ border-top: solid #ccc 0px;
+ border-bottom: solid #ccc 1px;
+ border-left: solid #ccc 1px;
+ border-right: solid #ccc 1px;
+ padding-left: 2px;
+ padding-right: 2px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ margin-top: 0;
+ margin-bottom: 2px;
+ text-align: justify;
+ background: #f5f5f5;
+ font-size: 11px;
+ -moz-border-radius: 0 0 10px 10px;
+}
+
+blockquote:before {
+ content: "\00AB";
+ font-size:1.3em;
+ font-weight: bold;
+ color: #552;
+}
+
+blockquote:after {
+ content: "\00BB";
+ font-size:1.3em;
+ font-weight: bold;
+ color: #552;
+}
+
+em{ font-style: italic; }
+
+#palliercontainer {
+ text-align: left;
+ margin-left: 20px;
+}
+
+#csscourantecontainer {
+ text-align: center;
+ padding-left: 20%;
+}
+
+#newcommentsnav {
+ float: right;
+ text-align: right;
+}
+
+.centraldiv table {
+border: 0;
+}
+.centraldiv table td {
+border: thin solid #BBBBBB;
+background:#E0DDD8;
+}
+
+.centraldiv table th {
+padding:1em;
+background:#CCCCCC;
+border:0;
+border-top:1em solid white;
+}
+
+div.extrait {
+visibility:hidden;
+position:fixed;
+bottom:1em;
+right:1em;
+left:1em;
+background:#E0DDD8;
+border:thin solid #BBBBBB;
+}
--- /dev/null
+/*
+natcompare.js -- Perform 'natural order' comparisons of strings in JavaScript.
+Copyright (C) 2005 by SCK-CEN (Belgian Nucleair Research Centre)
+Written by Kristof Coomans <kristof[dot]coomans[at]sckcen[dot]be>
+
+Based on the Java version by Pierre-Luc Paour, of which this is more or less a straight conversion.
+Copyright (C) 2003 by Pierre-Luc Paour <natorder@paour.com>
+
+The Java version was based on the C version by Martin Pool.
+Copyright (C) 2000 by Martin Pool <mbp@humbug.org.au>
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must not
+claim that you wrote the original software. If you use this software
+in a product, an acknowledgment in the product documentation would be
+appreciated but is not required.
+2. Altered source versions must be plainly marked as such, and must not be
+misrepresented as being the original software.
+3. This notice may not be removed or altered from any source distribution.
+*/
+
+
+function isWhitespaceChar(a)
+{
+ var charCode;
+ charCode = a.charCodeAt(0);
+
+ if ( charCode <= 32 )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+function isDigitChar(a)
+{
+ var charCode;
+ charCode = a.charCodeAt(0);
+
+ if ( charCode >= 48 && charCode <= 57 )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+function compareRight(a,b)
+{
+ var bias = 0;
+ var ia = 0;
+ var ib = 0;
+
+ var ca;
+ var cb;
+
+ // The longest run of digits wins. That aside, the greatest
+ // value wins, but we can't know that it will until we've scanned
+ // both numbers to know that they have the same magnitude, so we
+ // remember it in BIAS.
+ for (;; ia++, ib++) {
+ ca = a.charAt(ia);
+ cb = b.charAt(ib);
+
+ if (!isDigitChar(ca)
+ && !isDigitChar(cb)) {
+ return bias;
+ } else if (!isDigitChar(ca)) {
+ return -1;
+ } else if (!isDigitChar(cb)) {
+ return +1;
+ } else if (ca < cb) {
+ if (bias == 0) {
+ bias = -1;
+ }
+ } else if (ca > cb) {
+ if (bias == 0)
+ bias = +1;
+ } else if (ca == 0 && cb == 0) {
+ return bias;
+ }
+ }
+}
+
+function natcompare(a,b) {
+
+ var ia = 0, ib = 0;
+ var nza = 0, nzb = 0;
+ var ca, cb;
+ var result;
+
+ while (true)
+ {
+ // only count the number of zeroes leading the last number compared
+ nza = nzb = 0;
+
+ ca = a.charAt(ia);
+ cb = b.charAt(ib);
+
+ // skip over leading spaces or zeros
+ while ( isWhitespaceChar( ca ) || ca =='0' ) {
+ if (ca == '0') {
+ nza++;
+ } else {
+ // only count consecutive zeroes
+ nza = 0;
+ }
+
+ ca = a.charAt(++ia);
+ }
+
+ while ( isWhitespaceChar( cb ) || cb == '0') {
+ if (cb == '0') {
+ nzb++;
+ } else {
+ // only count consecutive zeroes
+ nzb = 0;
+ }
+
+ cb = b.charAt(++ib);
+ }
+
+ // process run of digits
+ if (isDigitChar(ca) && isDigitChar(cb)) {
+ if ((result = compareRight(a.substring(ia), b.substring(ib))) != 0) {
+ return result;
+ }
+ }
+
+ if (ca == 0 && cb == 0) {
+ // The strings compare the same. Perhaps the caller
+ // will want to call strcmp to break the tie.
+ return nza - nzb;
+ }
+
+ if (ca < cb) {
+ return -1;
+ } else if (ca > cb) {
+ return +1;
+ }
+
+ ++ia; ++ib;
+ }
+}
+
--- /dev/null
+/**
+ * Copyright 2005 New Roads School
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/**
+ * \class nrsTable
+ * This describes the nrsTable, which is a table created in JavaScript that is
+ * able to be sorted and displayed in different ways based on teh configuration
+ * parameters passed to it.
+ * to create a new table one only needs to call the setup function like so:
+ * <pre>
+ * nrsTable.setup(
+ * {
+ * table_name: "table_container",
+ * table_data: d,
+ * table_header: h
+ * }
+ * );
+ * </pre>
+ * Where table_name is the name of the table to build. THis must be defined in
+ * your HTML by putting a table declaration, such as <table id=table_name>
+ * </table>. This will declare where your table will be shown.
+ * All sorts of parameters can be customized here. For details look at the
+ * function setup.
+ * \see setup
+ */
+
+
+/**
+ * Debug function. Set debug to tru to view messages.
+ * \param msg Message to display in an alert.
+ */
+debug = false;
+function DEBUG(msg)
+{
+ if(debug)
+ alert(msg);
+}
+
+/**
+ * There is a memory leak problem that I can't seem to fix. I'm attching
+ * something that I found from Aaron Boodman, which will clean up all the
+ * memory leaks in the page (this can be found at http://youngpup.net/2005/0221010713
+ * ). This is a little clunky, but it will do till I track this problem.
+ * Oh, and this problem only occurrs is IE.
+ */
+if (window.attachEvent) {
+ var clearElementProps = [
+ 'data',
+ 'onmouseover',
+ 'onmouseout',
+ 'onmousedown',
+ 'onmouseup',
+ 'ondblclick',
+ 'onclick',
+ 'onselectstart',
+ 'oncontextmenu'
+ ];
+
+ window.attachEvent("onunload", function() {
+ var el;
+ for(var d = document.all.length;d--;){
+ el = document.all[d];
+ for(var c = clearElementProps.length;c--;){
+ el[clearElementProps[c]] = null;
+ }
+ }
+ });
+}
+
+
+/**
+ * This is the constructor.
+ * It only needs the name of the table. This should never be called directly,
+ * instead use setup function.
+ * \param table The name of the table to create.
+ * \see setup
+ */
+function nrsTable(table)
+{
+ this.my_table = table;
+ this.field_to_sort = 0;
+ this.field_asc = true;
+}
+
+new nrsTable('');
+/**
+ * This function is responsible for setting up an nrsTable. All the parameters
+ * can be configured directly from this function. The params array of this
+ * function is a class (or a associative array, depending on how you want to
+ * look at it) with the following possible parameters:
+ * - table_name: required. The id of the table tag.
+ * - table_header: required. An array containing the header names.
+ * - table_data: optional. A 2D array of strings containing the cell contents.
+ * - caption: optional. A caption to include on the table.
+ * - row_links: optional. An array with hyperlinks per row. Must be a javascript function.
+ * - cell_links: optional. A 2D array with links on every cell. Must be a javascript function
+ * - up_icon: optional. A path to the ascending sort arrow.
+ * - down_icon: optional. A path to the descending sort arrow.
+ * - prev_icon: optional. A path to the previous page icon in the navigation.
+ * - next_icon: optional. A path to the next page icon in the navigation.
+ * - rew_icon: optional. A path to the first page icon in the navigation.
+ * - fwd_icon: optional. A path to the last page icon in the navigation.
+ * - rows_per_page: optional. The number of rows per page to display at any one time.
+ * - display_nav: optional. Displays navigation (prev, next, first, last)
+ * - foot_headers: optional. Whether to display th eheaders at the foot of the table.
+ * - header_color: optional. The color of the header cells. Will default to whatever is defined in CSS.
+ * - even_cell_color: optional. The color of the even data cells. Will default to whatever is defined in CSS.
+ * - odd_cell_color: optional. The color of the odd data cells. Will default to whatever is defined in CSS.
+ * - footer_color: optional. The color of the footer cells. Will default to whatever is defined in CSS.
+ * - hover_color: optional. The color tha a row should turn when the mouse is over it.
+ * - padding: optional. Individual cell padding, in pixels.
+ * - natural_compare: optional. Uses the natural compare algorithm (separate from this program) to sort.
+ * - disable_sorting: optional. An array specifying the columns top disable sorting on (0 is the first column).
+ *
+ * \params params An array as described above.
+ */
+nrsTable.setup = function(params)
+{
+ //here we assign all the veriables that we are passed, or the defaults if
+ //they are not defined.
+ //Note that the only requirements are a table name and a header.
+ if(typeof params['table_name'] == "undefined")
+ {
+ alert("Error! You must supply a table name!");
+ return null;
+ }
+ if(typeof params['table_header'] == "undefined")
+ {
+ alert("Error! You must supply a table header!");
+ return null;
+ }
+
+ //check if the global array exists, else create it.
+ if(typeof(nrsTables) == "undefined")
+ {
+ eval("nrsTables = new Array();");
+ }
+ nrsTables[params['table_name']] = new nrsTable(params['table_name']);
+ nrsTables[params['table_name']].heading = params['table_header'].concat();
+
+ //now the non-required elements. Data elements first
+ nrsTables[params['table_name']].data = (typeof params['table_data'] == "undefined" || !params['table_data'])? null: params['table_data'].concat();
+ nrsTables[params['table_name']].caption = (typeof params['caption'] == "undefined")? null: params['caption'];
+ nrsTables[params['table_name']].row_links = (typeof params['row_links'] == "undefined" || !params['row_links'])? null: params['row_links'].concat();
+ nrsTables[params['table_name']].cell_links = (typeof params['cell_links'] == "undefined" || !params['row_links'])? null: params['cell_links'].concat();
+
+ //these are the icons.
+ nrsTables[params['table_name']].up_icon = (typeof params['up_icon'] == "undefined")? "up.gif": params['up_icon'];
+ nrsTables[params['table_name']].down_icon = (typeof params['down_icon'] == "undefined")? "down.gif": params['down_icon'];
+ nrsTables[params['table_name']].prev_icon = (typeof params['prev_icon'] == "undefined")? "left.gif": params['prev_icon'];
+ nrsTables[params['table_name']].next_icon = (typeof params['next_icon'] == "undefined")? "right.gif": params['next_icon'];
+ nrsTables[params['table_name']].rew_icon = (typeof params['rew_icon'] == "undefined")? "first.gif": params['rew_icon'];
+ nrsTables[params['table_name']].fwd_icon = (typeof params['fwd_icon'] == "undefined")? "last.gif": params['fwd_icon'];
+
+ //now the look and feel options.
+ nrsTables[params['table_name']].rows_per_page = (typeof params['rows_per_page'] == "undefined")? -1: params['rows_per_page'];
+ nrsTables[params['table_name']].page_nav = (typeof params['page_nav'] == "undefined")? false: params['page_nav'];
+ nrsTables[params['table_name']].foot_headers = (typeof params['foot_headers'] == "undefined")? false: params['foot_headers'];
+ nrsTables[params['table_name']].header_color = (typeof params['header_color'] == "undefined")? null: params['header_color'];
+ nrsTables[params['table_name']].even_cell_color = (typeof params['even_cell_color'] == "undefined")? null: params['even_cell_color'];
+ nrsTables[params['table_name']].odd_cell_color = (typeof params['odd_cell_color'] == "undefined")? null: params['odd_cell_color'];
+ nrsTables[params['table_name']].footer_color = (typeof params['footer_color'] == "undefined")? null: params['footer_color'];
+ nrsTables[params['table_name']].hover_color = (typeof params['hover_color'] == "undefined")? null: params['hover_color'];
+ nrsTables[params['table_name']].padding = (typeof params['padding'] == "undefined")? null: params['padding'];
+ nrsTables[params['table_name']].natural_compare = (typeof params['natural_compare'] == "undefined")? false: true;
+ nrsTables[params['table_name']].disable_sorting =
+ (typeof params['disable_sorting'] == "undefined")? false: "." + params['disable_sorting'].join(".") + ".";
+ //finally, build the table
+ nrsTables[params['table_name']].buildTable();
+};
+
+
+/**
+ * This is the Javascript quicksort implementation. This will sort the
+ * this.data and the this.data_nodes based on the this.field_to_sort parameter.
+ * \param left The left index of the array.
+ * \param right The right index of the array
+ */
+nrsTable.prototype.quickSort = function(left, right)
+{
+ if(!this.data || this.data.length == 0)
+ return;
+// alert("left = " + left + " right = " + right);
+ var i = left;
+ var j = right;
+ var k = this.data[Math.round((left + right) / 2)][this.field_to_sort];
+ if (k != '') {
+ if (isNaN(k)) {
+ k = k.toLowerCase();
+ } else {
+ k = parseInt(k, 10);
+ }
+ }
+
+ while(j > i)
+ {
+ if(this.field_asc)
+ {
+ while(this.data[i][this.field_to_sort].toLowerCase() < k)
+ i++;
+ while(this.data[j][this.field_to_sort].toLowerCase() > k)
+ j--;
+ }
+ else
+ {
+ while(this.data[i][this.field_to_sort].toLowerCase() > k)
+ i++;
+ while(this.data[j][this.field_to_sort].toLowerCase() < k)
+ j--;
+ }
+ if(i <= j )
+ {
+ //swap both values
+ //sort data
+ var temp = this.data[i];
+ this.data[i] = this.data[j];
+ this.data[j] = temp;
+
+ //sort contents
+ var temp = this.data_nodes[i];
+ this.data_nodes[i] = this.data_nodes[j];
+ this.data_nodes[j] = temp;
+ i++;
+ j--;
+ }
+ }
+ if(left < j)
+ this.quickSort(left, j);
+ if(right > i)
+ this.quickSort(i, right);
+}
+
+/**
+ * This is the Javascript natural sort function. Because of some obscure JavaScript
+ * quirck, we could not do quicsort while calling natcompare to compare, so this
+ * function will so a simple bubble sort using the natural compare algorithm.
+ */
+nrsTable.prototype.natSort = function()
+{
+ if(!this.data || this.data.length == 0)
+ return;
+ var swap;
+ for(i = 0; i < this.data.length - 1; i++)
+ {
+ for(j = i; j < this.data.length; j++)
+ {
+ if(!this.field_asc)
+ {
+ if(natcompare(this.data[i][this.field_to_sort].toLowerCase(),
+ this.data[j][this.field_to_sort].toLowerCase()) == -1)
+ swap = true;
+ else
+ swap = false;
+ }
+ else
+ {
+ if(natcompare(this.data[i][this.field_to_sort].toLowerCase(),
+ this.data[j][this.field_to_sort].toLowerCase()) == 1)
+ swap = true;
+ else
+ swap = false;
+ }
+ if(swap)
+ {
+ //swap both values
+ //sort data
+ var temp = this.data[i];
+ this.data[i] = this.data[j];
+ this.data[j] = temp;
+
+ //sort contents
+ var temp = this.data_nodes[i];
+ this.data_nodes[i] = this.data_nodes[j];
+ this.data_nodes[j] = temp;
+ }
+ }
+ }
+}
+
+/**
+ * This function will recolor all the the nodes to conform to the alternating
+ * row colors.
+ */
+nrsTable.prototype.recolorRows = function()
+{
+ if(this.even_cell_color || this.odd_cell_color)
+ {
+ DEBUG("Recoloring Rows. length = " + this.data_nodes.length);
+ for(var i = 0; i < this.data_nodes.length; i++)
+ {
+ if(i % 2 == 0)
+ {
+ if(this.even_cell_color)
+ this.data_nodes[i].style.backgroundColor = this.even_cell_color;
+ this.data_nodes[i].setAttribute("id", "even_row");
+ }
+ else
+ {
+ if(this.odd_cell_color)
+ this.data_nodes[i].style.backgroundColor = this.odd_cell_color;
+ this.data_nodes[i].setAttribute("id", "odd_row");
+ }
+ }
+ }
+}
+
+/**
+ * This function will create the Data Nodes, which are a reference to the table
+ * rows in the HTML.
+ */
+nrsTable.prototype.createDataNodes = function()
+{
+ if(this.data_nodes)
+ delete this.data_nodes;
+ this.data_nodes = new Array();
+ if(!this.data)
+ return;
+ for(var i = 0; i < this.data.length; i++)
+ {
+ var curr_row = document.createElement("TR");
+
+ for(var j = 0; j < this.data[i].length; j++)
+ {
+ var curr_cell = document.createElement("TD");
+ //do we need to create links on every cell?
+ if(this.cell_links)
+ {
+ var fn = new Function("", this.cell_links[i][j]);
+ curr_cell.onclick = fn;
+ curr_cell.style.cursor = 'pointer';
+ }
+ //workaround for IE
+ curr_cell.setAttribute("className", "dataTD" + j);
+ //assign the padding
+ if(this.padding)
+ {
+ curr_cell.style.paddingLeft = this.padding + "px";
+ curr_cell.style.paddingRight = this.padding + "px";
+ }
+
+ if (typeof this.data[i][j] == "object") {
+ curr_cell.appendChild(this.data[i][j]);
+ } else {
+ curr_cell.appendChild(document.createTextNode(this.data[i][j]));
+ }
+
+ curr_row.appendChild(curr_cell);
+ }
+ //do we need to create links on every row?
+ if(!this.cell_links && this.row_links)
+ {
+ var fn = new Function("", this.row_links[i]);
+ curr_row.onclick = fn;
+ curr_row.style.cursor = 'pointer';
+ }
+ //sets the id for odd and even rows.
+ if(i % 2 == 0)
+ {
+ curr_row.setAttribute("id", "even_row");
+ if(this.even_cell_color)
+ curr_row.style.backgroundColor = this.even_cell_color;
+ }
+ else
+ {
+ curr_row.setAttribute("id", "odd_row");
+ if(this.odd_cell_color)
+ curr_row.style.backgroundColor = this.odd_cell_color;
+ }
+ if(this.hover_color)
+ {
+ curr_row.onmouseover = new Function("", "this.style.backgroundColor='" + this.hover_color + "';");
+ curr_row.onmouseout = new Function("", "this.style.backgroundColor=(this.id=='even_row')?'" +
+ this.even_cell_color + "':'" + this.odd_cell_color + "';");
+ }
+ this.data_nodes[i] = curr_row;
+ }
+}
+
+/**
+ * This function will update the nav page display.
+ */
+nrsTable.prototype.updateNav = function()
+{
+ if(this.page_nav)
+ {
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var t = document.getElementById(this.my_table);
+ var nav = t.tFoot.childNodes[p];
+ if(nav)
+ {
+ var caption = t.tFoot.childNodes[p].childNodes[0].childNodes[2];
+ caption.innerHTML = "Page " + (this.current_page + 1) + " of " + this.num_pages;
+ }
+ else
+ {
+ if(this.num_pages > 1)
+ {
+ this.insertNav();
+ nav = t.tFoot.childNodes[p];
+ }
+ }
+ if(nav)
+ {
+ if(this.current_page == 0)
+ this.hideLeftArrows();
+ else
+ this.showLeftArrows();
+
+ if(this.current_page + 1 == this.num_pages)
+ this.hideRightArrows();
+ else
+ this.showRightArrows();
+ }
+ }
+}
+
+/**
+ * This function will flip the sort arrow in place. If a heading is used in the
+ * footer, then it will flip that one too.
+ */
+nrsTable.prototype.flipSortArrow = function()
+{
+ this.field_asc = !this.field_asc;
+ //flip the arrow on the heading.
+ var heading = document.getElementById(this.my_table).tHead.childNodes[0].childNodes[this.field_to_sort];
+ if(this.field_asc)
+ heading.getElementsByTagName("IMG")[0].setAttribute("src", this.up_icon);
+ else
+ heading.getElementsByTagName("IMG")[0].setAttribute("src", this.down_icon);
+ //is there a heading in the footer?
+ if(this.foot_headers)
+ {
+ //yes, so flip that arrow too.
+ var footer = document.getElementById(this.my_table).tFoot.childNodes[0].childNodes[this.field_to_sort];
+ if(this.field_asc)
+ footer.getElementsByTagName("IMG")[0].setAttribute("src", this.up_icon);
+ else
+ footer.getElementsByTagName("IMG")[0].setAttribute("src", this.down_icon);
+ }
+}
+
+/**
+ * This function will move the sorting arrow from the place specified in
+ * this.field_to_sort to the passed parameter. It will also set
+ * this.field_to_sort to the new value. It will also do it in the footers,
+ * if they exists.
+ * \param field The new field to move it to.
+ */
+nrsTable.prototype.moveSortArrow = function(field)
+{
+ var heading = document.getElementById(this.my_table).tHead.childNodes[0].childNodes[this.field_to_sort];
+ var img = heading.removeChild(heading.getElementsByTagName("IMG")[0]);
+ heading = document.getElementById(this.my_table).tHead.childNodes[0].childNodes[field];
+ heading.appendChild(img);
+ //are there headers in the footers.
+ if(this.foot_headers)
+ {
+ //yes, so switch them too.
+ var footer = document.getElementById(this.my_table).tFoot.childNodes[0].childNodes[this.field_to_sort];
+ var img = footer.removeChild(footer.getElementsByTagName("IMG")[0]);
+ footer = document.getElementById(this.my_table).tFoot.childNodes[0].childNodes[field];
+ footer.appendChild(img);
+ }
+ //finally, set the field to sort by.
+ this.field_to_sort = field;
+}
+
+/**
+ * This function completely destroys a table. Should be used only when building
+ * a brand new table (ie, new headers). Else you should use a function like
+ * buildNewData which only deletes the TBody section.
+ */
+nrsTable.prototype.emptyTable = function()
+{
+ var t = document.getElementById(this.my_table);
+ while(t.childNodes.length != 0)
+ t.removeChild(t.childNodes[0]);
+};
+
+/**
+ * This function builds a brand new table from scratch. This function should
+ * only be called when a brand new table (with headers, footers, etc) needs
+ * to be created. NOT when refreshing data or changing data.
+ */
+nrsTable.prototype.buildTable = function()
+{
+ //reset the sorting information.
+ this.field_to_sort = 0;
+ this.field_asc = true;
+
+ //remove the nodes links.
+ delete this.data_nodes;
+
+ //do we have to calculate the number of pages?
+ if(this.data && this.rows_per_page != -1)
+ {
+ //we do.
+ this.num_pages = Math.ceil(this.data.length / this.rows_per_page);
+ this.current_page = 0;
+ }
+
+ //blank out the table.
+ this.emptyTable();
+
+ //this is the table that we will be using.
+ var table = document.getElementById(this.my_table);
+
+ //is there a caption?
+ if(this.caption)
+ {
+ var caption = document.createElement("CAPTION");
+ caption.setAttribute("align", "top");
+ caption.appendChild(document.createTextNode(this.caption));
+ table.appendChild(caption);
+ }
+
+ //do the heading first
+ var table_header = document.createElement("THEAD");
+ var table_heading = document.createElement("TR");
+ //since this is a new table the first field is what's being sorted.
+ var curr_cell = document.createElement("TH");
+ var fn = new Function("", "nrsTables['" + this.my_table + "'].fieldSort(" + 0 + ");");
+ if(!this.disable_sorting || this.disable_sorting.indexOf(".0.") == -1)
+ curr_cell.onclick = fn;
+ if(this.header_color)
+ curr_cell.style.backgroundColor = this.header_color;
+ curr_cell.style.cursor = 'pointer';
+ var img = document.createElement("IMG");
+ img.setAttribute("src", this.up_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "8");
+ img.setAttribute("width", "8");
+ curr_cell.appendChild(document.createTextNode(this.heading[0]));
+ curr_cell.appendChild(img);
+ table_heading.appendChild(curr_cell);
+ //now do the rest of the heading.
+ for(var i = 1; i < this.heading.length; i++)
+ {
+ curr_cell = document.createElement("TH");
+ var fn = new Function("", "nrsTables['" + this.my_table + "'].fieldSort(" + i + ");");
+ if(!this.disable_sorting || this.disable_sorting.indexOf("." + i + ".") == -1)
+ curr_cell.onclick = fn;
+ if(this.header_color)
+ curr_cell.style.backgroundColor = this.header_color;
+ curr_cell.style.cursor = 'pointer';
+ //build the sorter
+ curr_cell.appendChild(document.createTextNode(this.heading[i]));
+ table_heading.appendChild(curr_cell);
+ }
+ table_header.appendChild(table_heading);
+
+ //now the content
+ var table_body = document.createElement("TBODY");
+ this.createDataNodes();
+ if(this.data)
+ {
+ if(this.natural_compare)
+ this.natSort(0, this.data.length - 1);
+ else
+ this.quickSort(0, this.data.length - 1);
+ this.recolorRows();
+ }
+
+ //finally, the footer
+ var table_footer = document.createElement("TFOOT");
+ if(this.foot_headers)
+ {
+ table_footer.appendChild(table_heading.cloneNode(true));
+ }
+
+ if(this.page_nav && this.num_pages > 1)
+ {
+ //print out the page navigation
+ //first and previous page
+ var nav = document.createElement("TR");
+ var nav_cell = document.createElement("TH");
+ nav_cell.colSpan = this.heading.length;
+
+ var left = document.createElement("DIV");
+ if(document.attachEvent)
+ left.style.styleFloat = "left";
+ else
+ left.style.cssFloat = "left";
+ var img = document.createElement("IMG");
+ img.setAttribute("src", this.rew_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].firstPage();");
+ img.style.cursor = 'pointer';
+ left.appendChild(img);
+ //hack to space the arrows, cause IE is absolute crap
+ left.appendChild(document.createTextNode(" "));
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.prev_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].prevPage();");
+ img.style.cursor = 'pointer';
+ left.appendChild(img);
+ //apend it to the cell
+ nav_cell.appendChild(left);
+
+ //next and last pages
+ var right = document.createElement("DIV");
+ if(document.attachEvent)
+ right.style.styleFloat = "right";
+ else
+ right.style.cssFloat = "right";
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.next_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].nextPage();");
+ img.style.cursor = 'pointer';
+ right.appendChild(img);
+ //hack to space the arrows, cause IE is absolute crap
+ right.appendChild(document.createTextNode(" "));
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.fwd_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "JavaScript:nrsTables['" + this.my_table + "'].lastPage();");
+ img.style.cursor = 'pointer';
+ right.appendChild(img);
+ //apend it to the cell
+ nav_cell.appendChild(right);
+
+ //page position
+ var pos = document.createElement("SPAN");
+ pos.setAttribute("id", "nav_pos");
+ pos.appendChild(document.createTextNode("Page " +
+ (this.current_page + 1) + " of " + this.num_pages));
+ //append it to the cell.
+ nav_cell.appendChild(pos);
+
+ nav.appendChild(nav_cell);
+ //append it to the footer
+ table_footer.appendChild(nav);
+ }
+
+ if(this.footer_color)
+ {
+ for(var i = 0; i < table_footer.childNodes.length; i++)
+ table_footer.childNodes[i].style.backgroundColor = this.footer_color;
+ }
+
+ //append the data
+ table.appendChild(table_header);
+ table.appendChild(table_body);
+ table.appendChild(table_footer);
+ if(this.data)
+ {
+ if(this.natural_compare)
+ this.natSort(0, this.data.length - 1);
+ else
+ this.quickSort(0, this.data.length - 1);
+ }
+ this.refreshTable();
+};
+
+/**
+ * This function will remove the elements in teh TBody section of the table and
+ * return an array of references of those elements. This array can then be
+ * sorted and re-inserted into the table.
+ * \return An array to references of the TBody contents.
+ */
+nrsTable.prototype.extractElements = function()
+{
+ var tbody = document.getElementById(this.my_table).tBodies[0];
+ var nodes = new Array();
+ var i = 0;
+ while(tbody.childNodes.length > 0)
+ {
+ nodes[i] = tbody.removeChild(tbody.childNodes[0]);
+ i++;
+ }
+ return nodes;
+}
+
+/**
+ * This function will re-insert an array of elements into the TBody of a table.
+ * Note that the array elements are stored in the this.data_nodes reference.
+ */
+nrsTable.prototype.insertElements = function()
+{
+ var tbody = document.getElementById(this.my_table).tBodies[0];
+ var start = 0;
+ var num_elements = this.data_nodes.length;
+ if(this.rows_per_page != -1)
+ {
+ start = this.current_page * this.rows_per_page;
+ num_elements = (this.data_nodes.length - start) > this.rows_per_page?
+ this.rows_per_page + start:
+ this.data_nodes.length;
+ }
+ DEBUG("start is " + start + " and num_elements is " + num_elements);
+ for(var i = start; i < num_elements; i++)
+ {
+ tbody.appendChild(this.data_nodes[i]);
+ }
+}
+
+/**
+ * This function will sort the table's data by a specific field. The field
+ * parameter referes to which field index should be sorted.
+ * \param field The field index which to sort on.
+ */
+nrsTable.prototype.fieldSort = function(field)
+{
+ if(this.field_to_sort == field)
+ {
+ //only need to reverse the array.
+ if(this.data)
+ {
+ this.data.reverse();
+ this.data_nodes.reverse();
+ }
+ //flip the arrow on the heading.
+ this.flipSortArrow();
+ }
+ else
+ {
+ //In this case, we need to sort the array. We'll sort it last, first
+ //make sure that the arrow images are set correctly.
+ this.moveSortArrow(field);
+ //finally, set the field to sort by.
+ this.field_to_sort = field;
+ if(this.data)
+ {
+ //we'll be using our implementation of quicksort
+ if(this.natural_compare)
+ this.natSort(0, this.data.length - 1);
+ else
+ this.quickSort(0, this.data.length - 1);
+ }
+ }
+ //finally, we refresh the table.
+ this.refreshTable();
+};
+
+/**
+ * This function will refresh the data in the table. This function should be
+ * used whenever the nodes have changed, or when chanign pages. Note that
+ * this will NOT re-sort.
+ */
+nrsTable.prototype.refreshTable = function()
+{
+ this.extractElements();
+ this.recolorRows();
+ this.insertElements();
+ //finally, if there is a nav, upate it.
+ this.updateNav();
+}
+
+/**
+ * This function will advance a page. If we are already at the last page, then
+ * it will remain there.
+ */
+nrsTable.prototype.nextPage = function()
+{
+ DEBUG("current page is " + this.current_page + " and num_pages is " + this.num_pages);
+ if(this.current_page + 1 != this.num_pages)
+ {
+ this.current_page++;
+ this.refreshTable();
+ }
+ DEBUG("current page is " + this.current_page + " and num_pages is " + this.num_pages);
+}
+
+/**
+ * This function will go back a page. If we are already at the first page, then
+ * it will remain there.
+ */
+nrsTable.prototype.prevPage = function()
+{
+ DEBUG("current page is " + this.current_page + " and num_pages is " + this.num_pages);
+ if(this.current_page != 0)
+ {
+ this.current_page--;
+ this.refreshTable();
+ }
+ DEBUG("current page is " + this.current_page + " and num_pages is " + this.num_pages);
+}
+
+/**
+ * This function will go to the first page.
+ */
+nrsTable.prototype.firstPage = function()
+{
+ if(this.current_page != 0)
+ {
+ this.current_page = 0;
+ this.refreshTable();
+ }
+}
+
+/**
+ * This function will go to the last page.
+ */
+nrsTable.prototype.lastPage = function()
+{
+ DEBUG("lastPage(), current_page: " + this.current_page + " and num_pages: " + this.num_pages);
+ if(this.current_page != (this.num_pages - 1))
+ {
+ this.current_page = this.num_pages - 1;
+ this.refreshTable();
+ }
+}
+
+/**
+ * This function will go to a specific page. valid values are pages 1 to
+ * however many number of pages there are.
+ * \param page The page number to go to.
+ */
+nrsTable.prototype.gotoPage = function(page)
+{
+ page--;
+ if(page >=0 && page < this.num_pages)
+ {
+ this.current_page = page;
+ this.refreshTable();
+ }
+}
+
+/**
+ * This function can be used to change the number of entries per row displayed
+ * on the fly.
+ * \param entries The number of entries per page.
+ */
+nrsTable.prototype.changeNumRows = function(entries)
+{
+ if(entries > 0)
+ {
+ this.rows_per_page = entries;
+ //we do.
+ this.num_pages = Math.ceil(this.data.length / this.rows_per_page);
+ this.refreshTable();
+ }
+}
+
+/**
+ * This function will take in a new data array and , optionally, a new cell_link
+ * array OR a new row_link array. Only one will be used, with the cell_link
+ * array taking precedence. It will then re-build the table with the new data
+ * array.
+ * \param new_data This is the new data array. This is required.
+ * \param cell_links This is the new cell links array, a 2D array for each cell.
+ * \param row_links This is the new row links array, a 1D array for each row.
+ */
+nrsTable.prototype.newData = function(new_data, cell_links, row_links)
+{
+ //extract the elements from teh table to clear the table.
+ this.extractElements();
+ //now delete all the data related to this table. I do this so that
+ //(hopefully) the memory will be freed. This is realy needed for IE, whose
+ //memory handling is almost non-existant
+ delete this.data;
+ delete this.data_nodes;
+ delete this.cell_links;
+ delete this.row_links
+ //now re-assign.
+ this.data = new_data;
+ this.cell_links = cell_links;
+ this.row_links = row_links;
+ if(this.rows_per_page != -1)
+ {
+ //we do.
+ this.num_pages = Math.ceil(this.data.length / this.rows_per_page);
+ if(this.num_pages <= 1 && this.page_nav)
+ this.removeNav();
+ else if(this.page_nav)
+ this.insertNav();
+ this.current_page = 0;
+ }
+ this.createDataNodes();
+ if(this.field_to_sort != 0)
+ this.moveSortArrow(0);
+ if(!this.field_asc)
+ this.flipSortArrow();
+ this.insertElements();
+ this.updateNav();
+}
+
+/**
+ * This function will remove the NAV bar (if one exists) from the table.
+ */
+nrsTable.prototype.removeNav = function()
+{
+ if(this.page_nav)
+ {
+ //in this case, remove the nav from the existing structure.
+ var table = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var nav = table.tFoot.childNodes[p];
+ if(nav)
+ {
+ table.tFoot.removeChild(nav);
+ delete nav;
+ }
+ }
+}
+
+/**
+ * This function wil re-insert the nav into the table.
+ */
+nrsTable.prototype.insertNav = function()
+{
+ table = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ if(this.page_nav && !table.tFoot.childNodes[p])
+ {
+ //this means there should be a nav and there isn't one.
+ //print out the page navigation
+ //first and previous page
+ var nav = document.createElement("TR");
+ var nav_cell = document.createElement("TH");
+ nav_cell.colSpan = this.heading.length;
+
+ var left = document.createElement("DIV");
+ if(document.attachEvent)
+ left.style.styleFloat = "left";
+ else
+ left.style.cssFloat = "left";
+ var img = document.createElement("IMG");
+ img.setAttribute("src", this.rew_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].firstPage();");
+ img.style.cursor = 'pointer';
+ left.appendChild(img);
+ //hack to space the arrows, cause IE is absolute crap
+ left.appendChild(document.createTextNode(" "));
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.prev_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].prevPage();");
+ img.style.cursor = 'pointer';
+ left.appendChild(img);
+ //apend it to the cell
+ nav_cell.appendChild(left);
+
+ //next and last pages
+ var right = document.createElement("DIV");
+ if(document.attachEvent)
+ right.style.styleFloat = "right";
+ else
+ right.style.cssFloat = "right";
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.next_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "nrsTables['" + this.my_table + "'].nextPage();");
+ img.style.cursor = 'pointer';
+ right.appendChild(img);
+ //hack to space the arrows, cause IE is absolute crap
+ right.appendChild(document.createTextNode(" "));
+ img = document.createElement("IMG");
+ img.setAttribute("src", this.fwd_icon);
+ img.setAttribute("border", "0");
+ img.setAttribute("height", "10");
+ img.setAttribute("width", "10");
+ img.onclick = new Function("", "JavaScript:nrsTables['" + this.my_table + "'].lastPage();");
+ img.style.cursor = 'pointer';
+ right.appendChild(img);
+ //apend it to the cell
+ nav_cell.appendChild(right);
+
+ //page position
+ var pos = document.createElement("SPAN");
+ pos.setAttribute("id", "nav_pos");
+ pos.appendChild(document.createTextNode("Page " +
+ (this.current_page + 1) + " of " + this.num_pages));
+ //append it to the cell.
+ nav_cell.appendChild(pos);
+
+ nav.appendChild(nav_cell);
+ //append it to the footer
+ table.tFoot.appendChild(nav);
+ }
+}
+
+/**
+ * This function will hide the previous arrow and the rewind arrows from the
+ * nav field.
+ */
+nrsTable.prototype.hideLeftArrows = function()
+{
+ if(!this.page_nav)
+ return;
+ var myTable = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var nav = myTable.tFoot.childNodes[p];
+ nav.childNodes[0].childNodes[0].style.display = "none";
+}
+
+/**
+ * This function will show the previous arrow and the rewind arrows from the
+ * nav field.
+ */
+nrsTable.prototype.showLeftArrows = function()
+{
+ if(!this.page_nav)
+ return;
+ table = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var nav = table.tFoot.childNodes[p];
+ nav.childNodes[0].childNodes[0].style.display = "block";
+}
+
+/**
+ * This function will hide the next arrow and the fast foward arrows from the
+ * nav field.
+ */
+nrsTable.prototype.hideRightArrows = function()
+{
+ if(!this.page_nav)
+ return;
+ table = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var nav = table.tFoot.childNodes[p];
+ nav.childNodes[0].childNodes[1].style.display = "none";
+}
+
+/**
+ * This function will show the next arrow and the fast foward arrows from the
+ * nav field.
+ */
+nrsTable.prototype.showRightArrows = function()
+{
+ if(!this.page_nav)
+ return;
+ table = document.getElementById(this.my_table);
+ var p = 0;
+ if(this.foot_headers)
+ p++;
+ var nav = table.tFoot.childNodes[p];
+ nav.childNodes[0].childNodes[1].style.display = "block";
+}
--- /dev/null
+* {
+ font-family: Verdana, Arial, Helvetica, sans-serif, monospace;
+}
+
+body {
+ background: #606060;
+ color: #000000;
+}
+
+a:link {
+ color: #0000FF;
+ background: transparent;
+ text-decoration: none;
+}
+
+a:visited {
+ color: #990099;
+ background: transparent;
+ text-decoration: none;
+}
+
+a:active {
+ color: #000000;
+ background: #ADD8E6;
+ text-decoration: none;
+}
+
+h1.rubrique_info {
+ color: #990033;
+ margin: 0px 0px 0px 0px;
+ padding: 0em;
+ border: 0px;
+ font-size: 12px;
+}
+h1.connexe {
+ font-size: 12px;
+ padding: 0em;
+ margin: 0px 0px 0px 0px;
+ color: #990033;
+}
+
+a.rubrique_infolink {
+ text-decoration: none;
+}
+ul.rubrique_infoul {
+ display: inline;
+ list-style-type: square;
+}
+ul.rubrique_infoul * {
+ width: 100%;
+}
+li.rubrique_infoul {
+ margin-left: 15px;
+}
+h1 {
+ color: #990033;
+}
+
+div.main {
+ background: white;
+ color: #000000;
+ margin-left: 5px;
+ margin-right: 5px;
+ /*padding-left: 10px;
+ padding-right: 10px;*/
+ border: 1px black solid;
+ text-align: left;
+ font-size: 12px;
+}
+div.lsfnbanner {
+ margin-left: 150px;
+ margin-right: 170px;
+ border-top: none;
+ padding-left: 10px;
+ padding-right: 10px;
+ border-bottom: 1px black solid;
+ border-right: 1px black solid;
+ border-left: 1px black solid;
+ text-align: left;
+ font-size: 11px;
+ padding-top: 2px;
+ background-color: #eeeae6;
+}
+div.footer {
+ padding-top: 5px;
+ padding-bottom: 3px;
+ border-top: 1px black solid;
+ border-left: 1px black solid;
+ border-right: 1px black solid;
+ text-align: left;
+ font-size: 9px;
+ background: #dcdff4;
+ margin-top: 40px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+div.footer p {
+ margin-left: 10px;
+ margin-top: 2px;
+ margin-bottom: 2px;
+}
+a.lsfnlink:link,a.lsfnlink:visited,a.lsfnlink:active {
+ text-decoration: none;
+ color: #333333;
+ font-size: 10px;
+}
+a.lsfnlink:hover {
+ text-decoration: underline;
+ color: black;
+}
+div.menubartop {
+ margin-bottom: 10px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 0px;
+ font-size: 13px;
+}
+div.smallmenubar {
+ background: white;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ font-weight:bold;
+ font-size: 10px;
+ text-align: right;
+}
+div.menuevent {
+ float: left;
+ width: 350px;
+ font-size: 11px;
+ text-align: left;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ padding-left: 10px;
+ font-weight: bold;
+ margin: 0px;
+}
+div.menubar {
+ background: #cac2a8;
+ border-top: 1px black solid;
+ border-bottom: 1px black solid;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 3px;
+ padding-bottom: 2px;
+ font-weight:bold;
+ font-size: 13px;
+}
+div.menubar a {
+ text-decoration: none;
+}
+div.menubar p {
+ padding: 0px;
+ margin: 0px;
+}
+div.menudate {
+ float: left;
+ width: 130px;
+ padding-top: 5px;
+}
+div.menuaccroche {
+ margin-left: 30px;
+ float: left;
+ text-decoration: underline;
+ font-size: 14px;
+}
+div.menusearch {
+ float: right;
+ text-align: right;
+ padding-top: 5px;
+ width: 170px;
+}
+div.menusearch p {
+ margin: 0px 0px 0px 0px;
+}
+div.newsletter {
+ float: right;
+ text-align: right;
+ margin: 0px;
+ padding: 0px;
+ font-size: 8px;
+ font-weight: normal;
+}
+div.newsletter p {
+ margin: 0px 0px 0px 0px;
+}
+.searchinput {
+ border: solid 1px black;
+}
+.newsletterinput {
+ border: solid 1px black;
+ font-weight: normal;
+}
+a#menulinkselect {
+ color: #ed7e00;
+}
+
+div.leftbox {
+ width: 200px;
+ float: left;
+ padding-left: 5px;
+ padding-right: 5px;
+ padding-bottom: 5px;
+ border-right: 1px black solid;
+ border-bottom: 1px black solid;
+ background: white;
+ margin-bottom: 10px;
+ margin-right: 20px;
+ overflow: hidden;
+}
+div.leftbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 10px;
+ margin: 0px;
+}
+div.leftbox h2 {
+ font-weight: bold;
+ font-size: 12px;
+ margin: 0px;
+}
+div.leftbox ul {
+ list-style-type: square;
+ margin-bottom: 10px;
+ margin-left: 0em;
+ padding-left: 0em;
+}
+div.leftbox li {
+ margin-left: 10px;
+ margin-top: 10px;
+}
+
+div.rightbox {
+ width: 150px;
+ float: right;
+ padding-top: 10px;
+ padding-left: 5px;
+ padding-right: 15px;
+ padding-bottom: 5px;
+ border-left: 1px black solid;
+ border-bottom: 1px black solid;
+ text-align: left;
+}
+div.tipdiv {
+ margin-right: 50px;
+ padding-top: 5px;
+ padding-right: 10px;
+ padding-left: 10px;
+ text-align: justify;
+ background-color: #eee;
+ border: black solid 1px;
+ margin-left: 10px;
+}
+div.tipdiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ color: black;
+ margin-top: 0px;
+ margin-bottom: 20px;
+}
+div.tipdiv h2 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 12px;
+ margin: 0px;
+}
+
+div.newsdiv {
+ margin-left: 220px;
+ margin-right: 180px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+ text-align: justify;
+}
+div.newsdiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ margin: 0px;
+}
+div.newsdiv h2 {
+ font-weight: normal;
+ font-size: 12px;
+ margin: 0px;
+}
+div.newsdiv h3 {
+ font-weight: normal;
+ font-size: 12px;
+ margin-bottom: 20px;
+}
+div.objdiv {
+ margin-left: 220px;
+ margin-right: 20px;
+ margin-top: 10px;
+ margin-bottom: 20px;
+}
+
+h1.newstitle {
+ text-align: left;
+ font-size: 14px;
+ margin: 0px 0px 0px 0px;
+ color: black;
+}
+div.titlediv {
+ border-top: solid #cac2a8 2px;
+ margin-top: 20px;
+ background-color: #eeeae6;
+ padding-left: 10px;
+ font-size: 11px;
+}
+div.bodydiv {
+ border: solid #9e9784 1px;
+ padding-left: 10px;
+ padding-right: 10px;
+ padding-top: 10px;
+ padding-bottom: 5px;
+ margin-top: 2px;
+ text-align: justify;
+}
+div.bodydiv ul {
+ list-style-type: square;
+}
+
+div.comments {
+ padding: 10px;
+ border-top: solid 2px #d37537;
+ border-bottom: solid 2px #d37537;
+ margin-top: 20px;
+ margin-bottom: 10px;
+ background-color: #cacaca;
+ font-size: 12px;
+ line-height: 1.3em;
+}
+
+.comment-vote {
+ font-style: italic;
+}
+
+p.commentsbody {
+ border-left-style: solid;
+ border-width: 1px;
+ border-color: rgb(0, 0, 0);
+ padding-left: 10px;
+ text-align: justify;
+ margin-right: 20px;
+}
+div.commentsreply {
+ /*margin-left: 220px;*/
+ text-align: center;
+ margin-top: 50px;
+}
+
+ul.commentsul {
+ list-style-type: none;
+ margin-bottom: 10px;
+ margin-left: 1.25em;
+ padding-left: 0em;
+ /*border-left: 1px solid black;*/
+}
+ul.commentsli {
+ margin: 10px;
+}
+div.comments li {
+ margin-top: 20px;
+ margin-left: 2px;
+}
+
+div.comments h1 {
+ font-size: 12px;
+ color: black;
+ margin: 0px 20px 3px 0px;
+ background-color: rgb(226, 226, 226);
+ padding-left: 1px;
+ font-weight: normal;
+}
+
+div.articlediv {
+ padding-left: 20px;
+ padding-right: 20px;
+ padding-top: 10px;
+ padding-bottom: 20px;
+ margin-right: 180px;
+ margin-left: 220px;
+ border: solid 1px black;
+ margin-top: 10px;
+ text-align: justify;
+ background-color: #E2E2E2;
+}
+img {
+ border: 0px;
+}
+div.sectionimg {
+ float: left;
+ margin-right: 10px;
+ margin-top: 5px;
+}
+p.hautpage {
+ margin-top: 20px;
+ margin-bottom: 20px;
+ margin-left: 10px;
+}
+div.leftcol {
+ /*width: 202px;*/
+ width: 202px;
+ float: left;
+ padding: 0px;
+ /*overflow: hidden;*/
+}
+div.logodiv {
+ border-right: 1px black solid;
+ border-bottom: 1px black solid;
+ padding: 0px;
+ line-height: 0px
+}
+div.loginbox {
+ margin-left: 4px;
+ border: solid #a59f8b 1px;
+ margin-top: 2px;
+ padding: 5px;
+ background-color: #fff2e8;
+ font-size: 10px;
+}
+div.loginbox p {
+ margin: 0px;
+ padding: 0px;
+}
+div.loginbox ul {
+ margin-left: 10px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ padding: 0px;
+ list-style-type: none;
+}
+div.loginbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 10px;
+ margin: 0px;
+}
+div.loginbox h2 {
+ font-weight: bold;
+ font-size: 12px;
+ margin: 0px;
+}
+div.loginbox h3 {
+ margin-top: 0px;
+ margin-bottom: 5px;
+ font-size: 12px;
+}
+div.polldivtitle {
+ margin-bottom: 1px;
+ background-color: #cac2a8;
+ margin-left: 4px;
+ margin-top: 15px;
+ padding-left: 5px;
+ font-size: 10px;
+ border-top: solid #a59f8b 1px;
+ border-bottom: solid #a59f8b 1px;
+ text-transform: uppercase;
+}
+div.polldiv {
+ margin-left: 4px;
+ border: 1px #a59f8b solid;
+ margin-top: 0px;
+ padding: 5px;
+ background: #fff2e8;
+}
+div.polldiv p {
+margin: 5px; padding: 0px;
+}
+div.polldiv ul {
+ margin-left: 5px;
+ margin-top: 0px;
+ margin-bottom: 10px;
+ padding: 0px;
+ list-style-type: none;
+}
+div.otherboxtitle {
+ margin-bottom: 2px;
+ background-color: #e3dabc;
+ margin-left: 4px;
+ margin-top: 10px;
+ padding-left: 5px;
+ padding-top: 2px;
+ font-size: 11px;
+ border-top: solid #777364 1px;
+ border-bottom: solid #777364 1px;
+ /*text-transform: uppercase;*/
+ /*font-weight: bold;*/
+}
+div.otherbox {
+ margin-left: 4px;
+ margin-right: 1px;
+ border: 1px #a59f8b solid;
+ margin-top: 0px;
+ padding: 5px;
+ text-align: justify;
+ background: #fff2e8;
+ font-size: 10px;
+}
+div.otherbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 10px;
+ margin: 0px;
+}
+div.otherbox h2 {
+ font-weight: bold;
+ font-size: 11px;
+ margin: 0px;
+}
+div.otherbox p {
+ margin-top: 5px;
+ margin-bottom: 10px;
+}
+div.rightlogo {
+ width: 90px;
+ float: right;
+ margin-right: 0px;
+ padding-top: 0px;
+ padding-left: 0px;
+ padding-bottom: 0px;
+}
+div.centraldiv {
+ margin-left: 220px;
+ margin-right: 10px;
+ margin-bottom: 20px;
+ margin-top: 10px;
+}
+div.centraldiv h1 {
+ font-weight: bold;
+ font-size: 14px;
+ margin: 0px;
+}
+div.centraldiv h2 {
+ font-weight: normal;
+ font-size: 12px;
+ margin: 0px;
+}
+div.centraldiv h3 {
+ font-weight: normal;
+ font-size: 12px;
+ margin-bottom: 20px;
+}
+div.centralinfo {
+ margin-bottom: 20px;
+}
+div.lefttopbox {
+ padding-left: 5px;
+ padding-right: 5px;
+ padding-top: 5px;
+ text-align: justify;
+ border: 2px #a59f8b solid;
+ background: #ffffbb;
+ /*
+ background: #c8ff9b;
+ */
+ font-size: 13px;
+ width: 60%;
+ float: left;
+}
+div.lefttopbox p {
+ margin-top: 5px; margin-bottom: 10px;
+}
+div.lefttopbox h1 {
+ font-size: 13px;
+ font-weight: bold;
+ margin: 0px;
+ text-align: right;
+}
+div.lefttopbox h2 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 13px;
+ margin: 0px;
+}
+div.lefttopbox h3 {
+ font-weight: bold;
+ font-size: 14px;
+ margin: 0px;
+}
+div.righttopbox {
+ border: 1px #a59f8b solid;
+ background: white;
+ padding: 5px;
+ text-align: justify;
+ font-size: 12px;
+ width: 34%;
+ float: right;
+}
+div.righttopbox h1 {
+ text-transform: uppercase;
+ font-weight: bold;
+ color: #ed7e00;
+ font-size: 10px;
+ margin: 0px;
+}
+div.righttopbox h2 {
+ font-weight: bold;
+ font-size: 12px;
+ margin: 0px;
+}
+div.boardindex {
+ text-align: justify;
+ font-size: 11px;
+ padding: 10px;
+ margin-left: 20px;
+ margin-right: 20px;
+}
+a.boardindex:link,a.boardindex:visited,a.boardindex:active {
+ text-decoration:none;
+ color: red;
+}
+div.boardleftmsg {
+ float: left;
+ margin-top: 3px;
+}
+div.boardrightmsg {
+ margin-left: 130px;
+ margin-top: 3px;
+ padding-left: 5px;
+}
+div.journalbody {
+ margin-left: 40px;
+ margin-top: 40px;
+}
+div.journalbody h1 {
+ font-size: 15px;
+ font-weight: bold;
+}
+div.journalbody p {
+ margin-bottom: 20px;
+}
+.formulaire {
+ border: solid 1px black;
+ font-size: 12px;
+ background-color: #fffbf7;
+ color: #000000;
+}
+.formulaire:focus {
+ background-color: #eeeae6;
+ border: 1px solid #777;
+ color: #000000;
+}
+.newcomments {
+ color: red;
+ font-weight: bold;
+}
+.misspelled {
+ color: red;
+ font-weight: bold;
+}
+div.commentsreplythanks {
+ margin-left: 100px;
+ margin-top: 50px;
+ margin-right: 100px;
+ background-color: #eee;
+ border: black solid 1px;
+ padding: 10px;
+}
+div.archivediv {
+ margin-right: 20px;
+}
+.archivedate {
+ color:#f30;
+}
+.archivelink {
+ font-size: 14px;
+ font-weight: bold;
+ text-decoration: underline;
+}
+div.forumgroup {
+ text-align: left;
+ border: solid black 1px;
+ padding: 10px 20px 20px 10px;
+ background-color: #eee;
+}
+
+div.adminall {
+ font-size: 12px;
+ padding: 10px;
+}
+div.floatwin {
+ position:absolute;
+ top: 150px;
+ left: 450px;
+ width:200px;
+ visibility:hidden;
+ background: #dcdff4;
+ padding: 5px;
+ border: solid black 1px;
+}
+
+div.poll-result-bar {
+ background-color: #bbb;
+ color: black;
+ border: solid 1px black;
+}
+div.funbanner {
+ text-align: right;
+ border-top: #aaa solid 1px;
+ border-bottom: #aaa solid 1px;
+ padding-right: 20px;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ font-size: 10px;
+ background-color: #fff2e8;
+ font-weight: bold;
+ margin-top: 10px;
+}
+
+div.replie
+{
+ display: none;
+}
--- /dev/null
+#!/bin/sh
+#
+# Change the following four environment variables to suit your system
+# Note, the configuration below works on a stock SuSE 10.2 system
+#
+# You can, in general, execute this file multiple times. However, if
+# you edit certain of the installed files, your changes might be lost.
+# This script will not overwrite any installed /etc/bacula/bweb.conf
+#
+# Configuration directory for bweb
+CONF_DIR=/etc/bacula
+#
+# Root directory of web files
+#
+WEB_DIR=/srv/www/htdocs
+#
+# share directory for programs
+#
+SHARE_DIR=/usr/share
+#
+# cgi_bin location for web server
+#
+CGI_BIN=/srv/www/cgi-bin
+#
+# Userid the web server is running under
+#
+HTTP_USER=wwwrun
+HTTP_GROUP=www
+
+# Bacula database name and password
+DB=bacula
+DB_PW=""
+EMAIL_ADDR="xxx@localhost"
+MTX="/sbin/mtx"
+
+if [ `whoami` != "root" ] ; then
+ echo "You must be root to run this script."
+ exit 1
+fi
+
+#
+# Normally you should not need to change the following
+#
+# first, copy the bweb perl library into your PERL5 INC path
+sed -i "s!/etc/bacula!${CONF_DIR!" lib/Bweb.conf
+perl Makefile.PL
+make install
+
+# copy the bweb perl program to your cgi location
+if [ ! -d ${CGI_BIN}/bweb ] ; then
+ mkdir -m 755 ${CGI_BIN}/bweb
+fi
+install -m 755 -o root -g root cgi/*.pl ${CGI_BIN}/bweb
+
+# get a config file
+if [ ! -d ${CONF_DIR} ] ; then
+ mkdir -m 755 ${CONF_DIR}
+ chown root:bacula ${CONF_DIR}
+fi
+
+# Setup a default ${CONF_DIR}/bweb.conf
+if [ ! -e ${CONF_DIR}/bweb.conf ] ; then
+cat > ${CONF_DIR}/bweb.conf <<END_OF_DATA
+\$VAR1 = bless( {
+ 'graph_font' => '/usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf',
+ 'name' => undef,
+ 'config_file' => '${CONF_DIR}/bweb.conf',
+ 'bconsole' => '/usr/sbin/bconsole -n -c /etc/bacula/bconsole.conf',
+ 'ach_list' => {
+ 'S1_L80' => bless( {
+ 'info' => {
+ 'drive' => 0,
+ 'io' => 0,
+ 'slot' => 0
+ },
+ 'name' => 'S1_L80',
+ 'bweb' => undef,
+ 'device' => '/dev/changer',
+ 'drive' => [],
+ 'debug' => 0,
+ 'label' => {},
+ 'precmd' => 'sudo',
+ 'io' => [],
+ 'mtxcmd' => '${MTX}',
+ 'drive_name' => [
+ 'S1_L80_SDLT0',
+ 'S1_L80_SDLT1'
+ ],
+ 'slot' => []
+ }, 'Bweb::Autochanger' )
+ },
+ 'password' => '${DB_PW}',
+ 'template_dir' => '${SHARE_DIR}/bweb/tpl',
+ 'dbi' => 'DBI:mysql:database=bacula',
+ 'error' => '',
+ 'debug' => 0,
+ 'user' => '${DB}',
+ 'email_media' => '${EMAIL_ADDR}'
+}, 'Bweb::Config' );
+END_OF_DATA
+fi
+
+chown ${HTTP_USER} ${CONF_DIR}/bweb.conf
+
+# copy the bweb template file
+if [ ! -d ${SHARE_DIR}/bweb/tpl ] ; then
+ mkdir -p ${SHARE_DIR}/bweb/tpl
+fi
+install -m 644 -o root -g root tpl/*.tpl ${SHARE_DIR}/bweb/tpl
+
+# copy the bweb graphics elements (bweb elements must reside in /bweb)
+if [ ! -d ${WEB_DIR} ] ; then
+ mkdir ${WEB_DIR}/bweb
+fi
+install -m 644 -o root -g root html/*.{js,png,css,gif,ico,html} ${WEB_DIR}/bweb
+
+echo " "
+echo "Please edit /etc/bweb.conf and ensure that all the paths to"
+echo " the various programs and files such as bconsole, bconsole.conf"
+echo " are correct, and that the DBI driver is correct (default mysql)"
+echo " and has the right password for your DB"
+echo " "
+
+
+
+# done !
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Acerca </h1>
+</div>
+<div class='bodydiv'>
+<pre>
+ 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.
+
+ nrsTable Copyright 2005 New Roads School (GPL)
+ kaiska css Copyright Willy Morin (BWL)
+</pre>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'><TMPL_UNLESS name>Nueva</TMPL_UNLESS> Autochanger </h1>
+</div>
+<div class='bodydiv'>
+ Tiene que crear una Ubicación, la cual deberá tener el mismo nombre.<br/><br/>
+
+ <form name='form1' action="?" method='get'>
+ <table>
+ <tr><td>Nombre :</td>
+ <td>
+ <select name='ach' class='formulaire' id='ach'>
+<TMPL_LOOP devices><option value='<TMPL_VAR name>'><TMPL_VAR name></option></TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Pre-comando :</td>
+ <td> <input class="formulaire" type='text' id='precmd' value='sudo'
+ title='can be "sudo" or "ssh storage@storagehost"...' name='precmd'>
+ </td>
+ </tr>
+ <tr><td>Comando mtx :</td>
+ <td> <input class="formulaire" type='text' name='mtxcmd' size='32'
+ value='/usr/sbin/mtx' id='mtxcmd'>
+ </td>
+ </tr>
+ <tr><td>Device :</td>
+ <td> <input class="formulaire" type='text' name='device'
+ value='/dev/changer' id='device'>
+ </td>
+ </tr>
+ <tr><td><b>Drives</b></td><td/></tr>
+ <TMPL_LOOP devices>
+ <tr>
+ <td><input class='formulaire' type='checkbox' id='drive_<TMPL_VAR name>'
+ name='drives' value='<TMPL_VAR name>'><TMPL_VAR name>
+ </td>
+ <td>index <input type='text' title='drive index' class='formulaire'
+ id='index_<TMPL_VAR name>' value=''
+ name='index_<TMPL_VAR name>' size='3'>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <input type="image" name='action' value='ach_add' src='/bweb/save.png'>
+ </form>
+</div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF name>
+ for (var i=0; i < document.form1.ach.length; ++i) {
+ if (document.form1.ach[i].value == '<TMPL_VAR name>') {
+ document.form1.ach[i].selected = true;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF mtxcmd>
+ document.getElementById('mtxcmd').value='<TMPL_VAR mtxcmd>';
+ </TMPL_IF>
+ <TMPL_IF precmd>
+ document.getElementById('precmd').value='<TMPL_VAR precmd>';
+ </TMPL_IF>
+ <TMPL_IF device>
+ document.getElementById('device').value='<TMPL_VAR device>';
+ </TMPL_IF>
+ <TMPL_IF drives>
+ <TMPL_LOOP drives>
+ document.getElementById('drive_<TMPL_VAR name>').checked=true;
+ document.getElementById('index_<TMPL_VAR name>').value=<TMPL_VAR index>;
+ </TMPL_LOOP>
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+Autochanger : <TMPL_VAR Name> (<TMPL_VAR nb_drive> Drives
+<TMPL_IF nb_io><TMPL_VAR nb_io> IMPORT/EXPORT</TMPL_IF>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='get'>
+ <input type='hidden' name='ach' value='<TMPL_VAR name>'>
+ <TMPL_IF "Update">
+ <font color='red'> Debe ejecutar el comando update slot, El estado del Autochanger es diferente al de las slots de bacula </font>
+ <br/>
+ </TMPL_IF>
+ <table border='0'>
+ <tr>
+ <td valign='top'>
+ <div class='otherboxtitle'>
+ Tools
+ </div>
+ <div class='otherbox'>
+<label>
+<input type="image" name='action' value='label_barcodes'
+ title='run label barcodes' src='/bweb/label.png'>Label
+</label>
+<TMPL_IF nb_io>
+<label>
+<input type="image" name='action' value='eject'
+ title='put selected media on i/o' src='/bweb/extern.png'>
+Exportar
+</label>
+<label>
+<input type="image" name='action' value='clear_io'
+ title='Clear i/o' src='/bweb/intern.png'>
+Limpiar I/O
+</label>
+</TMPL_IF>
+<label>
+<input type="image" name='action' value='update_slots'
+ title='run update slots' src='/bweb/update.png'>
+Actualizar
+</label>
+<br/><br/>
+<label>
+<input type="image" name='action' value='ach_load'
+ title='mount drive' src='/bweb/load.png'>
+Montar
+</label>
+<label>
+<input type="image" name='action' value='ach_unload'
+ title='umount drive' src='/bweb/unload.png'>
+Desmontar
+</label>
+
+ </div>
+ <td width='200'/>
+ <td>
+ <b> Drives: </b><br/>
+ <table id='id_drive'></table> <br/>
+ </td>
+ </tr>
+ </table>
+ <b> Contenido: </b><br/>
+ <table id='id_ach'></table>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Slot Real", "Slot", "Nombre Volumen","Bytes Vol","Estado Vol",
+ "Tipo Medio","Nombre Pool","Fecha Escritura",
+ "Expiración", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Slots>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'slot';
+chkbox.value = '<TMPL_VAR realslot>';
+
+data.push( new Array(
+"<TMPL_VAR realslot>",
+"<TMPL_VAR slot>",
+"<TMPL_VAR volumename>",
+human_size(<TMPL_VAR volbytes>),
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR name>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_ach",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6)
+ padding: 3
+}
+);
+
+var header = new Array("Index", "Nombre Drive", "Nombre Volumen", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Drives>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'drive';
+chkbox.value = '<TMPL_VAR index>';
+
+data.push( new Array(
+"<TMPL_VAR index>",
+"<TMPL_VAR name>",
+"<TMPL_VAR load>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_drive",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6),
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+<head>
+<title>Bweb - Bacula Web Interface</title>
+<link rel="SHORTCUT ICON" href="/bweb/favicon.ico">
+<script type="text/javascript" language="JavaScript" src="/bweb/natcompare.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/nrs_table.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/bweb.js"></script>
+<link type="text/css" rel="stylesheet" href="/bweb/style.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/kaiska.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/bweb.css"/>
+</head>
+<body>
+
+<script type="text/javascript" language="JavaScript">
+if (navigator.appName == 'Konqueror') {
+ alert("Sorry at this moment, bweb works only with mozilla.");
+}
+</script>
+
+<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=run_job">Jobs</a>
+ <ul>
+ <li><a href="bweb.pl?action=run_job">Jobs Definidos</a>
+ <li><a href="bweb.pl?action=job">Últimos Jobs</a> </li>
+ <li><a href="bweb.pl?action=running">Jobs en Ejecución</a>
+ <li><a href="bweb.pl?action=next_job">Próximos Jobs</a> </li>
+ <li><a href="bweb.pl?action=restore" title="Launch brestore">Recuperación</a> </li>
+ </ul>
+ </li>
+ <li style="padding: 0.25em 2em;">Medios
+ <ul>
+ <li><a href="bweb.pl?action=pool">Pools</a> </li>
+ <li><a href="bweb.pl?action=location">Ubicaciones</a> </li>
+ <li><a href="bweb.pl?action=media">Todos los Medios</a><hr></li>
+ <li><a href="bweb.pl?action=extern_media">Expulsar Medio</a> </li>
+ <li><a href="bweb.pl?action=intern_media">Cargar Medio</a> </li>
+ </ul>
+ </li>
+<TMPL_IF achs>
+ <li style="padding: 0.25em 2em;">Libreria
+ <ul>
+<TMPL_LOOP achs>
+ <li><a href="bweb.pl?action=ach_view;ach=<TMPL_VAR name>"><TMPL_VAR name></a></li>
+</TMPL_LOOP>
+ </ul>
+ </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=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;">
+<input type="image" class="button" title="buscar medio" onclick="search_media();" src="/bweb/tape.png"><input type="image" title="buscar cliente" 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="buscar..." onclick="this.value='';" title="buscar por medio o cliente"></li>
+</ul>
+
+<form name="search" action="bweb.pl?" method='GET'>
+ <input type="hidden" name="action" value="">
+ <input type="hidden" name="re_media" value="">
+ <input type="hidden" name="re_client" value="">
+</form>
+
+<div style="clear: left;">
+<div style="float: left;">
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'>
+ Mover medio
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<form action="?" method='GET'>
+<table>
+<tr>
+<td><b>Para: </b></td><td><input class='formulaire' name='email' value='<TMPL_VAR email>'></td>
+</tr><tr>
+<td><b>Asunto: </b></td><td><input class='formulaire' name='subject' value='[BACULA] Mover medio a <TMPL_VAR newlocation>' size='80'></td>
+</tr><tr>
+<td></td>
+<td>
+<textarea name='content' class='formulaire' cols='80' rows='32'>
+Estimado,
+
+Puede mover este medio a <TMPL_VAR newlocation>
+Medio :
+<TMPL_LOOP Medias>
+ - <TMPL_VAR VolumeName> (<TMPL_VAR location>)
+</TMPL_LOOP>
+
+Cuando finalice, puede actualizar la ubicacion del medio ?
+(puede usar este link : <TMPL_VAR url>).
+
+Gracias
+</textarea>
+</td></tr></table>
+<input class='formulaire' type='submit' name='action' value='move_email'>
+</form>
+<br>
+<a href="<TMPL_VAR url>"><img alt='update now' src='/bweb/update.png'>Actualizar Ahora</a>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Job ejecutándose <TMPL_VAR JobName> en <TMPL_VAR Client>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<table>
+ <tr>
+ <td> <b> Nombre Job: </b> <td> <td> <TMPL_VAR jobname> (<TMPL_VAR jobid>) <td>
+ </tr>
+ <tr>
+ <td> <b> Archivo en proceso: </b> <td> <td> <TMPL_VAR "processing file"> </td>
+ </tr>
+ <tr>
+ <td> <b> Velocidad: </b> <td> <td> <TMPL_VAR "bytes/sec"> B/s</td>
+ </tr>
+ <tr>
+ <td> <b> Archivos Examinados: </b> <td> <td> <TMPL_VAR "files examined"></td>
+ </tr>
+ <tr>
+ <td> <b> Bytes: </b> <td> <td> <TMPL_VAR bytes></td>
+ </tr>
+</table>
+<form name='form1' action='?' method='GET'>
+<input type="image" name='action' value='dsp_cur_job'
+ src='/bweb/update.png' title='refresh'>
+<input type='hidden' name='client' value='<TMPL_VAR Client>'>
+<input type='hidden' name='jobid' value='<TMPL_VAR JobId>'>
+<input type="image" name='action' value='cancel_job'
+ onclick="return confirm('Do you want to cancel this job ?')"
+ title='Cancel job' src='/bweb/cancel.png'>
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ bweb_add_refresh();
+</script>
+
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Clientes</h1>
+ </div>
+ <div class='bodydiv'>
+<form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <div class="otherboxtitle">
+ Acciones
+ </div>
+ <div class="otherbox">
+<!-- <h1>Acciones</h1> -->
+ <label>
+ <input type="image" name='action' value='job' title='Mostrar últimos jobs' src='/bweb/zoom.png'>Últimos Jobs</label>
+ <label>
+ <input type="image" name='action' value='dsp_cur_job' title='Mostrar job actual' src='/bweb/zoom.png'>Jobs Actuales</label>
+ <label>
+ <input type="image" name='action' value='client_status' title='Mostrar estado del cliente' src='/bweb/zoom.png'>Estado</label>
+ <label>
+ <input type="image" name='action' value='client_stats' title='EstadÃsticas del Cliente' src='/bweb/chart.png'>EstadÃsticas</label>
+ </label>
+ </div>
+
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Nombre", "Selección", "Descripción", "Auto Prune", "Retención Archivos", "Retención Jobs");
+
+var data = new Array();
+var chkbox ;
+
+<TMPL_LOOP NAME=Clients>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'client';
+chkbox.value = '<TMPL_VAR NAME=Name>';
+
+data.push(
+ new Array( "<TMPL_VAR NAME=Name>",
+ chkbox,
+ "<TMPL_VAR NAME=Uname>",
+ "<TMPL_VAR NAME=AutoPrune>",
+ human_sec(<TMPL_VAR NAME=FileRetention>),
+ human_sec(<TMPL_VAR NAME=JobRetention>)
+ )
+) ;
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1)
+}
+);
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> <TMPL_VAR title> : <TMPL_VAR name></h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log' style='font-size: 10px'>
+<TMPL_VAR content>
+ </pre>
+ </div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuración </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='post'>
+ <table>
+ <tr> <td><b>SQL Connection</b></td> <td/></tr>
+
+ <tr><td>DBI :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR dbi>' size='64' name='dbi'>
+ </td>
+ </tr>
+ <tr><td>Usuario :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR user>' name='user'>
+ </td>
+ </tr>
+ <tr><td>Clave :</td>
+ <td> <input class="formulaire" type='password' value='<TMPL_VAR password>' name='password'>
+ </td></tr>
+
+ <tr> <td><b>Opciones Generales</b></td> <td/></tr>
+
+ <tr><td>email_media :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR email_media>' name='email_media'>
+ </td></tr>
+ </td></tr>
+
+ <tr> <td><b>Configuración Bweb</b></td> <td/></tr>
+
+ <tr><td>graph_font :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR graph_font>' size='64' name='graph_font'>
+ </td></tr>
+ <tr><td>template_dir :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR template_dir>' size='64' name='template_dir'>
+ </td></tr>
+ <tr><td>fv_write_path :</td>
+ <td> <input class="formulaire" title="Este directorio debe tener permisos de escritura para el usuario apache y debe ser accesible en /bweb/fv" type='text' value='<TMPL_VAR fv_write_path>' size='64' name='fv_write_path'>
+ </td></tr>
+ <tr><td>bconsole :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR bconsole>' size='64' name='bconsole'>
+ </td></tr>
+ <tr><td>debug :</td>
+ <td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
+ </td></tr>
+ </table>
+ <input type="image" name='action' value='apply_conf' src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuración </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr> <td><b>Conexión SQL</b></td> <td/></tr>
+ <tr><td>DBI :</td> <td> <TMPL_VAR dbi> </td></tr>
+ <tr><td>Ususario :</td> <td> <TMPL_VAR user> </td></tr>
+ <tr><td>Clave :</td> <td> xxxxx </td></tr>
+ <tr> <td><b>Opciones Generales</b></td> <td/></tr>
+ <tr><td>email_media :</td> <td> <TMPL_VAR email_media> </td></tr>
+ <tr> <td><b>Configuración Bweb</b></td> <td/></tr>
+ <tr><td>config_file :</td> <td> <TMPL_VAR config_file> </td></tr>
+ <tr><td title="/path/a/tu/template_dir">template_dir :</td> <td> <TMPL_VAR template_dir> </td></tr>
+ <tr><td title="/path/a/a/font.ttf">graph_font :</td> <td> <TMPL_VAR graph_font> </td></tr>
+ <tr><td title="Este directorio debe tener permisos de escritura para el usuario apache y debe ser accesible en /bweb/fv">fv_write_path :</td> <td> <TMPL_VAR fv_write_path> </td></tr>
+ <tr><td title="/path/a/bconsole -n -c /path/to/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
+ <tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
+ <TMPL_IF achs>
+ <tr> <td><b>Libreria</b></td> <td/></tr>
+ <tr>
+ <td>
+ <form action='?' method='GET'>
+ <table border='0'>
+ <TMPL_LOOP achs>
+ <tr>
+ <td>
+<label>
+ <input type='radio' name='ach' value='<TMPL_VAR name>'><TMPL_VAR name>
+</label>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <td>
+
+ <input type="image" name="action" value="ach_edit" title="editar" src='/bweb/edit.png'>
+ <input type="image" name='action' value='ach_del' title='borrar' src='/bweb/remove.png'>
+ <input type='image' name='action' value='ach_view' title='ver' src='/bweb/zoom.png'>
+ </form>
+ </td>
+ </tr>
+ </TMPL_IF achs>
+ <tr>
+ <td><hr></td><td></td>
+ </tr>
+ </table>
+
+ <form action='?' method='GET'>
+ <label>
+ <input name='action' value='edit_conf' type="image" title='Edit' src='/bweb/edit.png'> Editar
+ </label>
+ <label>
+ <input name='action' value='ach_add' type="image" title='Add an autochanger' src='/bweb/add.png'> Agregar libreria
+ </label>
+ </form>
+
+ <TMPL_IF error>
+ info : <TMPL_VAR error> </br>
+ </TMPL_IF>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Cliente : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=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'>
+<!-- <div class="otherboxtitle">
+ Actions
+ </div>
+ <div class="otherbox">
+ <h1>Acciones</h1>
+ <input type="image" name='action' value='job' title='Mostrar último job'
+ src='/bweb/zoom.png'>
+ <input type="image" name='action' value='dsp_cur_job' title='Mostrar job actual' src='/bweb/zoom.png'>
+ <input type="image" name='action' value='client_stat' title='EstadÃsticas Cliente' src='/bweb/zoom.png'>
+ </div>
+-->
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Nombre", "Nb Jobs", "Nb Bytes", "Nb Files", "Nb Errors");
+
+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>"
+ )
+) ;
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+// disable_sorting: new Array(1),
+ rows_per_page: rows_per_page
+}
+);
+</script>
--- /dev/null
+<br/>
+<div class="otherboxtitle">
+ Filter
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Nivel</h2>
+ <select name='level' class='formulaire'>
+ <option id='level_Any' value='Any'>Cualquiera</option>
+ <option id='level_F' value='F'>Completo</option>
+ <option id='level_D' value='D'>Diferencial</option>
+ <option id='level_I' value='I'>Incremental</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Estado</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Cualquiera</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Error</option>
+ <option id='status_A' value='A'>Cancelados</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_all' value=''>Todos</option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Tiempo</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>Esta semana</option>
+ <option id='age_2678400' value='2678400'>Últimos 30 dias</option>
+ <option id='age_15552000' value='15552000'>Últimos 6 meses</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Number of items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Tipo Job</h2>
+ <select name='jobtype' class='formulaire'>
+ <option id='jobtype_any' value='all type'>Cualquiera</option>
+ <option id='jobtype_B' value='B'>Backup</option>
+ <option id='jobtype_R' value='R'>Recuperación</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clientes</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<!--
+<tr>
+ <td valign='top'>
+ <h2>FileSet</h2>
+ <select name='fileset' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_filesets>
+ <option id='client_<TMPL_VAR fileset>'><TMPL_VAR NAME=fileset></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+-->
+</table>
+ <input type="image" name='action' value='job' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF jobtype>
+ document.getElementById('jobtype_<TMPL_VAR jobtype>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qfilesets>
+ document.getElementById('fileset_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+<div class="otherboxtitle">
+ Filter
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Tipo Medio</h2>
+ <select name='mediatype' class='formulaire'>
+ <option id='mediatype_all' value=''></option>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option id='mediatype_<TMPL_VAR mediatype>'><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Ubicación</h2>
+ <select name='location' class='formulaire'>
+ <option id='location_all>' value=''></option>
+<TMPL_LOOP NAME=db_locations>
+ <option id='location_<TMPL_VAR location>'><TMPL_VAR location></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Estado</h2>
+ <select name='volstatus' class='formulaire'>
+ <option id='volstatus_All' value=''></option>
+ <option id='volstatus_Append' value='Append'>Abierta</option>
+ <option id='volstatus_Full' value='Full'>Llena</option>
+ <option id='volstatus_Error' value='Error'>Error</option>
+ <option id='volstatus_Used' value='Used'>Usada</option>
+ <option id='volstatus_Purged' value='Purged'>Purgada</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_all>' value=''></option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Nombre</h2>
+ <input type='text' name='re_media'
+ <TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
+ class='formulaire' size='8'>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Number of items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+
+</table>
+ <input type="image" name='action' value='media' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF volstatus>
+ document.getElementById('volstatus_<TMPL_VAR volstatus>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qmediatypes>
+ document.getElementById('mediatype_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+ <TMPL_LOOP qlocations>
+ document.getElementById('location_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'> Últimos Jobs (<TMPL_VAR Filter>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <table id='id<TMPL_VAR ID>'></table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+<TMPL_IF status>
+document.getElementById('status_<TMPL_VAR status>').checked = true;
+</TMPL_IF>
+
+
+
+var header = new Array("IdJob",
+ "Cliente",
+ "Nombre Job",
+ "FileSet",
+// "Pool",
+ "Nivel",
+ "Inicio",
+ "Duración",
+ "Archivos Job",
+ "Bytes Job",
+ "Errores",
+ "Estado");
+
+var data = new Array();
+
+<TMPL_LOOP Jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+//"<TMPL_VAR Pool>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR Duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+a
+ )
+);
+</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(10),
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
+</script>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>Información acerca jobs</h1>
+ </div>
+ <div class="bodydiv">
+ <table id='id0'></table>
+ <table><td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <label>
+ <input type="image" name='action' value='delete' title='delete this job'
+ onclick="return confirm('¿ Seguro quiere borrar este job del catálogo ?');"
+ src='/bweb/purge.png'> Borrar
+ </label>
+ </form>
+ </td><td>
+ <form action='bweb.pl?'>
+ <TMPL_LOOP volumes>
+ <input type='hidden' name='media' value='<TMPL_VAR VolumeName>'>
+ </TMPL_LOOP>
+ <label>
+ <input type="image" name='action' value='media' title='view media'
+ src='/bweb/zoom.png'>Ver medio
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <label>
+ <input type="image" name='action' value='job' title='view <TMPL_VAR Client> jobs' src='/bweb/zoom.png'>Ver jobs
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='age' value='2678400'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <input type='hidden' name='jobname' value='<TMPL_VAR jobname>'>
+ <label>
+ <input type="image" name='action' value='graph' title='view trends'
+ src='/bweb/chart.png'> Ver estadÃsticas
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='fileset' value='<TMPL_VAR FileSet>'>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='view fileset'
+ src='/bweb/zoom.png'> Ver FileSet
+ </label>
+ </form>
+ </td>
+<!-- Remove this to activate bfileview -->
+ <td>
+ <form action='bfileview.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <input type='hidden' name='where' value='/'>
+ <label>
+ <input type="image" name='action' value='bfileview' title='view file usage'
+ src='/bweb/colorscm.png' onclick='if (<TMPL_VAR JobFiles> > 50000) { return confirm("Puede demorar, ¿ Seguro quiere continuar ?")} else { return 1; }'> Ver uso de archivos
+ </label>
+ </form>
+ </td>
+<!-- -->
+ </table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var header = new Array("IdJob",
+ "Cliente",
+ "Nombre Job",
+ "FileSet",
+ "Nivel",
+ "Inicio",
+ "Duración",
+ "Archivos Job",
+ "Bytes Job",
+ "Errores",
+ "Pool",
+ "Nombre Volumen",
+ "Estado");
+
+var data = new Array();
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+"<TMPL_VAR poolname>",
+"<TMPL_LOOP volumes><TMPL_VAR VolumeName>\n</TMPL_LOOP>",
+img
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id0",
+ 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(6)
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Ubicaciones</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='location_add' title='Agregar Ubicación' src='/bweb/add.png'>
+ <input type="image" name='action' value='location_del' title='Remover Ubicación' src='/bweb/remove.png'>
+ <input type="image" name='action' value='location_edit' title='Editar Ubicación' src='/bweb/edit.png'>
+
+ <input type="image" name='action' value='media' title='Mostrar contenido'
+ src='/bweb/zoom.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre","Activado", "Cost", "Nb volumes", "Selección");
+
+var data = new Array();
+var chkbox;
+
+var img;
+
+<TMPL_LOOP Locations>
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR enabled>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'location';
+chkbox.value = '<TMPL_VAR Location>';
+
+data.push( new Array(
+"<TMPL_VAR Location>",
+img,
+"<TMPL_VAR Cost>",
+"<TMPL_VAR name=volnum>",
+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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Log : <TMPL_VAR name> en <TMPL_VAR client> (<TMPL_VAR jobid>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log'>
+<TMPL_VAR lines>
+ </pre>
+ </div>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Medios
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<TMPL_IF Pool>
+<h2>
+Pool : <a href="?action=pool;pool=<TMPL_VAR Pool>">
+ <TMPL_VAR Pool>
+ </a>
+</h2>
+</TMPL_IF>
+<TMPL_IF Location>
+<h2>
+Location : <TMPL_VAR location>
+</h2>
+</TMPL_IF>
+
+ <form action='?action=test' method='get'>
+ <table id='id_pool_<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='extern' title='Move out' src='/bweb/extern.png' onclick='return confirm("¿Seguro quiere expulsar el medio seleccionado?");'>
+ <input type="image" name='action' value='intern' title='Move in' src='/bweb/intern.png'>
+ <input type="image" name='action' value='update_media' title='Update medium' src='/bweb/edit.png'>
+ <input type="image" name='action' value='media_zoom' title='Informations' src='/bweb/zoom.png'>
+<!--
+ <input type="image" name='action' value='purge' title='Purge' src='/bweb/purge.png'>
+-->
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre Volumen","Online","Bytes Vol", "Uso Vol", "Estado Vol",
+ "Pool", "Tipo Medio",
+ "Fecha Escritura", "Expiración", "Selección");
+
+var data = new Array();
+var img;
+var chkbox;
+var d;
+
+<TMPL_LOOP Medias>
+d = percent_usage(<TMPL_VAR volusage>);
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value = '<TMPL_VAR volumename>';
+
+data.push( new Array(
+"<TMPL_VAR volumename>",
+img,
+human_size(<TMPL_VAR volbytes>),
+d,
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR poolname>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_pool_<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,
+ even_cell_color: even_cell_color,
+ odd_cell_color: odd_cell_color,
+ header_color: header_color,
+ page_nav: true,
+ padding: 3,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1,3,9)
+}
+);
+</script>
--- /dev/null
+<table>
+<td valign='top'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Medium : <TMPL_VAR volumename> <TMPL_VAR comment></h1>
+ </div>
+ <div class='bodydiv'>
+ <b> Medium Infos</b><br/>
+ <table id='id_info_<TMPL_VAR volumename>'></table>
+ <b> Medium Stats</b><br/>
+ <table id='id_media_<TMPL_VAR volumename>'></table>
+ <b> Job List </b></br>
+ <table id='id_jobs_<TMPL_VAR volumename>'></table>
+ <b> Actions </b></br>
+ <form action='?' method='get'>
+ <input type='hidden' name='media' value='<TMPL_VAR volumename>'>
+<TMPL_IF online>
+ <input type="image" name='action' value='extern' onclick='return confirm("Do you want to eject this medium ?");' title='move out' src='/bweb/extern.png'>
+<TMPL_ELSE>
+ <input type="image" name='action' value='intern' title='move in' src='/bweb/intern.png'>
+</TMPL_IF>
+ <input type="image" name='action' value='update_media' title='Update' src='/bweb/edit.png'>
+ <input type="image" name='action' value='purge' title='Purge' src='/bweb/purge.png' onclick="return confirm('Do you want to purge this volume ?')">
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+<TMPL_IF Locationlog>
+ <a href='#' onclick='document.getElementById("locationlog").style.visibility="visible";'><img title='View location log' src='/bweb/zoom.png'></a>
+</TMPL_IF>
+ </form>
+ </div>
+</td>
+<td valign='top'style="visibility:hidden;" id='locationlog'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Location log </h1>
+ </div>
+ <div class='bodydiv'>
+<pre>
+ <TMPL_VAR LocationLog>
+</pre>
+ </div>
+</td>
+</table>
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Pool","Online","Location","Vol Status", "Vol Bytes", "Expire",
+ "Retention","Max use duration", "Max jobs" );
+
+var data = new Array();
+var img;
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+data.push( new Array(
+"<TMPL_VAR poolname>",
+img,
+"<TMPL_VAR location>",
+"<TMPL_VAR volstatus>",
+human_size(<TMPL_VAR nb_bytes>),
+"<TMPL_VAR expire>",
+human_sec(<TMPL_VAR volretention>),
+human_sec(<TMPL_VAR voluseduration>),
+"<TMPL_VAR maxvoljobs>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_info_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+ padding: 3,
+ disable_sorting: new Array(1)
+}
+);
+
+var header = new Array( "Vol Mounts", "Recycle count", "Read time", "Write time", "Errors");
+
+var data = new Array();
+data.push( new Array(
+"<TMPL_VAR nb_mounts>",
+"<TMPL_VAR recyclecount>",
+human_sec(<TMPL_VAR volreadtime>),
+human_sec(<TMPL_VAR volwritetime>),
+"<TMPL_VAR nb_errors>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_media_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+// disable_sorting: new Array()
+ padding: 3
+}
+);
+
+
+var header = new Array("IdJob","Nombre","Inicio","Tipo",
+ "Nivel", "Archivos", "Bytes", "Estado");
+
+var data = new Array();
+var a;
+var img;
+
+<TMPL_LOOP jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src="/bweb/<TMPL_VAR status>.png";
+img.title=jobstatus['<TMPL_VAR status>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR jobid>",
+"<TMPL_VAR name>",
+"<TMPL_VAR starttime>",
+"<TMPL_VAR type>",
+"<TMPL_VAR level>",
+"<TMPL_VAR files>",
+human_size(<TMPL_VAR bytes>),
+a
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_jobs_<TMPL_VAR volumename>",
+ 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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Pools</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" type='submit' name='action' value='media' title='Mostrar contenido' src='/bweb/zoom.png'>
+<TMPL_LOOP MediaType>
+ <input type='hidden' name='mediatype' value=<TMPL_VAR name>>
+</TMPL_LOOP>
+ </form>
+ <br/>
+ Nota: Para modificar las propiedades de un pool, se debe editar la configuración de Bacula
+ y aplicar el comando "reload", Luego se debe ejecutar "update pool=mypool" con la bconsole.
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre","Reciclado","Retención","Use Duration",
+ "Máx Jobs/Volumen","Máx Archivos/Volumen",
+ "Máx Tamaño/Volumen","Nb volumes", "Estado Volumen", "Uso", "Selección");
+
+var data = new Array();
+var chkbox;
+var img;
+var img2;
+
+<TMPL_LOOP Pools>
+
+img = percent_display([
+<TMPL_IF nb_recycle>{ name: "Reciclado", nb: <TMPL_VAR nb_recycle> },</TMPL_IF>
+<TMPL_IF nb_purged> { name: "Purgado", nb: <TMPL_VAR nb_purged> },</TMPL_IF>
+<TMPL_IF nb_append> { name: "Abierto", nb: <TMPL_VAR nb_append> },</TMPL_IF>
+<TMPL_IF nb_full> { name: "Lleno", nb: <TMPL_VAR nb_full> }, </TMPL_IF>
+<TMPL_IF nb_disabled> { name: "Disactivado", nb: <TMPL_VAR nb_disabled> }, </TMPL_IF>
+<TMPL_IF nb_error> { name: "Error", nb: <TMPL_VAR nb_error> }, </TMPL_IF>
+<TMPL_IF nb_archive>{ name: "Archivado", nb: <TMPL_VAR nb_archive> },</TMPL_IF>
+<TMPL_IF nb_used> { name: "Usado", nb: <TMPL_VAR nb_used> }, </TMPL_IF>
+<TMPL_IF NAME='nb_read-only'> { name: "Lectura", nb: <TMPL_VAR NAME='nb_read-only'> }, </TMPL_IF>
+{}
+]);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.value = '<TMPL_VAR Name>';
+chkbox.name = 'pool';
+
+img2 = percent_usage(<TMPL_VAR poolusage>);
+
+data.push( new Array(
+"<TMPL_VAR Name>",
+"<TMPL_VAR Recycle>",
+human_sec(<TMPL_VAR VolRetention>),
+human_sec(<TMPL_VAR VolUseDuration>),
+"<TMPL_VAR MaxVolJobs>",
+"<TMPL_VAR MaxVolFiles>",
+human_size(<TMPL_VAR MaxVolBytes>),
+"<TMPL_VAR VolNum>",
+img,
+img2,
+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>
--- /dev/null
+</div>
+<div style="float: right;"></div>
+</div>
+</body>
+</html>
--- /dev/null
+<h1>Un error ha ocurrido :</h1>
+<pre>
+<TMPL_VAR NAME=error>
+</pre>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ FileSet <TMPL_VAR fileset>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+ <img src="/bweb/add.png" alt="included"> Incluido :
+ <pre>
+<TMPL_LOOP I><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+ <img src="/bweb/remove.png" alt="excluded"> Excluido :
+ <pre>
+<TMPL_LOOP E><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+Nota: Cuidado, este es el fileset actual, puede haber cambiado...
+
+ </div>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> Información acerca <TMPL_VAR filename> </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr><td>size : </td> <td> <TMPL_VAR size> </td></tr>
+ <tr><td>ctime :</td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>mtime :</td> <td> <TMPL_VAR mtime> </td></tr>
+ <tr><td>atime :</td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>md5 : </td> <td> <TMPL_VAR md5> </td></tr>
+ </table>
+</div>
--- /dev/null
+
+<script type="text/javascript" language="JavaScript">
+bweb_add_refresh();
+</script>
+<div class='titlediv'>
+ <h1 class="newstitle">
+ Informaciones
+ </h1>
+</div>
+<div class="bodydiv">
+ <table>
+ <tr><td>Cantidad Clientes:</td> <td> <TMPL_VAR nb_client> </td>
+ <td>Bytes Almacenados:</td> <td> <TMPL_VAR nb_bytes> </td>
+ <td>Cantidad Medios:</td> <td> <TMPL_VAR nb_media> </td>
+ </tr>
+ <tr><td>Tamaño Base de Datos:</td> <td> <TMPL_VAR db_size> </td>
+ <td>Cantidad Pools:</td> <td> <TMPL_VAR nb_pool> </td>
+ <td>Cantidad Jobs:</td> <td> <TMPL_VAR nb_job> </td>
+ </tr>
+ <tr><td>Jobs fallados (<TMPL_VAR label>):</td>
+
+<td <TMPL_IF nb_err> class='joberr' </TMPL_IF>>
+ <TMPL_VAR nb_err>
+</td>
+ <td></td> <td></td>
+ <td></td> <td></td>
+ </tr>
+
+ </table>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>EstadÃsticas</h1>
+ </div>
+ <div class='bodydiv'>
+<table border='0'>
+<td>
+<form name='form1' action='?' method='GET'>
+ <div class="otherboxtitle">
+ Options
+ </div>
+ <div class="otherbox">
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Nivel</h2>
+ <select name='level' class='formulaire'>
+ <option id='level_Any' value='Any'>Cualquiera</option>
+ <option id='level_F' value='F'>Completo</option>
+ <option id='level_D' value='D'>Diferencial</option>
+ <option id='level_I' value='I'>Incremental</option>
+ </select>
+ </td><td valign='top'>
+ <h2>Estado</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Cualquiera</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Error</option>
+ <option id='status_A' value='A'>Cancelado</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Tiempo</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>Esta Semana</option>
+ <option id='age_2678400' value='2678400'>Últimos 30 dÃas</option>
+ <option id='age_15552000' value='15552000'>Últimos 6 meses</option>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Tamaño</h2>
+ Ancho: <input class='formulaire' type='text'
+ name='width' value='<TMPL_VAR width>' size='4'><br/>
+ Alto: <input type='text' class='formulaire'
+ name='height' value='<TMPL_VAR height>' size='4'><br/>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clients</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Nombre Job</h2>
+ <select name='jobname' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_jobnames>
+ <option><TMPL_VAR NAME=jobname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td> <h2> Tipo </h2>
+ <select name='graph' class='formulaire'>
+ <option id='job_size' value='job_size' title="Job size per job for the period">Tamaño Job</option>
+ <option id='job_duration' value='job_duration' title="Job duration per job for the period">Duración Job</option>
+ <option id='job_rate' value='job_rate' title="Job rate per job for the period">Velocidad Job</option>
+ <option id='job_file' value='job_file' title="Number of backed files per job for the period">Archivos Job</option>
+ <option id='job_count_phour' value='job_count_phour' title="Number of jobs per hour for the period">Job por hora</option>
+ <option id='job_count_pday' value='job_count_pday' title="Number of jobs per day for the period">Job por dia</option>
+ <option id='job_avg_phour' value='job_avg_pday' title="Average backup size per day for the period">Job avg B/hour</option>
+ <option id='job_avg_pday' value='job_avg_pday' title="Average backup size per hour for the period">Job avg B/day</option>
+ <option id='job_sum_phour' value='job_sum_phour' title="Job size per hour">Job total B/hour</option>
+ <option id='job_sum_pday' value='job_sum_pday' title="Job size per day">Job total B/day</option>
+ <option id='job_count_hour' value='job_count_hour' title="Number of jobs per hour for the period">Jobs Count (h)</option>
+ <option id='job_count_day' value='job_count_day' title="Number of jobs per day for the period">Jobs Count (d)</option>
+ <option id='job_avg_hour' value='job_avg_hour' title="Average backup size per hour for the period">Job avg size (h)</option>
+ <option id='job_avg_day' value='job_avg_day' title="Average backup size per day for the period">Job avg size (d)</option>
+ <option id='job_sum_hour' value='job_sum_hour' title="Job size per hour for the period">Job Bytes (h)</option>
+ <option id='job_sum_day' value='job_sum_day' title="Job size per day for the period">Job Bytes (d)</option>
+ </select>
+ </td>
+ <td valign='bottom'>
+ <h2>Número de items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+<td><h2> Tipo de Gráfico </h2>
+ <select name='gtype' class='formulaire'>
+ <option id='gtype_bars' value='bars'>Barras</option>
+<!-- <option id='gtype_bars3d' value='bars3d'>Barras3d</option> -->
+ <option id='gtype_lines' value='lines'>LÃneas</option>
+ <option id='gtype_linespoints' value='linespoints'>LÃneas y puntos</option>
+</td>
+<td>
+ <input type='hidden' name='action' value='graph'>
+ <input type='submit' name='_action' value='graficar' class='formulaire'>
+</td>
+</tr>
+</table>
+ </div>
+
+</form>
+</td>
+<td>
+
+ <div class="otherboxtitle">
+ Current
+ </div>
+ <div class="otherbox">
+ <img src='bgraph.pl?<TMPL_VAR NAME=url>' alt='Nothing to display, Try a bigger date range'>
+ </div>
+
+</td>
+</table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+<TMPL_IF qfilesets>
+ for (var i=0; i < document.form1.fileset.length; ++i) {
+ <TMPL_LOOP qfilesets>
+ if (document.form1.fileset[i].value == <TMPL_VAR name>) {
+ document.form1.fileset[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+<TMPL_IF qjobnames>
+ for (var i=0; i < document.form1.jobname.length; ++i) {
+ <TMPL_LOOP qjobnames>
+ if (document.form1.jobname[i].value == <TMPL_VAR name>) {
+ document.form1.jobname[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+ <TMPL_IF graph>
+ document.getElementById('<TMPL_VAR graph>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF gtype>
+ document.getElementById('gtype_<TMPL_VAR gtype>').selected=true;
+ </TMPL_IF>
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Ayuda para expulsar medios (part 1/2)</h1>
+</div>
+<div class='bodydiv'>
+Se seleccionará el mejor candidato para expulsar. Se le pedirá
+realizar su selección en la próxima pantalla.
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_pools>
+ <option><TMPL_VAR NAME=name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Tipo Medio:</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option><TMPL_VAR NAME=mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Ubicación : </td>
+ <td><select name='location' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Número de medio <br/> a expulsar:</td>
+ <td> <input type='text' name='limit' size='3' class='formulaire'
+ value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_extern_media' title='Siguiente' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Ayuda para expulsar medios (part 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Ahora puede verificar la selección y expulsar el medio.
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='extern' src='/bweb/extern.png' title='Expulsar selección'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre Volumen","Estado Volumen",
+ "Tipo Medio","Nombre Pool","Fecha Escritura",
+ "Expiración", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR NAME=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Ayuda para cargar medios (part 1/2)</h1>
+</div>
+<div class="bodydiv">
+Se seleccionará el mejor candidato para cargar. Se le pedirá
+realizar su selección en la próxima pantalla.
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP db_pools>
+ <option><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Tipo Medio:</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP db_mediatypes>
+ <option><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>
+ Ubicación :
+ </td><td><select name='location' class='formulaire'>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Expiración :</td>
+ <td> <input type='checkbox' name='expired' class='formulaire'
+ checked> </td>
+ </tr>
+ <tr>
+ <td>Número de medio <br/> a cargar:</td>
+ <td> <input type='text' name='limit' class='formulaire'
+ size='3' value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_intern_media'
+ title='Siguiente' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Ayuda para cargar medios (part 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Ahora puede verificar la selección y cargar el medio
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='move_media' title='Cargar selección' src='/bweb/intern.png'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre Volumen","Estado Volumen",
+ "Tipo de Medio","Nombre Pool","Fecha Escritura",
+ "Expiración", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value= '<TMPL_VAR NAME=volumename>';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Install notes </h1>
+</div>
+<div class='bodydiv'>
+
+
+
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Nueva Ubicación </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <table>
+ <tr><td>Ubicación :</td>
+ <td>
+ <input class="formulaire" type='text' value='' size='32' name='location'>
+ </td>
+ </tr>
+ <tr><td>Cost :</td>
+ <td> <input class="formulaire" type='text' value='10' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Activado :</td>
+ <td> <input class="formulaire" type='checkbox' value='10' name='enabled'>
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_add' title="save"
+ src='/bweb/save.png'>
+ </form>
+
+Tips: It's a good idea to have a location per autochanger.
+
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Ubicación : <TMPL_VAR Location></h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <input type='hidden' name='location' value='<TMPL_VAR location>'>
+ <table>
+ <tr><td>Ubicación :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR location>' size='32' name='newlocation'>
+ </td>
+ </tr>
+ <tr><td>Cost :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR cost>' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Activado :</td>
+ <td> <input class="formulaire" type='checkbox' name='enabled' <TMPL_IF enabled> checked </TMPL_IF> >
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_save'
+ src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Mover Medio</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <table border='0'>
+ <tr><td> Nueva Ubicación: </td><td>
+<select name='newlocation' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <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>
+</select>
+ </td><tr><td> Usuario: </td><td>
+<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
+ </td></tr>
+ </td></tr><tr><td> Comentario: </td><td>
+<textarea name="comment" class='formulaire'></textarea>
+ </td></tr>
+ </table>
+ <label>
+ <input type="image" type='submit' name='action' value='change_location' src='/bweb/apply.png'> Mover
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre Volumen", "Ubicación", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Jobs Definidos: </h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Nombre Job: </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr>
+ </table>
+ <br/>
+ <label>
+ <input type="image" name='action' value='enable_job' title='Activar'
+ src='/bweb/inflag1.png'> Activado
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job' title='Desactivar'
+ src='/bweb/inflag0.png'> Desactivado
+ </label>
+ <label>
+ <input type="image" name='action' value='run_job_mod' title='Ejecutar Ahora'
+ src='/bweb/R.png'> Ejecutar Ahora
+ </label>
+ </form>
+ </div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Ejecutar Job : <TMPL_VAR job> on <TMPL_VAR client></h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Nombre del Job: </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Pool: </td><td>
+
+ <select name='pool'>
+ <option value=''></option>
+ <TMPL_LOOP pools>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Cliente: </td><td>
+
+ <select name='client'>
+ <TMPL_LOOP clients>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>FileSet: </td><td>
+ <select name='fileset'>
+ <TMPL_LOOP filesets>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Almacenamiento: </td><td>
+ <select name='storage'>
+ <TMPL_LOOP storages>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Nivel: </td><td>
+ <select name='level'>
+ <option id='level_Incremental' value='Incremental'>Incremental</option>
+ <option id='level_Full' value='Full'>Completo</option>
+ <option id='level_Differential' value='Differential'>Diferencial</option>
+ </select>
+
+ </td></tr><tr id='more1' style="visibility:hidden"><td>Hora Inicio: </td><td>
+ <input class='formulaire'
+ type='text' title='YYYY-MM-DD HH:MM:SS'
+ size='17' name='when' value='<TMPL_VAR when>'>
+
+ </td></tr><tr id='more2' style="visibility:hidden"><td>Prioridad: </td><td>
+ <input class='formulaire' type='text'
+ size='3' name='priority' value='<TMPL_VAR priority>'>
+
+ </td></tr>
+ </table>
+ <br/>
+ <label onclick='
+ document.getElementById("more1").style.visibility="visible";
+ document.getElementById("more2").style.visibility="visible";'>
+ <img title='Muestra más opciones' src='/bweb/add.png'>Más opciones</label>
+ <label>
+ <input type="image" name='action' value='run_job_now' title='Ejecutar job'
+ src='/bweb/R.png'>Ejecutar Ahora
+ </label>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='Ver FileSet'
+ src='/bweb/zoom.png'>Ver FileSet
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF job>
+ ok=1;
+ for(var i=0; ok && i < document.form1.job.length; i++) {
+ if (document.form1.job[i].value == '<TMPL_VAR job>') {
+ document.form1.job[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF client>
+ ok=1;
+ for(var i=0; ok && i < document.form1.client.length; i++) {
+ if (document.form1.client[i].value == '<TMPL_VAR client>') {
+ document.form1.client[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF pool>
+ ok=1;
+ for(var i=0; ok && i < document.form1.pool.length; i++) {
+ if (document.form1.pool[i].value == '<TMPL_VAR pool>') {
+ document.form1.pool[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF storage>
+ ok=1;
+ for(var i=0; ok && i < document.form1.storage.length; i++) {
+ if (document.form1.storage[i].value == '<TMPL_VAR storage>') {
+ document.form1.storage[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF level>
+<!-- document.getElementById('level_<TMPL_VAR level>').selected=true; -->
+ </TMPL_IF>
+ <TMPL_IF fileset>
+ ok=1;
+ for(var i=0; ok && i < document.form1.fileset.length; i++) {
+ if (document.form1.fileset[i].value == '<TMPL_VAR fileset>') {
+ document.form1.fileset[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Jobs en Ejecución </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <br/>
+<label>
+<input type='image' name='action' value='dsp_cur_job'
+ title='Ver job' src='/bweb/zoom.png'>
+</label>
+<label>
+<input type="image" type='submit' name='action' value='cancel_job'
+ onclick="return confirm('Esta seguro que quiere cancelar el Job?')"
+ title='Cancelar job' src='/bweb/cancel.png'>
+</label>
+ </form>
+
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("JobId",
+ "Cliente",
+ "Nombre Job",
+ "Nivel",
+ "Inicio",
+ "Duración",
+// "Archivos Job",
+// "Bytes Job",
+ "Estado",
+ "Selección"
+ );
+
+var data = new Array();
+var chkbox;
+var img;
+
+<TMPL_LOOP NAME=Jobs>
+a = document.createElement('A');
+a.href='?action=dsp_cur_job;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src = '/bweb/<TMPL_VAR NAME=JobStatus>.png';
+img.title = jobstatus['<TMPL_VAR NAME=JobStatus>'];
+
+a.appendChild(img);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'jobid';
+chkbox.value = '<TMPL_VAR NAME=jobid>';
+
+data.push( new Array(
+"<TMPL_VAR NAME=JobId>",
+"<TMPL_VAR NAME=ClientName>",
+"<TMPL_VAR NAME=JobName>",
+joblevel['<TMPL_VAR NAME=Level>'],
+"<TMPL_VAR NAME=StartTime>",
+"<TMPL_VAR NAME=duration>",
+//"<TMPL_VAR NAME=JobFiles>",
+//"<TMPL_VAR NAME=JobBytes>",
+a,
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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(6)
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR NAME=ID>'].fieldSort(0);
+
+bweb_add_refresh();
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Próximos Jobs </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <label>
+ <input type="image" name='action' value='run_job_mod'
+ src='/bweb/R.png' title='Ejecutar Ahora'> Ejecutar Ahora
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job'
+ src='/bweb/inflag0.png' title='Desactivar'> Desactivar
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Programado",
+ "Nivel",
+ "Tipo",
+ "Prioridad",
+ "Nombre",
+ "Volumen",
+ "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP list>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'job';
+chkbox.value = '<TMPL_VAR name>';
+
+data.push( new Array(
+"<TMPL_VAR date>",
+"<TMPL_VAR level>",
+"<TMPL_VAR type>",
+"<TMPL_VAR priority>",
+"<TMPL_VAR name>",
+"<TMPL_VAR volume>",
+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: 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(6),
+ padding: 3
+}
+);
+</script>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> EstadÃsticas </h1>
+</div>
+<div class='bodydiv'>
+<a href='?action=job;age=172800;jobtype=B'>
+<img width='450' height='200' src='bgraph.pl?age=2678400;width=450;height=200;graph=job_sum_day;limit=500;action=graph;legend=off' alt='Nothing to display'>
+</a>
+</div>
+
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Actualizar Ubicación del Medio</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ New location : <select class='formulaire' name='newlocation'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ <input type="image" name='action' value='save_location' src='/bweb/apply.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nombre del Volumen", "Ubicación", "Selección");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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
+}
+);
+
+<TMPL_IF qnewlocation>
+ document.getElementById('loc_' + <TMPL_VAR qnewlocation>).selected=true;
+</TMPL_IF>
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Actualizar Medio <TMPL_VAR volumename></h1>
+</div>
+<div class='bodydiv'>
+ <form name='form1' action="?" method='GET'>
+ <table>
+ <tr><td>Nombre del Volumen:</td>
+ <td><input type='text' name='media' class='formulaire' value='<TMPL_VAR volumename>' title='Change this to update an other media'>
+ </td>
+ </tr>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Estado:</td>
+ <td><select name='volstatus' class='formulaire'>
+ <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='Read-Only'>Lectura</option>
+ <option value='Used'>Usado</option>
+ </select>
+ </td>
+ </tr>
+
+ <tr><td>Slot:</td>
+ <td>
+ <input class='formulaire' type='text'
+ name='slot' value='<TMPL_VAR slot>'>
+ </td>
+ </tr>
+
+ <tr><td>Cargado:</td>
+ <td>
+ <input class='formulaire' type='checkbox'
+ name='inchanger' <TMPL_IF inchanger>checked</TMPL_IF>>
+ </td>
+ </tr>
+
+ <tr><td> Ubicación : </td>
+ <td><select name='location' class='formulaire'>
+ <option value=''></option>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> PerÃodo de Retención: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='volretention' value='<TMPL_VAR volretention>'>
+ </td>
+ </tr>
+ <tr><td> Use duration: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='voluseduration' value='<TMPL_VAR voluseduration>'>
+ </td>
+ </tr>
+ <tr><td> Jobs Máximos: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10'
+ name='maxvoljobs' value='<TMPL_VAR maxvoljobs>'>
+ </td>
+ </tr>
+ <tr><td> Archivos Máximos: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10000'
+ name='maxvolfiles' value='<TMPL_VAR maxvolfiles>'>
+ </td>
+ </tr>
+ <tr><td> Bytes Máximos: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10M, 11G'
+ name='maxvolbytes' value='<TMPL_VAR maxvolbytes>'>
+ </td>
+ </tr>
+ <tr><td>Recycle Pool:</td>
+ <td><select name='poolrecycle' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Comentario: </td>
+ <td>
+ <input class='formulaire' type='text' title='a comment'
+ name='comment' value='<TMPL_VAR comment>'>
+ </td>
+ </tr>
+
+ </table>
+<table>
+ <td>
+ <label>
+ <input type="image" name='action' value='do_update_media' src='/bweb/apply.png'> Apply
+ </label>
+ <label>
+ <input type="image" name='action' title='Update from pool'
+ value='update_from_pool' src='/bweb/update.png'> Actualizar del Pool
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='?' method='GET'>
+ <input type='hidden' name='pool' value='<TMPL_VAR poolname>'>
+ <label>
+ <input type="image" name='action' value='media'
+ src='/bweb/zoom.png'> Ver Pool
+ </label>
+ </form>
+ </td>
+</table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var ok=1;
+for (var i=0; ok && i < document.form1.pool.length; ++i) {
+ if (document.form1.pool[i].value == '<TMPL_VAR poolname>') {
+ document.form1.pool[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.pool.length; ++i) {
+ if (document.form1.poolrecycle[i].value == '<TMPL_VAR poolrecycle>') {
+ document.form1.poolrecycle[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.location.length; ++i) {
+ if (document.form1.location[i].value == '<TMPL_VAR location>') {
+ document.form1.location[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.volstatus.length; ++i) {
+ if (document.form1.volstatus[i].value == '<TMPL_VAR volstatus>') {
+ document.form1.volstatus[i].selected = true;
+ ok=0;
+ }
+}
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> A propos </h1>
+</div>
+<div class='bodydiv'>
+<pre>
+ 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.
+
+ nrsTable Copyright 2005 New Roads School (GPL)
+ kaiska css Copyright Willy Morin (BWL)
+</pre>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'><TMPL_UNLESS name>Nouvelle</TMPL_UNLESS> Robotique (Autochanger) </h1>
+</div>
+<div class='bodydiv'>
+ Vous devez créer une Location du même nom que votre robotique.<br/><br/>
+ <form name='form1' action="?" method='get'>
+ <table>
+ <tr><td>Nom :</td>
+ <td>
+ <select name='ach' class='formulaire' id='ach'>
+<TMPL_LOOP devices><option value='<TMPL_VAR name>'><TMPL_VAR name></option></TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Pre-commande :</td>
+ <td> <input class="formulaire" type='text' id='precmd' value='sudo'
+ title='peut être "sudo" ou "ssh storage@storagehost"...' name='precmd'>
+ </td>
+ </tr>
+ <tr><td>commande mtx :</td>
+ <td> <input class="formulaire" type='text' name='mtxcmd' size='32'
+ value='/usr/sbin/mtx' id='mtxcmd'>
+ </td>
+ </tr>
+ <tr><td>Périphérique :</td>
+ <td> <input class="formulaire" type='text' name='device'
+ value='/dev/changer' id='device'>
+ </td>
+ </tr>
+ <tr><td><b>Lecteurs</b></td><td/></tr>
+ <TMPL_LOOP devices>
+ <tr>
+ <td><input class='formulaire' type='checkbox' id='drive_<TMPL_VAR name>'
+ name='drives' value='<TMPL_VAR name>'><TMPL_VAR name>
+ </td>
+ <td>index <input type='text' title='drive index' class='formulaire'
+ id='index_<TMPL_VAR name>' value=''
+ name='index_<TMPL_VAR name>' size='3'>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <input type="image" name='action' value='ach_add' src='/bweb/save.png'>
+ </form>
+</div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF name>
+ for (var i=0; i < document.form1.ach.length; ++i) {
+ if (document.form1.ach[i].value == '<TMPL_VAR name>') {
+ document.form1.ach[i].selected = true;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF mtxcmd>
+ document.getElementById('mtxcmd').value='<TMPL_VAR mtxcmd>';
+ </TMPL_IF>
+ <TMPL_IF precmd>
+ document.getElementById('precmd').value='<TMPL_VAR precmd>';
+ </TMPL_IF>
+ <TMPL_IF device>
+ document.getElementById('device').value='<TMPL_VAR device>';
+ </TMPL_IF>
+ <TMPL_IF drives>
+ <TMPL_LOOP drives>
+ document.getElementById('drive_<TMPL_VAR name>').checked=true;
+ document.getElementById('index_<TMPL_VAR name>').value=<TMPL_VAR index>;
+ </TMPL_LOOP>
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+Robotique : <TMPL_VAR Name> (<TMPL_VAR nb_drive> Lecteurs
+<TMPL_IF nb_io><TMPL_VAR nb_io> IMPORT/EXPORT</TMPL_IF>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='get'>
+ <input type='hidden' name='ach' value='<TMPL_VAR name>'>
+ <TMPL_IF "Update">
+ <font color='red'> Vous devez lancer la mise à jour des slots, le contenu de la robotique est différent de ce qui est indiqué dans la base de bacula. </font>
+ <br/>
+ </TMPL_IF>
+ <table border='0'>
+ <tr>
+ <td valign='top'>
+ <div class='otherboxtitle'>
+ Options
+ </div>
+ <div class='otherbox'>
+<label>
+<input type="image" name='action' value='label_barcodes'
+ title='Labélisation des médias, (label barcodes)' src='/bweb/label.png'>Labéliser
+</label>
+<TMPL_IF nb_io>
+<label>
+<input type="image" name='action' value='eject'
+ title='Mettre les médias sélectionnés dans le guichet' src='/bweb/extern.png'>
+Ejecter
+</label>
+<label>
+<input type="image" name='action' value='clear_io'
+ title='Vider le guichet' src='/bweb/intern.png'>
+Vider le guichet
+</label>
+</TMPL_IF>
+<label>
+<input type="image" name='action' value='update_slots'
+ title='Mettre à jour la base bacula, (update slots)' src='/bweb/update.png'>
+Scanner
+</label>
+<br/><br/>
+<label>
+<input type="image" name='action' value='ach_load'
+ title='Charger un lecteur' src='/bweb/load.png'>
+Mount
+</label>
+<label>
+<input type="image" name='action' value='ach_unload'
+ title='Décharger un lecteur' src='/bweb/unload.png'>
+Umount
+</label>
+
+ </div>
+ <td width='200'/>
+ <td>
+ <b> Lecteurs : </b><br/>
+ <table id='id_drive'></table> <br/>
+ </td>
+ </tr>
+ </table>
+ <b> Contenu : </b><br/>
+ <table id='id_ach'></table>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Slot réel", "Slot", "Nom de volume","Taille","Statut",
+ "Type","Pool","Dernière écriture",
+ "Expiration", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Slots>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'slot';
+chkbox.value = '<TMPL_VAR realslot>';
+
+data.push( new Array(
+"<TMPL_VAR realslot>",
+"<TMPL_VAR slot>",
+"<TMPL_VAR volumename>",
+human_size(<TMPL_VAR volbytes>),
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR name>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_ach",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6)
+ padding: 3
+}
+);
+
+var header = new Array("Index", "Nom du lecteur", "Nom de volume", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Drives>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'drive';
+chkbox.value = '<TMPL_VAR index>';
+
+data.push( new Array(
+"<TMPL_VAR index>",
+"<TMPL_VAR name>",
+"<TMPL_VAR load>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_drive",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6),
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+<head>
+<title>Bweb - Interface Web de Bacula</title>
+<link rel="SHORTCUT ICON" href="/bweb/favicon.ico">
+<script type="text/javascript" language="JavaScript" src="/bweb/natcompare.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/nrs_table.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/bweb.js"></script>
+<link type="text/css" rel="stylesheet" href="/bweb/style.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/kaiska.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/bweb.css"/>
+</head>
+<body>
+
+<script type="text/javascript" language="JavaScript">
+if (navigator.appName == 'Konqueror') {
+ alert("Désolé, bweb fonctionne seulement avec mozilla.");
+}
+</script>
+
+<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=run_job">Jobs</a>
+ <ul>
+ <li><a href="bweb.pl?action=run_job">Jobs définis</a>
+ <li><a href="bweb.pl?action=job">Historique</a> </li>
+ <li><a href="bweb.pl?action=running">Jobs en cours</a>
+ <li><a href="bweb.pl?action=next_job">Prochains Jobs</a> </li>
+ <li><a href="bweb.pl?action=restore" title="Lancer brestore">Restauration</a> </li>
+ </ul>
+ </li>
+ <li style="padding: 0.25em 2em;">Medias
+ <ul>
+ <li><a href="bweb.pl?action=pool">Pools</a> </li>
+ <li><a href="bweb.pl?action=location">Localisations</a> </li>
+ <li><a href="bweb.pl?action=media">Tous les Medias</a><hr></li>
+ <li><a href="bweb.pl?action=extern_media">Externaliser</a> </li>
+ <li><a href="bweb.pl?action=intern_media">Internaliser</a> </li>
+ </ul>
+ </li>
+<TMPL_IF achs>
+ <li style="padding: 0.25em 2em;">Robotiques
+ <ul>
+<TMPL_LOOP achs>
+ <li><a href="bweb.pl?action=ach_view;ach=<TMPL_VAR name>"><TMPL_VAR name></a></li>
+</TMPL_LOOP>
+ </ul>
+ </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=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;">
+<input type="image" class="button" title="chercher un media" onclick="search_media();" src="/bweb/tape.png"><input type="image" title="chercher un 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="rechercher..." onclick="this.value='';" title="chercher un media ou un client"></li>
+</ul>
+
+<form name="search" action="bweb.pl?" method='GET'>
+ <input type="hidden" name="action" value="">
+ <input type="hidden" name="re_media" value="">
+ <input type="hidden" name="re_client" value="">
+</form>
+
+<div style="clear: left;">
+<div style="float: left;">
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'>
+ Déplacer des médias
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<form action="?" method='GET'>
+<table>
+<tr>
+<td><b>To: </b></td><td><input class='formulaire' name='email' value='<TMPL_VAR email>'></td>
+</tr><tr>
+<td><b>Subject: </b></td><td><input class='formulaire' name='subject' value='[BACULA] Deplacement des medias vers <TMPL_VAR newlocation>' size='80'></td>
+</tr><tr>
+<td></td>
+<td>
+<textarea name='content' class='formulaire' cols='80' rows='32'>
+Bonjour,
+
+Pouvez vous déplacer ces médias vers <TMPL_VAR newlocation> ?
+Média :
+<TMPL_LOOP Medias>
+ - <TMPL_VAR VolumeName> (<TMPL_VAR location>)
+</TMPL_LOOP>
+
+Quand cela sera terminé, pouvez vouscliquer sur le lien ci-dessous pour mettre à jour la localisation ?
+(Vous pouvez utiliser ce lien : <TMPL_VAR url>).
+
+Merci
+</textarea>
+</td></tr></table>
+<input type="hidden" name='action' value='move_email'>
+<input class='formulaire' type='submit' name='bp' value='Envoyer'>
+</form>
+<br>
+<a href="<TMPL_VAR url>"><img alt='update now' src='/bweb/update.png'>Mettre à jour maintenant</a>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Job <TMPL_VAR JobName> en cours sur <TMPL_VAR Client>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<table>
+ <tr>
+ <td> <b> Nom du job : </b> <td> <td> <TMPL_VAR jobname> (<TMPL_VAR jobid>) <td>
+ </tr>
+ <tr>
+ <td> <b> Fichier en cours : </b> <td> <td> <TMPL_VAR "processing file"> </td>
+ </tr>
+ <tr>
+ <td> <b> Vitesse : </b> <td> <td> <TMPL_VAR "bytes/sec"> B/s</td>
+ </tr>
+ <tr>
+ <td> <b> Fichiers vus : </b> <td> <td> <TMPL_VAR "files examined"></td>
+ </tr>
+ <tr>
+ <td> <b> Taille : </b> <td> <td> <TMPL_VAR bytes></td>
+ </tr>
+</table>
+<form name='form1' action='?' method='GET'>
+<input type="image" name='action' value='dsp_cur_job'
+ src='/bweb/update.png' title='refresh'>
+<input type='hidden' name='client' value='<TMPL_VAR Client>'>
+<input type='hidden' name='jobid' value='<TMPL_VAR JobId>'>
+<input type="image" name='action' value='cancel_job'
+ onclick="return confirm('Vous voulez annuler ce job ?')"
+ title='Annuler ce job' src='/bweb/cancel.png'>
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ bweb_add_refresh();
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Clients</h1>
+ </div>
+ <div class='bodydiv'>
+<form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <div class="otherboxtitle">
+ Actions
+ </div>
+ <div class="otherbox">
+<!-- <h1>Actions</h1> -->
+ <label>
+ <input type="image" name='action' value='job' title="Voir l'historique" src='/bweb/zoom.png'>Historique</label>
+ <label>
+ <input type="image" name='action' value='dsp_cur_job' title='Voir les jobs courants' src='/bweb/zoom.png'>Jobs courants</label>
+ <label>
+ <input type="image" name='action' value='client_status' title='Statut' src='/bweb/zoom.png'>Statut</label>
+ <label>
+ <input type="image" name='action' value='client_stats' title='Statistiques' src='/bweb/chart.png'>Statistiques</h1></label>
+ </label>
+ </div>
+
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Nom", "Sélection", "Description", "Purge automatique", "Rétention des fichiers", "Rétention des jobs");
+
+var data = new Array();
+var chkbox ;
+
+<TMPL_LOOP NAME=Clients>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'client';
+chkbox.value = '<TMPL_VAR NAME=Name>';
+
+data.push(
+ new Array( "<TMPL_VAR NAME=Name>",
+ chkbox,
+ "<TMPL_VAR NAME=Uname>",
+ "<TMPL_VAR NAME=AutoPrune>",
+ human_sec(<TMPL_VAR NAME=FileRetention>),
+ human_sec(<TMPL_VAR NAME=JobRetention>)
+ )
+) ;
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1)
+}
+);
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> <TMPL_VAR title> : <TMPL_VAR name></h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log' style='font-size: 10px'>
+<TMPL_VAR content>
+ </pre>
+ </div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuration </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='post'>
+ <table>
+ <tr> <td><b>Connexion SQL</b></td> <td/></tr>
+
+ <tr><td>DBI :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR NAME=dbi>' size='64' name='dbi'>
+ </td>
+ </tr>
+ <tr><td>user :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR NAME=user>' name='user'>
+ </td>
+ </tr>
+ <tr><td>password :</td>
+ <td> <input class="formulaire" type='password' value='<TMPL_VAR NAME=password>' name='password'>
+ </td></tr>
+
+ <tr> <td><b>Options Générales</b></td> <td/></tr>
+
+ <tr><td>email_media :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR NAME=email_media>' name='email_media'>
+ </td></tr>
+ </td></tr>
+
+ <tr> <td><b>Configuration Bweb</b></td> <td/></tr>
+
+ <tr><td>graph_font :</td>
+ <td> <input class="formulaire" title="/chemin/vers/une/font.ttf" type='text' value='<TMPL_VAR NAME=graph_font>' size='64' name='graph_font'>
+ </td></tr>
+ <tr><td>template_dir :</td>
+ <td> <input class="formulaire" title="/chemin/vers/votre/template_dir" type='text' value='<TMPL_VAR NAME=template_dir>' size='64' name='template_dir'>
+ </td></tr>
+ <tr><td>fv_write_path :</td>
+ <td> <input class="formulaire" title="Ce répertoire doit être accessible en écriture pour apache et être sous /bweb/fv" type='text' value='<TMPL_VAR fv_write_path>' size='64' name='fv_write_path'>
+ </td></tr>
+ <tr><td>bconsole :</td>
+ <td> <input class="formulaire" title="/chemin/vers/bconsole -n -c /chemin/vers/bconsole.conf" type='text' value='<TMPL_VAR NAME=bconsole>' size='64' name='bconsole'>
+ </td></tr>
+ <tr><td>debug :</td>
+ <td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
+ </td></tr>
+ </table>
+ <input type="image" name='action' value='apply_conf' src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuration </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr> <td><b>Connexion SQL</b></td> <td/></tr>
+ <tr><td>DBI :</td> <td> <TMPL_VAR dbi> </td></tr>
+ <tr><td>user :</td> <td> <TMPL_VAR user> </td></tr>
+ <tr><td>password :</td> <td> xxxxxxxx </td></tr>
+ <tr> <td><b>Options Générales</b></td> <td/></tr>
+ <tr><td>email_media :</td> <td> <TMPL_VAR email_media> </td></tr>
+ <tr> <td><b>Configuration Bweb</b></td> <td/></tr>
+ <tr><td>config_file :</td> <td> <TMPL_VAR config_file> </td></tr>
+ <tr><td title="/chemin/vers/votre/template_dir">template_dir :</td> <td> <TMPL_VAR template_dir> </td></tr>
+ <tr><td title="/chemin/vers/une/font.ttf">graph_font :</td> <td> <TMPL_VAR graph_font> </td></tr>
+ <tr><td title="Ce répertoire doit être accessible en ecriture pour apache et être sous /bweb/fv">fv_write_path :</td> <td> <TMPL_VAR fv_write_path> </td></tr>
+ <tr><td title="/chemin/vers/bconsole -n -c /chemin/vers/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
+ <tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
+ <TMPL_IF achs>
+ <tr> <td><b>Robotique (Autochanger)</b></td> <td/></tr>
+ <tr>
+ <td>
+ <form action='?' method='GET'>
+ <table border='0'>
+ <TMPL_LOOP achs>
+ <tr>
+ <td>
+<label>
+ <input type='radio' name='ach' value='<TMPL_VAR name>'><TMPL_VAR name>
+</label>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <td>
+
+ <input type="image" name="action" value="ach_edit" title="modifier" src='/bweb/edit.png'>
+ <input type="image" name='action' value='ach_del' title='supprimer' src='/bweb/remove.png'>
+ <input type='image' name='action' value='ach_view' title='voir' src='/bweb/zoom.png'>
+ </form>
+ </td>
+ </tr>
+ </TMPL_IF achs>
+ <tr>
+ <td><hr></td><td></td>
+ </tr>
+ </table>
+
+ <form action='?' method='GET'>
+ <label>
+ <input name='action' value='edit_conf' type="image" title='Modifier' src='/bweb/edit.png'> Modifier
+ </label>
+ <label>
+ <input name='action' value='ach_add' type="image" title='Ajouter une robotique' src='/bweb/add.png'> Ajouter une robotique
+ </label>
+ </form>
+
+ <TMPL_IF error>
+ info : <TMPL_VAR error> </br>
+ </TMPL_IF>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Client : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=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'>
+<!-- <div class="otherboxtitle">
+ Actions
+ </div>
+ <div class="otherbox">
+ <h1>Actions</h1>
+ <input type="image" name='action' value='job' title="Voir l'historique"
+ src='/bweb/zoom.png'>
+ <input type="image" name='action' value='dsp_cur_job' title='Voir les job en cours' src='/bweb/zoom.png'>
+ <input type="image" name='action' value='client_stat' title='Statistiques' src='/bweb/zoom.png'>
+ </div>
+-->
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Nom", "Nb Jobs", "Taille", "Nb Fichiers", "Nb Erreurs");
+
+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>"
+ )
+) ;
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+// disable_sorting: new Array(1),
+ rows_per_page: rows_per_page
+}
+);
+</script>
--- /dev/null
+<br/>
+<div class="otherboxtitle">
+ Filtre
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Niveau</h2>
+ <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>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Statut</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Tous</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Erreur</option>
+ <option id='status_A' value='A'>Annulé</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_tout' value=''>Tous</option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Période</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>Cette semaine</option>
+ <option id='age_2678400' value='2678400'>30 derniers jours</option>
+ <option id='age_15552000' value='15552000'>6 derniers mois</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Nombre d'élément</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Type</h2>
+ <select name='jobtype' class='formulaire'>
+ <option id='jobtype_any' value='all type'>Tous</option>
+ <option id='jobtype_B' value='B'>Backup</option>
+ <option id='jobtype_R' value='R'>Restauration</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clients</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<!--
+<tr>
+ <td valign='top'>
+ <h2>File Set</h2>
+ <select name='fileset' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_filesets>
+ <option id='client_<TMPL_VAR fileset>'><TMPL_VAR NAME=fileset></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+-->
+</table>
+ <input type="image" name='action' value='job' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF jobtype>
+ document.getElementById('jobtype_<TMPL_VAR jobtype>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qfilesets>
+ document.getElementById('fileset_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+<div class="otherboxtitle">
+ Filtre
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Type de Média</h2>
+ <select name='mediatype' class='formulaire'>
+ <option id='mediatype_all' value=''></option>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option id='mediatype_<TMPL_VAR mediatype>'><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Localisation</h2>
+ <select name='location' class='formulaire'>
+ <option id='location_all>' value=''></option>
+<TMPL_LOOP NAME=db_locations>
+ <option id='location_<TMPL_VAR location>'><TMPL_VAR location></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Statut</h2>
+ <select name='volstatus' class='formulaire'>
+ <option id='volstatus_All' value=''></option>
+ <option id='volstatus_Append' value='Append'>Append</option>
+ <option id='volstatus_Full' value='Full'>Full</option>
+ <option id='volstatus_Error' value='Error'>Error</option>
+ <option id='volstatus_Used' value='Used'>Used</option>
+ <option id='volstatus_Purged' value='Purged'>Purged</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_all>' value=''></option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Label</h2>
+ <input type='text' name='re_media'
+ <TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
+ class='formulaire' size='8'>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Nombre d'éléments</h2>
+ <input type='text' name='limit' value='<TMPL_VAR limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+
+</table>
+ <input type="image" name='action' value='media' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF volstatus>
+ document.getElementById('volstatus_<TMPL_VAR volstatus>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qmediatypes>
+ document.getElementById('mediatype_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+ <TMPL_LOOP qlocations>
+ document.getElementById('location_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'> Historique des Jobs (<TMPL_VAR Filter>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <table id='id<TMPL_VAR ID>'></table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+<TMPL_IF status>
+document.getElementById('status_<TMPL_VAR status>').checked = true;
+</TMPL_IF>
+
+
+
+var header = new Array("JobId",
+ "Client",
+ "Nom du Job",
+ "FileSet",
+// "Pool",
+ "Niveau",
+ "Début",
+ "Durée",
+ "Nb Fichier",
+ "Taille",
+ "Erreurs",
+ "Statut");
+
+var data = new Array();
+
+<TMPL_LOOP Jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+//"<TMPL_VAR Pool>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR Duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+a
+ )
+);
+</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(10),
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
+</script>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>Information sur un job</h1>
+ </div>
+ <div class="bodydiv">
+ <table id='id0'></table>
+ <table><td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <label>
+ <input type="image" name='action' value='delete' title='Supprimer ce job'
+ onclick="return confirm('Voulez vous vraiment supprimer ce job du catalogue ?');"
+ src='/bweb/purge.png'> Purger
+ </label>
+ </form>
+ </td><td>
+ <form action='bweb.pl?'>
+ <TMPL_LOOP volumes>
+ <input type='hidden' name='media' value='<TMPL_VAR VolumeName>'>
+ </TMPL_LOOP>
+ <label>
+ <input type="image" name='action' value='media' title='Voir les médias associés'
+ src='/bweb/zoom.png'>Voir les médias
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <label>
+ <input type="image" name='action' value='job' title='Voir les jobs de <TMPL_VAR Client>' src='/bweb/zoom.png'>Voir les jobs
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='age' value='2678400'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <input type='hidden' name='jobname' value='<TMPL_VAR jobname>'>
+ <label>
+ <input type="image" name='action' value='graph' title='Voir les tendances'
+ src='/bweb/chart.png'> Voir les stats
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='fileset' value='<TMPL_VAR FileSet>'>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='Voir le fileset associé'
+ src='/bweb/zoom.png'> Voir le FileSet
+ </label>
+ </form>
+ </td>
+<!-- Enlever ce commentaire pour activer le bfileview
+ <td>
+ <form action='bfileview.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <input type='hidden' name='where' value='/'>
+ <label>
+ <input type="image" name='action' value='bfileview'
+ title="Voir la répartition des fichiers"
+ onclick='if (<TMPL_VAR JobFiles> > 5000) { return confirm("Ce traitement peut prendre beaucoup de temps, voulez vous continuer ?")} else { return 1;Â }'
+ src='/bweb/colorscm.png'> Voir la répartition des fichiers
+ </label>
+ </form>
+ </td>
+-->
+ </table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var header = new Array("JobId",
+ "Client",
+ "Nom du Job",
+ "FileSet",
+ "Niveau",
+ "Début",
+ "Durée",
+ "Fichiers",
+ "Taille",
+ "Erreurs",
+ "Pool",
+ "Volumes utilisés",
+ "Statut");
+
+var data = new Array();
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+"<TMPL_VAR poolname>",
+"<TMPL_LOOP volumes><TMPL_VAR VolumeName>\n</TMPL_LOOP>",
+img
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id0",
+ 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(6)
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Localisation</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='location_add' title='Ajouter' src='/bweb/add.png'>
+ <input type="image" name='action' value='location_del' title='Supprimer' src='/bweb/remove.png'>
+ <input type="image" name='action' value='location_edit' title='Modifier' src='/bweb/edit.png'>
+
+ <input type="image" name='action' value='media' title='Voir les médias'
+ src='/bweb/zoom.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom","Enabled", "Coût", "Nb de volumes", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+var img;
+
+<TMPL_LOOP Locations>
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR enabled>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'location';
+chkbox.value = '<TMPL_VAR Location>';
+
+data.push( new Array(
+"<TMPL_VAR Location>",
+img,
+"<TMPL_VAR Cost>",
+"<TMPL_VAR name=volnum>",
+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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Log : <TMPL_VAR name> sur <TMPL_VAR client> (<TMPL_VAR jobid>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log'>
+<TMPL_VAR lines>
+ </pre>
+ </div>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Médias
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<TMPL_IF Pool>
+<h2>
+Pool : <a href="?action=pool;pool=<TMPL_VAR Pool>">
+ <TMPL_VAR Pool>
+ </a>
+</h2>
+</TMPL_IF>
+<TMPL_IF Location>
+<h2>
+Localisation : <TMPL_VAR location>
+</h2>
+</TMPL_IF>
+
+ <form action='?action=test' method='get'>
+ <table id='id_pool_<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='extern' title='Externaliser' src='/bweb/extern.png' onclick='return confirm("Voulez vous vraiment éjecter les médias sélectionnés ?");'>
+ <input type="image" name='action' value='intern' title='Internaliser' src='/bweb/intern.png'>
+ <input type="image" name='action' value='update_media' title='Mettre à jour' src='/bweb/edit.png'>
+ <input type="image" name='action' value='media_zoom' title='Informations' src='/bweb/zoom.png'>
+<!--
+ <input type="image" name='action' value='purge' title='Purger' src='/bweb/purge.png'>
+-->
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom du volume","En ligne","Taille", "Utilisation", "Statut",
+ "Pool", "Type",
+ "Dernière écriture", "Expiration", "Sélection");
+
+var data = new Array();
+var img;
+var chkbox;
+var d;
+
+<TMPL_LOOP Medias>
+d = percent_usage(<TMPL_VAR volusage>);
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value = '<TMPL_VAR volumename>';
+
+data.push( new Array(
+"<TMPL_VAR volumename>",
+img,
+human_size(<TMPL_VAR volbytes>),
+d,
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR poolname>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_pool_<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,
+ even_cell_color: even_cell_color,
+ odd_cell_color: odd_cell_color,
+ header_color: header_color,
+ page_nav: true,
+ padding: 3,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1,3,9)
+}
+);
+</script>
--- /dev/null
+<table>
+<td valign='top'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Media : <TMPL_VAR volumename> <TMPL_VAR comment></h1>
+ </div>
+ <div class='bodydiv'>
+ <b> Informations </b><br/>
+ <table id='id_info_<TMPL_VAR volumename>'></table>
+ <b> Statistiques </b><br/>
+ <table id='id_media_<TMPL_VAR volumename>'></table>
+ <b> Contenu </b></br>
+ <table id='id_jobs_<TMPL_VAR volumename>'></table>
+ <b> Actions </b></br>
+ <form action='?' method='get'>
+ <input type='hidden' name='media' value='<TMPL_VAR volumename>'>
+<TMPL_IF online>
+ <input type="image" name='action' value='extern' onclick='return confirm("Voulez vous vraiment éjecter ce média ?");' title='Externaliser' src='/bweb/extern.png'>
+<TMPL_ELSE>
+ <input type="image" name='action' value='intern' title='Internaliser' src='/bweb/intern.png'>
+</TMPL_IF>
+ <input type="image" name='action' value='update_media' title='Mettre à jour' src='/bweb/edit.png'>
+ <input type="image" name='action' value='purge' title='Purger' src='/bweb/purge.png' onclick="return confirm('Vous voulez vraiment purger ce volume ?')">
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+<TMPL_IF Locationlog>
+ <a href='#' onclick='document.getElementById("locationlog").style.visibility="visible";'><img title='Voir les déplacements' src='/bweb/zoom.png'></a>
+</TMPL_IF>
+ </form>
+ </div>
+</td>
+<td valign='top'style="visibility:hidden;" id='locationlog'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Log sur les déplacements </h1>
+ </div>
+ <div class='bodydiv'>
+<pre>
+ <TMPL_VAR LocationLog>
+</pre>
+ </div>
+</td>
+</table>
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Pool","En ligne","Localisation","Statut", "Taille", "Expiration",
+ "Rétention","Temps maxi d'utilisation", "Nb de job maxi" );
+
+var data = new Array();
+var img;
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+data.push( new Array(
+"<TMPL_VAR poolname>",
+img,
+"<TMPL_VAR location>",
+"<TMPL_VAR volstatus>",
+human_size(<TMPL_VAR nb_bytes>),
+"<TMPL_VAR expire>",
+human_sec(<TMPL_VAR volretention>),
+human_sec(<TMPL_VAR voluseduration>),
+"<TMPL_VAR maxvoljobs>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_info_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+ padding: 3,
+ disable_sorting: new Array(1)
+}
+);
+
+var header = new Array( "Nb montages", "Nb recyclages", "Temps de lecture", "Temps d'écriture", "Erreurs");
+
+var data = new Array();
+data.push( new Array(
+"<TMPL_VAR nb_mounts>",
+"<TMPL_VAR recyclecount>",
+human_sec(<TMPL_VAR volreadtime>),
+human_sec(<TMPL_VAR volwritetime>),
+"<TMPL_VAR nb_errors>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_media_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+// disable_sorting: new Array()
+ padding: 3
+}
+);
+
+
+var header = new Array("JobId","Nom","Début","Type",
+ "Niveau", "Nb Fichiers", "Taille", "Statut");
+
+var data = new Array();
+var a;
+var img;
+
+<TMPL_LOOP jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src="/bweb/<TMPL_VAR status>.png";
+img.title=jobstatus['<TMPL_VAR status>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR jobid>",
+"<TMPL_VAR name>",
+"<TMPL_VAR starttime>",
+"<TMPL_VAR type>",
+"<TMPL_VAR level>",
+"<TMPL_VAR files>",
+human_size(<TMPL_VAR bytes>),
+a
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_jobs_<TMPL_VAR volumename>",
+ 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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Pools de média</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" type='submit' name='action' value='media' title='Afficher le contenu' src='/bweb/zoom.png'>
+<TMPL_LOOP MediaType>
+ <input type='hidden' name='mediatype' value=<TMPL_VAR name>>
+</TMPL_LOOP>
+ </form>
+ <br/>
+ Tips : Pour modifier les options d'un pool, vous devez éditer la configuration bacula et
+ la recharger (reload). Après vous devez lancer "update pool=mypool" sur la bconsole.
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom","Recyclage","Rétention","Durée d'utilisation",
+ "Nb job maxi par média","Nb fichier maxi par média",
+ "Taille maxi d'un média","Nb volumes", "Statut", "Utilisation", "Sélection");
+
+var data = new Array();
+var chkbox;
+var img;
+var img2;
+
+<TMPL_LOOP Pools>
+
+img = percent_display([
+<TMPL_IF nb_recycle>{ name: "Recycle", nb: <TMPL_VAR nb_recycle> },</TMPL_IF>
+<TMPL_IF nb_purged> { name: "Purged", nb: <TMPL_VAR nb_purged> },</TMPL_IF>
+<TMPL_IF nb_append> { name: "Append", nb: <TMPL_VAR nb_append> },</TMPL_IF>
+<TMPL_IF nb_full> { name: "Full", nb: <TMPL_VAR nb_full> }, </TMPL_IF>
+<TMPL_IF nb_disabled> { name: "Disabled", nb: <TMPL_VAR nb_disabled> }, </TMPL_IF>
+<TMPL_IF nb_error> { name: "Error", nb: <TMPL_VAR nb_error> }, </TMPL_IF>
+<TMPL_IF nb_archive>{ name: "Archive", nb: <TMPL_VAR nb_archive> },</TMPL_IF>
+<TMPL_IF nb_used> { name: "Used", nb: <TMPL_VAR nb_used> }, </TMPL_IF>
+<TMPL_IF NAME='nb_read-only'> { name: "Read-Only", nb: <TMPL_VAR NAME='nb_read-only'> }, </TMPL_IF>
+{}
+]);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.value = '<TMPL_VAR Name>';
+chkbox.name = 'pool';
+
+img2 = percent_usage(<TMPL_VAR poolusage>);
+
+data.push( new Array(
+"<TMPL_VAR Name>",
+"<TMPL_VAR Recycle>",
+human_sec(<TMPL_VAR VolRetention>),
+human_sec(<TMPL_VAR VolUseDuration>),
+"<TMPL_VAR MaxVolJobs>",
+"<TMPL_VAR MaxVolFiles>",
+human_size(<TMPL_VAR MaxVolBytes>),
+"<TMPL_VAR VolNum>",
+img,
+img2,
+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>
--- /dev/null
+</div>
+<div style="float: right;"></div>
+</div>
+</body>
+</html>
--- /dev/null
+<h1>Une erreur s'est produite :</h1>
+<pre>
+<TMPL_VAR NAME=error>
+</pre>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ FileSet <TMPL_VAR fileset>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+ <img src="/bweb/add.png" alt="included"> Ce qui est inclu :
+ <pre>
+<TMPL_LOOP I><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+ <img src="/bweb/remove.png" alt="excluded"> Ce qui est exclu :
+ <pre>
+<TMPL_LOOP E><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+Tips : Attention, ceci est le FileSet courant, sur une ancienne sauvegarde, il peut avoir changé.
+
+ </div>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> Informations sur <TMPL_VAR filename> </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr><td>taille : </td> <td> <TMPL_VAR size> </td></tr>
+ <tr><td>ctime : </td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>mtime : </td> <td> <TMPL_VAR mtime> </td></tr>
+ <tr><td>atime : </td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>md5 : </td> <td> <TMPL_VAR md5> </td></tr>
+ </table>
+</div>
--- /dev/null
+
+<script type="text/javascript" language="JavaScript">
+bweb_add_refresh();
+</script>
+<div class='titlediv'>
+ <h1 class="newstitle">
+ Informations
+ </h1>
+</div>
+<div class="bodydiv">
+ <table>
+ <tr><td>Nombre de clients :</td> <td> <TMPL_VAR nb_client> </td>
+ <td>Total des sauvegardes :</td> <td> <TMPL_VAR nb_bytes> </td>
+ <td>Nombre de medias :</td> <td> <TMPL_VAR nb_media> </td>
+ </tr>
+ <tr><td>Taille de la base :</td> <td> <TMPL_VAR db_size> </td>
+ <td>Nombre de Pools :</td> <td> <TMPL_VAR nb_pool> </td>
+ <td>Nombre de Jobs :</td> <td> <TMPL_VAR nb_job> </td>
+ </tr>
+ <tr><td>Job en erreur (<TMPL_VAR label>):</td>
+
+<td <TMPL_IF nb_err> class='joberr' </TMPL_IF>>
+ <TMPL_VAR nb_err>
+</td>
+ <td></td> <td></td>
+ <td></td> <td></td>
+ </tr>
+
+ </table>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Statistiques</h1>
+ </div>
+ <div class='bodydiv'>
+<table border='0'>
+<td>
+<form name='form1' action='?' method='GET'>
+ <div class="otherboxtitle">
+ Options
+ </div>
+ <div class="otherbox">
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Niveau</h2>
+ <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'>Differentielle</option>
+ <option id='level_I' value='I'>Incrémentale</option>
+ </select>
+ </td><td valign='top'>
+ <h2>Statut</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Tous</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Erreur</option>
+ <option id='status_A' value='A'>Annulé</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Période</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>Cette semaine</option>
+ <option id='age_2678400' value='2678400'>30 derniers jours</option>
+ <option id='age_15552000' value='15552000'>6 derniers mois</option>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Taille</h2>
+ Largeur : <input class='formulaire' type='text'
+ name='width' value='<TMPL_VAR width>' size='4'><br/>
+ Hauteur : <input type='text' class='formulaire'
+ name='height' value='<TMPL_VAR height>' size='4'><br/>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clients</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Job</h2>
+ <select name='jobname' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_jobnames>
+ <option><TMPL_VAR NAME=jobname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td> <h2> Type </h2>
+ <select name='graph' class='formulaire'>
+ <option id='job_size' value='job_size' title="Taille des jobs pour la période">Taille des Job </option>
+ <option id='job_duration' value='job_duration' title="Durée des jobs pour la période">Durée</option>
+ <option id='job_rate' value='job_rate' title="Débit des job pour la période">Débit</option>
+ <option id='job_file' value='job_file' title="Nombre de fichier sauvegardé par job pour la période">Nb fichiers</option>
+ <option id='job_count_phour' value='job_count_phour' title="Number of job per hour for the period">Job per hour</option>
+ <option id='job_count_pday' value='job_count_pday' title="Number of job per day for the period">Job per day</option>
+ <option id='job_avg_phour' value='job_avg_pday' title="Average backup size per day for the period">Job avg B/hour</option>
+ <option id='job_avg_pday' value='job_avg_pday' title="Average backup size per hour for the period">Job avg B/day</option>
+ <option id='job_sum_phour' value='job_sum_phour' title="Job size per hour">Job total B/hour</option>
+ <option id='job_sum_pday' value='job_sum_pday' title="Job size per day">Job total B/day</option>
+ <option id='job_count_hour' value='job_count_hour' title="Number of job per hour for the period">Jobs Count (h)</option>
+ <option id='job_count_day' value='job_count_day' title="Number of job per day for the period">Jobs Count (d)</option>
+ <option id='job_avg_hour' value='job_avg_hour' title="Average backup size per hour for the period">Job avg size (h)</option>
+ <option id='job_avg_day' value='job_avg_day' title="Average backup size per day for the period">Job avg size (d)</option>
+ <option id='job_sum_hour' value='job_sum_hour' title="Job size per hour for the period">Job Bytes (h)</option>
+ <option id='job_sum_day' value='job_sum_day' title="Job size per day for the period">Job Bytes (d)</option>
+ </select>
+ </td>
+ <td valign='bottom'>
+ <h2>Nombre d'élément</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+<td><h2> Type de graphique </h2>
+ <select name='gtype' class='formulaire'>
+ <option id='gtype_bars' value='bars'>Barres</option>
+<!-- <option id='gtype_bars3d' value='bars3d'>Bars3d</option> -->
+ <option id='gtype_lines' value='lines'>Lignes</option>
+ <option id='gtype_linespoints' value='linespoints'>Lignes avec points</option>
+</td>
+<td>
+ <input type='submit' name='action' value='graph' class='formulaire'>
+</td>
+</tr>
+</table>
+ </div>
+
+</form>
+</td>
+<td>
+
+ <div class="otherboxtitle">
+ Graphique
+ </div>
+ <div class="otherbox">
+ <img src='bgraph.pl?<TMPL_VAR NAME=url>' alt="Rien n'a afficher, essayer avec une autre période">
+ </div>
+
+</td>
+</table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+<TMPL_IF qfilesets>
+ for (var i=0; i < document.form1.fileset.length; ++i) {
+ <TMPL_LOOP qfilesets>
+ if (document.form1.fileset[i].value == <TMPL_VAR name>) {
+ document.form1.fileset[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+<TMPL_IF qjobnames>
+ for (var i=0; i < document.form1.jobname.length; ++i) {
+ <TMPL_LOOP qjobnames>
+ if (document.form1.jobname[i].value == <TMPL_VAR name>) {
+ document.form1.jobname[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+ <TMPL_IF graph>
+ document.getElementById('<TMPL_VAR graph>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF gtype>
+ document.getElementById('gtype_<TMPL_VAR gtype>').selected=true;
+ </TMPL_IF>
+
+</script>
+
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Assistant d'externalisation de médias (partie 1/2)</h1>
+</div>
+<div class='bodydiv'>
+Cet outil va sélectionner pour vous les meilleurs cartouches à externaliser.
+Vous devrez choisir parmi la sélection de l'écran suivant.
+
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool :</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_pools>
+ <option><TMPL_VAR NAME=name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Type de média:</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option><TMPL_VAR NAME=mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Localisation : </td>
+ <td><select name='location' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Nombre de médias <br/> à externaliser :</td>
+ <td> <input type='text' name='limit' size='3' class='formulaire'
+ value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_extern_media' title='Suivant' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Assistant d'externalisation de médias (partie 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Maintenant, vous devez vérifier la sélection et éjecter les médias.
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='extern' src='/bweb/extern.png' title='Externaliser la selection'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom de volume","Statut",
+ "Type de média","Pool","Dernière écriture",
+ "Expiration", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR NAME=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Assistant d'internalisation de médias (partie 1/2)</h1>
+</div>
+<div class="bodydiv">
+Cet outil va sélectionner pour vous les meilleurs cartouches à internaliser.
+Vous devrez choisir parmi la sélection de l'écran suivant.
+
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool :</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP db_pools>
+ <option><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Type de média :</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP db_mediatypes>
+ <option><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>
+ Localisation :
+ </td><td><select name='location' class='formulaire'>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Choisir des médias <br/> expirés :</td>
+ <td> <input type='checkbox' name='expired' class='formulaire'
+ checked> </td>
+ </tr>
+ <tr>
+ <td>Nombre de médias <br/> à internaliser :</td>
+ <td> <input type='text' name='limit' class='formulaire'
+ size='3' value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_intern_media'
+ title='Suivant' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Assistant d'internalisation de médias (partie 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Maintenant vous devez vérifier la sélection et charger les médias.
+
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='move_media'
+ src='/bweb/intern.png'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom de volume","Statut",
+ "Type de media","Pool","Dernière écriture",
+ "Expiration", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value= '<TMPL_VAR NAME=volumename>';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Ajouter une localisation </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <table>
+ <tr><td>Localisation :</td>
+ <td>
+ <input class="formulaire" type='text' value='' size='32' name='location'>
+ </td>
+ </tr>
+ <tr><td>Coût :</td>
+ <td> <input class="formulaire" type='text' value='10' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Enabled :</td>
+ <td> <input class="formulaire" type='checkbox' value='10' name='enabled'>
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_add' title="Sauvegarder"
+ src='/bweb/save.png'>
+ </form>
+
+Tips: Vous devez avoir une localisation par robotique.
+
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Localisation : <TMPL_VAR Location></h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <input type='hidden' name='location' value='<TMPL_VAR location>'>
+ <table>
+ <tr><td>Localisation :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR location>' size='32' name='newlocation'>
+ </td>
+ </tr>
+ <tr><td>Coût :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR cost>' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Enabled :</td>
+ <td> <input class="formulaire" type='checkbox' name='enabled' <TMPL_IF enabled> checked </TMPL_IF> >
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_save'
+ src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Déplacer un média</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <table border='0'>
+ <tr><td> Nouvelle localisation : </td><td>
+<select name='newlocation' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <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>
+</select>
+ </td><tr><td> Utilisateur : </td><td>
+<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
+ </td></tr>
+ </td></tr><tr><td> Commentaire : </td><td>
+<textarea name="comment" class='formulaire'></textarea>
+ </td></tr>
+ </table>
+ <label>
+ <input type="image" type='submit' name='action' value='change_location' src='/bweb/apply.png'> Déplacer
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom de volume", "Localisation", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Jobs définis : </h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Nom du job : </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr>
+ </table>
+ <br/>
+ <label>
+ <input type="image" name='action' value='enable_job' title='Activer'
+ src='/bweb/inflag1.png'> Activer
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job' title='Désactiver'
+ src='/bweb/inflag0.png'> Désactiver
+ </label>
+ <label>
+ <input type="image" name='action' value='run_job_mod' title='Lancer maintenant'
+ src='/bweb/R.png'> Lancer maintenant
+ </label>
+ </form>
+ </div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Lancer le job : <TMPL_VAR job> sur <TMPL_VAR client></h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Nom du job : </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Pool : </td><td>
+
+ <select name='pool'>
+ <option value=''></option>
+ <TMPL_LOOP pools>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Client : </td><td>
+
+ <select name='client'>
+ <TMPL_LOOP clients>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>FileSet : </td><td>
+ <select name='fileset'>
+ <TMPL_LOOP filesets>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Storage : </td><td>
+ <select name='storage'>
+ <TMPL_LOOP storages>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Niveau : </td><td>
+ <select name='level'>
+ <option id='level_Incremental' value='Incremental'>Incrémentale</option>
+ <option id='level_Full' value='Full'>Full</option>
+ <option id='level_Differential' value='Differential'>Différentielle</option>
+ </select>
+ </td></tr><tr id='more1' style="visibility:hidden"><td>Start Time: </td><td>
+ <input class='formulaire'
+ type='text' title='YYYY-MM-DD HH:MM:SS'
+ size='17' name='when' value='<TMPL_VAR when>'>
+
+ </td></tr><tr id='more2' style="visibility:hidden"><td>Priorité : </td><td>
+ <input class='formulaire' type='text'
+ size='3' name='priority' value='<TMPL_VAR priority>'>
+
+ </td></tr>
+ </table>
+ <br/>
+ <label onclick='
+ document.getElementById("more1").style.visibility="visible";
+ document.getElementById("more2").style.visibility="visible";'>
+ <img title="Affiche plus d'options" src='/bweb/add.png'>Options</label>
+ <label>
+ <label>
+ <input type="image" name='action' value='run_job_now' title='Lancer maintenant'
+ src='/bweb/R.png'>Lancer maintenant
+ </label>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='Voir le FileSet'
+ src='/bweb/zoom.png'>Voir le FileSet
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF job>
+ ok=1;
+ for(var i=0; ok && i < document.form1.job.length; i++) {
+ if (document.form1.job[i].value == '<TMPL_VAR job>') {
+ document.form1.job[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF client>
+ ok=1;
+ for(var i=0; ok && i < document.form1.client.length; i++) {
+ if (document.form1.client[i].value == '<TMPL_VAR client>') {
+ document.form1.client[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF pool>
+ ok=1;
+ for(var i=0; ok && i < document.form1.pool.length; i++) {
+ if (document.form1.pool[i].value == '<TMPL_VAR pool>') {
+ document.form1.pool[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF storage>
+ ok=1;
+ for(var i=0; ok && i < document.form1.storage.length; i++) {
+ if (document.form1.storage[i].value == '<TMPL_VAR storage>') {
+ document.form1.storage[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF level>
+<!-- document.getElementById('level_<TMPL_VAR level>').selected=true; -->
+ </TMPL_IF>
+ <TMPL_IF fileset>
+ ok=1;
+ for(var i=0; ok && i < document.form1.fileset.length; i++) {
+ if (document.form1.fileset[i].value == '<TMPL_VAR fileset>') {
+ document.form1.fileset[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Jobs en cours </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <br/>
+<label>
+<input type='image' name='action' value='dsp_cur_job'
+ title='Voir le job' src='/bweb/zoom.png'>
+</label>
+<label>
+<input type="image" type='submit' name='action' value='cancel_job'
+ onclick="return confirm('Vous voulez vraiment annuler ce job ?')"
+ title='Annuler le job' src='/bweb/cancel.png'>
+</label>
+ </form>
+
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("JobId",
+ "Client",
+ "Nom du Job",
+ "Niveau",
+ "Début",
+ "Durée",
+// "Job Files",
+// "Job Bytes",
+ "Statut",
+ "Sélection"
+ );
+
+var data = new Array();
+var chkbox;
+var img;
+
+<TMPL_LOOP NAME=Jobs>
+a = document.createElement('A');
+a.href='?action=dsp_cur_job;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src = '/bweb/<TMPL_VAR NAME=JobStatus>.png';
+img.title = jobstatus['<TMPL_VAR NAME=JobStatus>'];
+
+a.appendChild(img);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'jobid';
+chkbox.value = '<TMPL_VAR NAME=jobid>';
+
+data.push( new Array(
+"<TMPL_VAR NAME=JobId>",
+"<TMPL_VAR NAME=ClientName>",
+"<TMPL_VAR NAME=JobName>",
+joblevel['<TMPL_VAR NAME=Level>'],
+"<TMPL_VAR NAME=StartTime>",
+"<TMPL_VAR NAME=duration>",
+//"<TMPL_VAR NAME=JobFiles>",
+//"<TMPL_VAR NAME=JobBytes>",
+a,
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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(6)
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR NAME=ID>'].fieldSort(0);
+
+bweb_add_refresh();
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Prochains jobs </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <label>
+ <input type="image" name='action' value='run_job_mod'
+ src='/bweb/R.png' title='Lancer maintenant'> Lancer maintenant
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job'
+ src='/bweb/inflag0.png' title='Désactiver'> Désactiver
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Quand",
+ "Niveau",
+ "Type",
+ "Priorité",
+ "Nom",
+ "Volume",
+ "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP list>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'job';
+chkbox.value = '<TMPL_VAR name>';
+
+data.push( new Array(
+"<TMPL_VAR date>",
+"<TMPL_VAR level>",
+"<TMPL_VAR type>",
+"<TMPL_VAR priority>",
+"<TMPL_VAR name>",
+"<TMPL_VAR volume>",
+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: 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(6),
+ padding: 3
+}
+);
+</script>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> Statistiques </h1>
+</div>
+<div class='bodydiv'>
+<a href='?action=job;age=172800;jobtype=B'>
+<img width='450' height='200' src='bgraph.pl?age=2678400;width=450;height=200;graph=job_sum_day;limit=500;action=graph;legend=off' alt='Rien a afficher'>
+</a>
+</div>
+
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Mise à jour de la localisation d'un média</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ Nouvelle localisation : <select class='formulaire' name='newlocation'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ <input type="image" name='action' value='save_location' src='/bweb/apply.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Nom de volume", "Localisation", "Sélection");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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
+}
+);
+
+<TMPL_IF qnewlocation>
+ document.getElementById('loc_' + <TMPL_VAR qnewlocation>).selected=true;
+</TMPL_IF>
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Modifier le media <TMPL_VAR volumename></h1>
+</div>
+<div class='bodydiv'>
+ <form name='form1' action="?" method='GET'>
+ <table>
+ <tr><td>Nom du volume :</td>
+ <td><input type='text' name='media' class='formulaire' value='<TMPL_VAR volumename>' title='Modifiez ce champs pour mettre à jour un autre média'>
+ </td>
+ </tr>
+ <tr><td>Pool :</td>
+ <td><select name='pool' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Statut :</td>
+ <td><select name='volstatus' class='formulaire'>
+ <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='Read-Only'>Read-Only</option>
+ <option value='Used'>Used</option>
+ </select>
+ </td>
+ </tr>
+
+ <tr><td>Slot :</td>
+ <td>
+ <input class='formulaire' type='text'
+ name='slot' value='<TMPL_VAR slot>'>
+ </td>
+ </tr>
+
+ <tr><td>En ligne :</td>
+ <td>
+ <input class='formulaire' type='checkbox'
+ name='inchanger' <TMPL_IF inchanger>checked</TMPL_IF>>
+ </td>
+ </tr>
+
+ <tr><td> Localisation : </td>
+ <td><select name='location' class='formulaire'>
+ <option value=''></option>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Période de rétention : </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='volretention' value='<TMPL_VAR volretention>'>
+ </td>
+ </tr>
+ <tr><td> Durée d'utilisation : </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='voluseduration' value='<TMPL_VAR voluseduration>'>
+ </td>
+ </tr>
+ <tr><td> Nombre de jobs maxi : </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10'
+ name='maxvoljobs' value='<TMPL_VAR maxvoljobs>'>
+ </td>
+ </tr>
+ <tr><td> Nombre de fichiers maxi : </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10000'
+ name='maxvolfiles' value='<TMPL_VAR maxvolfiles>'>
+ </td>
+ </tr>
+ <tr><td> Taille maxi : </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10M, 11G'
+ name='maxvolbytes' value='<TMPL_VAR maxvolbytes>'>
+ </td>
+ </tr>
+ <tr><td>Pool de recyclage :</td>
+ <td><select name='poolrecycle' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Commentaire : </td>
+ <td>
+ <input class='formulaire' type='text' title='un commentaire'
+ name='comment' value='<TMPL_VAR comment>'>
+ </td>
+ </tr>
+ </table>
+<table>
+ <td>
+ <label>
+ <input type="image" name='action' value='do_update_media' src='/bweb/apply.png'> Appliquer
+ </label>
+ <label>
+ <input type="image" name='action' title='Mettre à jour à partir du pool'
+ value='update_from_pool' src='/bweb/update.png'> Reinitialiser
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='?' method='GET'>
+ <input type='hidden' name='pool' value='<TMPL_VAR poolname>'>
+ <label>
+ <input type="image" name='action' value='media'
+ src='/bweb/zoom.png'>Voir le pool
+ </label>
+ </form>
+ </td>
+</table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var ok=1;
+for (var i=0; ok && i < document.form1.pool.length; ++i) {
+ if (document.form1.pool[i].value == '<TMPL_VAR poolname>') {
+ document.form1.pool[i].selected = true;
+ ok=0;
+ }
+}
+ok=1;
+for (var i=0; ok && i < document.form1.poolrecycle.length; ++i) {
+ if (document.form1.poolrecycle[i].value == '<TMPL_VAR poolrecycle>') {
+ document.form1.poolrecycle[i].selected = true;
+ ok=0;
+ }
+}
+ok=1;
+for (var i=0; ok && i < document.form1.location.length; ++i) {
+ if (document.form1.location[i].value == '<TMPL_VAR location>') {
+ document.form1.location[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.volstatus.length; ++i) {
+ if (document.form1.volstatus[i].value == '<TMPL_VAR volstatus>') {
+ document.form1.volstatus[i].selected = true;
+ ok=0;
+ }
+}
+
+</script>
--- /dev/null
+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 VERSION
+
+ $Id$
+
+=cut
+
+
+################################################################
+# Manage with Expect the bconsole tool
+package Bconsole;
+use Expect;
+use POSIX qw/_exit/;
+
+# my $pref = new Pref(config_file => 'brestore.conf');
+# my $bconsole = new Bconsole(pref => $pref);
+sub new
+{
+ my ($class, %arg) = @_;
+
+ my $self = bless {
+ pref => $arg{pref}, # Pref object
+ bconsole => undef, # Expect object
+ log_stdout => $arg{log_stdout} || 0,
+ timeout => $arg{timeout} || 20,
+ debug => $arg{debug} || 0,
+ };
+
+ return $self;
+}
+
+sub run
+{
+ my ($self, %arg) = @_;
+
+ my $cmd = 'run ';
+ for my $key (keys %arg) {
+ if ($arg{$key}) {
+ $arg{$key} =~ tr/""/ /;
+ $cmd .= "$key=\"$arg{$key}\" ";
+ }
+ }
+
+ unless ($self->connect()) {
+ return 0;
+ }
+
+ print STDERR "===> $cmd yes\n";
+ $self->{bconsole}->clear_accum();
+ $self->send("$cmd yes\n");
+ $self->expect_it('-re',qr/^[*]/);
+ my $ret = $self->before();
+ if ($ret =~ /jobid=(\d+)/is) {
+ return $1;
+ } else {
+ return 0;
+ }
+}
+
+# for brestore.pl::BwebConsole
+sub prepare
+{
+ # do nothing
+}
+
+sub send
+{
+ my ($self, $what) = @_;
+ $self->{bconsole}->send($what);
+}
+
+sub expect_it
+{
+ my ($self, @what) = @_;
+ unless ($self->{bconsole}->expect($self->{timeout}, @what)) {
+ $self->{error} = $!;
+ return 0;
+ }
+ return 1;
+}
+
+sub log_stdout
+{
+ my ($self, $how) = @_;
+
+ if ($self->{bconsole}) {
+ $self->{bconsole}->log_stdout($how);
+ }
+
+ $self->{log_stdout} = $how;
+}
+
+sub connect
+{
+ my ($self) = @_;
+
+ if ($self->{error}) {
+ return 0 ;
+ }
+
+ unless ($self->{bconsole}) {
+ my @cmd = split(/\s+/, $self->{pref}->{bconsole}) ;
+ unless (@cmd) {
+ $self->{error} = "bconsole string not found";
+ return 0;
+ }
+ $self->{bconsole} = new Expect;
+ $self->{bconsole}->raw_pty(0);
+ $self->{bconsole}->debug($self->{debug});
+ $self->{bconsole}->log_stdout($self->{debug} || $self->{log_stdout});
+
+ # WARNING : die is trapped by gtk_main_loop()
+ # and exit() closes DBI connection
+ my $ret;
+ {
+ my $sav = $SIG{__DIE__};
+ $SIG{__DIE__} = sub { _exit 1 ;};
+ my $old = $ENV{COLUMNS};
+ $ENV{COLUMNS} = 300;
+ $ret = $self->{bconsole}->spawn(@cmd) ;
+ $ENV{COLUMNS} = $old;
+ $SIG{__DIE__} = $sav;
+ }
+
+ unless ($ret) {
+ $self->{error} = $!;
+ return 0;
+ }
+
+ # TODO : we must verify that expect return the good value
+
+ $self->expect_it('*');
+ $self->send_cmd('gui on');
+ }
+ return 1 ;
+}
+
+sub cancel
+{
+ my ($self, $jobid) = @_;
+ return $self->send_cmd("cancel jobid=$jobid");
+}
+
+# get text between to expect
+sub before
+{
+ my ($self) = @_;
+ return $self->{bconsole}->before();
+}
+
+sub send_cmd
+{
+ my ($self, $cmd) = @_;
+ unless ($self->connect()) {
+ return '';
+ }
+ $self->send("$cmd\n");
+ $self->expect_it($cmd);
+ $self->{bconsole}->clear_accum();
+ $self->expect_it('-re',qr/^[*]/);
+ return $self->before();
+}
+
+sub send_cmd_yes
+{
+ my ($self, $cmd) = @_;
+ unless ($self->connect()) {
+ return '';
+ }
+ $self->send("$cmd\n");
+ $self->expect_it('-re', '[?].+:');
+
+ $self->send("yes\n");
+ $self->expect_it("yes");
+ $self->{bconsole}->clear_accum();
+ $self->expect_it('-re',qr/^[*]/);
+ return $self->before();
+}
+
+sub label_barcodes
+{
+ my ($self, %arg) = @_;
+
+ unless ($arg{storage}) {
+ return '';
+ }
+
+ unless ($self->connect()) {
+ return '';
+ }
+
+ $arg{drive} = $arg{drive} || '0' ;
+ $arg{pool} = $arg{pool} || 'Scratch';
+
+ my $cmd = "label barcodes drive=$arg{drive} pool=\"$arg{pool}\" storage=\"$arg{storage}\"";
+
+ if ($arg{slots}) {
+ $cmd .= " slots=$arg{slots}";
+ }
+
+ $self->send("$cmd\n");
+ $self->expect_it('-re', '[?].+\).*:');
+ my $res = $self->before();
+ $self->send("yes\n");
+ $self->expect_it("yes");
+ $res .= $self->before();
+ $self->expect_it('-re',qr/^[*]/);
+ $res .= $self->before();
+ return $res;
+}
+
+#
+# return [ { name => 'test1', vol => '00001', ... },
+# { name => 'test2', vol => '00002', ... }... ]
+#
+sub director_get_sched
+{
+ my ($self, $days) = @_ ;
+
+ $days = $days || 1;
+
+ unless ($self->connect()) {
+ return '';
+ }
+
+ my $status = $self->send_cmd("st director days=$days") ;
+
+ my @ret;
+ foreach my $l (split(/\r?\n/, $status)) {
+ #Level Type Pri Scheduled Name Volume
+ #Incremental Backup 11 03-ao-06 23:05 TEST_DATA 000001
+ if ($l =~ /^(I|F|Di)\w+\s+\w+\s+\d+/i) {
+ my ($level, $type, $pri, $d, $h, @name_vol) = split(/\s+/, $l);
+
+ my $vol = pop @name_vol; # last element
+ my $name = join(" ", @name_vol); # can contains space
+
+ push @ret, {
+ level => $level,
+ type => $type,
+ priority => $pri,
+ date => "$d $h",
+ name => $name,
+ volume => $vol,
+ };
+ }
+
+ }
+ return \@ret;
+}
+
+sub update_slots
+{
+ my ($self, $storage, $drive) = @_;
+ $drive = $drive || 0;
+
+ return $self->send_cmd("update slots storage=$storage drive=$drive");
+}
+
+sub get_fileset
+{
+ my ($self, $fs) = @_;
+
+ my $out = $self->send_cmd("show fileset=\"$fs\"");
+
+ my $ret = {};
+
+ foreach my $l (split(/\r\n/, $out)) {
+ # I /usr/local
+ if ($l =~ /^\s+([I|E])\s+(.+)$/) { # include
+ push @{$ret->{$1}}, { file => $2 };
+ }
+ }
+
+ return $ret;
+}
+
+sub list_job
+{
+ my ($self) = @_;
+ return split(/\r\n/, $self->send_cmd(".jobs"));
+}
+
+sub list_fileset
+{
+ my ($self) = @_;
+ return split(/\r\n/, $self->send_cmd(".filesets"));
+}
+
+sub list_storage
+{
+ my ($self) = @_;
+ return split(/\r\n/, $self->send_cmd(".storage"));
+}
+
+sub list_client
+{
+ my ($self) = @_;
+ return split(/\r\n/, $self->send_cmd(".clients"));
+}
+
+sub list_pool
+{
+ my ($self) = @_;
+ return split(/\r\n/, $self->send_cmd(".pools"));
+}
+
+use Time::ParseDate qw/parsedate/;
+use POSIX qw/strftime/;
+use Data::Dumper;
+
+sub _get_volume
+{
+ my ($self, @volume) = @_;
+ return '' unless (@volume);
+
+ my $sel='';
+ foreach my $vol (@volume) {
+ if ($vol =~ /^([\w\d\.-]+)$/) {
+ $sel .= " volume=$1";
+
+ } else {
+ $self->{error} = "Sorry media is bad";
+ return '';
+ }
+ }
+
+ return $sel;
+}
+
+sub purge_volume
+{
+ my ($self, @volume) = @_;
+
+ my $sel = $self->_get_volume(@volume);
+ my $ret;
+ if ($sel) {
+ $ret = $self->send_cmd("purge $sel");
+ } else {
+ $ret = $self->{error};
+ }
+ return $ret;
+}
+
+sub prune_volume
+{
+ my ($self, @volume) = @_;
+
+ my $sel = $self->_get_volume(@volume);
+ my $ret;
+ if ($sel) {
+ $ret = $self->send_cmd("prune $sel yes");
+ } else {
+ $ret = $self->{error};
+ }
+ return $ret;
+}
+
+sub purge_job
+{
+ my ($self, @jobid) = @_;
+
+ return 0 unless (@jobid);
+
+ my $sel='';
+ foreach my $job (@jobid) {
+ if ($job =~ /^(\d+)$/) {
+ $sel .= " jobid=$1";
+
+ } else {
+ $self->{error} = "Sorry jobid is bad";
+ return 0;
+ }
+ }
+
+ $self->send_cmd("purge $sel");
+}
+
+sub close
+{
+ my ($self) = @_;
+ $self->send("quit\n");
+ $self->{bconsole}->soft_close();
+ $self->{bconsole} = undef;
+}
+
+1;
+
+__END__
+
+# to use this
+# grep -v __END__ Bconsole.pm | perl
+
+package main;
+
+print "test sans conio\n";
+
+my $c = new Bconsole(pref => {
+ bconsole => '/tmp/bacula/sbin/bconsole -n -c /tmp/bacula/etc/bconsole.conf',
+},
+ debug => 0);
+
+print "fileset : ", join(',', $c->list_fileset()), "\n";
+print "job : ", join(',', $c->list_job()), "\n";
+print "storage : ", join(',', $c->list_storage()), "\n";
+#print "prune : " . $c->prune_volume('000001'), "\n";
+#print "update : " . $c->send_cmd('update slots storage=SDLT-1-2, drive=0'), "\n";
+#print "label : ", join(',', $c->label_barcodes(storage => 'SDLT-1-2',
+# slots => 6,
+# drive => 0)), "\n";
+
+
--- /dev/null
+################################################################
+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 VERSION
+
+ $Id$
+
+=cut
+
+package Bweb::Gui;
+
+=head1 PACKAGE
+
+ Bweb::Gui - Base package for all Bweb object
+
+=head2 DESCRIPTION
+
+ This package define base fonction like new, display, etc..
+
+=cut
+
+use HTML::Template;
+our $template_dir='/usr/share/bweb/tpl';
+
+=head1 FUNCTION
+
+ new - creation a of new Bweb object
+
+=head2 DESCRIPTION
+
+ This function take an hash of argument and place them
+ on bless ref
+
+ IE : $obj = new Obj(name => 'test', age => '10');
+
+ $obj->{name} eq 'test' and $obj->{age} eq 10
+
+=cut
+
+sub new
+{
+ my ($class, %arg) = @_;
+ my $self = bless {
+ name => undef,
+ }, $class;
+
+ map { $self->{lc($_)} = $arg{$_} } keys %arg ;
+
+ return $self;
+}
+
+sub debug
+{
+ my ($self, $what) = @_;
+
+ if ($self->{debug}) {
+ if (ref $what) {
+ print "<pre>" . Data::Dumper::Dumper($what) . "</pre>";
+ } else {
+ print "<pre>$what</pre>";
+ }
+ }
+}
+
+=head1 FUNCTION
+
+ error - display an error to the user
+
+=head2 DESCRIPTION
+
+ this function set $self->{error} with arg, display a message with
+ error.tpl and return 0
+
+=head2 EXAMPLE
+
+ unless (...) {
+ return $self->error("Can't use this file");
+ }
+
+=cut
+
+sub error
+{
+ my ($self, $what) = @_;
+ $self->{error} = $what;
+ $self->display($self, 'error.tpl');
+ return 0;
+}
+
+=head1 FUNCTION
+
+ display - display an html page with HTML::Template
+
+=head2 DESCRIPTION
+
+ this function is use to render all html codes. it takes an
+ ref hash as arg in which all param are usable in template.
+
+ it will use global template_dir to search the template file.
+
+ hash keys are not sensitive. See HTML::Template for more
+ explanations about the hash ref. (it's can be quiet hard to understand)
+
+=head2 EXAMPLE
+
+ $ref = { name => 'me', age => 26Â };
+ $self->display($ref, "people.tpl");
+
+=cut
+
+sub display
+{
+ my ($self, $hash, $tpl) = @_ ;
+
+ my $template = HTML::Template->new(filename => $tpl,
+ path =>[$template_dir],
+ die_on_bad_params => 0,
+ case_sensitive => 0);
+
+ foreach my $var (qw/limit offset/) {
+
+ unless ($hash->{$var}) {
+ my $value = CGI::param($var) || '';
+
+ if ($value =~ /^(\d+)$/) {
+ $template->param($var, $1) ;
+ }
+ }
+ }
+
+ $template->param('thisurl', CGI::url(-relative => 1, -query=>1));
+ $template->param('loginname', CGI::remote_user());
+
+ $template->param($hash);
+ print $template->output();
+}
+1;
+
+################################################################
+
+package Bweb::Config;
+
+use base q/Bweb::Gui/;
+
+=head1 PACKAGE
+
+ Bweb::Config - read, write, display, modify configuration
+
+=head2 DESCRIPTION
+
+ this package is used for manage configuration
+
+=head2 USAGE
+
+ $conf = new Bweb::Config(config_file => '/path/to/conf');
+ $conf->load();
+
+ $conf->edit();
+
+ $conf->save();
+
+=cut
+
+use CGI;
+
+=head1 PACKAGE VARIABLE
+
+ %k_re - hash of all acceptable option.
+
+=head2 DESCRIPTION
+
+ this variable permit to check all option with a regexp.
+
+=cut
+
+our %k_re = ( dbi => qr/^(dbi:(Pg|mysql):(?:\w+=[\w\d\.-]+;?)+)$/i,
+ user => qr/^([\w\d\.-]+)$/i,
+ password => qr/^(.*)$/i,
+ fv_write_path => qr!^([/\w\d\.-]*)$!,
+ template_dir => qr!^([/\w\d\.-]+)$!,
+ debug => qr/^(on)?$/,
+ email_media => qr/^([\w\d\.-]+@[\d\w\.-]+)$/,
+ graph_font => qr!^([/\w\d\.-]+.ttf)$!,
+ bconsole => qr!^(.+)?$!,
+ syslog_file => qr!^(.+)?$!,
+ log_dir => qr!^(.+)?$!,
+ );
+
+=head1 FUNCTION
+
+ load - load config_file
+
+=head2 DESCRIPTION
+
+ this function load the specified config_file.
+
+=cut
+
+sub load
+{
+ my ($self) = @_ ;
+
+ unless (open(FP, $self->{config_file}))
+ {
+ return $self->error("can't load config_file $self->{config_file} : $!");
+ }
+ my $f=''; my $tmpbuffer;
+ while(read FP,$tmpbuffer,4096)
+ {
+ $f .= $tmpbuffer;
+ }
+ close(FP);
+
+ my $VAR1;
+
+ no strict; # I have no idea of the contents of the file
+ eval "$f" ;
+ 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...") ;
+ }
+
+ 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;
+ }
+ }
+
+ close(FP);
+ return 1;
+}
+
+=head1 FUNCTION
+
+ save - save the current configuration to config_file
+
+=cut
+
+sub save
+{
+ my ($self) = @_ ;
+
+ if ($self->{ach_list}) {
+ # shortcut for display_begin
+ $self->{achs} = [ map {{ name => $_ }}
+ keys %{$self->{ach_list}}
+ ];
+ }
+
+ unless (open(FP, ">$self->{config_file}"))
+ {
+ return $self->error("$self->{config_file} : $!\n" .
+ "You must add this to your config file\n"
+ . Data::Dumper::Dumper($self));
+ }
+
+ print FP Data::Dumper::Dumper($self);
+
+ close(FP);
+ return 1;
+}
+
+=head1 FUNCTIONS
+
+ edit, view, modify - html form ouput
+
+=cut
+
+sub edit
+{
+ my ($self) = @_ ;
+
+ $self->display($self, "config_edit.tpl");
+}
+
+sub view
+{
+ my ($self) = @_ ;
+ $self->display($self, "config_view.tpl");
+}
+
+sub modify
+{
+ my ($self) = @_;
+
+ $self->{error} = '';
+ $self->{debug} = 0;
+
+ foreach my $k (CGI::param())
+ {
+ next unless (exists $k_re{$k}) ;
+ my $val = CGI::param($k);
+ if ($val =~ $k_re{$k}) {
+ $self->{$k} = $1;
+ } else {
+ $self->{error} .= "bad parameter : $k = [$val]";
+ }
+ }
+
+ $self->view();
+
+ if ($self->{error}) { # an error as occured
+ $self->display($self, 'error.tpl');
+ } else {
+ $self->save();
+ }
+}
+
+1;
+
+################################################################
+
+package Bweb::Client;
+
+use base q/Bweb::Gui/;
+
+=head1 PACKAGE
+
+ Bweb::Client - Bacula FD
+
+=head2 DESCRIPTION
+
+ this package is use to do all Client operations like, parse status etc...
+
+=head2 USAGE
+
+ $client = new Bweb::Client(name => 'zog-fd');
+ $client->status(); # do a 'status client=zog-fd'
+
+=cut
+
+=head1 FUNCTION
+
+ display_running_job - Html display of a running job
+
+=head2 DESCRIPTION
+
+ this function is used to display information about a current job
+
+=cut
+
+sub display_running_job
+{
+ my ($self, $conf, $jobid) = @_ ;
+
+ my $status = $self->status($conf);
+
+ if ($jobid) {
+ if ($status->{$jobid}) {
+ $self->display($status->{$jobid}, "client_job_status.tpl");
+ }
+ } else {
+ for my $id (keys %$status) {
+ $self->display($status->{$id}, "client_job_status.tpl");
+ }
+ }
+}
+
+=head1 FUNCTION
+
+ $client = new Bweb::Client(name => 'plume-fd');
+
+ $client->status($bweb);
+
+=head2 DESCRIPTION
+
+ dirty hack to parse "status client=xxx-fd"
+
+=head2 INPUT
+
+ JobId 105 Job Full_plume.2006-06-06_17.22.23 is running.
+ Backup Job started: 06-jun-06 17:22
+ Files=8,971 Bytes=194,484,132 Bytes/sec=7,480,158
+ Files Examined=10,697
+ Processing file: /home/eric/.openoffice.org2/user/config/standard.sod
+ SDReadSeqNo=5 fd=5
+
+=head2 OUTPUT
+
+ $VAR1 = { 105 => {
+ JobName => Full_plume.2006-06-06_17.22.23,
+ JobId => 105,
+ Files => 8,971,
+ Bytes => 194,484,132,
+ ...
+ },
+ ...
+ };
+
+=cut
+
+sub status
+{
+ my ($self, $conf) = @_ ;
+
+ if (defined $self->{cur_jobs}) {
+ return $self->{cur_jobs} ;
+ }
+
+ my $arg = {};
+ my $b = new Bconsole(pref => $conf);
+ my $ret = $b->send_cmd("st client=$self->{name}");
+ my @param;
+ my $jobid;
+
+ for my $r (split(/\n/, $ret)) {
+ chomp($r);
+ $r =~ s/(^\s+|\s+$)//g;
+ if ($r =~ /JobId (\d+) Job (\S+)/) {
+ if ($jobid) {
+ $arg->{$jobid} = { @param, JobId => $jobid } ;
+ }
+
+ $jobid = $1;
+ @param = ( JobName => $2 );
+
+ } elsif ($r =~ /=.+=/) {
+ push @param, split(/\s+|\s*=\s*/, $r) ;
+
+ } elsif ($r =~ /=/) { # one per line
+ push @param, split(/\s*=\s*/, $r) ;
+
+ } elsif ($r =~ /:/) { # one per line
+ push @param, split(/\s*:\s*/, $r, 2) ;
+ }
+ }
+
+ if ($jobid and @param) {
+ $arg->{$jobid} = { @param,
+ JobId => $jobid,
+ Client => $self->{name},
+ } ;
+ }
+
+ $self->{cur_jobs} = $arg ;
+
+ return $arg;
+}
+1;
+
+################################################################
+
+package Bweb::Autochanger;
+
+use base q/Bweb::Gui/;
+
+=head1 PACKAGE
+
+ Bweb::Autochanger - Object to manage Autochanger
+
+=head2 DESCRIPTION
+
+ this package will parse the mtx output and manage drives.
+
+=head2 USAGE
+
+ $auto = new Bweb::Autochanger(precmd => 'sudo');
+ or
+ $auto = new Bweb::Autochanger(precmd => 'ssh root@robot');
+
+ $auto->status();
+
+ $auto->slot_is_full(10);
+ $auto->transfer(10, 11);
+
+=cut
+
+sub new
+{
+ my ($class, %arg) = @_;
+
+ my $self = bless {
+ name => '', # autochanger name
+ label => {}, # where are volume { label1 => 40, label2 => drive0 }
+ drive => [], # drive use [ 'media1', 'empty', ..]
+ slot => [], # slot use [ undef, 'empty', 'empty', ..] no slot 0
+ io => [], # io slot number list [ 41, 42, 43...]
+ info => {slot => 0, # informations (slot, drive, io)
+ io => 0,
+ drive=> 0,
+ },
+ mtxcmd => '/usr/sbin/mtx',
+ debug => 0,
+ device => '/dev/changer',
+ precmd => '', # ssh command
+ bweb => undef, # link to bacula web object (use for display)
+ } ;
+
+ map { $self->{lc($_)} = $arg{$_} } keys %arg ;
+
+ return $self;
+}
+
+=head1 FUNCTION
+
+ status - parse the output of mtx status
+
+=head2 DESCRIPTION
+
+ this function will launch mtx status and parse the output. it will
+ give a perlish view of the autochanger content.
+
+ it uses ssh if the autochanger is on a other host.
+
+=cut
+
+sub status
+{
+ my ($self) = @_;
+ my @out = `$self->{precmd} $self->{mtxcmd} -f $self->{device} status` ;
+
+ # TODO : reset all infos
+ $self->{info}->{drive} = 0;
+ $self->{info}->{slot} = 0;
+ $self->{info}->{io} = 0;
+
+ #my @out = `cat /home/eric/travail/brestore/plume/mtx` ;
+
+#
+# Storage Changer /dev/changer:2 Drives, 45 Slots ( 5 Import/Export )
+#Data Transfer Element 0:Full (Storage Element 1 Loaded):VolumeTag = 000000
+#Data Transfer Element 1:Empty
+# Storage Element 1:Empty
+# Storage Element 2:Full :VolumeTag=000002
+# Storage Element 3:Empty
+# Storage Element 4:Full :VolumeTag=000004
+# Storage Element 5:Full :VolumeTag=000001
+# Storage Element 6:Full :VolumeTag=000003
+# Storage Element 7:Empty
+# Storage Element 41 IMPORT/EXPORT:Empty
+# Storage Element 41 IMPORT/EXPORT:Full :VolumeTag=000002
+#
+
+ for my $l (@out) {
+
+ # Storage Element 7:Empty
+ # Storage Element 2:Full :VolumeTag=000002
+ if ($l =~ /Storage Element (\d+):(Empty|Full)(\s+:VolumeTag=([\w\d]+))?/){
+
+ if ($2 eq 'Empty') {
+ $self->set_empty_slot($1);
+ } else {
+ $self->set_slot($1, $4);
+ }
+
+ } elsif ($l =~ /Data Transfer.+(\d+):(Full|Empty)(\s+.Storage Element (\d+) Loaded.(:VolumeTag = ([\w\d]+))?)?/) {
+
+ if ($2 eq 'Empty') {
+ $self->set_empty_drive($1);
+ } else {
+ $self->set_drive($1, $4, $6);
+ }
+
+ } elsif ($l =~ /Storage Element (\d+).+IMPORT\/EXPORT:(Empty|Full)( :VolumeTag=([\d\w]+))?/)
+ {
+ if ($2 eq 'Empty') {
+ $self->set_empty_io($1);
+ } else {
+ $self->set_io($1, $4);
+ }
+
+# Storage Changer /dev/changer:2 Drives, 30 Slots ( 1 Import/Export )
+
+ } elsif ($l =~ /Storage Changer .+:(\d+) Drives, (\d+) Slots/) {
+ $self->{info}->{drive} = $1;
+ $self->{info}->{slot} = $2;
+ if ($l =~ /(\d+)\s+Import/) {
+ $self->{info}->{io} = $1 ;
+ } else {
+ $self->{info}->{io} = 0;
+ }
+ }
+ }
+
+ $self->debug($self) ;
+}
+
+sub is_slot_loaded
+{
+ my ($self, $slot) = @_;
+
+ # no barcodes
+ if ($self->{slot}->[$slot] eq 'loaded') {
+ return 1;
+ }
+
+ my $label = $self->{slot}->[$slot] ;
+
+ return $self->is_media_loaded($label);
+}
+
+sub unload
+{
+ my ($self, $drive, $slot) = @_;
+
+ return 0 if (not defined $drive or $self->{drive}->[$drive] eq 'empty') ;
+ return 0 if ($self->slot_is_full($slot)) ;
+
+ my $out = `$self->{precmd} $self->{mtxcmd} -f $self->{device} unload $slot $drive 2>&1`;
+
+ if ($? == 0) {
+ my $content = $self->get_slot($slot);
+ print "content = $content<br/> $drive => $slot<br/>";
+ $self->set_empty_drive($drive);
+ $self->set_slot($slot, $content);
+ return 1;
+ } else {
+ $self->{error} = $out;
+ return 0;
+ }
+}
+
+# TODO: load/unload have to use mtx script from bacula
+sub load
+{
+ my ($self, $drive, $slot) = @_;
+
+ return 0 if (not defined $drive or $self->{drive}->[$drive] ne 'empty') ;
+ return 0 unless ($self->slot_is_full($slot)) ;
+
+ print "Loading drive $drive with slot $slot<br/>\n";
+ my $out = `$self->{precmd} $self->{mtxcmd} -f $self->{device} load $slot $drive 2>&1`;
+
+ if ($? == 0) {
+ my $content = $self->get_slot($slot);
+ print "content = $content<br/> $slot => $drive<br/>";
+ $self->set_drive($drive, $slot, $content);
+ return 1;
+ } else {
+ $self->{error} = $out;
+ print $out;
+ return 0;
+ }
+}
+
+sub is_media_loaded
+{
+ my ($self, $media) = @_;
+
+ unless ($self->{label}->{$media}) {
+ return 0;
+ }
+
+ if ($self->{label}->{$media} =~ /drive\d+/) {
+ return 1;
+ }
+
+ return 0;
+}
+
+sub have_io
+{
+ my ($self) = @_;
+ return (defined $self->{info}->{io} and $self->{info}->{io} > 0);
+}
+
+sub set_io
+{
+ my ($self, $slot, $tag) = @_;
+ $self->{slot}->[$slot] = $tag || 'full';
+ push @{ $self->{io} }, $slot;
+
+ if ($tag) {
+ $self->{label}->{$tag} = $slot;
+ }
+}
+
+sub set_empty_io
+{
+ my ($self, $slot) = @_;
+
+ push @{ $self->{io} }, $slot;
+
+ unless ($self->{slot}->[$slot]) { # can be loaded (parse before)
+ $self->{slot}->[$slot] = 'empty';
+ }
+}
+
+sub get_slot
+{
+ my ($self, $slot) = @_;
+ return $self->{slot}->[$slot];
+}
+
+sub set_slot
+{
+ my ($self, $slot, $tag) = @_;
+ $self->{slot}->[$slot] = $tag || 'full';
+
+ if ($tag) {
+ $self->{label}->{$tag} = $slot;
+ }
+}
+
+sub set_empty_slot
+{
+ my ($self, $slot) = @_;
+
+ unless ($self->{slot}->[$slot]) { # can be loaded (parse before)
+ $self->{slot}->[$slot] = 'empty';
+ }
+}
+
+sub set_empty_drive
+{
+ my ($self, $drive) = @_;
+ $self->{drive}->[$drive] = 'empty';
+}
+
+sub set_drive
+{
+ my ($self, $drive, $slot, $tag) = @_;
+ $self->{drive}->[$drive] = $tag || $slot;
+
+ $self->{slot}->[$slot] = $tag || 'loaded';
+
+ if ($tag) {
+ $self->{label}->{$tag} = "drive$drive";
+ }
+}
+
+sub slot_is_full
+{
+ my ($self, $slot) = @_;
+
+ # slot don't exists => full
+ if (not defined $self->{slot}->[$slot]) {
+ return 0 ;
+ }
+
+ if ($self->{slot}->[$slot] eq 'empty') {
+ return 0;
+ }
+ return 1; # vol, full, loaded
+}
+
+sub slot_get_first_free
+{
+ my ($self) = @_;
+ for (my $slot=1; $slot < $self->{info}->{slot}; $slot++) {
+ return $slot unless ($self->slot_is_full($slot));
+ }
+}
+
+sub io_get_first_free
+{
+ my ($self) = @_;
+
+ foreach my $slot (@{ $self->{io} }) {
+ return $slot unless ($self->slot_is_full($slot));
+ }
+ return 0;
+}
+
+sub get_media_slot
+{
+ my ($self, $media) = @_;
+
+ return $self->{label}->{$media} ;
+}
+
+sub have_media
+{
+ my ($self, $media) = @_;
+
+ return defined $self->{label}->{$media} ;
+}
+
+sub send_to_io
+{
+ my ($self, $slot) = @_;
+
+ unless ($self->slot_is_full($slot)) {
+ print "Autochanger $self->{name} slot $slot is empty\n";
+ return 1; # ok
+ }
+
+ # first, eject it
+ if ($self->is_slot_loaded($slot)) {
+ # bconsole->umount
+ # self->eject
+ print "Autochanger $self->{name} $slot is currently in use\n";
+ return 0;
+ }
+
+ # autochanger must have I/O
+ unless ($self->have_io()) {
+ print "Autochanger $self->{name} don't have I/O, you can take media yourself\n";
+ return 0;
+ }
+
+ my $dst = $self->io_get_first_free();
+
+ unless ($dst) {
+ print "Autochanger $self->{name} you must empty I/O first\n";
+ }
+
+ $self->transfer($slot, $dst);
+}
+
+sub transfer
+{
+ my ($self, $src, $dst) = @_ ;
+ if ($self->{debug}) {
+ print "<pre>$self->{precmd} $self->{mtxcmd} -f $self->{device} transfer $src $dst</pre>\n";
+ }
+ my $out = `$self->{precmd} $self->{mtxcmd} -f $self->{device} transfer $src $dst 2>&1`;
+
+ if ($? == 0) {
+ my $content = $self->get_slot($src);
+ $self->{slot}->[$src] = 'empty';
+ $self->set_slot($dst, $content);
+ return 1;
+ } else {
+ $self->{error} = $out;
+ return 0;
+ }
+}
+
+sub get_drive_name
+{
+ my ($self, $index) = @_;
+ return $self->{drive_name}->[$index];
+}
+
+# TODO : do a tapeinfo request to get informations
+sub tapeinfo
+{
+ my ($self) = @_;
+}
+
+sub clear_io
+{
+ my ($self) = @_;
+
+ for my $slot (@{$self->{io}})
+ {
+ if ($self->is_slot_loaded($slot)) {
+ print "$slot is currently loaded\n";
+ next;
+ }
+
+ if ($self->slot_is_full($slot))
+ {
+ my $free = $self->slot_get_first_free() ;
+ print "move $slot to $free :\n";
+
+ if ($free) {
+ if ($self->transfer($slot, $free)) {
+ print "<img src='/bweb/T.png' alt='ok'><br/>\n";
+ } else {
+ print "<img src='/bweb/E.png' alt='ok' title='$self->{error}'><br/>\n";
+ }
+
+ } else {
+ $self->{error} = "<img src='/bweb/E.png' alt='ok' title='E : Can t find free slot'><br/>\n";
+ }
+ }
+ }
+}
+
+# TODO : this is with mtx status output,
+# we can do an other function from bacula view (with StorageId)
+sub display_content
+{
+ my ($self) = @_;
+ my $bweb = $self->{bweb};
+
+ # $self->{label} => ('vol1', 'vol2', 'vol3', ..);
+ my $media_list = $bweb->dbh_join( keys %{ $self->{label} });
+
+ my $query="
+SELECT Media.VolumeName AS volumename,
+ Media.VolStatus AS volstatus,
+ Media.LastWritten AS lastwritten,
+ Media.VolBytes AS volbytes,
+ Media.MediaType AS mediatype,
+ 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
+FROM Media
+ INNER JOIN Pool USING (PoolId)
+
+WHERE Media.VolumeName IN ($media_list)
+";
+
+ my $all = $bweb->dbh_selectall_hashref($query, 'volumename') ;
+
+ # TODO : verify slot and bacula slot
+ my $param = [];
+ my @to_update;
+
+ for (my $slot=1; $slot <= $self->{info}->{slot} ; $slot++) {
+
+ if ($self->slot_is_full($slot)) {
+
+ my $vol = $self->{slot}->[$slot];
+ if (defined $all->{$vol}) { # TODO : autochanger without barcodes
+
+ my $bslot = $all->{$vol}->{slot} ;
+ my $inchanger = $all->{$vol}->{inchanger};
+
+ # if bacula slot or inchanger flag is bad, we display a message
+ if ($bslot != $slot or !$inchanger) {
+ push @to_update, $slot;
+ }
+
+ $all->{$vol}->{realslot} = $slot;
+
+ push @{ $param }, $all->{$vol};
+
+ } else { # empty or no label
+ push @{ $param }, {realslot => $slot,
+ volstatus => 'Unknow',
+ volumename => $self->{slot}->[$slot]} ;
+ }
+ } else { # empty
+ push @{ $param }, {realslot => $slot, volumename => 'empty'} ;
+ }
+ }
+
+ my $i=0; my $drives = [] ;
+ foreach my $d (@{ $self->{drive} }) {
+ $drives->[$i] = { index => $i,
+ load => $self->{drive}->[$i],
+ name => $self->{drive_name}->[$i],
+ };
+ $i++;
+ }
+
+ $bweb->display({ Name => $self->{name},
+ nb_drive => $self->{info}->{drive},
+ nb_io => $self->{info}->{io},
+ Drives => $drives,
+ Slots => $param,
+ Update => scalar(@to_update) },
+ 'ach_content.tpl');
+
+}
+
+1;
+
+
+################################################################
+
+package Bweb;
+
+use base q/Bweb::Gui/;
+
+=head1 PACKAGE
+
+ Bweb - main Bweb package
+
+=head2
+
+ this package is use to compute and display informations
+
+=cut
+
+use DBI;
+use POSIX qw/strftime/;
+
+our $config_file='/etc/bacula/bweb.conf';
+
+our $cur_id=0;
+
+=head1 VARIABLE
+
+ %sql_func - hash to make query mysql/postgresql compliant
+
+=cut
+
+our %sql_func = (
+ Pg => {
+ UNIX_TIMESTAMP => '',
+ FROM_UNIXTIME => '',
+ TO_SEC => " interval '1 second' * ",
+ SEC_TO_INT => "SEC_TO_INT",
+ SEC_TO_TIME => '',
+ MATCH => " ~ ",
+ STARTTIME_DAY => " date_trunc('day', Job.StartTime) ",
+ STARTTIME_HOUR => " date_trunc('hour', Job.StartTime) ",
+ STARTTIME_MONTH => " date_trunc('month', Job.StartTime) ",
+ STARTTIME_PHOUR=> " date_part('hour', Job.StartTime) ",
+ STARTTIME_PDAY => " date_part('day', Job.StartTime) ",
+ STARTTIME_PMONTH => " date_part('month', Job.StartTime) ",
+ },
+ mysql => {
+ UNIX_TIMESTAMP => 'UNIX_TIMESTAMP',
+ FROM_UNIXTIME => 'FROM_UNIXTIME',
+ SEC_TO_INT => '',
+ TO_SEC => '',
+ SEC_TO_TIME => 'SEC_TO_TIME',
+ MATCH => " REGEXP ",
+ STARTTIME_DAY => " DATE_FORMAT(StartTime, '%Y-%m-%d') ",
+ STARTTIME_HOUR => " DATE_FORMAT(StartTime, '%Y-%m-%d %H') ",
+ STARTTIME_MONTH => " DATE_FORMAT(StartTime, '%Y-%m') ",
+ STARTTIME_PHOUR=> " DATE_FORMAT(StartTime, '%H') ",
+ STARTTIME_PDAY => " DATE_FORMAT(StartTime, '%d') ",
+ STARTTIME_PMONTH => " DATE_FORMAT(StartTime, '%m') ",
+ },
+ );
+
+sub dbh_selectall_arrayref
+{
+ my ($self, $query) = @_;
+ $self->connect_db();
+ $self->debug($query);
+ return $self->{dbh}->selectall_arrayref($query);
+}
+
+sub dbh_join
+{
+ my ($self, @what) = @_;
+ return join(',', $self->dbh_quote(@what)) ;
+}
+
+sub dbh_quote
+{
+ my ($self, @what) = @_;
+
+ $self->connect_db();
+ if (wantarray) {
+ return map { $self->{dbh}->quote($_) } @what;
+ } else {
+ return $self->{dbh}->quote($what[0]) ;
+ }
+}
+
+sub dbh_do
+{
+ my ($self, $query) = @_ ;
+ $self->connect_db();
+ $self->debug($query);
+ return $self->{dbh}->do($query);
+}
+
+sub dbh_selectall_hashref
+{
+ my ($self, $query, $join) = @_;
+
+ $self->connect_db();
+ $self->debug($query);
+ return $self->{dbh}->selectall_hashref($query, $join) ;
+}
+
+sub dbh_selectrow_hashref
+{
+ my ($self, $query) = @_;
+
+ $self->connect_db();
+ $self->debug($query);
+ return $self->{dbh}->selectrow_hashref($query) ;
+}
+
+# display Mb/Gb/Kb
+sub human_size
+{
+ my @unit = qw(b Kb Mb Gb Tb);
+ my $val = shift || 0;
+ my $i=0;
+ my $format = '%i %s';
+ while ($val / 1024 > 1) {
+ $i++;
+ $val /= 1024;
+ }
+ $format = ($i>0)?'%0.1f %s':'%i %s';
+ return sprintf($format, $val, $unit[$i]);
+}
+
+# display Day, Hour, Year
+sub human_sec
+{
+ use integer;
+
+ my $val = shift;
+ $val /= 60; # sec -> min
+
+ if ($val / 60 <= 1) {
+ return "$val mins";
+ }
+
+ $val /= 60; # min -> hour
+ if ($val / 24 <= 1) {
+ return "$val hours";
+ }
+
+ $val /= 24; # hour -> day
+ if ($val / 365 < 2) {
+ return "$val days";
+ }
+
+ $val /= 365 ; # day -> year
+
+ return "$val years";
+}
+
+# get Day, Hour, Year
+sub from_human_sec
+{
+ use integer;
+
+ my $val = shift;
+ unless ($val =~ /^\s*(\d+)\s*(\w)\w*\s*$/) {
+ return 0;
+ }
+
+ my %times = ( m => 60,
+ h => 60*60,
+ d => 60*60*24,
+ m => 60*60*24*31,
+ y => 60*60*24*365,
+ );
+ my $mult = $times{$2} || 0;
+
+ return $1 * $mult;
+}
+
+
+sub connect_db
+{
+ my ($self) = @_;
+
+ unless ($self->{dbh}) {
+ $self->{dbh} = DBI->connect($self->{info}->{dbi},
+ $self->{info}->{user},
+ $self->{info}->{password});
+
+ $self->error("Can't connect to your database:\n$DBI::errstr\n")
+ unless ($self->{dbh});
+
+ $self->{dbh}->{FetchHashKeyName} = 'NAME_lc';
+
+ if ($self->{info}->{dbi} =~ /^dbi:Pg/i) {
+ $self->{dbh}->do("SET datestyle TO 'ISO, YMD'");
+ }
+ }
+}
+
+sub new
+{
+ my ($class, %arg) = @_;
+ my $self = bless {
+ dbh => undef, # connect_db();
+ info => {
+ dbi => '', # DBI:Pg:database=bacula;host=127.0.0.1
+ user => 'bacula',
+ password => 'test',
+ },
+ } ;
+
+ map { $self->{lc($_)} = $arg{$_} } keys %arg ;
+
+ if ($self->{info}->{dbi} =~ /DBI:(\w+):/i) {
+ $self->{sql} = $sql_func{$1};
+ }
+
+ $self->{debug} = $self->{info}->{debug};
+ $Bweb::Gui::template_dir = $self->{info}->{template_dir};
+
+ return $self;
+}
+
+sub display_begin
+{
+ my ($self) = @_;
+ $self->display($self->{info}, "begin.tpl");
+}
+
+sub display_end
+{
+ my ($self) = @_;
+ $self->display($self->{info}, "end.tpl");
+}
+
+sub display_clients
+{
+ my ($self) = @_;
+
+ my $where='';
+ my $arg = $self->get_form("client", "qre_client");
+
+ if ($arg->{qre_client}) {
+ $where = "WHERE Name $self->{sql}->{MATCH} $arg->{qre_client} ";
+ } elsif ($arg->{client}) {
+ $where = "WHERE Name = '$arg->{client}' ";
+ }
+
+ my $query = "
+SELECT Name AS name,
+ Uname AS uname,
+ AutoPrune AS autoprune,
+ FileRetention AS fileretention,
+ JobRetention AS jobretention
+FROM Client
+$where
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'name') ;
+
+ my $dsp = { ID => $cur_id++,
+ clients => [ values %$all] };
+
+ $self->display($dsp, "client_list.tpl") ;
+}
+
+sub get_limit
+{
+ my ($self, %arg) = @_;
+
+ my $limit = '';
+ my $label = '';
+
+ if ($arg{age}) {
+ $limit =
+ "AND $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
+ >
+ ( $self->{sql}->{UNIX_TIMESTAMP}(NOW())
+ -
+ $self->{sql}->{TO_SEC}($arg{age})
+ )" ;
+
+ $label = "last " . human_sec($arg{age});
+ }
+
+ if ($arg{groupby}) {
+ $limit .= " GROUP BY $arg{groupby} ";
+ }
+
+ if ($arg{order}) {
+ $limit .= " ORDER BY $arg{order} ";
+ }
+
+ if ($arg{limit}) {
+ $limit .= " LIMIT $arg{limit} ";
+ $label .= " limited to $arg{limit}";
+ }
+
+ if ($arg{offset}) {
+ $limit .= " OFFSET $arg{offset} ";
+ $label .= " with $arg{offset} offset ";
+ }
+
+ unless ($label) {
+ $label = 'no filter';
+ }
+
+ return ($limit, $label);
+}
+
+=head1 FUNCTION
+
+ $bweb->get_form(...) - Get useful stuff
+
+=head2 DESCRIPTION
+
+ This function get and check parameters against regexp.
+
+ If word begin with 'q', the return will be quoted or join quoted
+ if it's end with 's'.
+
+
+=head2 EXAMPLE
+
+ $bweb->get_form('jobid', 'qclient', 'qpools') ;
+
+ { jobid => 12,
+ qclient => 'plume-fd',
+ qpools => "'plume-fd', 'test-fd', '...'",
+ }
+
+=cut
+
+sub get_form
+{
+ my ($self, @what) = @_;
+ my %what = map { $_ => 1 } @what;
+ my %ret;
+
+ my %opt_i = (
+ limit => 100,
+ cost => 10,
+ offset => 0,
+ width => 640,
+ height => 480,
+ jobid => 0,
+ slot => 0,
+ drive => 0,
+ priority => 10,
+ age => 60*60*24*7,
+ days => 1,
+ maxvoljobs => 0,
+ maxvolbytes => 0,
+ maxvolfiles => 0,
+ );
+
+ my %opt_ss =( # string with space
+ job => 1,
+ storage => 1,
+ );
+ my %opt_s = ( # default to ''
+ ach => 1,
+ status => 1,
+ volstatus => 1,
+ inchanger => 1,
+ client => 1,
+ level => 1,
+ pool => 1,
+ media => 1,
+ ach => 1,
+ jobtype=> 1,
+ graph => 1,
+ gtype => 1,
+ type => 1,
+ poolrecycle => 1,
+ replace => 1,
+ );
+ my %opt_p = ( # option with path
+ fileset=> 1,
+ mtxcmd => 1,
+ precmd => 1,
+ device => 1,
+ where => 1,
+ );
+
+ my %opt_d = ( # option with date
+ voluseduration=> 1,
+ volretention => 1,
+ );
+
+ foreach my $i (@what) {
+ if (exists $opt_i{$i}) {# integer param
+ my $value = CGI::param($i) || $opt_i{$i} ;
+ if ($value =~ /^(\d+)$/) {
+ $ret{$i} = $1;
+ }
+ } elsif ($opt_s{$i}) { # simple string param
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^([\w\d\.-]+)$/) {
+ $ret{$i} = $1;
+ }
+ } elsif ($opt_ss{$i}) { # simple string param (with space)
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^([\w\d\.\-\s]+)$/) {
+ $ret{$i} = $1;
+ }
+ } elsif ($i =~ /^j(\w+)s$/) { # quote join args
+ my @value = grep { ! /^\s*$/ } CGI::param($1) ;
+ if (@value) {
+ $ret{$i} = $self->dbh_join(@value) ;
+ }
+
+ } elsif ($i =~ /^q(\w+[^s])$/) { # 'arg1'
+ my $value = CGI::param($1) ;
+ if ($value) {
+ $ret{$i} = $self->dbh_quote($value);
+ }
+
+ } elsif ($i =~ /^q(\w+)s$/) { #[ 'arg1', 'arg2']
+ $ret{$i} = [ map { { name => $self->dbh_quote($_) } }
+ grep { ! /^\s*$/ } CGI::param($1) ];
+ } elsif (exists $opt_p{$i}) {
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^([\w\d\.\/\s:\@\-]+)$/) {
+ $ret{$i} = $1;
+ }
+ } elsif (exists $opt_d{$i}) {
+ my $value = CGI::param($i) || '';
+ if ($value =~ /^\s*(\d+\s+\w+)$/) {
+ $ret{$i} = $1;
+ }
+ }
+ }
+
+ if ($what{slots}) {
+ foreach my $s (CGI::param('slot')) {
+ if ($s =~ /^(\d+)$/) {
+ push @{$ret{slots}}, $s;
+ }
+ }
+ }
+
+ if ($what{when}) {
+ my $when = CGI::param('when') || '';
+ if ($when =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})$/) {
+ $ret{when} = $1;
+ }
+ }
+
+ if ($what{db_clients}) {
+ my $query = "
+SELECT Client.Name as clientname
+FROM Client
+";
+
+ my $clients = $self->dbh_selectall_hashref($query, 'clientname');
+ $ret{db_clients} = [sort {$a->{clientname} cmp $b->{clientname} }
+ values %$clients] ;
+ }
+
+ if ($what{db_mediatypes}) {
+ my $query = "
+SELECT MediaType as mediatype
+FROM MediaType
+";
+
+ my $medias = $self->dbh_selectall_hashref($query, 'mediatype');
+ $ret{db_mediatypes} = [sort {$a->{mediatype} cmp $b->{mediatype} }
+ values %$medias] ;
+ }
+
+ if ($what{db_locations}) {
+ my $query = "
+SELECT Location as location, Cost as cost FROM Location
+";
+ my $loc = $self->dbh_selectall_hashref($query, 'location');
+ $ret{db_locations} = [ sort { $a->{location}
+ cmp
+ $b->{location}
+ } values %$loc ];
+ }
+
+ if ($what{db_pools}) {
+ my $query = "SELECT Name as name FROM Pool";
+
+ my $all = $self->dbh_selectall_hashref($query, 'name') ;
+ $ret{db_pools} = [ sort { $a->{name} cmp $b->{name} } values %$all ];
+ }
+
+ if ($what{db_filesets}) {
+ my $query = "
+SELECT FileSet.FileSet AS fileset
+FROM FileSet
+";
+
+ my $filesets = $self->dbh_selectall_hashref($query, 'fileset');
+
+ $ret{db_filesets} = [sort {lc($a->{fileset}) cmp lc($b->{fileset}) }
+ values %$filesets] ;
+ }
+
+ if ($what{db_jobnames}) {
+ my $query = "
+SELECT DISTINCT Job.Name AS jobname
+FROM Job
+";
+
+ my $jobnames = $self->dbh_selectall_hashref($query, 'jobname');
+
+ $ret{db_jobnames} = [sort {lc($a->{jobname}) cmp lc($b->{jobname}) }
+ values %$jobnames] ;
+ }
+
+ if ($what{db_devices}) {
+ my $query = "
+SELECT Device.Name AS name
+FROM Device
+";
+
+ my $devices = $self->dbh_selectall_hashref($query, 'name');
+
+ $ret{db_devices} = [sort {lc($a->{name}) cmp lc($b->{name}) }
+ values %$devices] ;
+ }
+
+ return \%ret;
+}
+
+sub display_graph
+{
+ my ($self) = @_;
+
+ my $fields = $self->get_form(qw/age level status clients filesets
+ graph gtype type
+ db_clients limit db_filesets width height
+ qclients qfilesets qjobnames db_jobnames/);
+
+
+ my $url = CGI::url(-full => 0,
+ -base => 0,
+ -query => 1);
+ $url =~ s/^.+?\?//; # http://path/to/bweb.pl?arg => arg
+
+# this organisation is to keep user choice between 2 click
+# TODO : fileset and client selection doesn't work
+
+ $self->display({
+ url => $url,
+ %$fields,
+ }, "graph.tpl")
+
+}
+
+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');
+
+ unless ($medias->{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})
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
+
+ # { 'vol1' => { [volumename => 'vol1', location => 'ici'],
+ # ..
+ # }
+ # }
+ return $all;
+}
+
+sub move_media
+{
+ my ($self) = @_ ;
+
+ my $medias = $self->get_selected_media_location();
+
+ unless ($medias) {
+ return ;
+ }
+
+ my $elt = $self->get_form('db_locations');
+
+ $self->display({ ID => $cur_id++,
+ %$elt, # db_locations
+ medias => [
+ sort { $a->{volumename} cmp $b->{volumename} } values %$medias
+ ],
+ },
+ "move_media.tpl");
+}
+
+sub help_extern
+{
+ my ($self) = @_ ;
+
+ my $elt = $self->get_form(qw/db_pools db_mediatypes db_locations/) ;
+ $self->debug($elt);
+ $self->display($elt, "help_extern.tpl");
+}
+
+sub help_extern_compute
+{
+ my ($self) = @_;
+
+ my $number = CGI::param('limit') || '' ;
+ unless ($number =~ /^(\d+)$/) {
+ return $self->error("Bad arg number : $number ");
+ }
+
+ my ($sql, undef) = $self->get_param('pools',
+ 'locations', 'mediatypes');
+
+ my $query = "
+SELECT Media.VolumeName AS volumename,
+ Media.VolStatus AS volstatus,
+ Media.LastWritten AS lastwritten,
+ Media.MediaType AS mediatype,
+ 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
+FROM Media
+ INNER JOIN Pool ON (Pool.PoolId = Media.PoolId)
+ LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
+
+WHERE Media.InChanger = 1
+ AND Media.VolStatus IN ('Disabled', 'Error', 'Full')
+ $sql
+ORDER BY expire DESC, recycle, Media.VolMounts DESC
+LIMIT $number
+" ;
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
+
+ $self->display({ Medias => [ values %$all ] },
+ "help_extern_compute.tpl");
+}
+
+sub help_intern
+{
+ my ($self) = @_ ;
+
+ my $param = $self->get_form(qw/db_locations db_pools db_mediatypes/) ;
+ $self->display($param, "help_intern.tpl");
+}
+
+sub help_intern_compute
+{
+ my ($self) = @_;
+
+ my $number = CGI::param('limit') || '' ;
+ unless ($number =~ /^(\d+)$/) {
+ return $self->error("Bad arg number : $number ");
+ }
+
+ my ($sql, undef) = $self->get_param('pools', 'locations', 'mediatypes');
+
+ if (CGI::param('expired')) {
+ $sql = "
+AND ( $self->{sql}->{UNIX_TIMESTAMP}(Media.LastWritten)
+ + $self->{sql}->{TO_SEC}(Media.VolRetention)
+ ) < NOW()
+ " . $sql ;
+ }
+
+ my $query = "
+SELECT Media.VolumeName AS volumename,
+ Media.VolStatus AS volstatus,
+ Media.LastWritten AS lastwritten,
+ 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
+FROM Media
+ INNER JOIN Pool ON (Pool.PoolId = Media.PoolId)
+ LEFT JOIN Location ON (Location.LocationId = Media.LocationId)
+
+WHERE Media.InChanger <> 1
+ AND Media.VolStatus IN ('Purged', 'Full', 'Append')
+ AND Media.Recycle = 1
+ $sql
+ORDER BY Media.VolUseDuration DESC, Media.VolMounts ASC, expire ASC
+LIMIT $number
+" ;
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
+
+ $self->display({ Medias => [ values %$all ] },
+ "help_intern_compute.tpl");
+
+}
+
+sub display_general
+{
+ my ($self, %arg) = @_ ;
+
+ my ($limit, $label) = $self->get_limit(%arg);
+
+ my $query = "
+SELECT
+ (SELECT count(Pool.PoolId) FROM Pool) AS nb_pool,
+ (SELECT count(Media.MediaId) FROM Media) AS nb_media,
+ (SELECT count(Job.JobId) FROM Job) AS nb_job,
+ (SELECT sum(VolBytes) FROM Media) AS nb_bytes,
+ (SELECT count(Job.JobId)
+ FROM Job
+ WHERE Job.JobStatus IN ('E','e','f','A')
+ $limit
+ ) AS nb_err,
+ (SELECT count(Client.ClientId) FROM Client) AS nb_client
+";
+
+ my $row = $self->dbh_selectrow_hashref($query) ;
+
+ $row->{nb_bytes} = human_size($row->{nb_bytes});
+
+ $row->{db_size} = '???';
+ $row->{label} = $label;
+
+ $self->display($row, "general.tpl");
+}
+
+sub get_param
+{
+ my ($self, @what) = @_ ;
+ my %elt = map { $_ => 1 } @what;
+ my %ret;
+
+ my $limit = '';
+
+ if ($elt{clients}) {
+ my @clients = grep { ! /^\s*$/ } CGI::param('client');
+ if (@clients) {
+ $ret{clients} = \@clients;
+ my $str = $self->dbh_join(@clients);
+ $limit .= "AND Client.Name IN ($str) ";
+ }
+ }
+
+ if ($elt{filesets}) {
+ my @filesets = grep { ! /^\s*$/ } CGI::param('fileset');
+ if (@filesets) {
+ $ret{filesets} = \@filesets;
+ my $str = $self->dbh_join(@filesets);
+ $limit .= "AND FileSet.FileSet IN ($str) ";
+ }
+ }
+
+ if ($elt{mediatypes}) {
+ my @medias = grep { ! /^\s*$/ } CGI::param('mediatype');
+ if (@medias) {
+ $ret{mediatypes} = \@medias;
+ my $str = $self->dbh_join(@medias);
+ $limit .= "AND Media.MediaType IN ($str) ";
+ }
+ }
+
+ if ($elt{client}) {
+ my $client = CGI::param('client');
+ $ret{client} = $client;
+ $client = $self->dbh_join($client);
+ $limit .= "AND Client.Name = $client ";
+ }
+
+ if ($elt{level}) {
+ my $level = CGI::param('level') || '';
+ if ($level =~ /^(\w)$/) {
+ $ret{level} = $1;
+ $limit .= "AND Job.Level = '$1' ";
+ }
+ }
+
+ if ($elt{jobid}) {
+ my $jobid = CGI::param('jobid') || '';
+
+ if ($jobid =~ /^(\d+)$/) {
+ $ret{jobid} = $1;
+ $limit .= "AND Job.JobId = '$1' ";
+ }
+ }
+
+ if ($elt{status}) {
+ my $status = CGI::param('status') || '';
+ if ($status =~ /^(\w)$/) {
+ $ret{status} = $1;
+ if ($1 eq 'f') {
+ $limit .= "AND Job.JobStatus IN ('f','E') ";
+ } else {
+ $limit .= "AND Job.JobStatus = '$1' ";
+ }
+ }
+ }
+
+ if ($elt{volstatus}) {
+ my $status = CGI::param('volstatus') || '';
+ if ($status =~ /^(\w+)$/) {
+ $ret{status} = $1;
+ $limit .= "AND Media.VolStatus = '$1' ";
+ }
+ }
+
+ if ($elt{locations}) {
+ my @location = grep { ! /^\s*$/ } CGI::param('location') ;
+ if (@location) {
+ $ret{locations} = \@location;
+ my $str = $self->dbh_join(@location);
+ $limit .= "AND Location.Location IN ($str) ";
+ }
+ }
+
+ if ($elt{pools}) {
+ my @pool = grep { ! /^\s*$/ } CGI::param('pool') ;
+ if (@pool) {
+ $ret{pools} = \@pool;
+ my $str = $self->dbh_join(@pool);
+ $limit .= "AND Pool.Name IN ($str) ";
+ }
+ }
+
+ if ($elt{location}) {
+ my $location = CGI::param('location') || '';
+ if ($location) {
+ $ret{location} = $location;
+ $location = $self->dbh_quote($location);
+ $limit .= "AND Location.Location = $location ";
+ }
+ }
+
+ if ($elt{pool}) {
+ my $pool = CGI::param('pool') || '';
+ if ($pool) {
+ $ret{pool} = $pool;
+ $pool = $self->dbh_quote($pool);
+ $limit .= "AND Pool.Name = $pool ";
+ }
+ }
+
+ if ($elt{jobtype}) {
+ my $jobtype = CGI::param('jobtype') || '';
+ if ($jobtype =~ /^(\w)$/) {
+ $ret{jobtype} = $1;
+ $limit .= "AND Job.Type = '$1' ";
+ }
+ }
+
+ return ($limit, %ret);
+}
+
+=head1
+
+ get last backup
+
+=cut
+
+sub display_job
+{
+ my ($self, %arg) = @_ ;
+
+ $arg{order} = ' Job.JobId DESC ';
+
+ my ($limit, $label) = $self->get_limit(%arg);
+ my ($where, undef) = $self->get_param('clients',
+ 'level',
+ 'filesets',
+ 'jobtype',
+ 'pools',
+ 'jobid',
+ 'status');
+
+ my $query="
+SELECT Job.JobId AS jobid,
+ Client.Name AS client,
+ FileSet.FileSet AS fileset,
+ Job.Name AS jobname,
+ Level AS level,
+ StartTime AS starttime,
+ Pool.Name AS poolname,
+ JobFiles AS jobfiles,
+ JobBytes AS jobbytes,
+ JobStatus AS jobstatus,
+ $self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
+ - $self->{sql}->{UNIX_TIMESTAMP}(StartTime))
+ AS duration,
+
+ JobErrors AS joberrors
+
+ FROM Client,
+ Job LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
+ LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
+ WHERE Client.ClientId=Job.ClientId
+ AND Job.JobStatus != 'R'
+ $where
+ $limit
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
+
+ $self->display({ Filter => $label,
+ ID => $cur_id++,
+ Jobs =>
+ [
+ sort { $a->{jobid} <=> $b->{jobid} }
+ values %$all
+ ],
+ },
+ "display_job.tpl");
+}
+
+# display job informations
+sub display_job_zoom
+{
+ my ($self, $jobid) = @_ ;
+
+ $jobid = $self->dbh_quote($jobid);
+
+ my $query="
+SELECT DISTINCT Job.JobId AS jobid,
+ Client.Name AS client,
+ Job.Name AS jobname,
+ FileSet.FileSet AS fileset,
+ Level AS level,
+ Pool.Name AS poolname,
+ StartTime AS starttime,
+ JobFiles AS jobfiles,
+ JobBytes AS jobbytes,
+ JobStatus AS jobstatus,
+ JobErrors AS joberrors,
+ $self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(EndTime)
+ - $self->{sql}->{UNIX_TIMESTAMP}(StartTime)) AS duration
+
+ FROM Client,
+ Job LEFT JOIN FileSet ON (Job.FileSetId = FileSet.FileSetId)
+ LEFT JOIN Pool ON (Job.PoolId = Pool.PoolId)
+ WHERE Client.ClientId=Job.ClientId
+ AND Job.JobId = $jobid
+";
+
+ my $row = $self->dbh_selectrow_hashref($query) ;
+
+ # display all volumes associate with this job
+ $query="
+SELECT Media.VolumeName as volumename
+FROM Job,Media,JobMedia
+WHERE Job.JobId = $jobid
+ AND JobMedia.JobId=Job.JobId
+ AND JobMedia.MediaId=Media.MediaId
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename');
+
+ $row->{volumes} = [ values %$all ] ;
+
+ $self->display($row, "display_job_zoom.tpl");
+}
+
+sub display_media
+{
+ my ($self) = @_ ;
+
+ my ($where, %elt) = $self->get_param('pools',
+ 'mediatypes',
+ 'volstatus',
+ 'locations');
+
+ my $arg = $self->get_form('jmedias', 'qre_media');
+
+ 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";
+ }
+
+ my $query="
+SELECT Media.VolumeName AS volumename,
+ Media.VolBytes AS volbytes,
+ Media.VolStatus AS volstatus,
+ Media.MediaType AS mediatype,
+ Media.InChanger AS online,
+ Media.LastWritten AS lastwritten,
+ 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
+FROM Pool, Media
+LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
+LEFT JOIN (SELECT avg(Media.VolBytes) AS size,
+ Media.MediaType AS MediaType
+ FROM Media
+ WHERE Media.VolStatus = 'Full'
+ GROUP BY Media.MediaType
+ ) AS media_avg_size ON (Media.MediaType = media_avg_size.MediaType)
+
+WHERE Media.PoolId=Pool.PoolId
+$where
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
+
+ $self->display({ ID => $cur_id++,
+ Pool => $elt{pool},
+ Location => $elt{location},
+ Medias => [ values %$all ]
+ },
+ "display_media.tpl");
+}
+
+sub display_medias
+{
+ my ($self) = @_ ;
+
+ my $pool = $self->get_form('db_pools');
+
+ foreach my $name (@{ $pool->{db_pools} }) {
+ CGI::param('pool', $name->{name});
+ $self->display_media();
+ }
+}
+
+sub display_media_zoom
+{
+ my ($self) = @_ ;
+
+ my $medias = $self->get_form('jmedias');
+
+ unless ($medias->{jmedias}) {
+ return $self->error("Can't get media selection");
+ }
+
+ my $query="
+SELECT InChanger AS online,
+ VolBytes AS nb_bytes,
+ VolumeName AS volumename,
+ VolStatus AS volstatus,
+ VolMounts AS nb_mounts,
+ Media.VolUseDuration AS voluseduration,
+ Media.MaxVolJobs AS maxvoljobs,
+ Media.MaxVolFiles AS maxvolfiles,
+ Media.MaxVolBytes AS maxvolbytes,
+ VolErrors AS nb_errors,
+ Pool.Name AS poolname,
+ Location.Location AS location,
+ Media.Recycle AS recycle,
+ Media.VolRetention AS volretention,
+ Media.LastWritten AS lastwritten,
+ Media.VolReadTime/1000000 AS volreadtime,
+ 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
+ FROM Pool,
+ Media LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
+ WHERE Pool.PoolId = Media.PoolId
+ AND VolumeName IN ($medias->{jmedias})
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename') ;
+
+ foreach my $media (values %$all) {
+ my $mq = $self->dbh_quote($media->{volumename});
+
+ $query = "
+SELECT DISTINCT Job.JobId AS jobid,
+ Job.Name AS name,
+ Job.StartTime AS starttime,
+ Job.Type AS type,
+ Job.Level AS level,
+ Job.JobFiles AS files,
+ Job.JobBytes AS bytes,
+ Job.jobstatus AS status
+ FROM Media,JobMedia,Job
+ WHERE Media.VolumeName=$mq
+ AND Media.MediaId=JobMedia.MediaId
+ AND JobMedia.JobId=Job.JobId
+";
+
+ my $jobs = $self->dbh_selectall_hashref($query, 'jobid') ;
+
+ $query = "
+SELECT LocationLog.Date AS date,
+ Location.Location AS location,
+ LocationLog.Comment AS comment
+ FROM Media,LocationLog INNER JOIN Location ON (LocationLog.LocationId = Location.LocationId)
+ WHERE Media.MediaId = LocationLog.MediaId
+ AND Media.VolumeName = $mq
+";
+
+ my $logtxt = '';
+ my $log = $self->dbh_selectall_arrayref($query) ;
+ if ($log) {
+ $logtxt = join("\n", map { ($_->[0] . ' ' . $_->[1] . ' ' . $_->[2])} @$log ) ;
+ }
+
+ $self->display({ jobs => [ values %$jobs ],
+ LocationLog => $logtxt,
+ %$media },
+ "display_media_zoom.tpl");
+ }
+}
+
+sub location_edit
+{
+ my ($self) = @_ ;
+
+ my $loc = $self->get_form('qlocation');
+ unless ($loc->{qlocation}) {
+ return $self->error("Can't get location");
+ }
+
+ my $query = "
+SELECT Location.Location AS location,
+ Location.Cost AS cost,
+ Location.Enabled AS enabled
+FROM Location
+WHERE Location.Location = $loc->{qlocation}
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+
+ $self->display({ ID => $cur_id++,
+ %$row }, "location_edit.tpl") ;
+
+}
+
+sub location_save
+{
+ my ($self) = @_ ;
+
+ my $arg = $self->get_form(qw/qlocation qnewlocation cost/) ;
+ unless ($arg->{qlocation}) {
+ return $self->error("Can't get location");
+ }
+ unless ($arg->{qnewlocation}) {
+ return $self->error("Can't get new location name");
+ }
+ unless ($arg->{cost}) {
+ return $self->error("Can't get new cost");
+ }
+
+ my $enabled = CGI::param('enabled') || '';
+ $enabled = $enabled?1:0;
+
+ my $query = "
+UPDATE Location SET Cost = $arg->{cost},
+ Location = $arg->{qnewlocation},
+ Enabled = $enabled
+WHERE Location.Location = $arg->{qlocation}
+";
+
+ $self->dbh_do($query);
+
+ $self->display_location();
+}
+
+sub location_del
+{
+ my ($self) = @_ ;
+ my $arg = $self->get_form(qw/qlocation/) ;
+
+ unless ($arg->{qlocation}) {
+ return $self->error("Can't get location");
+ }
+
+ my $query = "
+SELECT count(Media.MediaId) AS nb
+ FROM Media INNER JOIN Location USING (LocationID)
+WHERE Location = $arg->{qlocation}
+";
+
+ my $res = $self->dbh_selectrow_hashref($query);
+
+ if ($res->{nb}) {
+ return $self->error("Sorry, the location must be empty");
+ }
+
+ $query = "
+DELETE FROM Location WHERE Location = $arg->{qlocation} LIMIT 1
+";
+
+ $self->dbh_do($query);
+
+ $self->display_location();
+}
+
+
+sub location_add
+{
+ my ($self) = @_ ;
+ my $arg = $self->get_form(qw/qlocation cost/) ;
+
+ unless ($arg->{qlocation}) {
+ $self->display({}, "location_add.tpl");
+ return 1;
+ }
+ unless ($arg->{cost}) {
+ return $self->error("Can't get new cost");
+ }
+
+ my $enabled = CGI::param('enabled') || '';
+ $enabled = $enabled?1:0;
+
+ my $query = "
+INSERT INTO Location (Location, Cost, Enabled)
+ VALUES ($arg->{qlocation}, $arg->{cost}, $enabled)
+";
+
+ $self->dbh_do($query);
+
+ $self->display_location();
+}
+
+sub display_location
+{
+ my ($self) = @_ ;
+
+ my $query = "
+SELECT Location.Location AS location,
+ Location.Cost AS cost,
+ Location.Enabled AS enabled,
+ (SELECT count(Media.MediaId)
+ FROM Media
+ WHERE Media.LocationId = Location.LocationId
+ ) AS volnum
+FROM Location
+";
+
+ my $location = $self->dbh_selectall_hashref($query, 'location');
+
+ $self->display({ ID => $cur_id++,
+ Locations => [ values %$location ] },
+ "display_location.tpl");
+}
+
+sub update_location
+{
+ my ($self) = @_ ;
+
+ my $medias = $self->get_selected_media_location();
+ unless ($medias) {
+ return ;
+ }
+
+ my $arg = $self->get_form('db_locations', 'qnewlocation');
+
+ $self->display({ email => $self->{info}->{email_media},
+ %$arg,
+ medias => [ values %$medias ],
+ },
+ "update_location.tpl");
+}
+
+sub get_media_max_size
+{
+ my ($self, $type) = @_;
+ my $query =
+"SELECT avg(VolBytes) AS size
+ FROM Media
+ WHERE Media.VolStatus = 'Full'
+ AND Media.MediaType = '$type'
+";
+
+ my $res = $self->selectrow_hashref($query);
+
+ if ($res) {
+ return $res->{size};
+ } else {
+ return 0;
+ }
+}
+
+sub update_media
+{
+ my ($self) = @_ ;
+
+ my $media = $self->get_form('qmedia');
+
+ unless ($media->{qmedia}) {
+ return $self->error("Can't get media");
+ }
+
+ my $query = "
+SELECT Media.Slot AS slot,
+ PoolMedia.Name AS poolname,
+ Media.VolStatus AS volstatus,
+ Media.InChanger AS inchanger,
+ Location.Location AS location,
+ Media.VolumeName AS volumename,
+ Media.MaxVolBytes AS maxvolbytes,
+ Media.MaxVolJobs AS maxvoljobs,
+ Media.MaxVolFiles AS maxvolfiles,
+ Media.VolUseDuration AS voluseduration,
+ Media.VolRetention AS volretention,
+ Media.Comment AS comment,
+ PoolRecycle.Name AS poolrecycle
+
+FROM Media INNER JOIN Pool AS PoolMedia ON (Media.PoolId = PoolMedia.PoolId)
+ LEFT JOIN Pool AS PoolRecycle ON (Media.RecyclePoolId = PoolRecycle.PoolId)
+ LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
+
+WHERE Media.VolumeName = $media->{qmedia}
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+ $row->{volretention} = human_sec($row->{volretention});
+ $row->{voluseduration} = human_sec($row->{voluseduration});
+
+ my $elt = $self->get_form(qw/db_pools db_locations/);
+
+ $self->display({
+ %$elt,
+ %$row,
+ }, "update_media.tpl");
+}
+
+sub save_location
+{
+ my ($self) = @_ ;
+
+ my $arg = $self->get_form('jmedias', 'qnewlocation') ;
+
+ unless ($arg->{jmedias}) {
+ return $self->error("Can't get selected media");
+ }
+
+ unless ($arg->{qnewlocation}) {
+ return $self->error("Can't get new location");
+ }
+
+ my $query = "
+ UPDATE Media
+ SET LocationId = (SELECT LocationId
+ FROM Location
+ WHERE Location = $arg->{qnewlocation})
+ WHERE Media.VolumeName IN ($arg->{jmedias})
+";
+
+ my $nb = $self->dbh_do($query);
+
+ print "$nb media updated, you may have to update your autochanger.";
+
+ $self->display_media();
+}
+
+sub change_location
+{
+ my ($self) = @_ ;
+
+ my $medias = $self->get_selected_media_location();
+ unless ($medias) {
+ return $self->error("Can't get media selection");
+ }
+ my $newloc = CGI::param('newlocation');
+
+ my $user = CGI::param('user') || 'unknow';
+ my $comm = CGI::param('comment') || '';
+ $comm = $self->dbh_quote("$user: $comm");
+
+ my $query;
+
+ foreach my $media (keys %$medias) {
+ $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')
+ )
+";
+ $self->dbh_do($query);
+ $self->debug($query);
+ }
+
+ my $q = new CGI;
+ $q->param('action', 'update_location');
+ my $url = $q->url(-full => 1, -query=>1);
+
+ $self->display({ email => $self->{info}->{email_media},
+ url => $url,
+ newlocation => $newloc,
+ # [ { volumename => 'vol1' }, { volumename => 'vol2'\81Â },..]
+ medias => [ values %$medias ],
+ },
+ "change_location.tpl");
+
+}
+
+sub display_client_stats
+{
+ my ($self, %arg) = @_ ;
+
+ my $client = $self->dbh_quote($arg{clientname});
+ 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.Name AS clientname
+FROM Job INNER JOIN Client USING (ClientId)
+WHERE
+ Client.Name = $client
+ $limit
+GROUP BY Client.Name
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+
+ $row->{ID} = $cur_id++;
+ $row->{label} = $label;
+
+ $self->display($row, "display_client_stats.tpl");
+}
+
+# poolname can be undef
+sub display_pool
+{
+ my ($self, $poolname) = @_ ;
+ my $whereA = '';
+ my $whereW = '';
+
+ my $arg = $self->get_form('jmediatypes', 'qmediatypes');
+ if ($arg->{jmediatypes}) {
+ $whereW = "WHERE MediaType IN ($arg->{jmediatypes}) ";
+ $whereA = "AND MediaType IN ($arg->{jmediatypes}) ";
+ }
+
+# TODO : afficher les tailles et les dates
+
+ my $query = "
+SELECT subq.volmax AS volmax,
+ subq.volnum AS volnum,
+ subq.voltotal AS voltotal,
+ Pool.Name AS name,
+ Pool.Recycle AS recycle,
+ Pool.VolRetention AS volretention,
+ Pool.VolUseDuration AS voluseduration,
+ Pool.MaxVolJobs AS maxvoljobs,
+ Pool.MaxVolFiles AS maxvolfiles,
+ Pool.MaxVolBytes AS maxvolbytes,
+ subq.PoolId AS PoolId
+FROM
+ (
+ SELECT COALESCE(media_avg_size.volavg,0) * count(Media.MediaId) AS volmax,
+ count(Media.MediaId) AS volnum,
+ sum(Media.VolBytes) AS voltotal,
+ Media.PoolId AS PoolId,
+ Media.MediaType AS MediaType
+ FROM Media
+ LEFT JOIN (SELECT avg(Media.VolBytes) AS volavg,
+ Media.MediaType AS MediaType
+ FROM Media
+ WHERE Media.VolStatus = 'Full'
+ GROUP BY Media.MediaType
+ ) AS media_avg_size ON (Media.MediaType = media_avg_size.MediaType)
+ GROUP BY Media.MediaType, Media.PoolId, media_avg_size.volavg
+ ) AS subq
+LEFT JOIN Pool ON (Pool.PoolId = subq.PoolId)
+$whereW
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'name') ;
+
+ $query = "
+SELECT Pool.Name AS name,
+ sum(VolBytes) AS size
+FROM Media JOIN Pool ON (Media.PoolId = Pool.PoolId)
+WHERE Media.VolStatus IN ('Recycled', 'Purged')
+ $whereA
+GROUP BY Pool.Name;
+";
+ my $empty = $self->dbh_selectall_hashref($query, 'name');
+
+ foreach my $p (values %$all) {
+ if ($p->{volmax} > 0) { # mysql returns 0.0000
+ # we remove Recycled/Purged media from pool usage
+ if (defined $empty->{$p->{name}}) {
+ $p->{voltotal} -= $empty->{$p->{name}}->{size};
+ }
+ $p->{poolusage} = sprintf('%.2f', $p->{voltotal} * 100/ $p->{volmax}) ;
+ } else {
+ $p->{poolusage} = 0;
+ }
+
+ $query = "
+ SELECT VolStatus AS volstatus, count(MediaId) AS nb
+ FROM Media
+ WHERE PoolId=$p->{poolid}
+ $whereA
+GROUP BY VolStatus
+";
+ my $content = $self->dbh_selectall_hashref($query, 'volstatus');
+ foreach my $t (values %$content) {
+ $p->{"nb_" . $t->{volstatus}} = $t->{nb} ;
+ }
+ }
+
+ $self->debug($all);
+ $self->display({ ID => $cur_id++,
+ MediaType => $arg->{qmediatypes}, # [ { name => type1 } , { name => type2 } ]
+ Pools => [ values %$all ]},
+ "display_pool.tpl");
+}
+
+sub display_running_job
+{
+ my ($self) = @_;
+
+ my $arg = $self->get_form('client', 'jobid');
+
+ if (!$arg->{client} and $arg->{jobid}) {
+
+ my $query = "
+SELECT Client.Name AS name
+FROM Job INNER JOIN Client USING (ClientId)
+WHERE Job.JobId = $arg->{jobid}
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+
+ if ($row) {
+ $arg->{client} = $row->{name};
+ CGI::param('client', $arg->{client});
+ }
+ }
+
+ if ($arg->{client}) {
+ my $cli = new Bweb::Client(name => $arg->{client});
+ $cli->display_running_job($self->{info}, $arg->{jobid});
+ if ($arg->{jobid}) {
+ $self->get_job_log();
+ }
+ } else {
+ $self->error("Can't get client or jobid");
+ }
+}
+
+sub display_running_jobs
+{
+ my ($self, $display_action) = @_;
+
+ my $query = "
+SELECT Job.JobId AS jobid,
+ Job.Name AS jobname,
+ Job.Level AS level,
+ Job.StartTime AS starttime,
+ Job.JobFiles AS jobfiles,
+ Job.JobBytes AS jobbytes,
+ Job.JobStatus AS jobstatus,
+$self->{sql}->{SEC_TO_TIME}( $self->{sql}->{UNIX_TIMESTAMP}(NOW())
+ - $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')
+";
+ my $all = $self->dbh_selectall_hashref($query, 'jobid') ;
+
+ $self->display({ ID => $cur_id++,
+ display_action => $display_action,
+ Jobs => [ values %$all ]},
+ "running_job.tpl") ;
+}
+
+# return the autochanger list to update
+sub eject_media
+{
+ my ($self) = @_;
+ my %ret;
+ my $arg = $self->get_form('jmedias');
+
+ unless ($arg->{jmedias}) {
+ return $self->error("Can't get media selection");
+ }
+
+ my $query = "
+SELECT Media.VolumeName AS volumename,
+ Storage.Name AS storage,
+ Location.Location AS location,
+ Media.Slot AS slot
+FROM Media INNER JOIN Storage ON (Media.StorageId = Storage.StorageId)
+ LEFT JOIN Location ON (Media.LocationId = Location.LocationId)
+WHERE Media.VolumeName IN ($arg->{jmedias})
+ AND Media.InChanger = 1
+";
+
+ my $all = $self->dbh_selectall_hashref($query, 'volumename');
+
+ foreach my $vol (values %$all) {
+ my $a = $self->ach_get($vol->{location});
+ next unless ($a) ;
+ $ret{$vol->{location}} = 1;
+
+ unless ($a->{have_status}) {
+ $a->status();
+ $a->{have_status} = 1;
+ }
+
+ print "eject $vol->{volumename} from $vol->{storage} : ";
+ if ($a->send_to_io($vol->{slot})) {
+ print "<img src='/bweb/T.png' alt='ok'><br/>";
+ } else {
+ print "<img src='/bweb/E.png' alt='err'><br/>";
+ }
+ }
+ return keys %ret;
+}
+
+sub move_email
+{
+ my ($self) = @_;
+
+ my ($to, $subject, $content) = (CGI::param('email'),
+ CGI::param('subject'),
+ CGI::param('content'));
+ $to =~ s/[^\w\d\.\@<>,]//;
+ $subject =~ s/[^\w\d\.\[\]]/ /;
+
+ open(MAIL, "|mail -s '$subject' '$to'") ;
+ print MAIL $content;
+ close(MAIL);
+
+ print "Mail sent";
+}
+
+sub restore
+{
+ my ($self) = @_;
+
+ my $arg = $self->get_form('jobid', 'client');
+
+ print CGI::header('text/brestore');
+ print "jobid=$arg->{jobid}\n" if ($arg->{jobid});
+ print "client=$arg->{client}\n" if ($arg->{client});
+ print "\n\nYou have to assign this mime type with /usr/bin/brestore.pl\n";
+ print "\n";
+}
+
+# TODO : move this to Bweb::Autochanger ?
+# TODO : make this internal to not eject tape ?
+use Bconsole;
+
+
+sub ach_get
+{
+ my ($self, $name) = @_;
+
+ unless ($name) {
+ return $self->error("Can't get your autochanger name ach");
+ }
+
+ unless ($self->{info}->{ach_list}) {
+ return $self->error("Could not find any autochanger");
+ }
+
+ my $a = $self->{info}->{ach_list}->{$name};
+
+ unless ($a) {
+ $self->error("Can't get your autochanger $name from your ach_list");
+ return undef;
+ }
+
+ $a->{bweb} = $self;
+ $a->{debug} = $self->{debug};
+
+ return $a;
+}
+
+sub ach_register
+{
+ my ($self, $ach) = @_;
+
+ $self->{info}->{ach_list}->{$ach->{name}} = $ach;
+
+ $self->{info}->save();
+
+ return 1;
+}
+
+sub ach_edit
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('ach');
+ if (!$arg->{ach}
+ or !$self->{info}->{ach_list}
+ or !$self->{info}->{ach_list}->{$arg->{ach}})
+ {
+ return $self->error("Can't get autochanger name");
+ }
+
+ my $ach = $self->{info}->{ach_list}->{$arg->{ach}};
+
+ my $i=0;
+ $ach->{drives} =
+ [ map { { name => $_, index => $i++ } } @{$ach->{drive_name}} ] ;
+
+ my $b = $self->get_bconsole();
+
+ my @storages = $b->list_storage() ;
+
+ $ach->{devices} = [ map { { name => $_ } } @storages ];
+
+ $self->display($ach, "ach_add.tpl");
+ delete $ach->{drives};
+ delete $ach->{devices};
+ return 1;
+}
+
+sub ach_del
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('ach');
+
+ if (!$arg->{ach}
+ or !$self->{info}->{ach_list}
+ or !$self->{info}->{ach_list}->{$arg->{ach}})
+ {
+ return $self->error("Can't get autochanger name");
+ }
+
+ delete $self->{info}->{ach_list}->{$arg->{ach}} ;
+
+ $self->{info}->save();
+ $self->{info}->view();
+}
+
+sub ach_add
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('ach', 'mtxcmd', 'device', 'precmd');
+
+ my $b = $self->get_bconsole();
+ my @storages = $b->list_storage() ;
+
+ unless ($arg->{ach}) {
+ $arg->{devices} = [ map { { name => $_ } } @storages ];
+ return $self->display($arg, "ach_add.tpl");
+ }
+
+ my @drives ;
+ foreach my $drive (CGI::param('drives'))
+ {
+ unless (grep(/^$drive$/,@storages)) {
+ return $self->error("Can't find $drive in storage list");
+ }
+
+ my $index = CGI::param("index_$drive");
+ unless (defined $index and $index =~ /^(\d+)$/) {
+ return $self->error("Can't get $drive index");
+ }
+
+ $drives[$index] = $drive;
+ }
+
+ unless (@drives) {
+ return $self->error("Can't get drives from Autochanger");
+ }
+
+ my $a = new Bweb::Autochanger(name => $arg->{ach},
+ precmd => $arg->{precmd},
+ drive_name => \@drives,
+ device => $arg->{device},
+ mtxcmd => $arg->{mtxcmd});
+
+ $self->ach_register($a) ;
+
+ $self->{info}->view();
+}
+
+sub delete
+{
+ my ($self) = @_;
+ my $arg = $self->get_form('jobid');
+
+ if ($arg->{jobid}) {
+ my $b = $self->get_bconsole();
+ my $ret = $b->send_cmd("delete jobid=\"$arg->{jobid}\"");
+
+ $self->display({
+ content => $ret,
+ title => "Delete a job ",
+ name => "delete jobid=$arg->{jobid}",
+ }, "command.tpl");
+ }
+}
+
+sub do_update_media
+{
+ my ($self) = @_ ;
+
+ my $arg = $self->get_form(qw/media volstatus inchanger pool
+ slot volretention voluseduration
+ maxvoljobs maxvolfiles maxvolbytes
+ qcomment poolrecycle
+ /);
+
+ unless ($arg->{media}) {
+ return $self->error("Can't find media selection");
+ }
+
+ my $update = "update volume=$arg->{media} ";
+
+ if ($arg->{volstatus}) {
+ $update .= " volstatus=$arg->{volstatus} ";
+ }
+
+ if ($arg->{inchanger}) {
+ $update .= " inchanger=yes " ;
+ if ($arg->{slot}) {
+ $update .= " slot=$arg->{slot} ";
+ }
+ } else {
+ $update .= " slot=0 inchanger=no ";
+ }
+
+ if ($arg->{pool}) {
+ $update .= " pool=$arg->{pool} " ;
+ }
+
+ if (defined $arg->{volretention}) {
+ $update .= " volretention=\"$arg->{volretention}\" " ;
+ }
+
+ if (defined $arg->{voluseduration}) {
+ $update .= " voluse=\"$arg->{voluseduration}\" " ;
+ }
+
+ if (defined $arg->{maxvoljobs}) {
+ $update .= " maxvoljobs=$arg->{maxvoljobs} " ;
+ }
+
+ if (defined $arg->{maxvolfiles}) {
+ $update .= " maxvolfiles=$arg->{maxvolfiles} " ;
+ }
+
+ if (defined $arg->{maxvolbytes}) {
+ $update .= " maxvolbytes=$arg->{maxvolbytes} " ;
+ }
+
+ my $b = $self->get_bconsole();
+
+ $self->display({
+ content => $b->send_cmd($update),
+ title => "Update a volume ",
+ name => $update,
+ }, "command.tpl");
+
+
+ my @q;
+ my $media = $self->dbh_quote($arg->{media});
+
+ my $loc = CGI::param('location') || '';
+ if ($loc) {
+ $loc = $self->dbh_quote($loc); # is checked by db
+ push @q, "LocationId=(SELECT LocationId FROM Location WHERE Location=$loc)";
+ }
+ if ($arg->{poolrecycle}) {
+ push @q, "RecyclePoolId=(SELECT PoolId FROM Pool WHERE Name='$arg->{poolrecycle}')";
+ }
+ if (!$arg->{qcomment}) {
+ $arg->{qcomment} = "''";
+ }
+ push @q, "Comment=$arg->{qcomment}";
+
+
+ my $query = "
+UPDATE Media
+ SET " . join (',', @q) . "
+ WHERE Media.VolumeName = $media
+";
+ $self->dbh_do($query);
+
+ $self->update_media();
+}
+
+sub update_slots
+{
+ my ($self) = @_;
+
+ my $ach = CGI::param('ach') ;
+ $ach = $self->ach_get($ach);
+ unless ($ach) {
+ return $self->error("Bad autochanger name");
+ }
+
+ print "<pre>";
+ my $b = new Bconsole(pref => $self->{info},timeout => 60,log_stdout => 1);
+ $b->update_slots($ach->{name});
+ print "</pre>\n"
+}
+
+sub get_job_log
+{
+ my ($self) = @_;
+
+ my $arg = $self->get_form('jobid');
+ unless ($arg->{jobid}) {
+ return $self->error("Can't get jobid");
+ }
+
+ my $t = CGI::param('time') || '';
+
+ my $query = "
+SELECT Job.Name as name, Client.Name as clientname
+ FROM Job INNER JOIN Client ON (Job.ClientId = Client.ClientId)
+ WHERE JobId = $arg->{jobid}
+";
+
+ my $row = $self->dbh_selectrow_hashref($query);
+
+ unless ($row) {
+ return $self->error("Can't find $arg->{jobid} in catalog");
+ }
+
+ $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})
+ )
+ ORDER BY LogId;
+";
+
+ my $log = $self->dbh_selectall_arrayref($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,
+ jobid => $arg->{jobid},
+ name => $row->{name},
+ client => $row->{clientname},
+ }, 'display_log.tpl');
+}
+
+
+sub label_barcodes
+{
+ my ($self) = @_ ;
+
+ my $arg = $self->get_form('ach', 'slots', 'drive');
+
+ unless ($arg->{ach}) {
+ return $self->error("Can't find autochanger name");
+ }
+
+ my $a = $self->ach_get($arg->{ach});
+ unless ($a) {
+ return $self->error("Can't find autochanger name in configuration");
+ }
+
+ my $storage = $a->get_drive_name($arg->{drive});
+ unless ($storage) {
+ return $self->error("Can't get your drive name");
+ }
+
+ my $slots = '';
+ my $t = 300 ;
+ if ($arg->{slots}) {
+ $slots = join(",", @{ $arg->{slots} });
+ $t += 60*scalar( @{ $arg->{slots} }) ;
+ }
+
+ my $b = new Bconsole(pref => $self->{info}, timeout => $t,log_stdout => 1);
+ print "<h1>This command can take long time, be patient...</h1>";
+ print "<pre>" ;
+ $b->label_barcodes(storage => $storage,
+ drive => $arg->{drive},
+ pool => 'Scratch',
+ slots => $slots) ;
+ $b->close();
+ print "</pre>";
+
+ $self->dbh_do("
+ UPDATE Media
+ SET LocationId = (SELECT LocationId
+ FROM Location
+ WHERE Location = '$arg->{ach}'),
+
+ RecyclePoolId = PoolId
+
+ WHERE Media.PoolId = (SELECT PoolId
+ FROM Pool
+ WHERE Name = 'Scratch')
+ AND (LocationId = 0 OR LocationId IS NULL)
+");
+
+}
+
+sub purge
+{
+ my ($self) = @_;
+
+ my @volume = CGI::param('media');
+
+ unless (@volume) {
+ return $self->error("Can't get media selection");
+ }
+
+ 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");
+ $b->close();
+}
+
+sub prune
+{
+ my ($self) = @_;
+
+ my @volume = CGI::param('media');
+ unless (@volume) {
+ return $self->error("Can't get media selection");
+ }
+
+ 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");
+
+ $b->close();
+}
+
+sub cancel_job
+{
+ my ($self) = @_;
+
+ my $arg = $self->get_form('jobid');
+ unless ($arg->{jobid}) {
+ return $self->error("Can't get jobid");
+ }
+
+ my $b = $self->get_bconsole();
+ $self->display({
+ content => $b->cancel($arg->{jobid}),
+ title => "Cancel job",
+ name => "cancel jobid=$arg->{jobid}",
+ }, "command.tpl");
+}
+
+sub fileset_view
+{
+ # Warning, we display current fileset
+ my ($self) = @_;
+
+ my $arg = $self->get_form('fileset');
+
+ if ($arg->{fileset}) {
+ my $b = $self->get_bconsole();
+ my $ret = $b->get_fileset($arg->{fileset});
+ $self->display({ fileset => $arg->{fileset},
+ %$ret,
+ }, "fileset_view.tpl");
+ } else {
+ $self->error("Can't get fileset name");
+ }
+}
+
+sub director_show_sched
+{
+ my ($self) = @_ ;
+
+ my $arg = $self->get_form('days');
+
+ my $b = $self->get_bconsole();
+ my $ret = $b->director_get_sched( $arg->{days} );
+
+ $self->display({
+ id => $cur_id++,
+ list => $ret,
+ }, "scheduled_job.tpl");
+}
+
+sub enable_disable_job
+{
+ my ($self, $what) = @_ ;
+
+ my $name = CGI::param('job') || '';
+ unless ($name =~ /^[\w\d\.\-\s]+$/) {
+ return $self->error("Can't find job name");
+ }
+
+ my $b = $self->get_bconsole();
+
+ my $cmd;
+ if ($what) {
+ $cmd = "enable";
+ } else {
+ $cmd = "disable";
+ }
+
+ $self->display({
+ content => $b->send_cmd("$cmd job=\"$name\""),
+ title => "$cmd $name",
+ name => "$cmd job=\"$name\"",
+ }, "command.tpl");
+}
+
+sub get_bconsole
+{
+ my ($self) = @_;
+ return new Bconsole(pref => $self->{info});
+}
+
+sub run_job_select
+{
+ my ($self) = @_;
+ my $b = $self->get_bconsole();
+
+ my $joblist = [ map { { name => $_ } } $b->list_job() ];
+
+ $self->display({ Jobs => $joblist }, "run_job.tpl");
+}
+
+sub run_parse_job
+{
+ my ($self, $ouput) = @_;
+
+ my %arg;
+ foreach my $l (split(/\r\n/, $ouput)) {
+ if ($l =~ /(\w+): name=([\w\d\.\s-]+?)(\s+\w+=.+)?$/) {
+ $arg{$1} = $2;
+ $l = $3
+ if ($3) ;
+ }
+
+ if (my @l = $l =~ /(\w+)=([\w\d*]+)/g) {
+ %arg = (%arg, @l);
+ }
+ }
+
+ my %lowcase ;
+ foreach my $k (keys %arg) {
+ $lowcase{lc($k)} = $arg{$k} ;
+ }
+
+ return \%lowcase;
+}
+
+sub run_job_mod
+{
+ my ($self) = @_;
+ my $b = $self->get_bconsole();
+
+ my $job = CGI::param('job') || '';
+
+ my $info = $b->send_cmd("show job=\"$job\"");
+ my $attr = $self->run_parse_job($info);
+
+ my $jobs = [ map {{ name => $_ }} $b->list_job() ];
+
+ my $pools = [ map { { name => $_ } } $b->list_pool() ];
+ my $clients = [ map { { name => $_ } }$b->list_client()];
+ my $filesets= [ map { { name => $_ } }$b->list_fileset() ];
+ my $storages= [ map { { name => $_ } }$b->list_storage()];
+
+ $self->display({
+ jobs => $jobs,
+ pools => $pools,
+ clients => $clients,
+ filesets => $filesets,
+ storages => $storages,
+ %$attr,
+ }, "run_job_mod.tpl");
+}
+
+sub run_job
+{
+ my ($self) = @_;
+ my $b = $self->get_bconsole();
+
+ my $jobs = [ map {{ name => $_ }} $b->list_job() ];
+
+ $self->display({
+ jobs => $jobs,
+ }, "run_job.tpl");
+}
+
+sub run_job_now
+{
+ my ($self) = @_;
+ my $b = $self->get_bconsole();
+
+ # TODO: check input (don't use pool, level)
+
+ my $arg = $self->get_form('pool', 'level', 'client', 'priority', 'when');
+ my $job = CGI::param('job') || '';
+ my $storage = CGI::param('storage') || '';
+
+ my $jobid = $b->run(job => $job,
+ client => $arg->{client},
+ priority => $arg->{priority},
+ level => $arg->{level},
+ storage => $storage,
+ pool => $arg->{pool},
+ when => $arg->{when},
+ );
+
+ print $jobid, $b->{error};
+
+ print "<br>You can follow job execution <a href='?action=dsp_cur_job;client=$arg->{client};jobid=$jobid'> here </a>";
+}
+
+1;
--- /dev/null
+package CCircle ;
+
+=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 VERSION
+
+ $Id$
+
+=cut
+
+use strict ;
+use GD ;
+
+my $pi = 3.14159265;
+
+our $gd ;
+our @color_tab ;
+
+our $black ;
+our $white ;
+
+our $last_level = 1 ;
+our @draw_label ;
+our $height ;
+our $width ;
+
+our $cur_color = 1 ;
+
+my $debug = 0 ;
+my $font_size = 6 ;
+
+our @image_map ;
+
+sub new
+{
+ my ($class, %arg) = @_ ;
+
+ my $self = {
+ start_degrees => 0,
+ degrees_complete => 0,
+ parent_percent => 100,
+ level => 1,
+ center_x => 600,
+ center_y => 300,
+ diameter => 75,
+ percent_total => 0,
+ min_percent => 2,
+ min_total => 0,
+ width => 1200,
+ height => 600,
+ min_label => 4,
+ min_degrees => 2,
+ max_label_level => 2,
+ display_other => 0,
+ base_url => '',
+ } ;
+
+ map { $self->{$_} = $arg{$_} } keys %arg ;
+
+ unless(defined $gd) {
+ $height = $self->{height} ;
+ $width = $self->{width} ;
+
+ $gd = new GD::Image($width,$height);
+ $white = $gd->colorAllocate(255,255,255);
+
+ push @color_tab, ($gd->colorAllocate(135,236,88),
+ $gd->colorAllocate(255,95,95),
+ $gd->colorAllocate(245,207,91),
+ $gd->colorAllocate( 255, 236, 139),
+ $gd->colorAllocate( 255, 174, 185),
+ $gd->colorAllocate( 179, 255, 58),
+ $gd->colorAllocate( 205, 133, 0),
+ $gd->colorAllocate(205, 133, 0 ),
+ $gd->colorAllocate(238, 238, 209),
+
+ ) ;
+
+
+ $black = $gd->colorAllocate(0,0,0);
+ #$gd->>transparent($white);
+ $gd->interlaced('true');
+
+ $gd->arc($self->{center_x},$self->{center_y},
+ $self->{diameter},$self->{diameter},
+ $self->{start_degrees},360, $black) ;
+
+ }
+
+ $self->{rayon} = $self->{diameter} / 2 ;
+
+ bless $self ;
+
+ # pour afficher les labels tout propre
+ if ($self->{level} > $last_level) {
+ $last_level = $self->{level} ;
+ }
+
+ return $self ;
+}
+
+sub calc_xy
+{
+ my ($self, $level, $deg) = @_ ;
+
+ my $x1 = $self->{center_x}+$self->{rayon} * $level * cos($deg*$pi/180) ;
+ my $y1 = $self->{center_y}+$self->{rayon} * $level * sin($deg*$pi/180) ;
+
+ return ($x1, $y1) ;
+
+}
+
+sub add_part
+{
+ my ($self, $percent, $label, $tips) = @_ ;
+
+ $tips = $tips || $label ;
+
+ if (($percent + $self->{percent_total}) > 100.05) {
+ print STDERR "Attention $label ($percent\% + $self->{percent_total}\%) > 100\%\n" ;
+ return undef;
+ }
+
+ if ($percent <= 0) {
+ print STDERR "Attention $label <= 0\%\n" ;
+ return undef;
+ }
+
+ # angle de depart de l'arc
+ my $start_degrees = (($self->{degrees_complete})?
+ $self->{degrees_complete}:$self->{start_degrees}) ;
+
+ # angle de fin de l'arc
+ my $end_degrees = $start_degrees +
+ ($percent * ( ( $self->{parent_percent} * 360 )/100 ) ) /100 ;
+
+# print STDERR "-------- $debug --------
+#percent = $percent%
+#label = $label
+#level = $self->{level}
+#start = $start_degrees
+#end = $end_degrees
+#parent= $self->{parent_percent}
+#" ;
+ if (($end_degrees - $start_degrees) < $self->{min_degrees}) {
+ $self->{min_total} += $percent ;
+ return undef ;
+ }
+
+ if ($percent <= $self->{min_percent}) {
+ $self->{min_total} += $percent ;
+ return undef ;
+ }
+
+ # on totalise les % en cours
+ $self->{percent_total} += $percent ;
+
+ #print STDERR "percent_total = $self->{percent_total}\n" ;
+
+ # position dans le cercle
+ my $n = $self->{level} ; # on ajoute/retire 0.005 pour depasser un peu
+ my $m = $n+1 ;
+
+ # si c'est la premiere tranche de la nouvelle serie, il faut dessiner
+ # la premiere limite
+
+ if ($self->{degrees_complete} == 0) {
+ my ($x1, $y1) = $self->calc_xy($n-0.005, $self->{start_degrees}) ;
+ my ($x2, $y2) = $self->calc_xy($m+0.005, $self->{start_degrees}) ;
+
+ $gd->line($x1, $y1, $x2, $y2, $black) ;
+ }
+
+ # seconde ligne
+ my ($x3, $y3) = $self->calc_xy($n-0.005, $end_degrees) ;
+
+ my ($x4, $y4) = $self->calc_xy($m+0.005, $end_degrees) ;
+
+ # ligne de bord exterieur
+ $gd->line($x3, $y3, $x4, $y4, $black);
+
+ # on dessine le bord
+ $gd->arc($self->{center_x},$self->{center_y},
+ $self->{diameter}*($self->{level}+1),
+ $self->{diameter}*($self->{level}+1),
+
+ $start_degrees-0.5,
+ $end_degrees+0.5, $black) ;
+
+ # on calcule le point qui est au milieu de la tranche
+ # angle = (angle nouvelle tranche)/2
+
+ # rayon = n*rayon - 0.5*rayon
+ # n=1 -> 0.5
+ # n=2 -> 1.5
+ # n=3 -> 2.5
+
+ my $mid_rad = ($end_degrees - $start_degrees) /2 + $start_degrees;
+
+ my $moy_x = ($self->{center_x}+
+ ($self->{rayon}*$m - 0.5*$self->{rayon})
+ *cos($mid_rad*$pi/180)) ;
+
+ my $moy_y = ($self->{center_y}+
+ ($self->{rayon}*$m - 0.5*$self->{rayon})
+ *sin($mid_rad*$pi/180)) ;
+
+ $gd->fillToBorder($moy_x,
+ $moy_y,
+ $black,
+ $cur_color) ;
+
+ # on prend une couleur au hasard
+ $cur_color = ($cur_color % $#color_tab) + 1 ;
+
+ # si le % est assez grand, on affiche le label
+ if ($percent > $self->{min_label}) {
+ push @draw_label, [$label,
+ $moy_x, $moy_y,
+ $self->{level}] ;
+
+ $self->push_image_map($label, $tips, $start_degrees, $end_degrees) ;
+ }
+
+ # pour pourvoir ajouter des sous donnees
+ my $ret = new CCircle(start_degrees => $start_degrees,
+ parent_percent => $percent*$self->{parent_percent}/100,
+ level => $m,
+ center_x => $self->{center_x},
+ center_y => $self->{center_y},
+ min_percent => $self->{min_percent},
+ min_degrees => $self->{min_degrees},
+ base_url => $self->{base_url} . $label,
+ ) ;
+
+ $self->{degrees_complete} = $end_degrees ;
+
+ #print STDERR "$debug : [$self->{level}] $label ($percent)\n" ;
+ #open(FP, sprintf(">/tmp/img.%.3i.png", $debug)) ;
+ #print FP $gd->png;
+ #close(FP) ;
+
+ $debug++ ;
+
+ return $ret ;
+}
+
+# on dessine le restant < min_percent
+sub finalize
+{
+ my ($self) = @_ ;
+
+ $self->add_part($self->{min_total},
+ "other < $self->{min_percent}%",
+ $black) ;
+
+}
+
+sub set_title
+{
+ my ($self, $title) = @_ ;
+
+ $gd->string(GD::gdSmallFont, $self->{center_x} - $self->{rayon}*0.7,
+ $self->{center_y}, $title, $black) ;
+}
+
+my $_image_map = '';
+
+sub get_imagemap
+{
+ my ($self, $title, $img) = @_ ;
+
+ return "
+<map name='testmap'>
+ $_image_map
+</map>
+<img src='$img' border=0 usemap='#testmap' alt=''>
+" ;
+
+}
+
+sub push_image_map
+{
+ my ($self, $label, $tips, $start_degrees, $end_degrees) = @_ ;
+
+ if ($label =~ /^other .*</) {
+ if (!$self->{display_other}) {
+ return ;
+ }
+ $label = '';
+ }
+
+ # on prend des points tous les $delta sur l'arc interieur et exterieur
+
+ my $delta = 3 ;
+
+ if (($end_degrees - $start_degrees) < $delta) {
+ return ;
+ }
+
+ my @pts ;
+
+ for (my $i = $start_degrees ;
+ $i <= $end_degrees ;
+ $i = $i + $delta)
+ {
+ my ($x1, $y1) = $self->calc_xy($self->{level}, $i) ;
+ my ($x2, $y2) = $self->calc_xy($self->{level} + 1, $i) ;
+
+ push @pts, sprintf("%.2f,%.2f",$x1,$y1) ;
+ unshift @pts, sprintf("%.2f,%.2f",$x2, $y2) ;
+ }
+
+ my ($x1, $y1) = $self->calc_xy($self->{level}, $end_degrees) ;
+ my ($x2, $y2) = $self->calc_xy($self->{level} + 1, $end_degrees) ;
+
+ push @pts, sprintf("%.2f,%.2f",$x1,$y1) ;
+ unshift @pts, sprintf("%.2f,%.2f",$x2, $y2) ;
+
+ my $ret = join(",", @pts) ;
+
+ # on refait le traitement avec $i = $end_degrees
+ $_image_map .= "<area shape='polygon' coords='$ret' ".
+ "title='$tips' href='$self->{base_url}$label'>\n" ;
+
+}
+
+sub get_labels_imagemap
+{
+ my ($self) = @_ ;
+ my @ret ;
+
+ for my $l (@draw_label)
+ {
+ # translation
+ my ($label, $x, $y, $level) = @{ $l } ;
+
+ next if ($level > $self->{max_label_level}) ;
+
+ next if (!$self->{display_other} and $label =~ /^other .*</) ;
+
+ my $dy = ($y - $self->{center_y})*($last_level - $level) + $y ;
+
+ my $x2 ;
+ my $xp ;
+
+ if ($x < $self->{center_x}) {
+ $x2 = $self->{center_x}
+ - $self->{rayon} * ($last_level + 3.7) ;
+ $xp = $x2 - (length($label) *6 + 2) ; # moins la taille de la police
+ } else {
+ $x2 = $self->{center_x} + $self->{rayon} * ($last_level + 3.7) ;
+ $xp = $x2 + 10 ;
+ }
+
+ push @ret, $xp - 1 . ";" . $dy - 6 . ";" . $xp + length($label) * $font_size . ";" . $dy + 10 . "\n" ;
+
+ $gd->rectangle($xp - 1, $dy - 6,
+ $xp + length($label) * $font_size,
+ $dy + 10, $black) ;
+ }
+}
+
+my $_label_hauteur ;
+my $_label_max ;
+my $_label_pos ;
+my $_label_init = 0 ;
+
+# on va stocker les positions dans un bitmap $_label_pos
+#
+# si on match pas la position exacte, on essaye la case
+# au dessus ou en dessous
+#
+# on a un bitmap pour les labels de gauche et un pour la droite
+# $_label_pos->[0] et $_label_pos->[1]
+sub get_label_pos
+{
+ my ($self, $x, $y) = @_ ;
+
+ unless ($_label_init) {
+ $_label_hauteur = $self->{rayon} * 2 * $last_level ;
+ # nombre max de label = hauteur max / taille de la font
+ $_label_max = $_label_hauteur / 12 ;
+ $_label_pos = [ [], [] ] ;
+ $_label_init = 1 ;
+ }
+
+ # on calcule la position du label dans le bitmap
+ use integer ;
+ my $num = $y * $_label_max / $_label_hauteur ;
+ no integer ;
+
+ my $n = 0 ; # nombre d'iteration
+ my $l ;
+
+ # on prend le bon bitmap
+ if ($x < $self->{center_x}) {
+ $l = $_label_pos->[0] ;
+ } else {
+ $l = $_label_pos->[1] ;
+ }
+
+ # on parcours le bitmap pour trouver la bonne position
+ while (($num - $n) > 0) {
+ if (not $l->[$num]) {
+ last ;
+ } elsif (not $l->[$num + $n]) {
+ $num = $num + $n ;
+ last ;
+ } elsif (not $l->[$num - $n]) {
+ $num = $num - $n ;
+ last ;
+ }
+ $n++ ;
+ }
+
+ $l->[$num] = 1 ; # on prend la position
+
+ if ($num <= 0) {
+ return 0 ;
+ }
+
+ # calcul de la position
+ $y = $num * $_label_hauteur / $_label_max ;
+
+ return $y ;
+}
+
+sub draw_labels
+{
+ my ($self) = @_ ;
+
+ $gd->fillToBorder(1,
+ 1,
+ $black,
+ $white) ;
+
+ for my $l (@draw_label)
+ {
+ # translation
+ my ($label, $x, $y, $level) = @{ $l } ;
+
+ next if ($level > $self->{max_label_level}) ;
+
+ next if (!$self->{display_other} and $label =~ /^other .*</) ;
+
+ my $dx = ($x - $self->{center_x})*($last_level - $level) + $x ;
+ my $dy = ($y - $self->{center_y})*($last_level - $level) + $y ;
+
+ $dy = $self->get_label_pos($dx, $dy) ;
+
+ next unless ($dy) ; # pas d'affichage si pas de place
+
+ $gd->line( $x, $y,
+ $dx, $dy,
+ $black) ;
+
+ my $x2 ;
+ my $xp ;
+
+ if ($x < $self->{center_x}) {
+ $x2 = $self->{center_x}
+ - $self->{rayon} * ($last_level + 3.7) ;
+ $xp = $x2 - (length($label) * $font_size + 2) ; # moins la taille de la police
+ } else {
+ $x2 = $self->{center_x} + $self->{rayon} * ($last_level + 3.7) ;
+ $xp = $x2 + 10 ;
+ }
+
+ $gd->line($dx, $dy,
+ $x2, $dy,
+ $black) ;
+
+ $gd->string(GD::gdSmallFont, $xp, $dy - 5, $label, $black) ;
+ }
+}
+
+1;
+__END__
+
+package main ;
+
+my $top = new CCircle() ;
+
+my $chld1 = $top->add_part(50, 'test') ;
+my $chld2 = $top->add_part(20, 'test') ;
+my $chld3 = $top->add_part(10, 'test') ;
+my $chld4 = $top->add_part(20, 'test') ;
+
+
+$chld1->add_part(20, 'test1') ;
+$chld1->add_part(20, 'test1') ;
+
+$chld2->add_part(20, 'test1') ;
+$chld2->add_part(20, 'test1') ;
+
+$chld3->add_part(20, 'test1') ;
+my $chld5 = $chld3->add_part(20, 'test1') ;
+
+$chld5->add_part(50, 'test3') ;
+
+$top->finalize() ;
+$chld1->finalize() ;
+$chld2->finalize() ;
+$chld3->finalize() ;
+$chld4->finalize() ;
+$chld5->finalize() ;
+
+$top->draw_labels() ;
+# make sure we are writing to a binary stream
+binmode STDOUT;
+
+# Convert the image to PNG and print it on standard output
+print $CCircle::gd->png;
--- /dev/null
+CREATE PROCEDURAL LANGUAGE plpgsql;
+BEGIN;
+
+CREATE FUNCTION SEC_TO_TIME(timestamp with time zone)
+RETURNS timestamp with time zone AS $$
+ select date_trunc('second', $1);
+$$ LANGUAGE SQL;
+
+CREATE FUNCTION SEC_TO_TIME(bigint)
+RETURNS interval AS $$
+ select date_trunc('second', $1 * interval '1 second');
+$$ LANGUAGE SQL;
+
+CREATE FUNCTION UNIX_TIMESTAMP(timestamp with time zone)
+RETURNS double precision AS $$
+ select date_part('epoch', $1);
+$$ LANGUAGE SQL;
+
+CREATE FUNCTION SEC_TO_INT(interval)
+RETURNS double precision AS $$
+ select extract(epoch from $1);
+$$ LANGUAGE SQL;
+
+CREATE OR REPLACE FUNCTION base64_decode_lstat(int4, varchar) RETURNS int8 AS $$
+DECLARE
+val int8;
+b64 varchar(64);
+size varchar(64);
+i int;
+BEGIN
+size := split_part($2, ' ', $1);
+b64 := 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+val := 0;
+FOR i IN 1..length(size) LOOP
+val := val + (strpos(b64, substr(size, i, 1))-1) * (64^(length(size)-i));
+END LOOP;
+RETURN val;
+END;
+$$ language 'plpgsql';
+
+COMMIT;
--- /dev/null
+<Directory /usr/lib/cgi-bin/bweb>
+ Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
+
+# use this to be more safe (htpasswd -c /etc/bacula/bweb.htpasswd admin)
+# AuthType Basic
+# AuthName Bweb
+# AuthUserFile /etc/bacula/bweb.htpasswd
+# Require valid-user
+
+ AllowOverride None
+</Directory>
+
+Alias /bweb/fv /var/spool/bweb
+<Directory "/var/spool/bweb">
+ Options None
+ AllowOverride AuthConfig
+ Order allow,deny
+ Allow from all
+</Directory>
+
+Alias /bweb /usr/share/bweb/html
+<Directory "/usr/share/bweb/html">
+ Options None
+ AllowOverride AuthConfig
+ Order allow,deny
+ Allow from all
+</Directory>
+
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> About </h1>
+</div>
+<div class='bodydiv'>
+<pre>
+ 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.
+
+ nrsTable Copyright 2005 New Roads School (GPL)
+ kaiska css Copyright Willy Morin (BWL)
+</pre>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'><TMPL_UNLESS name>New</TMPL_UNLESS> Autochanger </h1>
+</div>
+<div class='bodydiv'>
+ You have to create a Location which will have the same name.<br/><br/>
+
+ <form name='form1' action="?" method='get'>
+ <table>
+ <tr><td>Name :</td>
+ <td>
+ <select name='ach' class='formulaire' id='ach'>
+<TMPL_LOOP devices><option value='<TMPL_VAR name>'><TMPL_VAR name></option></TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Pre-command :</td>
+ <td> <input class="formulaire" type='text' id='precmd' value='sudo'
+ title='can be "sudo" or "ssh storage@storagehost"...' name='precmd'>
+ </td>
+ </tr>
+ <tr><td>mtx command :</td>
+ <td> <input class="formulaire" type='text' name='mtxcmd' size='32'
+ value='/usr/sbin/mtx' id='mtxcmd'>
+ </td>
+ </tr>
+ <tr><td>Device :</td>
+ <td> <input class="formulaire" type='text' name='device'
+ value='/dev/changer' id='device'>
+ </td>
+ </tr>
+ <tr><td><b>Drives</b></td><td/></tr>
+ <TMPL_LOOP devices>
+ <tr>
+ <td><input class='formulaire' type='checkbox' id='drive_<TMPL_VAR name>'
+ name='drives' value='<TMPL_VAR name>'><TMPL_VAR name>
+ </td>
+ <td>index <input type='text' title='drive index' class='formulaire'
+ id='index_<TMPL_VAR name>' value=''
+ name='index_<TMPL_VAR name>' size='3'>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <input type="image" name='action' value='ach_add' src='/bweb/save.png'>
+ </form>
+</div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF name>
+ for (var i=0; i < document.form1.ach.length; ++i) {
+ if (document.form1.ach[i].value == '<TMPL_VAR name>') {
+ document.form1.ach[i].selected = true;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF mtxcmd>
+ document.getElementById('mtxcmd').value='<TMPL_VAR mtxcmd>';
+ </TMPL_IF>
+ <TMPL_IF precmd>
+ document.getElementById('precmd').value='<TMPL_VAR precmd>';
+ </TMPL_IF>
+ <TMPL_IF device>
+ document.getElementById('device').value='<TMPL_VAR device>';
+ </TMPL_IF>
+ <TMPL_IF drives>
+ <TMPL_LOOP drives>
+ document.getElementById('drive_<TMPL_VAR name>').checked=true;
+ document.getElementById('index_<TMPL_VAR name>').value=<TMPL_VAR index>;
+ </TMPL_LOOP>
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+Autochanger : <TMPL_VAR Name> (<TMPL_VAR nb_drive> Drives
+<TMPL_IF nb_io><TMPL_VAR nb_io> IMPORT/EXPORT</TMPL_IF>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='get'>
+ <input type='hidden' name='ach' value='<TMPL_VAR name>'>
+ <TMPL_IF "Update">
+ <font color='red'> You must run update slot, Autochanger status is different from bacula slots </font>
+ <br/>
+ </TMPL_IF>
+ <table border='0'>
+ <tr>
+ <td valign='top'>
+ <div class='otherboxtitle'>
+ Tools
+ </div>
+ <div class='otherbox'>
+<label>
+<input type="image" name='action' value='label_barcodes'
+ title='run label barcodes' src='/bweb/label.png'>Label
+</label>
+<TMPL_IF nb_io>
+<label>
+<input type="image" name='action' value='eject'
+ title='put selected media on i/o' src='/bweb/extern.png'>
+Eject
+</label>
+<label>
+<input type="image" name='action' value='clear_io'
+ title='Clear i/o' src='/bweb/intern.png'>
+Clear I/O
+</label>
+</TMPL_IF>
+<label>
+<input type="image" name='action' value='update_slots'
+ title='run update slots' src='/bweb/update.png'>
+Update
+</label>
+<br/><br/>
+<label>
+<input type="image" name='action' value='ach_load'
+ title='mount drive' src='/bweb/load.png'>
+Mount
+</label>
+<label>
+<input type="image" name='action' value='ach_unload'
+ title='umount drive' src='/bweb/unload.png'>
+Umount
+</label>
+
+ </div>
+ <td width='200'/>
+ <td>
+ <b> Drives: </b><br/>
+ <table id='id_drive'></table> <br/>
+ </td>
+ </tr>
+ </table>
+ <b> Content: </b><br/>
+ <table id='id_ach'></table>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Real Slot", "Slot", "Volume Name","Vol Bytes","Vol Status",
+ "Media Type","Pool Name","Last Written",
+ "When expire ?", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Slots>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'slot';
+chkbox.value = '<TMPL_VAR realslot>';
+
+data.push( new Array(
+"<TMPL_VAR realslot>",
+"<TMPL_VAR slot>",
+"<TMPL_VAR volumename>",
+human_size(<TMPL_VAR volbytes>),
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR name>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_ach",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6)
+ padding: 3
+}
+);
+
+var header = new Array("Index", "Drive Name", "Volume Name", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP Drives>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'drive';
+chkbox.value = '<TMPL_VAR index>';
+
+data.push( new Array(
+"<TMPL_VAR index>",
+"<TMPL_VAR name>",
+"<TMPL_VAR load>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_drive",
+ 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,
+// rows_per_page: rows_per_page,
+// disable_sorting: new Array(5,6),
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
+<html>
+<head>
+<title>Bweb - Bacula Web Interface</title>
+<link rel="SHORTCUT ICON" href="/bweb/favicon.ico">
+<script type="text/javascript" language="JavaScript" src="/bweb/natcompare.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/nrs_table.js"></script>
+<script type="text/javascript" language="JavaScript" src="/bweb/bweb.js"></script>
+<link type="text/css" rel="stylesheet" href="/bweb/style.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/kaiska.css"/>
+<link type="text/css" rel="stylesheet" href="/bweb/bweb.css"/>
+</head>
+<body>
+
+<script type="text/javascript" language="JavaScript">
+if (navigator.appName == 'Konqueror') {
+ alert("Sorry at this moment, bweb works only with mozilla.");
+}
+</script>
+
+<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=run_job">Jobs</a>
+ <ul>
+ <li><a href="bweb.pl?action=run_job">Defined Jobs</a>
+ <li><a href="bweb.pl?action=job">Last Jobs</a> </li>
+ <li><a href="bweb.pl?action=running">Running Jobs</a>
+ <li><a href="bweb.pl?action=next_job">Next Jobs</a> </li>
+ <li><a href="bweb.pl?action=restore" title="Launch brestore">Restore</a> </li>
+ </ul>
+ </li>
+ <li style="padding: 0.25em 2em;">Media
+ <ul>
+ <li><a href="bweb.pl?action=pool">Pools</a> </li>
+ <li><a href="bweb.pl?action=location">Locations</a> </li>
+ <li><a href="bweb.pl?action=media">All Media</a><hr></li>
+ <li><a href="bweb.pl?action=extern_media">Eject Media</a> </li>
+ <li><a href="bweb.pl?action=intern_media">Load Media</a> </li>
+ </ul>
+ </li>
+<TMPL_IF achs>
+ <li style="padding: 0.25em 2em;">Autochanger
+ <ul>
+<TMPL_LOOP achs>
+ <li><a href="bweb.pl?action=ach_view;ach=<TMPL_VAR name>"><TMPL_VAR name></a></li>
+</TMPL_LOOP>
+ </ul>
+ </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=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>
+</ul>
+
+<form name="search" action="bweb.pl?" method='GET'>
+ <input type="hidden" name="action" value="">
+ <input type="hidden" name="re_media" value="">
+ <input type="hidden" name="re_client" value="">
+</form>
+
+<div style="clear: left;">
+<div style="float: left;">
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'>
+ Move media
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<form action="?" method='GET'>
+<table>
+<tr>
+<td><b>To: </b></td><td><input class='formulaire' name='email' value='<TMPL_VAR email>'></td>
+</tr><tr>
+<td><b>Subject: </b></td><td><input class='formulaire' name='subject' value='[BACULA] Move media to <TMPL_VAR newlocation>' size='80'></td>
+</tr><tr>
+<td></td>
+<td>
+<textarea name='content' class='formulaire' cols='80' rows='32'>
+Hi,
+
+Could you move these media to <TMPL_VAR newlocation>
+Media :
+<TMPL_LOOP Medias>
+ - <TMPL_VAR VolumeName> (<TMPL_VAR location>)
+</TMPL_LOOP>
+
+When it's finish, could you update media location ?
+(you can use this link : <TMPL_VAR url>).
+
+Thanks
+</textarea>
+</td></tr></table>
+<input class='formulaire' type='submit' name='action' value='move_email'>
+</form>
+<br>
+<a href="<TMPL_VAR url>"><img alt='update now' src='/bweb/update.png'>Update now</a>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Running job <TMPL_VAR JobName> on <TMPL_VAR Client>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<table>
+ <tr>
+ <td> <b> JobName: </b> <td> <td> <TMPL_VAR jobname> (<TMPL_VAR jobid>) <td>
+ </tr>
+ <tr>
+ <td> <b> Processing file: </b> <td> <td> <TMPL_VAR "processing file"> </td>
+ </tr>
+ <tr>
+ <td> <b> Speed: </b> <td> <td> <TMPL_VAR "bytes/sec"> B/s</td>
+ </tr>
+ <tr>
+ <td> <b> Files Examined: </b> <td> <td> <TMPL_VAR "files examined"></td>
+ </tr>
+ <tr>
+ <td> <b> Bytes: </b> <td> <td> <TMPL_VAR bytes></td>
+ </tr>
+</table>
+<form name='form1' action='?' method='GET'>
+<input type="image" name='action' value='dsp_cur_job'
+ src='/bweb/update.png' title='refresh'>
+<input type='hidden' name='client' value='<TMPL_VAR Client>'>
+<input type='hidden' name='jobid' value='<TMPL_VAR JobId>'>
+<input type="image" name='action' value='cancel_job'
+ onclick="return confirm('Do you want to cancel this job ?')"
+ title='Cancel job' src='/bweb/cancel.png'>
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ bweb_add_refresh();
+</script>
+
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Clients</h1>
+ </div>
+ <div class='bodydiv'>
+<form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <div class="otherboxtitle">
+ Actions
+ </div>
+ <div class="otherbox">
+<!-- <h1>Actions</h1> -->
+ <label>
+ <input type="image" name='action' value='job' title='Show last job' src='/bweb/zoom.png'>Last jobs</label>
+ <label>
+ <input type="image" name='action' value='dsp_cur_job' title='Show current job' src='/bweb/zoom.png'>Current jobs</label>
+ <label>
+ <input type="image" name='action' value='client_status' title='Show client status' src='/bweb/zoom.png'>Status</label>
+ <label>
+ <input type="image" name='action' value='client_stats' title='Client stats' src='/bweb/chart.png'>Stats</label>
+ </label>
+ </div>
+
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Name", "Select", "Desc", "Auto Prune", "File Retention", "Job Retention");
+
+var data = new Array();
+var chkbox ;
+
+<TMPL_LOOP NAME=Clients>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'client';
+chkbox.value = '<TMPL_VAR NAME=Name>';
+
+data.push(
+ new Array( "<TMPL_VAR NAME=Name>",
+ chkbox,
+ "<TMPL_VAR NAME=Uname>",
+ "<TMPL_VAR NAME=AutoPrune>",
+ human_sec(<TMPL_VAR NAME=FileRetention>),
+ human_sec(<TMPL_VAR NAME=JobRetention>)
+ )
+) ;
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1)
+}
+);
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> <TMPL_VAR title> : <TMPL_VAR name></h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log' style='font-size: 10px'>
+<TMPL_VAR content>
+ </pre>
+ </div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuration </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='post'>
+ <table>
+ <tr> <td><b>SQL Connection</b></td> <td/></tr>
+
+ <tr><td>DBI :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR dbi>' size='64' name='dbi'>
+ </td>
+ </tr>
+ <tr><td>user :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR user>' name='user'>
+ </td>
+ </tr>
+ <tr><td>password :</td>
+ <td> <input class="formulaire" type='password' value='<TMPL_VAR password>' name='password'>
+ </td></tr>
+
+ <tr> <td><b>General Options</b></td> <td/></tr>
+
+ <tr><td>email_media :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR email_media>' name='email_media'>
+ </td></tr>
+ </td></tr>
+
+ <tr> <td><b>Bweb Configuration</b></td> <td/></tr>
+
+ <tr><td>graph_font :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR graph_font>' size='64' name='graph_font'>
+ </td></tr>
+ <tr><td>template_dir :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR template_dir>' size='64' name='template_dir'>
+ </td></tr>
+ <tr><td>fv_write_path :</td>
+ <td> <input class="formulaire" title="This folder must be writable by apache user and must be accessible on /bweb/fv" type='text' value='<TMPL_VAR fv_write_path>' size='64' name='fv_write_path'>
+ </td></tr>
+ <tr><td>bconsole :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR bconsole>' size='64' name='bconsole'>
+ </td></tr>
+ <tr><td>debug :</td>
+ <td> <input class="formulaire" type='checkbox' name='debug' <TMPL_IF debug> checked='checked' value='on' </TMPL_IF> >
+ </td></tr>
+ </table>
+ <input type="image" name='action' value='apply_conf' src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Configuration </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr> <td><b>SQL Connection</b></td> <td/></tr>
+ <tr><td>DBI :</td> <td> <TMPL_VAR dbi> </td></tr>
+ <tr><td>user :</td> <td> <TMPL_VAR user> </td></tr>
+ <tr><td>password :</td> <td> xxxxx </td></tr>
+ <tr> <td><b>General Options</b></td> <td/></tr>
+ <tr><td>email_media :</td> <td> <TMPL_VAR email_media> </td></tr>
+ <tr> <td><b>Bweb Configuration</b></td> <td/></tr>
+ <tr><td>config_file :</td> <td> <TMPL_VAR config_file> </td></tr>
+ <tr><td title="/path/to/your/template_dir">template_dir :</td> <td> <TMPL_VAR template_dir> </td></tr>
+ <tr><td title="/path/to/a/font.ttf">graph_font :</td> <td> <TMPL_VAR graph_font> </td></tr>
+ <tr><td title="This folder must be writable by apache user and must be accessible on /bweb/fv">fv_write_path :</td> <td> <TMPL_VAR fv_write_path> </td></tr>
+ <tr><td title="/path/to/bconsole -n -c /path/to/bconsole.conf">bconsole :</td> <td> <TMPL_VAR bconsole> </td></tr>
+ <tr><td>debug :</td> <td> <TMPL_VAR debug> </td></tr>
+ <TMPL_IF achs>
+ <tr> <td><b>Autochanger</b></td> <td/></tr>
+ <tr>
+ <td>
+ <form action='?' method='GET'>
+ <table border='0'>
+ <TMPL_LOOP achs>
+ <tr>
+ <td>
+<label>
+ <input type='radio' name='ach' value='<TMPL_VAR name>'><TMPL_VAR name>
+</label>
+ </td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ <td>
+
+ <input type="image" name="action" value="ach_edit" title="edit" src='/bweb/edit.png'>
+ <input type="image" name='action' value='ach_del' title='delete' src='/bweb/remove.png'>
+ <input type='image' name='action' value='ach_view' title='view' src='/bweb/zoom.png'>
+ </form>
+ </td>
+ </tr>
+ </TMPL_IF achs>
+ <tr>
+ <td><hr></td><td></td>
+ </tr>
+ </table>
+
+ <form action='?' method='GET'>
+ <label>
+ <input name='action' value='edit_conf' type="image" title='Edit' src='/bweb/edit.png'> Edit
+ </label>
+ <label>
+ <input name='action' value='ach_add' type="image" title='Add an autochanger' src='/bweb/add.png'> Add autochanger
+ </label>
+ </form>
+
+ <TMPL_IF error>
+ info : <TMPL_VAR error> </br>
+ </TMPL_IF>
+</div>
--- /dev/null
+<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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Client : <TMPL_VAR NAME=clientname> (<TMPL_VAR NAME=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'>
+<!-- <div class="otherboxtitle">
+ Actions
+ </div>
+ <div class="otherbox">
+ <h1>Actions</h1>
+ <input type="image" name='action' value='job' title='Show last job'
+ src='/bweb/zoom.png'>
+ <input type="image" name='action' value='dsp_cur_job' title='Show current job' src='/bweb/zoom.png'>
+ <input type="image" name='action' value='client_stat' title='Client stats' src='/bweb/zoom.png'>
+ </div>
+-->
+</form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("Name", "Nb Jobs", "Nb Bytes", "Nb Files", "Nb Errors");
+
+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>"
+ )
+) ;
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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,
+// disable_sorting: new Array(1),
+ rows_per_page: rows_per_page
+}
+);
+</script>
--- /dev/null
+<br/>
+<div class="otherboxtitle">
+ Filter
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Level</h2>
+ <select name='level' class='formulaire'>
+ <option id='level_Any' value='Any'>Any</option>
+ <option id='level_F' value='F'>Full</option>
+ <option id='level_D' value='D'>Differential</option>
+ <option id='level_I' value='I'>Incremental</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Status</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Any</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Error</option>
+ <option id='status_A' value='A'>Canceled</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_all' value=''>All</option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Age</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>This week</option>
+ <option id='age_2678400' value='2678400'>Last 30 days</option>
+ <option id='age_15552000' value='15552000'>Last 6 month</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Number of items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Job Type</h2>
+ <select name='jobtype' class='formulaire'>
+ <option id='jobtype_any' value='all type'>Any</option>
+ <option id='jobtype_B' value='B'>Backup</option>
+ <option id='jobtype_R' value='R'>Restore</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clients</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<!--
+<tr>
+ <td valign='top'>
+ <h2>File Set</h2>
+ <select name='fileset' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_filesets>
+ <option id='client_<TMPL_VAR fileset>'><TMPL_VAR NAME=fileset></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+-->
+</table>
+ <input type="image" name='action' value='job' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF jobtype>
+ document.getElementById('jobtype_<TMPL_VAR jobtype>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qfilesets>
+ document.getElementById('fileset_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+<div class="otherboxtitle">
+ Filter
+</div>
+<div class="otherbox">
+<form name='form1' action='?' method='GET'>
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Media Type</h2>
+ <select name='mediatype' class='formulaire'>
+ <option id='mediatype_all' value=''></option>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option id='mediatype_<TMPL_VAR mediatype>'><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Location</h2>
+ <select name='location' class='formulaire'>
+ <option id='location_all>' value=''></option>
+<TMPL_LOOP NAME=db_locations>
+ <option id='location_<TMPL_VAR location>'><TMPL_VAR location></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Status</h2>
+ <select name='volstatus' class='formulaire'>
+ <option id='volstatus_All' value=''></option>
+ <option id='volstatus_Append' value='Append'>Append</option>
+ <option id='volstatus_Full' value='Full'>Full</option>
+ <option id='volstatus_Error' value='Error'>Error</option>
+ <option id='volstatus_Used' value='Used'>Used</option>
+ <option id='volstatus_Purged' value='Purged'>Purged</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Pool</h2>
+ <select name='pool' class='formulaire'>
+ <option id='pool_all>' value=''></option>
+<TMPL_LOOP NAME=db_pools>
+ <option id='pool_<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Name</h2>
+ <input type='text' name='re_media'
+ <TMPL_IF qre_media>value=<TMPL_VAR qre_media></TMPL_IF>
+ class='formulaire' size='8'>
+ </td>
+</tr>
+ <tr>
+ <td valign='bottom'>
+ <h2>Number of items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+
+</table>
+ <input type="image" name='action' value='media' src='/bweb/update.png'>
+
+</form>
+</div>
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF volstatus>
+ document.getElementById('volstatus_<TMPL_VAR volstatus>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_LOOP qmediatypes>
+ document.getElementById('mediatype_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_LOOP qpools>
+ document.getElementById('pool_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+ <TMPL_LOOP qlocations>
+ document.getElementById('location_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+</script>
+
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'> Last Jobs (<TMPL_VAR Filter>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <table id='id<TMPL_VAR ID>'></table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+<TMPL_IF status>
+document.getElementById('status_<TMPL_VAR status>').checked = true;
+</TMPL_IF>
+
+
+
+var header = new Array("JobId",
+ "Client",
+ "Job Name",
+ "FileSet",
+// "Pool",
+ "Level",
+ "StartTime",
+ "Duration",
+ "JobFiles",
+ "JobBytes",
+ "Errors",
+ "Status");
+
+var data = new Array();
+
+<TMPL_LOOP Jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+//"<TMPL_VAR Pool>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR Duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+a
+ )
+);
+</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(10),
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR ID>'].fieldSort(0);
+</script>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>Information about job</h1>
+ </div>
+ <div class="bodydiv">
+ <table id='id0'></table>
+ <table><td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <label>
+ <input type="image" name='action' value='delete' title='delete this job'
+ onclick="return confirm('Do you want to delete this job from the catalog ?');"
+ src='/bweb/purge.png'> Delete
+ </label>
+ </form>
+ </td><td>
+ <form action='bweb.pl?'>
+ <TMPL_LOOP volumes>
+ <input type='hidden' name='media' value='<TMPL_VAR VolumeName>'>
+ </TMPL_LOOP>
+ <label>
+ <input type="image" name='action' value='media' title='view media'
+ src='/bweb/zoom.png'>View media
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <label>
+ <input type="image" name='action' value='job' title='view <TMPL_VAR Client> jobs' src='/bweb/zoom.png'>View jobs
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='age' value='2678400'>
+ <input type='hidden' name='client' value='<TMPL_VAR Client>'>
+ <input type='hidden' name='jobname' value='<TMPL_VAR jobname>'>
+ <label>
+ <input type="image" name='action' value='graph' title='view trends'
+ src='/bweb/chart.png'> View stats
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='bweb.pl?'>
+ <input type='hidden' name='fileset' value='<TMPL_VAR FileSet>'>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='view fileset'
+ src='/bweb/zoom.png'> View FileSet
+ </label>
+ </form>
+ </td>
+<!-- Remove this to activate bfileview -->
+ <td>
+ <form action='bfileview.pl?'>
+ <input type='hidden' name='jobid' value='<TMPL_VAR jobid>'>
+ <input type='hidden' name='where' value='/'>
+ <label>
+ <input type="image" name='action' value='bfileview' title='view file usage'
+ src='/bweb/colorscm.png' onclick='if (<TMPL_VAR JobFiles> > 50000) { return confirm("It could take long time, do you want to continue ?")} else { return 1;Â }'> View file usage
+ </label>
+ </form>
+ </td>
+<!-- -->
+ </table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var header = new Array("JobId",
+ "Client",
+ "Job Name",
+ "FileSet",
+ "Level",
+ "StartTime",
+ "Duration",
+ "JobFiles",
+ "JobBytes",
+ "Errors",
+ "Pool",
+ "Volume Name",
+ "Status");
+
+var data = new Array();
+
+img = document.createElement("IMG");
+img.src=bweb_get_job_img("<TMPL_VAR JobStatus>", <TMPL_VAR joberrors>);
+img.title=jobstatus['<TMPL_VAR JobStatus>'];
+
+data.push( new Array(
+"<TMPL_VAR JobId>",
+"<TMPL_VAR Client>",
+"<TMPL_VAR JobName>",
+"<TMPL_VAR FileSet>",
+"<TMPL_VAR Level>",
+"<TMPL_VAR StartTime>",
+"<TMPL_VAR duration>",
+"<TMPL_VAR JobFiles>",
+human_size(<TMPL_VAR JobBytes>),
+"<TMPL_VAR joberrors>",
+"<TMPL_VAR poolname>",
+"<TMPL_LOOP volumes><TMPL_VAR VolumeName>\n</TMPL_LOOP>",
+img
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id0",
+ 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(6)
+ padding: 3
+}
+);
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Locations</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='location_add' title='Add a location' src='/bweb/add.png'>
+ <input type="image" name='action' value='location_del' title='Remove a location' src='/bweb/remove.png'>
+ <input type="image" name='action' value='location_edit' title='Edit a location' src='/bweb/edit.png'>
+
+ <input type="image" name='action' value='media' title='Show content'
+ src='/bweb/zoom.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Name","Enabled", "Cost", "Nb volumes", "Select");
+
+var data = new Array();
+var chkbox;
+
+var img;
+
+<TMPL_LOOP Locations>
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR enabled>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'location';
+chkbox.value = '<TMPL_VAR Location>';
+
+data.push( new Array(
+"<TMPL_VAR Location>",
+img,
+"<TMPL_VAR Cost>",
+"<TMPL_VAR name=volnum>",
+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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Log : <TMPL_VAR name> on <TMPL_VAR client> (<TMPL_VAR jobid>)</h1>
+ </div>
+ <div class='bodydiv'>
+ <pre id='log'>
+<TMPL_VAR lines>
+ </pre>
+ </div>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ Media
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+<TMPL_IF Pool>
+<h2>
+Pool : <a href="?action=pool;pool=<TMPL_VAR Pool>">
+ <TMPL_VAR Pool>
+ </a>
+</h2>
+</TMPL_IF>
+<TMPL_IF Location>
+<h2>
+Location : <TMPL_VAR location>
+</h2>
+</TMPL_IF>
+
+ <form action='?action=test' method='get'>
+ <table id='id_pool_<TMPL_VAR ID>'></table>
+ <input type="image" name='action' value='extern' title='Move out' src='/bweb/extern.png' onclick='return confirm("Do you want to eject selected media ?");'>
+ <input type="image" name='action' value='intern' title='Move in' src='/bweb/intern.png'>
+ <input type="image" name='action' value='update_media' title='Update medium' src='/bweb/edit.png'>
+ <input type="image" name='action' value='media_zoom' title='Information' src='/bweb/zoom.png'>
+<!--
+ <input type="image" name='action' value='purge' title='Purge' src='/bweb/purge.png'>
+-->
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Volume Name","Online","Vol Bytes", "Vol Usage", "Vol Status",
+ "Pool", "Media Type",
+ "Last Written", "When expire ?", "Select");
+
+var data = new Array();
+var img;
+var chkbox;
+var d;
+
+<TMPL_LOOP Medias>
+d = percent_usage(<TMPL_VAR volusage>);
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value = '<TMPL_VAR volumename>';
+
+data.push( new Array(
+"<TMPL_VAR volumename>",
+img,
+human_size(<TMPL_VAR volbytes>),
+d,
+"<TMPL_VAR volstatus>",
+"<TMPL_VAR poolname>",
+"<TMPL_VAR mediatype>",
+"<TMPL_VAR lastwritten>",
+"<TMPL_VAR expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_pool_<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,
+ even_cell_color: even_cell_color,
+ odd_cell_color: odd_cell_color,
+ header_color: header_color,
+ page_nav: true,
+ padding: 3,
+ rows_per_page: rows_per_page,
+ disable_sorting: new Array(1,3,9)
+}
+);
+</script>
--- /dev/null
+<table>
+<td valign='top'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Medium : <TMPL_VAR volumename> <TMPL_VAR comment></h1>
+ </div>
+ <div class='bodydiv'>
+ <b> Medium Infos</b><br/>
+ <table id='id_info_<TMPL_VAR volumename>'></table>
+ <b> Medium Stats</b><br/>
+ <table id='id_media_<TMPL_VAR volumename>'></table>
+ <b> Job List </b></br>
+ <table id='id_jobs_<TMPL_VAR volumename>'></table>
+ <b> Actions </b></br>
+ <form action='?' method='get'>
+ <input type='hidden' name='media' value='<TMPL_VAR volumename>'>
+<TMPL_IF online>
+ <input type="image" name='action' value='extern' onclick='return confirm("Do you want to eject this medium ?");' title='move out' src='/bweb/extern.png'>
+<TMPL_ELSE>
+ <input type="image" name='action' value='intern' title='move in' src='/bweb/intern.png'>
+</TMPL_IF>
+ <input type="image" name='action' value='update_media' title='Update' src='/bweb/edit.png'>
+ <input type="image" name='action' value='purge' title='Purge' src='/bweb/purge.png' onclick="return confirm('Do you want to purge this volume ?')">
+ <input type="image" name='action' value='prune' title='Prune' src='/bweb/prune.png'>
+<TMPL_IF Locationlog>
+ <a href='#' onclick='document.getElementById("locationlog").style.visibility="visible";'><img title='View location log' src='/bweb/zoom.png'></a>
+</TMPL_IF>
+ </form>
+ </div>
+</td>
+<td valign='top'style="visibility:hidden;" id='locationlog'>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Location log </h1>
+ </div>
+ <div class='bodydiv'>
+<pre>
+ <TMPL_VAR LocationLog>
+</pre>
+ </div>
+</td>
+</table>
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Pool","Online","Location","Vol Status", "Vol Bytes", "Expire",
+ "Retention","Max use duration", "Max jobs" );
+
+var data = new Array();
+var img;
+
+img = document.createElement('IMG');
+img.src = '/bweb/inflag<TMPL_VAR online>.png';
+
+data.push( new Array(
+"<TMPL_VAR poolname>",
+img,
+"<TMPL_VAR location>",
+"<TMPL_VAR volstatus>",
+human_size(<TMPL_VAR nb_bytes>),
+"<TMPL_VAR expire>",
+human_sec(<TMPL_VAR volretention>),
+human_sec(<TMPL_VAR voluseduration>),
+"<TMPL_VAR maxvoljobs>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_info_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+ padding: 3,
+ disable_sorting: new Array(1)
+}
+);
+
+var header = new Array( "Vol Mounts", "Recycle count", "Read time", "Write time", "Errors");
+
+var data = new Array();
+data.push( new Array(
+"<TMPL_VAR nb_mounts>",
+"<TMPL_VAR recyclecount>",
+human_sec(<TMPL_VAR volreadtime>),
+human_sec(<TMPL_VAR volwritetime>),
+"<TMPL_VAR nb_errors>"
+ )
+);
+
+nrsTable.setup(
+{
+ table_name: "id_media_<TMPL_VAR volumename>",
+ table_header: header,
+ up_icon: up_icon,
+ down_icon: down_icon,
+ prev_icon: prev_icon,
+ next_icon: next_icon,
+ rew_icon: rew_icon,
+ fwd_icon: fwd_icon,
+ table_data: data,
+ header_color: header_color,
+// disable_sorting: new Array()
+ padding: 3
+}
+);
+
+
+var header = new Array("JobId","Name","Start Time","Type",
+ "Level", "Files", "Bytes", "Status");
+
+var data = new Array();
+var a;
+var img;
+
+<TMPL_LOOP jobs>
+a = document.createElement('A');
+a.href='?action=job_zoom;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src="/bweb/<TMPL_VAR status>.png";
+img.title=jobstatus['<TMPL_VAR status>'];
+
+a.appendChild(img);
+
+data.push( new Array(
+"<TMPL_VAR jobid>",
+"<TMPL_VAR name>",
+"<TMPL_VAR starttime>",
+"<TMPL_VAR type>",
+"<TMPL_VAR level>",
+"<TMPL_VAR files>",
+human_size(<TMPL_VAR bytes>),
+a
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id_jobs_<TMPL_VAR volumename>",
+ 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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Pools</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <input type="image" type='submit' name='action' value='media' title='Show content' src='/bweb/zoom.png'>
+<TMPL_LOOP MediaType>
+ <input type='hidden' name='mediatype' value=<TMPL_VAR name>>
+</TMPL_LOOP>
+ </form>
+ <br/>
+ Tips: To modify pool properties, you have to edit your Bacula configuration
+ and reload it. After, you have to run "update pool=mypool" with bconsole.
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Name","Recycle","Retention","Use Duration",
+ "Max jobs per volume","Max files per volume",
+ "Max volume size","Nb volumes", "Vol Status", "Usage", "Select");
+
+var data = new Array();
+var chkbox;
+var img;
+var img2;
+
+<TMPL_LOOP Pools>
+
+img = percent_display([
+<TMPL_IF nb_recycle>{ name: "Recycle", nb: <TMPL_VAR nb_recycle> },</TMPL_IF>
+<TMPL_IF nb_purged> { name: "Purged", nb: <TMPL_VAR nb_purged> },</TMPL_IF>
+<TMPL_IF nb_append> { name: "Append", nb: <TMPL_VAR nb_append> },</TMPL_IF>
+<TMPL_IF nb_full> { name: "Full", nb: <TMPL_VAR nb_full> }, </TMPL_IF>
+<TMPL_IF nb_disabled> { name: "Disabled", nb: <TMPL_VAR nb_disabled> }, </TMPL_IF>
+<TMPL_IF nb_error> { name: "Error", nb: <TMPL_VAR nb_error> }, </TMPL_IF>
+<TMPL_IF nb_archive>{ name: "Archive", nb: <TMPL_VAR nb_archive> },</TMPL_IF>
+<TMPL_IF nb_used> { name: "Used", nb: <TMPL_VAR nb_used> }, </TMPL_IF>
+<TMPL_IF NAME='nb_read-only'> { name: "Read-Only", nb: <TMPL_VAR NAME='nb_read-only'> }, </TMPL_IF>
+{}
+]);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.value = '<TMPL_VAR Name>';
+chkbox.name = 'pool';
+
+img2 = percent_usage(<TMPL_VAR poolusage>);
+
+data.push( new Array(
+"<TMPL_VAR Name>",
+"<TMPL_VAR Recycle>",
+human_sec(<TMPL_VAR VolRetention>),
+human_sec(<TMPL_VAR VolUseDuration>),
+"<TMPL_VAR MaxVolJobs>",
+"<TMPL_VAR MaxVolFiles>",
+human_size(<TMPL_VAR MaxVolBytes>),
+"<TMPL_VAR VolNum>",
+img,
+img2,
+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>
--- /dev/null
+</div>
+<div style="float: right;"></div>
+</div>
+</body>
+</html>
--- /dev/null
+<h1>An error has occurred :</h1>
+<pre>
+<TMPL_VAR NAME=error>
+</pre>
--- /dev/null
+ <div class='titlediv'>
+ <h1 class='newstitle'>
+ FileSet <TMPL_VAR fileset>
+ </h1>
+ </div>
+ <div class='bodydiv'>
+
+ <img src="/bweb/add.png" alt="included"> What is included :
+ <pre>
+<TMPL_LOOP I><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+ <img src="/bweb/remove.png" alt="excluded"> What is excluded :
+ <pre>
+<TMPL_LOOP E><TMPL_VAR file>
+</TMPL_LOOP></pre>
+
+Tips: Warning, this is the current fileset, it could have changed ...
+
+ </div>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> Information about <TMPL_VAR filename> </h1>
+</div>
+<div class='bodydiv'>
+ <table>
+ <tr><td>size : </td> <td> <TMPL_VAR size> </td></tr>
+ <tr><td>ctime :</td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>mtime :</td> <td> <TMPL_VAR mtime> </td></tr>
+ <tr><td>atime :</td> <td> <TMPL_VAR atime> </td></tr>
+ <tr><td>md5 : </td> <td> <TMPL_VAR md5> </td></tr>
+ </table>
+</div>
--- /dev/null
+
+<script type="text/javascript" language="JavaScript">
+bweb_add_refresh();
+</script>
+<div class='titlediv'>
+ <h1 class="newstitle">
+ Information
+ </h1>
+</div>
+<div class="bodydiv">
+ <table>
+ <tr><td>Total clients:</td> <td> <TMPL_VAR nb_client> </td>
+ <td>Total bytes stored:</td> <td> <TMPL_VAR nb_bytes> </td>
+ <td>Total media:</td> <td> <TMPL_VAR nb_media> </td>
+ </tr>
+ <tr><td>Database size:</td> <td> <TMPL_VAR db_size> </td>
+ <td>Total Pool:</td> <td> <TMPL_VAR nb_pool> </td>
+ <td>Total Job:</td> <td> <TMPL_VAR nb_job> </td>
+ </tr>
+ <tr><td>Job failed (<TMPL_VAR label>):</td>
+
+<td <TMPL_IF nb_err> class='joberr' </TMPL_IF>>
+ <TMPL_VAR nb_err>
+</td>
+ <td></td> <td></td>
+ <td></td> <td></td>
+ </tr>
+
+ </table>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Statistics</h1>
+ </div>
+ <div class='bodydiv'>
+<table border='0'>
+<td>
+<form name='form1' action='?' method='GET'>
+ <div class="otherboxtitle">
+ Options
+ </div>
+ <div class="otherbox">
+<table border='0'>
+<tr>
+ <td valign='top'>
+ <h2>Level</h2>
+ <select name='level' class='formulaire'>
+ <option id='level_Any' value='Any'>Any</option>
+ <option id='level_F' value='F'>Full</option>
+ <option id='level_D' value='D'>Differential</option>
+ <option id='level_I' value='I'>Incremental</option>
+ </select>
+ </td><td valign='top'>
+ <h2>Status</h2>
+ <select name='status' class='formulaire'>
+ <option id='status_Any' value='Any'>Any</option>
+ <option id='status_T' value='T'>Ok</option>
+ <option id='status_f' value='f'>Error</option>
+ <option id='status_A' value='A'>Canceled</option>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Age</h2>
+ <select name='age' class='formulaire'>
+ <option id='age_604800' value='604800'>This week</option>
+ <option id='age_2678400' value='2678400'>Last 30 days</option>
+ <option id='age_15552000' value='15552000'>Last 6 month</option>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Size</h2>
+ Width: <input class='formulaire' type='text'
+ name='width' value='<TMPL_VAR width>' size='4'><br/>
+ Height: <input type='text' class='formulaire'
+ name='height' value='<TMPL_VAR height>' size='4'><br/>
+ </td>
+</tr>
+<tr>
+ <td valign='top'>
+ <h2>Clients</h2>
+ <select name='client' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_clients>
+ <option id='client_<TMPL_VAR clientname>'><TMPL_VAR clientname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ <td valign='top'>
+ <h2>Job Name</h2>
+ <select name='jobname' size='15' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_jobnames>
+ <option><TMPL_VAR NAME=jobname></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+</tr>
+<tr>
+ <td> <h2> Type </h2>
+ <select name='graph' class='formulaire'>
+ <option id='job_size' value='job_size' title="Job size per job for the period">Job Size</option>
+ <option id='job_duration' value='job_duration' title="Job duration per job for the period">Job Duration</option>
+ <option id='job_rate' value='job_rate' title="Job rate per job for the period">Job Rate</option>
+ <option id='job_file' value='job_file' title="Number of backed files per job for the period">Job Files</option>
+ <option id='job_count_phour' value='job_count_phour' title="Number of jobs per hour for the period">Job per hour</option>
+ <option id='job_count_pday' value='job_count_pday' title="Number of jobs per day for the period">Job per day</option>
+ <option id='job_avg_phour' value='job_avg_pday' title="Average backup size per day for the period">Job avg B/hour</option>
+ <option id='job_avg_pday' value='job_avg_pday' title="Average backup size per hour for the period">Job avg B/day</option>
+ <option id='job_sum_phour' value='job_sum_phour' title="Job size per hour">Job total B/hour</option>
+ <option id='job_sum_pday' value='job_sum_pday' title="Job size per day">Job total B/day</option>
+ <option id='job_count_hour' value='job_count_hour' title="Number of jobs per hour for the period">Jobs Count (h)</option>
+ <option id='job_count_day' value='job_count_day' title="Number of jobs per day for the period">Jobs Count (d)</option>
+ <option id='job_avg_hour' value='job_avg_hour' title="Average backup size per hour for the period">Job avg size (h)</option>
+ <option id='job_avg_day' value='job_avg_day' title="Average backup size per day for the period">Job avg size (d)</option>
+ <option id='job_sum_hour' value='job_sum_hour' title="Job size per hour for the period">Job Bytes (h)</option>
+ <option id='job_sum_day' value='job_sum_day' title="Job size per day for the period">Job Bytes (d)</option>
+ </select>
+ </td>
+ <td valign='bottom'>
+ <h2>Number of items</h2>
+ <input type='text' name='limit' value='<TMPL_VAR NAME=limit>'
+ class='formulaire' size='4'>
+ </td>
+</tr>
+<tr>
+<td><h2> Graph type </h2>
+ <select name='gtype' class='formulaire'>
+ <option id='gtype_bars' value='bars'>Bars</option>
+<!-- <option id='gtype_bars3d' value='bars3d'>Bars3d</option> -->
+ <option id='gtype_lines' value='lines'>Lines</option>
+ <option id='gtype_linespoints' value='linespoints'>Lines points</option>
+</td>
+<td>
+ <input type='submit' name='action' value='graph' class='formulaire'>
+</td>
+</tr>
+</table>
+ </div>
+
+</form>
+</td>
+<td>
+
+ <div class="otherboxtitle">
+ Current
+ </div>
+ <div class="otherbox">
+ <img src='bgraph.pl?<TMPL_VAR NAME=url>' alt='Nothing to display, Try a bigger date range'>
+ </div>
+
+</td>
+</table>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+ <TMPL_LOOP qclients>
+ document.getElementById('client_' + <TMPL_VAR name>).selected = true;
+ </TMPL_LOOP>
+
+ <TMPL_IF status>
+ document.getElementById('status_<TMPL_VAR status>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF level>
+ document.getElementById('level_<TMPL_VAR level>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF age>
+ document.getElementById('age_<TMPL_VAR age>').selected=true;
+ </TMPL_IF>
+
+<TMPL_IF qfilesets>
+ for (var i=0; i < document.form1.fileset.length; ++i) {
+ <TMPL_LOOP qfilesets>
+ if (document.form1.fileset[i].value == <TMPL_VAR name>) {
+ document.form1.fileset[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+<TMPL_IF qjobnames>
+ for (var i=0; i < document.form1.jobname.length; ++i) {
+ <TMPL_LOOP qjobnames>
+ if (document.form1.jobname[i].value == <TMPL_VAR name>) {
+ document.form1.jobname[i].selected = true;
+ }
+ </TMPL_LOOP>
+ }
+</TMPL_IF>
+
+ <TMPL_IF graph>
+ document.getElementById('<TMPL_VAR graph>').selected=true;
+ </TMPL_IF>
+
+ <TMPL_IF gtype>
+ document.getElementById('gtype_<TMPL_VAR gtype>').selected=true;
+ </TMPL_IF>
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Help ejecting media (part 1/2)</h1>
+</div>
+<div class='bodydiv'>
+This tool will select the best candidates to eject. You will
+be asked to make your selection on the next screen.
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_pools>
+ <option><TMPL_VAR NAME=name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Media Type:</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP NAME=db_mediatypes>
+ <option><TMPL_VAR NAME=mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Location : </td>
+ <td><select name='location' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Number of media <br/> to eject:</td>
+ <td> <input type='text' name='limit' size='3' class='formulaire'
+ value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_extern_media' title='Next' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Help to eject media (part 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Now, you can verify the selection and eject the media.
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='extern' src='/bweb/extern.png' title='Eject selection'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Volume Name","Vol Status",
+ "Media Type","Pool Name","Last Written",
+ "When expire ?", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR NAME=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Help loading media (part 1/2)</h1>
+</div>
+<div class="bodydiv">
+This tool will select the best candidates to load. You will
+be asked to make your selection on the next screen.
+ <form action="?" method='GET'>
+ <table>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire' multiple>
+<TMPL_LOOP db_pools>
+ <option><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Media Type:</td>
+ <td><select name='mediatype' class='formulaire' multiple>
+<TMPL_LOOP db_mediatypes>
+ <option><TMPL_VAR mediatype></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>
+ Location :
+ </td><td><select name='location' class='formulaire'>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>Expired :</td>
+ <td> <input type='checkbox' name='expired' class='formulaire'
+ checked> </td>
+ </tr>
+ <tr>
+ <td>Number of media <br/> to load:</td>
+ <td> <input type='text' name='limit' class='formulaire'
+ size='3' value='10'> </td>
+ </tr>
+ <tr>
+ <td><input type="image" name='action' value='compute_intern_media'
+ title='Next' src='/bweb/next.png'>
+ </td><td/>
+ </tr>
+ </table>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Help to load media (part 2/2)</h1>
+ </div>
+ <div class='bodydiv'>
+ Now, you can verify the selection and load the media.
+ <form action='?' method='get'>
+ <table id='compute'></table>
+ <table><tr>
+ <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="image" name='action' value='move_media'
+ src='/bweb/intern.png'>
+ </td></tr>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Volume Name","Vol Status",
+ "Media Type","Pool Name","Last Written",
+ "When expire ?", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=Medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.name = 'media';
+chkbox.value= '<TMPL_VAR NAME=volumename>';
+chkbox.checked = 'on';
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=volstatus>",
+"<TMPL_VAR NAME=mediatype>",
+"<TMPL_VAR NAME=name>",
+"<TMPL_VAR NAME=lastwritten>",
+"<TMPL_VAR NAME=expire>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "compute",
+ 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>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Install notes </h1>
+</div>
+<div class='bodydiv'>
+
+
+
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> New location </h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <table>
+ <tr><td>Location :</td>
+ <td>
+ <input class="formulaire" type='text' value='' size='32' name='location'>
+ </td>
+ </tr>
+ <tr><td>Cost :</td>
+ <td> <input class="formulaire" type='text' value='10' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Enabled :</td>
+ <td> <input class="formulaire" type='checkbox' value='10' name='enabled'>
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_add' title="save"
+ src='/bweb/save.png'>
+ </form>
+
+Tips: It's a good idea to have a location per autochanger.
+
+</div>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Location : <TMPL_VAR Location></h1>
+</div>
+<div class='bodydiv'>
+ <form action="?" method='get'>
+ <input type='hidden' name='location' value='<TMPL_VAR location>'>
+ <table>
+ <tr><td>Location :</td>
+ <td>
+ <input class="formulaire" type='text' value='<TMPL_VAR location>' size='32' name='newlocation'>
+ </td>
+ </tr>
+ <tr><td>Cost :</td>
+ <td> <input class="formulaire" type='text' value='<TMPL_VAR cost>' name='cost' size='3'>
+ </td>
+ </tr>
+ <tr><td>Enabled :</td>
+ <td> <input class="formulaire" type='checkbox' name='enabled' <TMPL_IF enabled> checked </TMPL_IF> >
+ </td>
+ </tr>
+ </table>
+ <input type="image" name='action' value='location_save'
+ src='/bweb/save.png'>
+ </form>
+</div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Move media</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <table border='0'>
+ <tr><td> New location: </td><td>
+<select name='newlocation' class='formulaire'>
+ <TMPL_LOOP NAME=db_locations>
+ <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>
+</select>
+ </td><tr><td> User: </td><td>
+<input type='text' name='user' value='<TMPL_VAR loginname>' class='formulaire'>
+ </td></tr>
+ </td></tr><tr><td> Comment: </td><td>
+<textarea name="comment" class='formulaire'></textarea>
+ </td></tr>
+ </table>
+ <label>
+ <input type="image" type='submit' name='action' value='change_location' src='/bweb/apply.png'> Move
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Volume Name", "Location", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Defined jobs : </h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Job Name: </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr>
+ </table>
+ <br/>
+ <label>
+ <input type="image" name='action' value='enable_job' title='Enable'
+ src='/bweb/inflag1.png'> Enable
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job' title='Disable'
+ src='/bweb/inflag0.png'> Disable
+ </label>
+ <label>
+ <input type="image" name='action' value='run_job_mod' title='Run now'
+ src='/bweb/R.png'> Run now
+ </label>
+ </form>
+ </div>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Run job : <TMPL_VAR job> on <TMPL_VAR client></h1>
+ </div>
+ <div class='bodydiv'>
+ <form name='form1' action='?' method='GET'>
+ <table border='0'>
+
+ <tr><td>Job Name: </td><td>
+ <select name='job'>
+ <TMPL_LOOP jobs>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Pool: </td><td>
+
+ <select name='pool'>
+ <option value=''></option>
+ <TMPL_LOOP pools>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+ </td></tr><tr><td>Client: </td><td>
+
+ <select name='client'>
+ <TMPL_LOOP clients>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>FileSet: </td><td>
+ <select name='fileset'>
+ <TMPL_LOOP filesets>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Storage: </td><td>
+ <select name='storage'>
+ <TMPL_LOOP storages>
+ <option value='<TMPL_VAR name>'>
+ <TMPL_VAR name>
+ </option>
+ </TMPL_LOOP>
+ </select>
+
+ </td></tr><tr><td>Level: </td><td>
+ <select name='level'>
+ <option id='level_Incremental' value='Incremental'>Incremental</option>
+ <option id='level_Full' value='Full'>Full</option>
+ <option id='level_Differential' value='Differential'>Differential</option>
+ </select>
+
+ </td></tr><tr id='more1' style="visibility:hidden"><td>Start Time: </td><td>
+ <input class='formulaire'
+ type='text' title='YYYY-MM-DD HH:MM:SS'
+ size='17' name='when' value='<TMPL_VAR when>'>
+
+ </td></tr><tr id='more2' style="visibility:hidden"><td>Priority: </td><td>
+ <input class='formulaire' type='text'
+ size='3' name='priority' value='<TMPL_VAR priority>'>
+
+ </td></tr>
+ </table>
+ <br/>
+ <label onclick='
+ document.getElementById("more1").style.visibility="visible";
+ document.getElementById("more2").style.visibility="visible";'>
+ <img title='Display more options' src='/bweb/add.png'>More options</label>
+ <label>
+ <input type="image" name='action' value='run_job_now' title='Run job'
+ src='/bweb/R.png'>Run now
+ </label>
+ <label>
+ <input type="image" name='action' value='fileset_view' title='View FileSet'
+ src='/bweb/zoom.png'>View FileSet
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+ <TMPL_IF job>
+ ok=1;
+ for(var i=0; ok && i < document.form1.job.length; i++) {
+ if (document.form1.job[i].value == '<TMPL_VAR job>') {
+ document.form1.job[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF client>
+ ok=1;
+ for(var i=0; ok && i < document.form1.client.length; i++) {
+ if (document.form1.client[i].value == '<TMPL_VAR client>') {
+ document.form1.client[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF pool>
+ ok=1;
+ for(var i=0; ok && i < document.form1.pool.length; i++) {
+ if (document.form1.pool[i].value == '<TMPL_VAR pool>') {
+ document.form1.pool[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF storage>
+ ok=1;
+ for(var i=0; ok && i < document.form1.storage.length; i++) {
+ if (document.form1.storage[i].value == '<TMPL_VAR storage>') {
+ document.form1.storage[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+ <TMPL_IF level>
+<!-- document.getElementById('level_<TMPL_VAR level>').selected=true; -->
+ </TMPL_IF>
+ <TMPL_IF fileset>
+ ok=1;
+ for(var i=0; ok && i < document.form1.fileset.length; i++) {
+ if (document.form1.fileset[i].value == '<TMPL_VAR fileset>') {
+ document.form1.fileset[i].selected=true;
+ ok=0;
+ }
+ }
+ </TMPL_IF>
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Running Jobs </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ <br/>
+<label>
+<input type='image' name='action' value='dsp_cur_job'
+ title='View job' src='/bweb/zoom.png'>
+</label>
+<label>
+<input type="image" type='submit' name='action' value='cancel_job'
+ onclick="return confirm('Do you want to cancel the selected job?')"
+ title='Cancel job' src='/bweb/cancel.png'>
+</label>
+ </form>
+
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+var header = new Array("JobId",
+ "Client",
+ "Job Name",
+ "Level",
+ "Start Time",
+ "Duration",
+// "Job Files",
+// "Job Bytes",
+ "Status",
+ "Select"
+ );
+
+var data = new Array();
+var chkbox;
+var img;
+
+<TMPL_LOOP NAME=Jobs>
+a = document.createElement('A');
+a.href='?action=dsp_cur_job;jobid=<TMPL_VAR JobId>';
+
+img = document.createElement("IMG");
+img.src = '/bweb/<TMPL_VAR NAME=JobStatus>.png';
+img.title = jobstatus['<TMPL_VAR NAME=JobStatus>'];
+
+a.appendChild(img);
+
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'jobid';
+chkbox.value = '<TMPL_VAR NAME=jobid>';
+
+data.push( new Array(
+"<TMPL_VAR NAME=JobId>",
+"<TMPL_VAR NAME=ClientName>",
+"<TMPL_VAR NAME=JobName>",
+joblevel['<TMPL_VAR NAME=Level>'],
+"<TMPL_VAR NAME=StartTime>",
+"<TMPL_VAR NAME=duration>",
+//"<TMPL_VAR NAME=JobFiles>",
+//"<TMPL_VAR NAME=JobBytes>",
+a,
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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(6)
+ padding: 3
+}
+);
+
+// get newest backup first
+nrsTables['id<TMPL_VAR NAME=ID>'].fieldSort(0);
+
+bweb_add_refresh();
+
+</script>
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'> Next Jobs </h1>
+ </div>
+ <div class='bodydiv'>
+ <form action='?' method='GET'>
+ <table id='id<TMPL_VAR ID>'></table>
+ <label>
+ <input type="image" name='action' value='run_job_mod'
+ src='/bweb/R.png' title='Run now'> Run now
+ </label>
+ <label>
+ <input type="image" name='action' value='disable_job'
+ src='/bweb/inflag0.png' title='Disable'> Disable
+ </label>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Scheduled",
+ "Level",
+ "Type",
+ "Priority",
+ "Name",
+ "Volume",
+ "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP list>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'radio';
+chkbox.name = 'job';
+chkbox.value = '<TMPL_VAR name>';
+
+data.push( new Array(
+"<TMPL_VAR date>",
+"<TMPL_VAR level>",
+"<TMPL_VAR type>",
+"<TMPL_VAR priority>",
+"<TMPL_VAR name>",
+"<TMPL_VAR volume>",
+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: 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(6),
+ padding: 3
+}
+);
+</script>
--- /dev/null
+<div class='titlediv'>
+ <h1 class='newstitle'> Statistics </h1>
+</div>
+<div class='bodydiv'>
+<a href='?action=job;age=172800;jobtype=B'>
+<img width='450' height='200' src='bgraph.pl?age=2678400;width=450;height=200;graph=job_sum_day;limit=500;action=graph;legend=off' alt='Nothing to display'>
+</a>
+</div>
+
--- /dev/null
+<br/>
+ <div class='titlediv'>
+ <h1 class='newstitle'>Update media location</h1>
+ </div>
+ <div class="bodydiv">
+ <form action='?' method='get'>
+ <table id='id<TMPL_VAR NAME=ID>'></table>
+ New location : <select class='formulaire' name='newlocation'>
+ <TMPL_LOOP NAME=db_locations>
+ <option id='loc_<TMPL_VAR NAME=location>' value='<TMPL_VAR NAME=location>'><TMPL_VAR NAME=location></option>
+ </TMPL_LOOP>
+ </select>
+ <input type="image" name='action' value='save_location' src='/bweb/apply.png'>
+ </form>
+ </div>
+
+<script type="text/javascript" language="JavaScript">
+
+var header = new Array("Volume Name", "Location", "Select");
+
+var data = new Array();
+var chkbox;
+
+<TMPL_LOOP NAME=medias>
+chkbox = document.createElement('INPUT');
+chkbox.type = 'checkbox';
+chkbox.value = '<TMPL_VAR name=volumename>';
+chkbox.name = 'media';
+chkbox.checked = 1;
+
+data.push( new Array(
+"<TMPL_VAR NAME=volumename>",
+"<TMPL_VAR NAME=location>",
+chkbox
+ )
+);
+</TMPL_LOOP>
+
+nrsTable.setup(
+{
+ table_name: "id<TMPL_VAR NAME=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
+}
+);
+
+<TMPL_IF qnewlocation>
+ document.getElementById('loc_' + <TMPL_VAR qnewlocation>).selected=true;
+</TMPL_IF>
+
+</script>
--- /dev/null
+<br/>
+<div class='titlediv'>
+ <h1 class='newstitle'> Update media <TMPL_VAR volumename></h1>
+</div>
+<div class='bodydiv'>
+ <form name='form1' action="?" method='GET'>
+ <table>
+ <tr><td>Volume Name:</td>
+ <td><input type='text' name='media' class='formulaire' value='<TMPL_VAR volumename>' title='Change this to update an other media'>
+ </td>
+ </tr>
+ <tr><td>Pool:</td>
+ <td><select name='pool' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td>Status:</td>
+ <td><select name='volstatus' class='formulaire'>
+ <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='Read-Only'>Read-Only</option>
+ <option value='Used'>Used</option>
+ </select>
+ </td>
+ </tr>
+
+ <tr><td>Slot:</td>
+ <td>
+ <input class='formulaire' type='text'
+ name='slot' value='<TMPL_VAR slot>'>
+ </td>
+ </tr>
+
+ <tr><td>InChanger Flag:</td>
+ <td>
+ <input class='formulaire' type='checkbox'
+ name='inchanger' <TMPL_IF inchanger>checked</TMPL_IF>>
+ </td>
+ </tr>
+
+ <tr><td> Location : </td>
+ <td><select name='location' class='formulaire'>
+ <option value=''></option>
+ <TMPL_LOOP db_locations>
+ <option value='<TMPL_VAR location>'><TMPL_VAR location></option>
+ </TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Retention period: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='volretention' value='<TMPL_VAR volretention>'>
+ </td>
+ </tr>
+ <tr><td> Use duration: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 3 days, 1 month'
+ name='voluseduration' value='<TMPL_VAR voluseduration>'>
+ </td>
+ </tr>
+ <tr><td> Max Jobs: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10'
+ name='maxvoljobs' value='<TMPL_VAR maxvoljobs>'>
+ </td>
+ </tr>
+ <tr><td> Max Files: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10000'
+ name='maxvolfiles' value='<TMPL_VAR maxvolfiles>'>
+ </td>
+ </tr>
+ <tr><td> Max Bytes: </td>
+ <td>
+ <input class='formulaire' type='text' title='ex: 10M, 11G'
+ name='maxvolbytes' value='<TMPL_VAR maxvolbytes>'>
+ </td>
+ </tr>
+ <tr><td>Recycle Pool:</td>
+ <td><select name='poolrecycle' class='formulaire'>
+<TMPL_LOOP db_pools>
+ <option value='<TMPL_VAR name>'><TMPL_VAR name></option>
+</TMPL_LOOP>
+ </select>
+ </td>
+ </tr>
+ <tr><td> Comment: </td>
+ <td>
+ <input class='formulaire' type='text' title='a comment'
+ name='comment' value='<TMPL_VAR comment>'>
+ </td>
+ </tr>
+
+ </table>
+<table>
+ <td>
+ <label>
+ <input type="image" name='action' value='do_update_media' src='/bweb/apply.png'> Apply
+ </label>
+ <label>
+ <input type="image" name='action' title='Update from pool'
+ value='update_from_pool' src='/bweb/update.png'> Update from pool
+ </label>
+ </form>
+ </td>
+ <td>
+ <form action='?' method='GET'>
+ <input type='hidden' name='pool' value='<TMPL_VAR poolname>'>
+ <label>
+ <input type="image" name='action' value='media'
+ src='/bweb/zoom.png'>View Pool
+ </label>
+ </form>
+ </td>
+</table>
+</div>
+
+<script type="text/javascript" language='JavaScript'>
+var ok=1;
+for (var i=0; ok && i < document.form1.pool.length; ++i) {
+ if (document.form1.pool[i].value == '<TMPL_VAR poolname>') {
+ document.form1.pool[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.pool.length; ++i) {
+ if (document.form1.poolrecycle[i].value == '<TMPL_VAR poolrecycle>') {
+ document.form1.poolrecycle[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.location.length; ++i) {
+ if (document.form1.location[i].value == '<TMPL_VAR location>') {
+ document.form1.location[i].selected = true;
+ ok=0;
+ }
+}
+
+ok=1;
+for (var i=0; ok && i < document.form1.volstatus.length; ++i) {
+ if (document.form1.volstatus[i].value == '<TMPL_VAR volstatus>') {
+ document.form1.volstatus[i].selected = true;
+ ok=0;
+ }
+}
+
+</script>