From 9a152871403282cfbe18d9ef807468dabcd830ca Mon Sep 17 00:00:00 2001 From: Eric Bollengier Date: Wed, 26 Sep 2007 21:39:27 +0000 Subject: [PATCH] ebl Add brestore ajax port. git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@5664 91ce42f0-d328-0410-95d8-f526ca767f89 --- gui/bweb/INSTALL | 20 ++ gui/bweb/ReleaseNotes | 3 + gui/bweb/cgi/bresto.pl | 535 ++++++++++++++++++++++++++++++++++++++ gui/bweb/html/bresto.html | 27 ++ gui/bweb/html/bresto.js | 524 +++++++++++++++++++++++++++++++++++++ 5 files changed, 1109 insertions(+) create mode 100755 gui/bweb/cgi/bresto.pl create mode 100644 gui/bweb/html/bresto.html create mode 100644 gui/bweb/html/bresto.js diff --git a/gui/bweb/INSTALL b/gui/bweb/INSTALL index 620ee32c4b..857070914a 100644 --- a/gui/bweb/INSTALL +++ b/gui/bweb/INSTALL @@ -17,6 +17,7 @@ Bweb works well with 1.39 release or later. 11) setting mysql read-only account 12) get more statistics 13) use groups with bweb +14) setup restoration in bweb (not yet working) ################ FILE COPY ##################################### # you must get bweb svn files @@ -322,6 +323,25 @@ It works with postgresql and mysql5 (4 not tested). With mysql, load bweb/script/bweb-mysql.sql into your catalog For postgresql, it will be done with bweb/script/bweb-postgresql.sql (already done) +################ MADE RESTORATION WITH BWEB #################### + +Warning, this function is not for production use at this time ! +It will do some basics things on a working bweb/brestore setup. + +1) Go to http://extjs.com and download their toolkit (last 1.X release) +2) Install files in /bweb/ext web root + example on debian : + root@localhost:~# mv ext-1.1.1 /usr/share/bweb/html/ext + +3) Make sure that brestore cache tables are updated with brestore.pl + bacula@localhost:~$ brestore.pl -b + +4) Enable bresto.pl cgi. + edit the bweb/cgi/bresto.pl script and change $bresto_enable=0; to $bresto_enable=1; + on the top of the file. + +4) Go on http://you-director/bweb/bresto.html + ################################################################ Enjoy ! diff --git a/gui/bweb/ReleaseNotes b/gui/bweb/ReleaseNotes index a4c3f51ee1..afacabf50a 100644 --- a/gui/bweb/ReleaseNotes +++ b/gui/bweb/ReleaseNotes @@ -1,5 +1,8 @@ Release Notes for bweb 2.2 +2007/09/26 + - add bresto.pl, bresto.html and bresto.js + 2007/08/20 - fix update of locationid field during label barcodes - use update recyclepool= instead of UPDATE Media SET RecyclePoolId diff --git a/gui/bweb/cgi/bresto.pl b/gui/bweb/cgi/bresto.pl new file mode 100755 index 0000000000..c6a54f3b23 --- /dev/null +++ b/gui/bweb/cgi/bresto.pl @@ -0,0 +1,535 @@ +#!/usr/bin/perl -w + +my $bresto_enable = 0; +die "bresto is not enabled" if (not $bresto_enable); + +=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; + +package Bvfs; +use base qw/Bweb/; + +sub get_root +{ + my ($self, $dir) = @_; + return $self->get_pathid(''); +} + +sub ch_dir +{ + my ($self, $pathid) = @_; + $self->{cwdid} = $pathid; +} + +sub up_dir +{ + my ($self) = @_ ; + my $query = " + SELECT PPathId + FROM brestore_pathhierarchy + WHERE PathId IN ($self->{cwdid}) "; + + my $all = $self->dbh_selectall_arrayref($query); + return unless ($all); # already at root + + my $dir = join(',', map { $_->[0] } @$all); + if ($dir) { + $self->ch_dir($dir); + } +} + +sub pwd +{ + my ($self) = @_; + return $self->get_path($self->{cwdid}); +} + +sub get_path +{ + my ($self, $pathid) = @_; + $self->debug("Call with pathid = $pathid"); + my $query = + "SELECT Path FROM Path WHERE PathId IN (?)"; + + my $sth = $self->dbh_prepare($query); + $sth->execute($pathid); + my $result = $sth->fetchrow_arrayref(); + $sth->finish(); + return $result->[0]; +} + +sub set_curjobids +{ + my ($self, @jobids) = @_; + $self->{curjobids} = join(',', @jobids); +# $self->update_brestore_table(@jobids); +} + +sub get_pathid +{ + my ($self, $dir) = @_; + my $query = + "SELECT PathId FROM Path WHERE Path = ?"; + my $sth = $self->dbh_prepare($query); + $sth->execute($dir); + my $result = $sth->fetchall_arrayref(); + $sth->finish(); + + return join(',', map { $_->[0] } @$result); +} + +sub set_limits +{ + my ($self, $offset, $limit) = @_; + $self->{limit} = $limit || 100; + $self->{offset} = $offset || 0; +} + +sub ls_files +{ + my ($self) = @_; + + return undef unless ($self->{curjobids}); + + my $inclause = $self->{curjobids}; + my $inlistpath = $self->{cwdid}; + + my $query = +"SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId + FROM + (SELECT Filename.Name, max(File.FileId) as id + FROM File, Filename + WHERE File.FilenameId = Filename.FilenameId + AND Filename.Name != '' + AND File.PathId IN ($inlistpath) + AND File.JobId IN ($inclause) + GROUP BY Filename.Name + ORDER BY Filename.Name) AS listfiles, +File +WHERE File.FileId = listfiles.id"; + + $self->debug($query); + my $result = $self->dbh_selectall_arrayref($query); + $self->debug($result); + + return $result; +} + + +# return ($dirid,$dir_basename,$lstat,$jobid) +sub ls_dirs +{ + my ($self) = @_; + + return undef unless ($self->{curjobids}); + + my $pathid = $self->{cwdid}; + my $jobclause = $self->{curjobids}; + + # Let's retrieve the list of the visible dirs in this dir ... + # First, I need the empty filenameid to locate efficiently the dirs in the file table + my $query = "SELECT FilenameId FROM Filename WHERE Name = ''"; + my $sth = $self->dbh_prepare($query); + $sth->execute(); + my $result = $sth->fetchrow_arrayref(); + $sth->finish(); + my $dir_filenameid = $result->[0]; + + # Then we get all the dir entries from File ... + $query = " +SELECT PathId, Path, JobId, Lstat FROM ( + + SELECT Path1.PathId, Path1.Path, lower(Path1.Path), + listfile1.JobId, listfile1.Lstat + FROM ( + SELECT DISTINCT brestore_pathhierarchy1.PathId + FROM brestore_pathhierarchy AS brestore_pathhierarchy1 + JOIN Path AS Path2 + ON (brestore_pathhierarchy1.PathId = Path2.PathId) + JOIN brestore_pathvisibility AS brestore_pathvisibility1 + ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId) + WHERE brestore_pathhierarchy1.PPathId = $pathid + AND brestore_pathvisibility1.jobid IN ($jobclause)) AS listpath1 + JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId) + LEFT JOIN ( + SELECT File1.PathId, File1.JobId, File1.Lstat FROM File AS File1 + WHERE File1.FilenameId = $dir_filenameid + AND File1.JobId IN ($jobclause)) AS listfile1 + ON (listpath1.PathId = listfile1.PathId) + ) AS A ORDER BY 2,3 DESC +"; + $self->debug($query); + $sth=$self->dbh_prepare($query); + $sth->execute(); + $result = $sth->fetchall_arrayref(); + my @return_list; + my $prev_dir=''; + foreach my $refrow (@{$result}) + { + my $dirid = $refrow->[0]; + my $dir = $refrow->[1]; + my $lstat = $refrow->[3]; + my $jobid = $refrow->[2] || 0; + next if ($dirid eq $prev_dir); + # We have to clean up this dirname ... we only want it's 'basename' + my $return_value; + if ($dir ne '/') + { + my @temp = split ('/',$dir); + $return_value = pop @temp; + } + else + { + $return_value = '/'; + } + my @return_array = ($dirid,$return_value,$lstat,$jobid); + push @return_list,(\@return_array); + $prev_dir = $dirid; + } + $self->debug(\@return_list); + return \@return_list; +} + + +# Returns list of versions of a file that could be restored +# returns an array of +# (jobid,fileindex,mtime,size,inchanger,md5,volname,fileid) +# there will be only one jobid in the array of jobids... +sub get_all_file_versions +{ + my ($self,$pathid,$fileid,$client,$see_all)=@_; + + defined $see_all or $see_all=0; + + my @versions; + my $query; + $query = +"SELECT File.JobId, File.FileId, File.Lstat, + File.Md5, Media.VolumeName, Media.InChanger + FROM File, Job, Client, JobMedia, Media + WHERE File.FilenameId = $fileid + AND File.PathId=$pathid + AND File.JobId = Job.JobId + AND Job.ClientId = Client.ClientId + AND Job.JobId = JobMedia.JobId + AND File.FileIndex >= JobMedia.FirstIndex + AND File.FileIndex <= JobMedia.LastIndex + AND JobMedia.MediaId = Media.MediaId + AND Client.Name = '$client'"; + + $self->debug($query); + + my $result = $self->dbh_selectall_arrayref($query); + + foreach my $refrow (@$result) + { + my ($jobid, $fid, $lstat, $md5, $volname, $inchanger) = @$refrow; + my @attribs = parse_lstat($lstat); + my $mtime = array_attrib('st_mtime',\@attribs); + my $size = array_attrib('st_size',\@attribs); + + my @list = ($pathid,$fileid,$jobid, + $fid, $mtime, $size, $inchanger, + $md5, $volname); + push @versions, (\@list); + } + + # We have the list of all versions of this file. + # We'll sort it by mtime desc, size, md5, inchanger desc + # the rest of the algorithm will be simpler + # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname) + @versions = sort { $b->[4] <=> $a->[4] + || $a->[5] <=> $b->[5] + || $a->[7] cmp $a->[7] + || $b->[6] <=> $a->[6]} @versions; + + + my @good_versions; + my %allready_seen_by_mtime; + my %allready_seen_by_md5; + # Now we should create a new array with only the interesting records + foreach my $ref (@versions) + { + if ($ref->[7]) + { + # The file has a md5. We compare his md5 to other known md5... + # We take size into account. It may happen that 2 files + # have the same md5sum and are different. size is a supplementary + # criterion + + # If we allready have a (better) version + next if ( (not $see_all) + and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}); + + # we never met this one before... + $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1; + } + # Even if it has a md5, we should also work with mtimes + # We allready have a (better) version + next if ( (not $see_all) + and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]}); + $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1; + + # We reached there. The file hasn't been seen. + push @good_versions,($ref); + } + + # To be nice with the user, we re-sort good_versions by + # inchanger desc, mtime desc + @good_versions = sort { $b->[4] <=> $a->[4] + || $b->[2] <=> $a->[2]} @good_versions; + + return \@good_versions; +} +{ + my %attrib_name_id = ( 'st_dev' => 0,'st_ino' => 1,'st_mode' => 2, + 'st_nlink' => 3,'st_uid' => 4,'st_gid' => 5, + 'st_rdev' => 6,'st_size' => 7,'st_blksize' => 8, + 'st_blocks' => 9,'st_atime' => 10,'st_mtime' => 11, + 'st_ctime' => 12,'LinkFI' => 13,'st_flags' => 14, + 'data_stream' => 15);; + sub array_attrib + { + my ($attrib,$ref_attrib)=@_; + return $ref_attrib->[$attrib_name_id{$attrib}]; + } + + sub file_attrib + { # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId] + + my ($file, $attrib)=@_; + + if (defined $attrib_name_id{$attrib}) { + + my @d = split(' ', $file->[3]) ; # TODO : cache this + + return from_base64($d[$attrib_name_id{$attrib}]); + + } elsif ($attrib eq 'jobid') { + + return $file->[4]; + + } elsif ($attrib eq 'name') { + + return $file->[2]; + + } else { + die "Attribute not known : $attrib.\n"; + } + } + + sub lstat_attrib + { + my ($lstat,$attrib)=@_; + if ($lstat and defined $attrib_name_id{$attrib}) + { + my @d = split(' ', $lstat) ; # TODO : cache this + return from_base64($d[$attrib_name_id{$attrib}]); + } + return 0; + } +} + +{ + # Base 64 functions, directly from recover.pl. + # Thanks to + # Karl Hakimian + # This section is also under GPL v2 or later. + my @base64_digits; + my @base64_map; + my $is_init=0; + sub init_base64 + { + @base64_digits = ( + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' + ); + @base64_map = (0) x 128; + + for (my $i=0; $i<64; $i++) { + $base64_map[ord($base64_digits[$i])] = $i; + } + $is_init = 1; + } + + sub from_base64 { + if(not $is_init) + { + init_base64(); + } + my $where = shift; + my $val = 0; + my $i = 0; + my $neg = 0; + + if (substr($where, 0, 1) eq '-') { + $neg = 1; + $where = substr($where, 1); + } + + while ($where ne '') { + $val *= 64; + my $d = substr($where, 0, 1); + $val += $base64_map[ord(substr($where, 0, 1))]; + $where = substr($where, 1); + } + + return $val; + } + + sub parse_lstat { + my ($lstat)=@_; + my @attribs = split(' ',$lstat); + foreach my $element (@attribs) + { + $element = from_base64($element); + } + return @attribs; + } +} + + +################################################################ + + +package main; +use strict; +use Bweb; + +my $conf = new Bweb::Config(config_file => $Bweb::config_file); +$conf->load(); + +my $bvfs = new Bvfs(info => $conf); +$bvfs->connect_db(); + +my $action = CGI::param('action') || ''; + +my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid', + 'limit', 'offset', 'client'); + +my @jobid = grep { /^\d+$/ } CGI::param('jobid'); + +$bvfs->set_curjobids(@jobid); +$bvfs->set_limits($args->{limit}, $args->{offset}); +#$bvfs->{debug}=1; + +print CGI::header('application/x-javascript'); + +if ($action eq 'list_client') { + + my $q = 'SELECT Name FROM Client'; + my $ret = $bvfs->dbh_selectall_arrayref($q); + + print "["; + print join(',', map { "['$_->[0]']" } @$ret); + print "]\n"; + exit 0; + +} elsif ($action eq 'list_job') { + + my $query = " + SELECT Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus, Job.JobId + FROM Job,Client,FileSet + WHERE Job.ClientId=Client.ClientId + AND Client.Name = '$args->{client}' + AND Job.Type = 'B' + AND JobStatus IN ('f', 'T') + AND Job.FileSetId = FileSet.FileSetId + ORDER BY EndTime desc"; + my $result = $bvfs->dbh_selectall_arrayref($query); + + print "["; + + print join(',', map { + "[$_->[4], '$_->[0] $_->[1] $_->[2] ($_->[3])']" + } @$result); + + print "]\n"; +} + +my $pathid = CGI::param('node') || 0; +if ($pathid =~ /^(\d+)$/) { + $pathid = $1; +} else { + $pathid = $bvfs->get_root(); +} + +$bvfs->ch_dir($pathid); + +if ($action eq 'list_files') { + print "["; + my $files = $bvfs->ls_files(); +# [ 1, 2, 3, "Bill", 10, '2007-01-01 00:00:00'], +# File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId + + print join(',', + map { "[$_->[1], $_->[0], $pathid, \"$_->[2]\", 10, \"2007-01-01 00:00:00\"]" } + @$files); + print "]\n"; + +} elsif ($action eq 'list_dirs') { + + print "["; + my $dirs = $bvfs->ls_dirs(); + # return ($dirid,$dir_basename,$lstat,$jobid) + + print join(',', + map { "{ 'id': '$_->[0]', 'text': '$_->[1]', 'cls':'folder'}" } + @$dirs); + + print "]\n"; + +} elsif ($action eq 'list_versions') { + + print "["; + #  0 1 2 3 4 5 6 7 8 + #($pathid,$fileid,$jobid, $fid, $mtime, $size, $inchanger, $md5, $volname); + my $files = $bvfs->get_all_file_versions($args->{pathid}, $args->{filenameid}, $args->{client}, 1); + print join(',', + map { "[ $_->[3], $_->[1], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5], $_->[4] ]" } + @$files); + print "]\n"; + +} + + + diff --git a/gui/bweb/html/bresto.html b/gui/bweb/html/bresto.html new file mode 100644 index 0000000000..0c7f76afd5 --- /dev/null +++ b/gui/bweb/html/bresto.html @@ -0,0 +1,27 @@ + + + + + Bweb - restore + + + + + + + + + + + + +
+
+
+
+
+
+
+
+ + diff --git a/gui/bweb/html/bresto.js b/gui/bweb/html/bresto.js new file mode 100644 index 0000000000..a46a9c93ea --- /dev/null +++ b/gui/bweb/html/bresto.js @@ -0,0 +1,524 @@ + +// Bweb - A Bacula web interface +// Bacula® - The Network Backup Solution +// +// Copyright (C) 2000-2006 Free Software Foundation Europe e.V. +// +// The main author of Bweb is Eric Bollengier. +// The main author of Bacula is Kern Sibbald, with contributions from +// many others, a complete list can be found in the file AUTHORS. +// +// This program is Free Software; you can redistribute it and/or +// modify it under the terms of version two of the GNU General Public +// License as published by the Free Software Foundation plus additions +// that are listed in the file LICENSE. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +// 02110-1301, USA. +// +// Bacula® is a registered trademark of John Walker. +// The licensor of Bacula is the Free Software Foundation Europe +// (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich, +// Switzerland, email:ftf@fsfeurope.org. + +// render if vol is online/offline +function rd_vol_is_online(val) +{ + return ''; +} + +// TODO: fichier ou rep +function rd_file_or_dir(val) +{ + if (val == 'F') { + return ''; + } else { + return ''; + } +} + +Ext.namespace('Ext.brestore'); + +Ext.brestore.jobid=0; +Ext.brestore.client=''; + +function ext_init() +{ +//////////////////////////////////////////////////////////////: + var Tree = Ext.tree; + var tree_loader = new Ext.tree.TreeLoader({ + baseParams:{}, //action:'list_dirs', pathid:103, jobid:9 }, + dataUrl:'/cgi-bin/bweb/bresto.pl' + }); + + var tree = new Ext.tree.TreePanel('div-tree', { + animate:true, + loader: tree_loader, + enableDD:true, + enableDragDrop: true, + containerScroll: true + }); + + // set the root node + var root = new Ext.tree.AsyncTreeNode({ + text: 'Root', + draggable:false, + id:'source' + }); + tree.setRootNode(root); + + // render the tree + tree.render(); +// root.expand(); + + tree.on('click', function(e) { + file_store.removeAll(); + file_store.load({params:{action: 'list_files', + jobid:Ext.brestore.jobid, + node:e.id} + }); + return true; + }); + + tree.on('beforeload', function(e) { + file_store.removeAll(); + return true; + }); + + +//////////////////////////////////////////////////////////////// + + var file_store = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({ + url: '/cgi-bin/bweb/bresto.pl', + method: 'GET', + params:{action: 'list_files', offset:0, limit:50 } + }), + + reader: new Ext.data.ArrayReader({ + }, Ext.data.Record.create([ + {name: 'fileid' }, + {name: 'filenameid'}, + {name: 'pathid' }, + {name: 'name' }, + {name: 'size', type: 'int' }, + {name: 'mtime', type: 'date', dateFormat: 'Y-m-d h:i:s'} + ])) + }); + + var cm = new Ext.grid.ColumnModel([{ + id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 }) + header: 'File', + dataIndex: 'name', + width: 250, + css: 'white-space:normal;' + },{ + header: "Size", + dataIndex: 'size', + renderer: human_size, + width: 50 + },{ + header: "Date", + dataIndex: 'mtime', + width: 50 + },{ + dataIndex: 'pathid', + hidden: true + },{ + dataIndex: 'filenameid', + hidden: true + },{ + dataIndex: 'fileid', + hidden: true + } + ]); + + // by default columns are sortable + cm.defaultSortable = true; + + // create the grid + var files_grid = new Ext.grid.Grid('div-files', { + ds: file_store, + cm: cm, + ddGroup : 'TreeDD', + enableDrag: true, + enableDragDrop: true, + selModel: new Ext.grid.RowSelectionModel(), + loadMask: true, + enableColLock:false + + }); + + // when we reload the view, + // we clear the file version box + file_store.on('beforeload', function(e) { + file_versions_store.removeAll(); + return true; + }); + + files_grid.selModel.on('rowselect', function(e,i,r) { + Ext.brestore.filename = r.json[3]; + file_versions_store.load({params:{action: 'list_versions', + client: Ext.brestore.client, + pathid: r.json[2], + filenameid: r.json[1] + } + }); + return true; + }); + files_grid.render(); + +//////////////////////////////////////////////////////////////: + + var file_selection_store = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({ + url: '/cgi-bin/bweb/bresto.pl', + method: 'GET', + params:{offset:0, limit:50 } + }), + + reader: new Ext.data.ArrayReader({ + }, Ext.data.Record.create([ + {name: 'jobid' }, + {name: 'fileid' }, + {name: 'filenameid'}, + {name: 'pathid' }, + {name: 'size', type: 'int' }, + {name: 'mtime', type: 'date', dateFormat: 'Y-m-d h:i:s'} + ])) + }); + + var file_selection_cm = new Ext.grid.ColumnModel([{ + id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 }) + dataIndex: 'name', + hidden: true + },{ + header: "JobId", + dataIndex: 'jobid' + },{ + header: "Size", + dataIndex: 'size', + renderer: human_size, + width: 50 + },{ + header: "Date", + dataIndex: 'mtime', + width: 50 + },{ + dataIndex: 'pathid', + hidden: true + },{ + dataIndex: 'filenameid', + hidden: true + },{ + dataIndex: 'fileid', + hidden: true + } + ]); + + + // create the grid + var file_selection_grid = new Ext.grid.Grid('div-file-selection', { + cm: file_selection_cm, + ds: file_selection_store, + ddGroup : 'TreeDD', + enableDragDrop: true, + enableDrop: true, + selModel: new Ext.grid.RowSelectionModel(), + loadMask: true, + enableColLock:false + + }); + +// http://extjs.com/forum/showthread.php?t=12582&highlight=drag+drop +// var ddrow = new Ext.dd.DropTarget(grid.container, { +// ddGroup : 'TreeDD', +// copy:false, +// notifyDrop : function(dd, e, data){ +// var sm=grid.getSelectionModel(); +// var rows=sm.getSelections(); +// var cindex=dd.getDragData(e).rowIndex; +// for(i = 0; i < rows.length; i++) { +// rowData=ds.getById(rows[i].id); +// if(!this.copy) +// ds.remove(ds.getById(rows[i].id)); +// ds.insert(cindex,rowData); +// }; +// } +// }); + + + file_selection_grid.on('enddrag', function(dd,e) { + alert(e) ; return true; + }); + file_selection_grid.on('notifyDrop', function(dd,e) { + alert(e) ; return true; + }); + file_selection_grid.render(); + +/////////////////////////////////////////////////////// + + var file_versions_store = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({ + url: '/cgi-bin/bweb/bresto.pl', + method: 'GET', + params:{offset:0, limit:50 } + }), + + reader: new Ext.data.ArrayReader({ + }, Ext.data.Record.create([ + {name: 'fileid' }, + {name: 'filenameid'}, + {name: 'pathid' }, + {name: 'jobid' }, + {name: 'volume' }, + {name: 'inchanger' }, + {name: 'md5' }, + {name: 'size', type: 'int' }, + {name: 'mtime'} //, type: 'date', dateFormat: 'Y-m-d h:i:s'} + ])) + }); + + var file_versions_cm = new Ext.grid.ColumnModel([{ + id: 'name', // id assigned so we can apply custom css (e.g. .x-grid-col-topic b { color:#333 }) + dataIndex: 'name', + hidden: true + },{ + header: "InChanger", + dataIndex: 'inchanger', + renderer: rd_vol_is_online + },{ + header: "Volume", + dataIndex: 'volume' + },{ + header: "JobId", + dataIndex: 'jobid' + },{ + header: "Size", + dataIndex: 'size', + renderer: human_size, + width: 50 + },{ + header: "Date", + dataIndex: 'mtime', + width: 50 + },{ + header: "MD5", + dataIndex: 'md5', + width: 50 + },{ + dataIndex: 'pathid', + hidden: true + },{ + dataIndex: 'filenameid', + hidden: true + },{ + dataIndex: 'fileid', + hidden: true + } + ]); + + // by default columns are sortable + file_versions_cm.defaultSortable = true; + + // create the grid + var file_versions_grid = new Ext.grid.Grid('div-file-versions', { + ds: file_versions_store, + cm: file_versions_cm, + ddGroup : 'TreeDD', + enableDrag: true, + enableDrop: false, + selModel: new Ext.grid.RowSelectionModel(), + loadMask: true, + enableColLock:false + + }); + + file_versions_grid.on('rowdblclick', function(e) { + alert(e) ; file_versions_store.removeAll(); return true; + }); + file_versions_grid.render(); + +//////////////////////////////////////////////////////////////: + + + var client_store = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({ + url: '/cgi-bin/bweb/bresto.pl', + method: 'GET', + params:{action:'list_client'} + }), + + reader: new Ext.data.ArrayReader({ + }, Ext.data.Record.create([ + {name: 'name' } + ])) + }); + + var client_combo = new Ext.form.ComboBox({ + fieldLabel: 'Clients', + store: client_store, + displayField:'name', + typeAhead: true, + mode: 'local', + triggerAction: 'all', + emptyText:'Select a client...', + selectOnFocus:true, + forceSelection: true, + width:135 + }); + + client_combo.on('valid', function(e) { + Ext.brestore.client = e.getValue(); + job_store.load( {params:{action: 'list_job',client:Ext.brestore.client}}); + return true; + }); + +//////////////////////////////////////////////////////////////: + + var job_store = new Ext.data.Store({ + proxy: new Ext.data.HttpProxy({ + url: '/cgi-bin/bweb/bresto.pl', + method: 'GET', + params:{offset:0, limit:50 } + }), + + reader: new Ext.data.ArrayReader({ + }, Ext.data.Record.create([ + {name: 'jobid' }, + {name: 'jobname' } + ])) + }); + + var job_combo = new Ext.form.ComboBox({ + fieldLabel: 'Jobs', + store: job_store, + displayField:'jobname', + typeAhead: true, + mode: 'local', + triggerAction: 'all', + emptyText:'Select a job...', + selectOnFocus:true, + forceSelection: true, + width:300 + }); + + job_combo.on('select', function(e,c) { + Ext.brestore.jobid = c.json[0]; + + tree_loader.baseParams = { action:'list_dirs', + jobid:Ext.brestore.jobid }; + root.reload(); + }); + +////////////////////////////////////////////////////////////////: + + // create the primary toolbar + var tb2 = new Ext.Toolbar('div-tb-sel'); + tb2.add({ + id:'save', + text:'Save', + disabled:true, +// handler:save, + cls:'x-btn-text-icon save', + tooltip:'Saves all components to the server' + },'-', { + id:'add', + text:'Component', +// handler:addComponent, + cls:'x-btn-text-icon add-cmp', + tooltip:'Add a new Component to the dependency builder' + }, { + id:'option', + text:'Option', + disabled:true, +// handler:addOption, + cls:'x-btn-text-icon add-opt', + tooltip:'Add a new optional dependency to the selected component' + },'-',{ + id:'remove', + text:'Remove', + disabled:true, +// handler:removeNode, + cls:'x-btn-text-icon remove', + tooltip:'Remove the selected item' + }); + + var tb = new Ext.Toolbar('div-toolbar', [ + client_combo, + job_combo, + '-', + { + id: 'tb_home', +// icon: '/bweb/up.gif', + text: 'Change location', + cls:'x-btn-text-icon', + handler: function() { alert('do chdir') } + }, + new Ext.form.TextField({ + fieldLabel: 'Location', + name: 'where', + width:175, + allowBlank:false + }) + ]); + +//////////////////////////////////////////////////////////////// + + var layout = new Ext.BorderLayout(document.body, { + north: { +// split: true + }, + south: { + split: true, initialSize: 300 + }, + east: { + split: true, initialSize: 500 + }, + west: { + split: true, initialSize: 300 + }, + center: { + initialSize: 600 + } + + }); + +layout.beginUpdate(); + layout.add('north', new Ext.ContentPanel('div-toolbar', { + fitToFrame: true, autoCreate:true,closable: false + })); + layout.add('south', new Ext.ContentPanel('div-file-selection', { + toolbar: tb2,resizeEl:'div-container', + fitToFrame: true, autoCreate:true,closable: false + })); + layout.add('east', new Ext.ContentPanel('div-file-versions', { + fitToFrame: true, autoCreate:true,closable: false + })); + layout.add('west', new Ext.ContentPanel('div-tree', { + autoScroll:true, fitToFrame: true, + autoCreate:true,closable: false + })); + layout.add('center', new Ext.ContentPanel('div-files', { + autoScroll:true,autoCreate:true,fitToFrame: true + })); +layout.endUpdate(); + + +//////////////////////////////////////////////////////////////// + +// job_store.load(); + client_store.load({params:{action: 'list_client'}}); +// file_store.load({params:{offset:0, limit:50}}); +// file_versions_store.load({params:{offset:0, limit:50}}); +// file_selection_store.load(); + +} +Ext.onReady( ext_init ); -- 2.39.5