4 die "bresto is not enabled" if (not $bresto_enable);
8 Bweb - A Bacula web interface
9 Bacula® - The Network Backup Solution
11 Copyright (C) 2000-2006 Free Software Foundation Europe e.V.
13 The main author of Bweb is Eric Bollengier.
14 The main author of Bacula is Kern Sibbald, with contributions from
15 many others, a complete list can be found in the file AUTHORS.
17 This program is Free Software; you can redistribute it and/or
18 modify it under the terms of version two of the GNU General Public
19 License as published by the Free Software Foundation plus additions
20 that are listed in the file LICENSE.
22 This program is distributed in the hope that it will be useful, but
23 WITHOUT ANY WARRANTY; without even the implied warranty of
24 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
25 General Public License for more details.
27 You should have received a copy of the GNU General Public License
28 along with this program; if not, write to the Free Software
29 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
32 Bacula® is a registered trademark of John Walker.
33 The licensor of Bacula is the Free Software Foundation Europe
34 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zurich,
35 Switzerland, email:ftf@fsfeurope.org.
51 return $self->get_pathid('');
54 # change the current directory
57 my ($self, $pathid) = @_;
58 $self->{cwdid} = $pathid;
67 FROM brestore_pathhierarchy
68 WHERE PathId IN ($self->{cwdid}) ";
70 my $all = $self->dbh_selectall_arrayref($query);
71 return unless ($all); # already at root
73 my $dir = join(',', map { $_->[0] } @$all);
79 # return the current PWD
83 return $self->get_path($self->{cwdid});
86 # get the Path from a PathId
89 my ($self, $pathid) = @_;
90 $self->debug("Call with pathid = $pathid");
91 my $query = "SELECT Path FROM Path WHERE PathId = ?";
92 my $sth = $self->dbh_prepare($query);
93 $sth->execute($pathid);
94 my $result = $sth->fetchrow_arrayref();
99 # we are working with these jobids
102 my ($self, @jobids) = @_;
103 $self->{curjobids} = join(',', @jobids);
104 # $self->update_brestore_table(@jobids);
107 # get the PathId from a Path
110 my ($self, $dir) = @_;
112 "SELECT PathId FROM Path WHERE Path = ?";
113 my $sth = $self->dbh_prepare($query);
115 my $result = $sth->fetchrow_arrayref();
123 my ($self, $offset, $limit) = @_;
124 $self->{limit} = $limit || 100;
125 $self->{offset} = $offset || 0;
130 my ($self, $pattern) = @_;
131 $self->{pattern} = $pattern;
134 # fill brestore_xxx tables for speedup
139 $self->{dbh}->begin_work();
141 # getting all Jobs to "cache"
143 SELECT JobId from Job
144 WHERE JobId NOT IN (SELECT JobId FROM brestore_knownjobid)
145 AND Type IN ('B') AND JobStatus IN ('T', 'f', 'A')
147 my $jobs = $self->dbh_selectall_arrayref($query);
149 $self->update_brestore_table(map { $_->[0] } @$jobs);
151 $self->{dbh}->commit();
152 $self->{dbh}->begin_work(); # we can break here
154 print STDERR "Cleaning path visibility\n";
156 my $nb = $self->dbh_do("
157 DELETE FROM brestore_pathvisibility
159 (SELECT 1 FROM Job WHERE JobId=brestore_pathvisibility.JobId)");
161 print STDERR "$nb rows affected\n";
162 print STDERR "Cleaning known jobid\n";
164 $nb = $self->dbh_do("
165 DELETE FROM brestore_knownjobid
167 (SELECT 1 FROM Job WHERE JobId=brestore_knownjobid.JobId)");
169 print STDERR "$nb rows affected\n";
171 $self->{dbh}->commit();
174 sub update_brestore_table
176 my ($self, @jobs) = @_;
178 $self->debug(\@jobs);
180 foreach my $job (sort {$a <=> $b} @jobs)
182 my $query = "SELECT 1 FROM brestore_knownjobid WHERE JobId = $job";
183 my $retour = $self->dbh_selectrow_arrayref($query);
184 next if ($retour and ($retour->[0] == 1)); # We have allready done this one ...
186 print STDERR "Inserting path records for JobId $job\n";
187 $query = "INSERT INTO brestore_pathvisibility (PathId, JobId)
188 (SELECT DISTINCT PathId, JobId FROM File WHERE JobId = $job)";
190 $self->dbh_do($query);
192 # Now we have to do the directory recursion stuff to determine missing visibility
193 # We try to avoid recursion, to be as fast as possible
194 # We also only work on not allready hierarchised directories...
196 print STDERR "Creating missing recursion paths for $job\n";
199 SELECT brestore_pathvisibility.PathId, Path FROM brestore_pathvisibility
200 JOIN Path ON( brestore_pathvisibility.PathId = Path.PathId)
201 LEFT JOIN brestore_pathhierarchy ON (brestore_pathvisibility.PathId = brestore_pathhierarchy.PathId)
202 WHERE brestore_pathvisibility.JobId = $job
203 AND brestore_pathhierarchy.PathId IS NULL
206 my $sth = $self->dbh_prepare($query);
208 my $pathid; my $path;
209 $sth->bind_columns(\$pathid,\$path);
213 $self->build_path_hierarchy($path,$pathid);
217 # Great. We have calculated all dependancies. We can use them to add the missing pathids ...
218 # This query gives all parent pathids for a given jobid that aren't stored.
219 # It has to be called until no record is updated ...
221 INSERT INTO brestore_pathvisibility (PathId, JobId) (
224 SELECT DISTINCT h.PPathId AS PathId
225 FROM brestore_pathhierarchy AS h
226 JOIN brestore_pathvisibility AS p ON (h.PathId=p.PathId)
227 WHERE p.JobId=$job) AS a LEFT JOIN
229 FROM brestore_pathvisibility
230 WHERE JobId=$job) AS b ON (a.PathId = b.PathId)
231 WHERE b.PathId IS NULL)";
234 while (($rows_affected = $self->dbh_do($query)) and ($rows_affected !~ /^0/))
236 print STDERR "Recursively adding $rows_affected records from $job\n";
239 $query = "INSERT INTO brestore_knownjobid (JobId) VALUES ($job)";
240 $self->dbh_do($query);
244 # compute the parent directorie
253 # Root Windows case :
254 if ($path =~ /^[a-z]+:\/$/i)
259 my @tmp = split('/',$path);
260 # We remove the last ...
262 my $tmp = join ('/',@tmp) . '/';
266 sub build_path_hierarchy
268 my ($self, $path,$pathid)=@_;
269 # Does the ppathid exist for this ? we use a memory cache...
270 # In order to avoid the full loop, we consider that if a dir is allready in the
271 # brestore_pathhierarchy table, then there is no need to calculate all the hierarchy
274 if (! $self->{cache_ppathid}->{$pathid})
276 my $query = "SELECT PPathId FROM brestore_pathhierarchy WHERE PathId = ?";
277 my $sth2 = $self->{dbh}->prepare_cached($query);
278 $sth2->execute($pathid);
279 # Do we have a result ?
280 if (my $refrow = $sth2->fetchrow_arrayref)
282 $self->{cache_ppathid}->{$pathid}=$refrow->[0];
284 # This dir was in the db ...
285 # It means we can leave, the tree has allready been built for
290 # We have to create the record ...
291 # What's the current p_path ?
292 my $ppath = parent_dir($path);
293 my $ppathid = $self->return_pathid_from_path($ppath);
294 $self->{cache_ppathid}->{$pathid}= $ppathid;
296 $query = "INSERT INTO brestore_pathhierarchy (pathid, ppathid) VALUES (?,?)";
297 $sth2 = $self->{dbh}->prepare_cached($query);
298 $sth2->execute($pathid,$ppathid);
304 # It's allready in the cache.
305 # We can leave, no time to waste here, all the parent dirs have allready
313 sub return_pathid_from_path
315 my ($self, $path) = @_;
316 my $query = "SELECT PathId FROM Path WHERE Path = ?";
318 #print STDERR $query,"\n" if $debug;
319 my $sth = $self->{dbh}->prepare_cached($query);
320 $sth->execute($path);
321 my $result =$sth->fetchrow_arrayref();
328 # A bit dirty : we insert into path, and we have to be sure
329 # we aren't deleted by a purge. We still need to insert into path to get
330 # the pathid, because of mysql
331 $query = "INSERT INTO Path (Path) VALUES (?)";
332 #print STDERR $query,"\n" if $debug;
333 $sth = $self->{dbh}->prepare_cached($query);
334 $sth->execute($path);
337 $query = "SELECT PathId FROM Path WHERE Path = ?";
338 #print STDERR $query,"\n" if $debug;
339 $sth = $self->{dbh}->prepare_cached($query);
340 $sth->execute($path);
341 $result = $sth->fetchrow_arrayref();
347 # list all files in a directory, accross curjobids
352 return undef unless ($self->{curjobids});
354 my $inclause = $self->{curjobids};
355 my $inpath = $self->{cwdid};
357 if ($self->{pattern}) {
358 $filter = " AND Filename.Name $self->{sql}->{MATCH} $self->{pattern} ";
362 "SELECT File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
364 SELECT Filename.Name, max(File.FileId) as id
366 WHERE File.FilenameId = Filename.FilenameId
367 AND Filename.Name != ''
368 AND File.PathId = $inpath
369 AND File.JobId IN ($inclause)
371 GROUP BY Filename.Name
372 ORDER BY Filename.Name LIMIT $self->{limit} OFFSET $self->{offset}
374 WHERE File.FileId = listfiles.id";
377 $self->debug($query);
378 my $result = $self->dbh_selectall_arrayref($query);
379 $self->debug($result);
384 # list all directories in a directory, accross curjobids
385 # return ($dirid,$dir_basename,$lstat,$jobid)
390 return undef unless ($self->{curjobids});
392 my $pathid = $self->{cwdid};
393 my $jobclause = $self->{curjobids};
395 # Let's retrieve the list of the visible dirs in this dir ...
396 # First, I need the empty filenameid to locate efficiently
397 # the dirs in the file table
398 my $query = "SELECT FilenameId FROM Filename WHERE Name = ''";
399 my $sth = $self->dbh_prepare($query);
401 my $result = $sth->fetchrow_arrayref();
403 my $dir_filenameid = $result->[0];
405 # Then we get all the dir entries from File ...
407 SELECT PathId, Path, JobId, Lstat FROM (
409 SELECT Path1.PathId, Path1.Path, lower(Path1.Path),
410 listfile1.JobId, listfile1.Lstat
412 SELECT DISTINCT brestore_pathhierarchy1.PathId
413 FROM brestore_pathhierarchy AS brestore_pathhierarchy1
415 ON (brestore_pathhierarchy1.PathId = Path2.PathId)
416 JOIN brestore_pathvisibility AS brestore_pathvisibility1
417 ON (brestore_pathhierarchy1.PathId = brestore_pathvisibility1.PathId)
418 WHERE brestore_pathhierarchy1.PPathId = $pathid
419 AND brestore_pathvisibility1.jobid IN ($jobclause)) AS listpath1
420 JOIN Path AS Path1 ON (listpath1.PathId = Path1.PathId)
422 SELECT File1.PathId, File1.JobId, File1.Lstat FROM File AS File1
423 WHERE File1.FilenameId = $dir_filenameid
424 AND File1.JobId IN ($jobclause)) AS listfile1
425 ON (listpath1.PathId = listfile1.PathId)
426 ) AS A ORDER BY 2,3 DESC LIMIT $self->{limit} OFFSET $self->{offset}
428 $self->debug($query);
430 $sth=$self->dbh_prepare($query);
432 $result = $sth->fetchall_arrayref();
435 foreach my $refrow (@{$result})
437 my $dirid = $refrow->[0];
438 my $dir = $refrow->[1];
439 my $lstat = $refrow->[3];
440 my $jobid = $refrow->[2] || 0;
441 next if ($dirid eq $prev_dir);
442 # We have to clean up this dirname ... we only want it's 'basename'
446 my @temp = split ('/',$dir);
447 $return_value = pop @temp;
453 my @return_array = ($dirid,$return_value,$lstat,$jobid);
454 push @return_list,(\@return_array);
457 $self->debug(\@return_list);
458 return \@return_list;
461 # TODO : we want be able to restore files from a bad ended backup
462 # we have JobStatus IN ('T', 'A', 'E') and we must
464 # Data acces subs from here. Interaction with SGBD and caching
466 # This sub retrieves the list of jobs corresponding to the jobs selected in the
467 # GUI and stores them in @CurrentJobIds.
468 # date must be quoted
469 sub set_job_ids_for_date
471 my ($self, $client, $date)=@_;
473 if (!$client or !$date) {
476 my $filter = $self->get_client_filter();
477 # The algorithm : for a client, we get all the backups for each
478 # fileset, in reverse order Then, for each fileset, we store the 'good'
479 # incrementals and differentials until we have found a full so it goes
480 # like this : store all incrementals until we have found a differential
481 # or a full, then find the full
483 SELECT JobId, FileSet, Level, JobStatus
485 JOIN FileSet USING (FileSetId)
486 JOIN Client USING (ClientId) $filter
487 WHERE EndTime <= $date
488 AND Client.Name = '$client'
490 AND JobStatus IN ('T')
491 ORDER BY FileSet, JobTDate DESC";
494 my $result = $self->dbh_selectall_arrayref($query);
496 foreach my $refrow (@$result)
498 my $jobid = $refrow->[0];
499 my $fileset = $refrow->[1];
500 my $level = $refrow->[2];
502 defined $progress{$fileset} or $progress{$fileset}='U'; # U for unknown
504 next if $progress{$fileset} eq 'F'; # It's over for this fileset...
508 next unless ($progress{$fileset} eq 'U' or $progress{$fileset} eq 'I');
509 push @CurrentJobIds,($jobid);
511 elsif ($level eq 'D')
513 next if $progress{$fileset} eq 'D'; # We allready have a differential
514 push @CurrentJobIds,($jobid);
516 elsif ($level eq 'F')
518 push @CurrentJobIds,($jobid);
521 my $status = $refrow->[3] ;
522 if ($status eq 'T') { # good end of job
523 $progress{$fileset} = $level;
527 return @CurrentJobIds;
530 sub dbh_selectrow_arrayref
532 my ($self, $query) = @_;
533 $self->debug($query, up => 1);
534 return $self->{dbh}->selectrow_arrayref($query);
537 # Returns list of versions of a file that could be restored
538 # returns an array of
539 # (jobid,fileindex,mtime,size,inchanger,md5,volname,fileid)
540 # there will be only one jobid in the array of jobids...
541 sub get_all_file_versions
543 my ($self,$pathid,$fileid,$client,$see_all)=@_;
545 defined $see_all or $see_all=0;
550 "SELECT File.JobId, File.FileId, File.Lstat,
551 File.Md5, Media.VolumeName, Media.InChanger
552 FROM File, Job, Client, JobMedia, Media
553 WHERE File.FilenameId = $fileid
554 AND File.PathId=$pathid
555 AND File.JobId = Job.JobId
556 AND Job.ClientId = Client.ClientId
557 AND Job.JobId = JobMedia.JobId
558 AND File.FileIndex >= JobMedia.FirstIndex
559 AND File.FileIndex <= JobMedia.LastIndex
560 AND JobMedia.MediaId = Media.MediaId
561 AND Client.Name = '$client'";
563 $self->debug($query);
564 my $result = $self->dbh_selectall_arrayref($query);
566 foreach my $refrow (@$result)
568 my ($jobid, $fid, $lstat, $md5, $volname, $inchanger) = @$refrow;
569 my @attribs = parse_lstat($lstat);
570 my $mtime = array_attrib('st_mtime',\@attribs);
571 my $size = array_attrib('st_size',\@attribs);
573 my @list = ($pathid,$fileid,$jobid,
574 $fid, $mtime, $size, $inchanger,
576 push @versions, (\@list);
579 # We have the list of all versions of this file.
580 # We'll sort it by mtime desc, size, md5, inchanger desc, FileId
581 # the rest of the algorithm will be simpler
582 # ('FILE:',filename,jobid,fileindex,mtime,size,inchanger,md5,volname)
583 @versions = sort { $b->[4] <=> $a->[4]
584 || $a->[5] <=> $b->[5]
585 || $a->[7] cmp $a->[7]
586 || $b->[6] <=> $a->[6]} @versions;
589 my %allready_seen_by_mtime;
590 my %allready_seen_by_md5;
591 # Now we should create a new array with only the interesting records
592 foreach my $ref (@versions)
596 # The file has a md5. We compare his md5 to other known md5...
597 # We take size into account. It may happen that 2 files
598 # have the same md5sum and are different. size is a supplementary
601 # If we allready have a (better) version
602 next if ( (not $see_all)
603 and $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]});
605 # we never met this one before...
606 $allready_seen_by_md5{$ref->[7] .'-'. $ref->[5]}=1;
608 # Even if it has a md5, we should also work with mtimes
609 # We allready have a (better) version
610 next if ( (not $see_all)
611 and $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5]});
612 $allready_seen_by_mtime{$ref->[4] .'-'. $ref->[5] . '-' . $ref->[7]}=1;
614 # We reached there. The file hasn't been seen.
615 push @good_versions,($ref);
618 # To be nice with the user, we re-sort good_versions by
619 # inchanger desc, mtime desc
620 @good_versions = sort { $b->[4] <=> $a->[4]
621 || $b->[2] <=> $a->[2]} @good_versions;
623 return \@good_versions;
626 my %attrib_name_id = ( 'st_dev' => 0,'st_ino' => 1,'st_mode' => 2,
627 'st_nlink' => 3,'st_uid' => 4,'st_gid' => 5,
628 'st_rdev' => 6,'st_size' => 7,'st_blksize' => 8,
629 'st_blocks' => 9,'st_atime' => 10,'st_mtime' => 11,
630 'st_ctime' => 12,'LinkFI' => 13,'st_flags' => 14,
631 'data_stream' => 15);;
634 my ($attrib,$ref_attrib)=@_;
635 return $ref_attrib->[$attrib_name_id{$attrib}];
639 { # $file = [filenameid,listfiles.id,listfiles.Name, File.LStat, File.JobId]
641 my ($file, $attrib)=@_;
643 if (defined $attrib_name_id{$attrib}) {
645 my @d = split(' ', $file->[3]) ; # TODO : cache this
647 return from_base64($d[$attrib_name_id{$attrib}]);
649 } elsif ($attrib eq 'jobid') {
653 } elsif ($attrib eq 'name') {
658 die "Attribute not known : $attrib.\n";
664 my ($lstat,$attrib)=@_;
665 if ($lstat and defined $attrib_name_id{$attrib})
667 my @d = split(' ', $lstat) ; # TODO : cache this
668 return from_base64($d[$attrib_name_id{$attrib}]);
675 # Base 64 functions, directly from recover.pl.
677 # Karl Hakimian <hakimian@aha.com>
678 # This section is also under GPL v2 or later.
685 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
686 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
687 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
688 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
689 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
691 @base64_map = (0) x 128;
693 for (my $i=0; $i<64; $i++) {
694 $base64_map[ord($base64_digits[$i])] = $i;
709 if (substr($where, 0, 1) eq '-') {
711 $where = substr($where, 1);
714 while ($where ne '') {
716 my $d = substr($where, 0, 1);
717 $val += $base64_map[ord(substr($where, 0, 1))];
718 $where = substr($where, 1);
726 my @attribs = split(' ',$lstat);
727 foreach my $element (@attribs)
729 $element = from_base64($element);
735 # get jobids that the current user can view (ACL)
738 my ($self, @jobid) = @_;
739 my $filter = $self->get_client_filter();
741 my $jobids = $self->dbh_join(@jobid);
744 FROM Job JOIN Client USING (ClientId) $filter
745 WHERE Jobid IN ($jobids)";
746 my $res = $self->dbh_selectall_arrayref($q);
747 @jobid = map { $_->[0] } @$res;
752 ################################################################
757 use POSIX qw/strftime/;
760 my $conf = new Bweb::Config(config_file => $Bweb::config_file);
763 my $bvfs = new Bvfs(info => $conf);
766 my $action = CGI::param('action') || '';
768 my $args = $bvfs->get_form('pathid', 'filenameid', 'fileid', 'qdate',
769 'limit', 'offset', 'client', 'qpattern');
771 if ($action eq 'batch') {
772 $bvfs->update_cache();
776 # All these functions are returning JSON compatible data
777 # for javascript parsing
779 if ($action eq 'list_client') { # list all client [ ['c1'],['c2']..]
780 print CGI::header('application/x-javascript');
782 my $filter = $bvfs->get_client_filter();
783 my $q = "SELECT Name FROM Client $filter";
784 my $ret = $bvfs->dbh_selectall_arrayref($q);
787 print join(',', map { "['$_->[0]']" } @$ret);
791 } elsif ($action eq 'list_job') { # list jobs for a client [[jobid,endtime,'desc'],..]
792 print CGI::header('application/x-javascript');
794 my $filter = $bvfs->get_client_filter();
796 SELECT Job.JobId,Job.EndTime, FileSet.FileSet, Job.Level, Job.JobStatus
797 FROM Job JOIN FileSet USING (FileSetId) JOIN Client USING (ClientId) $filter
798 WHERE Client.Name = '$args->{client}'
800 AND JobStatus IN ('f', 'T')
801 ORDER BY EndTime desc";
802 my $result = $bvfs->dbh_selectall_arrayref($query);
806 print join(',', map {
807 "[$_->[0], '$_->[1]', '$_->[1] $_->[2] $_->[3] ($_->[4]) $_->[0]']"
812 } elsif ($action eq 'list_storage') { # TODO: use .storage here
813 print CGI::header('application/x-javascript');
815 my $q="SELECT Name FROM Storage";
816 my $lst = $bvfs->dbh_selectall_arrayref($q);
818 print join(',', map { "[ '$_->[0]' ]" } @$lst);
823 # get jobid param and apply user filter
824 my @jobid = $bvfs->get_jobids(grep { /^\d+(,\d+)*$/ } CGI::param('jobid'));
825 # get jobid from date arg
826 if (!scalar(@jobid) and $args->{qdate} and $args->{client}) {
827 @jobid = $bvfs->set_job_ids_for_date($args->{client}, $args->{qdate});
829 $bvfs->set_curjobids(@jobid);
830 print STDERR "limit=$args->{limit}:$args->{offset} date=$args->{qdate} currentjobids = ", join(",", @jobid), "\n";
831 $bvfs->set_limits($args->{offset}, $args->{limit});
833 if (!scalar(@jobid)) {
837 if (CGI::param('init')) { # used when choosing a job
838 $bvfs->update_brestore_table(@jobid);
841 my $pathid = CGI::param('node') || '';
842 my $path = CGI::param('path');
844 if ($pathid =~ /^(\d+)$/) {
847 $pathid = $bvfs->get_pathid($path);
849 $pathid = $bvfs->get_root();
851 $bvfs->ch_dir($pathid);
853 # permit to use a regex filter
854 if ($args->{qpattern}) {
855 $bvfs->set_pattern($args->{qpattern});
858 if ($action eq 'restore') {
860 # TODO: pouvoir choisir le replace et le jobname
861 my $arg = $bvfs->get_form(qw/client storage regexwhere where/);
863 if (!$arg->{client}) {
864 print "ERROR: missing client\n";
868 my $fileid = join(',', grep { /^\d+$/ } CGI::param('fileid'));
869 my @dirid = grep { /^\d+$/ } CGI::param('dirid');
870 my $inclause = join(',', @jobid);
876 "(SELECT JobId, FileIndex, FilenameId, PathId FROM File WHERE FileId IN ($fileid))";
879 # using this is not good because the sql engine doesn't know
880 # what LIKE will use. It will be better to get Path% in perl
881 # but it doesn't work with accents... :(
882 foreach my $dirid (@dirid) {
884 (SELECT File.JobId, File.FileIndex, File.FilenameId, File.PathId
885 FROM Path JOIN File USING (PathId)
887 (SELECT ". $bvfs->dbh_strcat('Path',"'\%'") ." FROM Path
888 WHERE PathId = $dirid
890 AND File.JobId IN ($inclause))";
893 return unless scalar(@union);
895 my $u = join(" UNION ", @union);
897 $bvfs->dbh_do("CREATE TEMPORARY TABLE btemp AS $u");
898 # TODO: remove FilenameId et PathId
900 # now we have to choose the file with the max(jobid)
901 # for each file of btemp
902 if ($bvfs->dbh_is_mysql()) {
903 $bvfs->dbh_do("CREATE TEMPORARY TABLE btemp2 AS (
904 SELECT max(JobId) as JobId, PathId, FilenameId
906 GROUP BY PathId, FilenameId
909 $bvfs->dbh_do("CREATE TABLE b2$$ AS (
910 SELECT btemp.JobId, btemp.FileIndex, btemp.FilenameId, btemp.PathId
912 WHERE btemp2.JobId = btemp.JobId
913 AND btemp2.PathId= btemp.PathId
914 AND btemp2.FilenameId = btemp.FilenameId
916 } else { # postgresql have distinct with more than one criteria...
917 $bvfs->dbh_do("CREATE TABLE b2$$ AS (
918 SELECT JobId, FileIndex
920 SELECT DISTINCT ON (PathId, FilenameId) JobId, FileIndex
922 ORDER BY PathId, FilenameId, JobId DESC
928 my $bconsole = $bvfs->get_bconsole();
929 # TODO: pouvoir choisir le replace et le jobname
930 my $jobid = $bconsole->run(client => $arg->{client},
931 storage => $arg->{storage},
932 where => $arg->{where},
933 regexwhere=> $arg->{regexwhere},
937 $bvfs->dbh_do("DROP TABLE b2$$");
940 print CGI::header('text/html');
941 $bvfs->display_begin();
942 $bvfs->error("Can't start your job:<br/>" . $bconsole->before());
943 $bvfs->display_end();
947 print CGI::redirect("bweb.pl?action=dsp_cur_job;jobid=$jobid") ;
958 print CGI::header('application/x-javascript');
960 if ($action eq 'list_files') {
961 print "[[0,0,0,0,'.',4096,'1970-01-01 00:00:00'],";
962 my $files = $bvfs->ls_files();
963 # [ 1, 2, 3, "Bill", 10, '2007-01-01 00:00:00'],
964 # File.FilenameId, listfiles.id, listfiles.Name, File.LStat, File.JobId
967 map { my @p=Bvfs::parse_lstat($_->[3]);
973 "'" . escape_quote($_->[2]) . "'",
975 "'" . strftime('%Y-%m-%d %H:%m:%S', localtime($p[11])) . "'") .
980 } elsif ($action eq 'list_dirs') {
983 my $dirs = $bvfs->ls_dirs();
984 # return ($dirid,$dir_basename,$lstat,$jobid)
987 map { "{ 'jobid': '$bvfs->{curjobids}', 'id': '$_->[0]'," .
988 "'text': '" . escape_quote($_->[1]) . "', 'cls':'folder'}" }
992 } elsif ($action eq 'list_versions') {
994 my $vafv = CGI::param('vafv') || 'false'; # view all file versions
995 $vafv = ($vafv eq 'false')?0:1;
999 #($pathid,$fileid,$jobid, $fid, $mtime, $size, $inchanger, $md5, $volname);
1000 my $files = $bvfs->get_all_file_versions($args->{pathid}, $args->{filenameid}, $args->{client}, $vafv);
1002 map { "[ $_->[3], $_->[1], $_->[0], $_->[2], '$_->[8]', $_->[6], '$_->[7]', $_->[5],'" . strftime('%Y-%m-%d %H:%m:%S', localtime($_->[4])) . "']" }
1006 } elsif ($action eq 'get_media') {
1008 my $jobid = join(',', @jobid);
1009 my $fileid = join(',', grep { /^\d+(,\d+)*$/ } CGI::param('fileid'));
1012 SELECT DISTINCT VolumeName, Enabled, InChanger
1014 ( -- Get all media from this job
1015 SELECT MIN(FirstIndex) AS FirstIndex, MAX(LastIndex) AS LastIndex,
1016 VolumeName, Enabled, Inchanger
1017 FROM JobMedia JOIN Media USING (MediaId)
1018 WHERE JobId IN ($jobid)
1019 GROUP BY VolumeName,Enabled,InChanger
1021 WHERE File.FileId IN ($fileid)
1022 AND File.FileIndex >= allmedia.FirstIndex
1023 AND File.FileIndex <= allmedia.LastIndex
1025 my $lst = $bvfs->dbh_selectall_arrayref($q);
1027 print join(',', map { "['$_->[0]',$_->[1],$_->[2]]" } @$lst);
1034 CREATE VIEW files AS
1035 SELECT path || name AS name,pathid,filenameid,fileid,jobid
1036 FROM File JOIN FileName USING (FilenameId) JOIN Path USING (PathId);
1038 SELECT 'drop table ' || tablename || ';'
1039 FROM pg_tables WHERE tablename ~ '^b[0-9]';