r11655@llin: dpavlin | 2005-12-13 00:23:11 +0100
[BackupPC.git] / lib / BackupPC / SearchLib.pm
1 #!/usr/bin/perl
2 package BackupPC::SearchLib;
3
4 use strict;
5 use BackupPC::CGI::Lib qw(:all);
6 use BackupPC::Attrib qw(:all);
7 use DBI;
8 use DateTime;
9 use vars qw(%In $MyURL);
10 use Time::HiRes qw/time/;
11 use XML::Writer;
12 use IO::File;
13
14 my $on_page = 100;
15 my $pager_pages = 10;
16
17 my $dsn = $Conf{SearchDSN};
18 my $db_user = $Conf{SearchUser} || '';
19
20 my $hest_index_path = $Conf{HyperEstraierIndex};
21
22 my $dbh;
23
24 sub get_dbh {
25         $dbh ||= DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
26         return $dbh;
27 }
28
29 sub getUnits() {
30         my @ret;
31
32         my $dbh = get_dbh();
33         my $sth = $dbh->prepare(qq{
34                 SELECT
35                         shares.id       as id,
36                         hosts.name || ':' || shares.name as share
37                 FROM shares
38                 JOIN hosts on hostid = hosts.id
39                 ORDER BY share
40         } );
41         $sth->execute();
42         push @ret, { 'id' => '', 'share' => '-'};       # dummy any
43
44         while ( my $row = $sth->fetchrow_hashref() ) {
45                 push @ret, $row;
46         }
47         return @ret;
48 }
49
50 sub epoch_to_iso {
51         my $t = shift || return;
52         my $iso = BackupPC::Lib::timeStamp(undef, $t);
53         $iso =~ s/\s/ /g;
54         return $iso;
55 }
56
57 sub dates_from_form($) {
58         my $param = shift || return;
59
60         sub mk_epoch_date($$) {
61                 my ($name,$suffix) = @_;
62
63                 my $yyyy = $param->{ $name . '_year_' . $suffix} || return undef;
64                 my $mm .= $param->{ $name . '_month_' . $suffix} ||
65                         ( $suffix eq 'from' ? 1 : 12);
66                 my $dd .= $param->{ $name . '_day_' . $suffix} ||
67                         ( $suffix eq 'from' ? 1 : 31);
68
69                 $yyyy =~ s/\D//g;
70                 $mm =~ s/\D//g;
71                 $dd =~ s/\D//g;
72
73                 my $h = my $m = my $s = 0;
74                 if ($suffix eq 'to') {
75                         $h = 23;
76                         $m = 59;
77                         $s = 59;
78                 }
79
80                 my $dt = new DateTime(
81                         year => $yyyy,
82                         month => $mm,
83                         day => $dd,
84                         hour => $h,
85                         minute => $m,
86                         second => $s,
87                 );
88                 print STDERR "mk_epoch_date($name,$suffix) [$yyyy-$mm-$dd] = " . $dt->ymd . " " . $dt->hms . "\n";
89                 return $dt->epoch || 'NULL';
90         }
91
92         my @ret = (
93                 mk_epoch_date('search_backup', 'from'),
94                 mk_epoch_date('search_backup', 'to'),
95                 mk_epoch_date('search', 'from'),
96                 mk_epoch_date('search', 'to'),
97         );
98
99         return @ret;
100
101 }
102
103
104 sub getWhere($) {
105         my $param = shift || return;
106
107         my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
108
109         my @conditions;
110         push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
111         push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
112         push @conditions, qq{ files.date >= $files_from } if ($files_from);
113         push @conditions, qq{ files.date <= $files_to } if ($files_to);
114
115         print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" and ",@conditions);
116
117         push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
118         push (@conditions, " upper(files.path) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
119
120         return join(" and ", @conditions);
121 }
122
123 my $sort_def = {
124         search => {
125                 default => 'date_a',
126                 sql => {
127                         share_d => 'shares.name DESC',
128                         share_a => 'shares.name ASC',
129                         path_d => 'files.path DESC',
130                         path_a => 'files.path ASC',
131                         num_d => 'files.backupnum DESC',
132                         num_a => 'files.backupnum ASC',
133                         size_d => 'files.size DESC',
134                         size_a => 'files.size ASC',
135                         date_d => 'files.date DESC',
136                         date_a => 'files.date ASC',
137                 },
138                 est => {
139                         share_d => 'sname STRD',
140                         share_a => 'sname STRA',
141                         path_d => 'filepath STRD',
142                         path_a => 'filepath STRA',
143                         num_d => 'backupnum NUMD',
144                         num_a => 'backupnum NUMA',
145                         size_d => 'size NUMD',
146                         size_a => 'size NUMA',
147                         date_d => 'date NUMD',
148                         date_a => 'date NUMA',
149                 }
150         }, burn => {
151                 default => 'date_a',
152                 sql => {
153                         share_d => 'host DESC, share DESC',
154                         share_a => 'host ASC, share ASC',
155                         num_d => 'backupnum DESC',
156                         num_a => 'backupnum ASC',
157                         date_d => 'date DESC',
158                         date_a => 'date ASC',
159                         age_d => 'age DESC',
160                         age_a => 'age ASC',
161                         size_d => 'size DESC',
162                         size_a => 'size ASC',
163                         incsize_d => 'inc_size DESC',
164                         incsize_a => 'inc_size ASC',
165                 }
166         }
167 };
168
169 sub getSort($$$) {
170         my ($part,$type, $sort_order) = @_;
171
172         die "unknown part: $part" unless ($sort_def->{$part});
173         die "unknown type: $type" unless ($sort_def->{$part}->{$type});
174
175         $sort_order ||= $sort_def->{$part}->{'default'};
176
177         if (my $ret = $sort_def->{$part}->{$type}->{$sort_order}) {
178                 return $ret;
179         } else {
180                 # fallback to default sort order
181                 return $sort_def->{$part}->{$type}->{ $sort_def->{$part}->{'default'} };
182         }
183 }
184
185 sub getFiles($) {
186         my ($param) = @_;
187
188         my $offset = $param->{'offset'} || 0;
189         $offset *= $on_page;
190
191         my $dbh = get_dbh();
192
193         my $sql_cols = qq{
194                 files.id                        AS fid,
195                 hosts.name                      AS hname,
196                 shares.name                     AS sname,
197                 files.backupnum                 AS backupnum,
198                 files.path                      AS filepath,
199                 files.date                      AS date,
200                 files.type                      AS type,
201                 files.size                      AS size
202         };
203
204         my $sql_from = qq{
205                 FROM files 
206                         INNER JOIN shares       ON files.shareID=shares.ID
207                         INNER JOIN hosts        ON hosts.ID = shares.hostID
208                         INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
209         };
210
211         my $sql_where;
212         my $where = getWhere($param);
213         $sql_where = " WHERE ". $where if ($where);
214
215         my $order = getSort('search', 'sql', $param->{'sort'});
216
217         my $sql_order = qq{
218                 ORDER BY $order
219                 LIMIT $on_page
220                 OFFSET ?
221         };
222
223         my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
224         my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
225
226         my $sth = $dbh->prepare($sql_count);
227         $sth->execute();
228         my ($results) = $sth->fetchrow_array();
229
230         $sth = $dbh->prepare($sql_results);
231         $sth->execute( $offset );
232
233         if ($sth->rows != $results) {
234                 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
235                 $bug =~ s/\s+/ /gs;
236                 print STDERR "$bug\n";
237         }
238
239         my @ret;
240       
241         while (my $row = $sth->fetchrow_hashref()) {
242                 push @ret, $row;
243         }
244      
245         $sth->finish();
246         return ($results, \@ret);
247 }
248
249 sub getHyperEstraier_url($) {
250         my ($use_hest) = @_;
251
252         return unless $use_hest;
253
254         use HyperEstraier;
255         my ($index_path, $index_node_url);
256
257         if ($use_hest =~ m#^http://#) {
258                 $index_node_url = $use_hest;
259         } else {
260                 $index_path = $TopDir . '/' . $use_hest;
261                 $index_path =~ s#//#/#g;
262         }
263         return ($index_path, $index_node_url);
264 }
265
266 sub getFilesHyperEstraier($) {
267         my ($param) = @_;
268
269         my $offset = $param->{'offset'} || 0;
270         $offset *= $on_page;
271
272         die "no index_path?" unless ($hest_index_path);
273
274         use HyperEstraier;
275
276         my ($index_path, $index_node_url) = getHyperEstraier_url($hest_index_path);
277
278         # open the database
279         my $db;
280         if ($index_path) {
281                 $db = HyperEstraier::Database->new();
282                 $db->open($index_path, $HyperEstraier::ESTDBREADER);
283         } elsif ($index_node_url) {
284                 $db ||= HyperEstraier::Node->new($index_node_url);
285                 $db->set_auth('admin', 'admin');
286         } else {
287                 die "BUG: unimplemented";
288         }
289
290         # create a search condition object
291         my $cond = HyperEstraier::Condition->new();
292
293         my $q = $param->{'search_filename'};
294         my $shareid = $param->{'search_share'};
295
296         if (length($q) > 0) {
297                 # exact match
298                 $cond->add_attr("filepath ISTRINC $q");
299
300                 $q =~ s/(.)/$1 /g;
301                 # set the search phrase to the search condition object
302                 $cond->set_phrase($q);
303         }
304
305         my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
306
307         $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
308         $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
309
310         $cond->add_attr("date NUMGE $files_from") if ($files_from);
311         $cond->add_attr("date NUMLE $files_to") if ($files_to);
312
313         $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
314
315 #       $cond->set_max( $offset + $on_page );
316         $cond->set_options( $HyperEstraier::Condition::SURE );
317         $cond->set_order( getSort('search', 'est', $param->{'sort'} ) );
318
319         # get the result of search
320         my @res;
321         my ($result, $hits);
322
323         if ($index_path) {
324                 $result = $db->search($cond, 0);
325                 $hits = $result->size;
326         } elsif ($index_node_url) {
327                 $result = $db->search($cond, 0);
328                 $hits = $result->doc_num;
329         } else {
330                 die "BUG: unimplemented";
331         }
332
333         # for each document in result
334         for my $i ($offset .. ($offset + $on_page - 1)) {
335                 last if ($i >= $hits);
336
337                 my $doc;
338                 if ($index_path) {
339                         my $id = $result->get($i);
340                         $doc = $db->get_doc($id, 0);
341                 } elsif ($index_node_url) {
342                         $doc = $result->get_doc($i);
343                 } else {
344                         die "BUG: unimplemented";
345                 }
346
347                 my $row;
348                 foreach my $c (qw/fid hname sname backupnum filepath date type size/) {
349                         $row->{$c} = $doc->attr($c);
350                 }
351                 push @res, $row;
352         }
353
354         return ($hits, \@res);
355 }
356
357 sub getGzipName($$$)
358 {
359         my ($host, $share, $backupnum) = @_;
360         my $ret = $Conf{GzipSchema};
361         
362         $share =~ s/\//_/g;
363         $ret =~ s/\\h/$host/ge;
364         $ret =~ s/\\s/$share/ge;
365         $ret =~ s/\\n/$backupnum/ge;
366
367         $ret =~ s/__+/_/g;
368
369         return $ret;
370         
371 }
372
373 sub get_tgz_size_by_name($) {
374         my $name = shift;
375
376         my $tgz = $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.$name;
377
378         my $size = -1;
379
380         if (-f "${tgz}.tar.gz") {
381                 $size = (stat("${tgz}.tar.gz"))[7];
382         } elsif (-d $tgz) {
383                 opendir(my $dir, $tgz) || die "can't opendir $tgz: $!";
384                 my @parts = grep { !/^\./ && !/md5/ && -f "$tgz/$_" } readdir($dir);
385                 $size = 0;
386                 foreach my $part (@parts) {
387                         $size += (stat("$tgz/$part"))[7] || die "can't stat $tgz/$part: $!";
388                 }
389                 closedir $dir;
390         } else {
391                 return -1;
392         }
393
394         return $size;
395 }
396
397 sub getGzipSize($$)
398 {
399         my ($hostID, $backupNum) = @_;
400         my $sql;
401         my $dbh = get_dbh();
402         
403         $sql = q{ 
404                                 SELECT hosts.name  as host,
405                                            shares.name as share,
406                                            backups.num as backupnum
407                                 FROM hosts, backups, shares
408                                 WHERE shares.id=backups.shareid AND
409                                           hosts.id =backups.hostid AND
410                                           hosts.id=? AND
411                                           backups.num=?
412                         };
413         my $sth = $dbh->prepare($sql);
414         $sth->execute($hostID, $backupNum);
415
416         my $row = $sth->fetchrow_hashref();
417
418         return get_tgz_size_by_name(
419                 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'})
420         );
421 }
422
423 sub getVolumes($) {
424         my $id = shift;
425
426         my $max_archive_size = $Conf{MaxArchiveSize} || die "no MaxArchiveSize";
427
428         my $sth = $dbh->prepare(qq{
429                 select
430                         size
431                 from backup_parts
432                 where backup_id = ?
433                 order by part_nr asc
434         });
435
436         $sth->execute($id);
437
438         my $cumulative_size = 0;
439         my $volumes = 1;
440
441         while(my ($size) = $sth->fetchrow_array) {
442                 if ($cumulative_size + $size > $max_archive_size) {
443                         $volumes++;
444                         $cumulative_size = $size;
445                 } else {
446                         $cumulative_size += $size;
447                 }
448         }
449
450         return ($volumes,$cumulative_size);
451 }
452
453 sub getBackupsNotBurned($) {
454
455         my $param = shift;
456         my $dbh = get_dbh();
457
458         my $order = getSort('burn', 'sql', $param->{'sort'});
459
460 print STDERR "## sort=". ($param->{'sort'} || 'no sort param') . " burn sql order: $order\n";
461
462         my $sql = qq{
463                 SELECT 
464                         backups.hostID AS hostID,
465                         hosts.name AS host,
466                         shares.name AS share,
467                         backups.num AS backupnum,
468                         backups.type AS type,
469                         backups.date AS date,
470                         date_part('epoch',now()) - backups.date as age,
471                         backups.size AS size,
472                         backups.id AS id,
473                         backups.inc_size AS inc_size,
474                         backups.parts AS parts
475                 FROM backups 
476                 INNER JOIN shares       ON backups.shareID=shares.ID
477                 INNER JOIN hosts        ON backups.hostID = hosts.ID
478                 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id 
479                 WHERE backups.inc_size > 0 AND backups.inc_deleted is false AND archive_backup.backup_id IS NULL
480                 GROUP BY
481                         backups.hostID,
482                         hosts.name,
483                         shares.name,
484                         backups.num,
485                         backups.shareid,
486                         backups.id,
487                         backups.type,
488                         backups.date,
489                         backups.size,
490                         backups.inc_size,
491                         backups.parts
492                 ORDER BY $order
493         };
494         my $sth = $dbh->prepare( $sql );
495         my @ret;
496         $sth->execute();
497
498         while ( my $row = $sth->fetchrow_hashref() ) {
499                 $row->{'age'} = sprintf("%0.1f", ( $row->{'age'} / 86400 ) );
500                 #$row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
501
502                 my $max_archive_size = $Conf{MaxArchiveSize} || die "no MaxArchiveSize";
503                 if ($row->{size} > $max_archive_size) {
504                         ($row->{volumes}, $row->{inc_size_calc}) = getVolumes($row->{id});
505                 }
506
507                 $row->{size} = sprintf("%0.2f", $row->{size} / 1024 / 1024);
508
509                 # do some cluster calculation (approximate)
510                 $row->{inc_size} = int(( ($row->{inc_size} + 1023 ) / 2 )  * 2);
511                 $row->{inc_size_calc} ||= $row->{inc_size};
512                 push @ret, $row;
513         }
514       
515         return @ret;
516 }
517
518 sub displayBackupsGrid($) {
519
520         my $param = shift;
521
522         my $max_archive_size = $Conf{MaxArchiveSize} || die "no MaxArchiveSize";
523         my $max_archive_file_size = $Conf{MaxArchiveFileSize}  || die "no MaxFileInSize";
524
525         my $retHTML .= q{
526                 <form id="forma" method="POST" action="}.$MyURL.q{?action=burn">
527         };
528
529         $retHTML .= <<'EOF3';
530 <style type="text/css">
531 <!--
532 DIV#fixedBox {
533         position: absolute;
534         top: 50em;
535         left: -24%;
536         padding: 0.5em;
537         width: 20%;
538         background-color: #E0F0E0;
539         border: 1px solid #00C000;
540 }
541
542 DIV#fixedBox, DIV#fixedBox INPUT, DIV#fixedBox TEXTAREA {
543         font-size: 10pt;
544 }
545
546 FORM>DIV#fixedBox {
547         position: fixed !important;
548         left: 0.5em !important;
549         top: auto !important;
550         bottom: 1em !important;
551         width: 15% !important;
552 }
553
554 DIV#fixedBox INPUT[type=text], DIV#fixedBox TEXTAREA {
555         border: 1px solid #00C000;
556 }
557
558 DIV#fixedBox #note {
559         display: block;
560         width: 100%;
561 }
562
563 DIV#fixedBox #submitBurner {
564         display: block;
565         width: 100%;
566         margin-top: 0.5em;
567         cursor: pointer;
568 }
569
570 * HTML {
571         overflow-y: hidden;
572 }
573
574 * HTML BODY {
575         overflow-y: auto;
576         height: 100%;
577         font-size: 100%;
578 }
579
580 * HTML DIV#fixedBox {
581         position: absolute;
582 }
583
584 #mContainer, #gradient, #mask, #progressIndicator {
585         display: block;
586         width: 100%;
587         font-size: 10pt;
588         font-weight: bold;
589         text-align: center;
590         vertical-align: middle;
591         padding: 1px;
592 }
593
594 #gradient, #mask, #progressIndicator {
595         left: 0;
596         border-width: 1px;
597         border-style: solid;
598         border-color: #000000;
599         color: #404040;
600         margin: 0.4em;
601         position: absolute;
602         margin-left: -1px;
603         margin-top: -1px;
604         margin-bottom: -1px;
605         overflow: hidden;
606 }
607
608 #mContainer {
609         display: block;
610         position: relative;
611         padding: 0px;
612         margin-top: 0.4em;
613         margin-bottom: 0.5em;
614 }
615
616 #gradient {
617         z-index: 1;
618         background-color: #FFFF00;
619 }
620
621 #mask {
622         z-index: 2;
623         background-color: #FFFFFF;
624 }
625
626 #progressIndicator {
627         z-index: 3;
628         background-color: transparent;
629 }
630
631 #volumes {
632         padding: 0.4em;
633         display: none;
634         width: 100%;
635         font-size: 80%;
636         color: #ff0000;
637         text-align: center;
638 }
639 -->
640 </style>
641 <script type="text/javascript">
642 <!--
643
644 var debug_div = null;
645 EOF3
646
647         # take maximum archive size from configuration
648         $retHTML .= qq{
649 var media_size = $max_archive_size ;
650 var max_file_size = $max_archive_file_size;
651
652 };
653
654         $retHTML .= <<'EOF3';
655
656 function debug(msg) {
657 //      return; // Disable debugging
658
659         if (! debug_div) debug_div = document.getElementById('debug');
660
661         // this will create debug div if it doesn't exist.
662         if (! debug_div) {
663                 debug_div = document.createElement('div');
664                 if (document.body) document.body.appendChild(debug_div);
665                 else debug_div = null;
666         }
667         if (debug_div) {
668                 debug_div.appendChild(document.createTextNode(msg));
669                 debug_div.appendChild(document.createElement("br"));
670         }
671 }
672
673
674 var element_id_cache = Array();
675
676 function element_id(name,element) {
677         if (! element_id_cache[name]) {
678                 element_id_cache[name] = self.document.getElementById(name);
679         }
680         return element_id_cache[name];
681 }
682
683 function checkAll(location) {
684         var f = element_id('forma') || null;
685         if (!f) return false;
686
687         var len = f.elements.length;
688         var check_all = element_id('allFiles');
689         var suma = check_all.checked ? (parseInt(f.elements['totalsize'].value) || 0) : 0;
690
691         for (var i = 0; i < len; i++) {
692                 var e = f.elements[i];
693                 if (e.name != 'all' && e.name.substr(0, 3) == 'fcb') {
694                         if (check_all.checked) {
695                                 if (e.checked) continue;
696                                 var el = element_id("fss" + e.name.substr(3));
697                                 var size = parseInt(el.value) || 0;
698                                 debug('suma: '+suma+' size: '+size);
699                                 if ((suma + size) < media_size) {
700                                         suma += size;
701                                         e.checked = true;
702                                 } else {
703                                         break;
704                                 }
705                         } else {
706                                 e.checked = false;
707                         }
708                 }
709         }
710         update_sum(suma);
711 }
712
713 function update_sum(suma, suma_disp) {
714         if (! suma_disp) suma_disp = suma;
715         suma_disp = Math.floor(suma_disp / 1024);
716         element_id('forma').elements['totalsize_kb'].value = suma_disp;
717         element_id('forma').elements['totalsize'].value = suma;
718         pbar_set(suma, media_size);
719         debug('total size: ' + suma);
720 }
721
722 function update_size(name, checked, suma) {
723         var size = parseInt( element_id("fss" + name).value);
724
725         if (checked) {
726                 suma += size;
727         } else {
728                 suma -= size;
729         }
730
731         var volumes = parseInt( element_id("prt" + name).value);
732         debug('update_size('+name+','+checked+') suma: '+suma+' volumes: '+volumes);
733         if (volumes > 1) {
734                 if (checked) {
735                         element_id("volumes").innerHTML = "This will take "+volumes+" mediums!";
736                         element_id("volumes").style.display = 'block';
737                         suma = size;
738                         update_sum(suma);
739                 } else {
740                         suma -= size;
741                         element_id("volumes").style.display = 'none';
742                 }
743         }
744
745         return suma;
746 }
747
748 function sumiraj(e) {
749         var suma = parseInt(element_id('forma').elements['totalsize'].value) || 0;
750         var len = element_id('forma').elements.length;
751         if (e) {
752                 suma = update_size(e.name.substr(3), e.checked, suma);
753                 if (suma < 0) suma = 0;
754         } else {
755                 suma = 0;
756                 for (var i = 0; i < len; i++) {
757                         var fel = element_id('forma').elements[i];
758                         if (fel.name != 'all' && fel.checked && fel.name.substr(0,3) == 'fcb') {
759                                 suma = update_size(fel.name.substr(3), fel.checked, suma);
760                         }
761                 }
762         }
763         update_sum(suma);
764         return suma;
765 }
766
767 /* progress bar */
768
769 var _pbar_width = null;
770 var _pbar_warn = 10;    // change color in last 10%
771
772 function pbar_reset() {
773         element_id("mask").style.left = "0px";
774         _pbar_width = element_id("mContainer").offsetWidth - 2;
775         element_id("mask").style.width = _pbar_width + "px";
776         element_id("mask").style.display = "block";
777         element_id("progressIndicator").style.zIndex  = 10;
778         element_id("progressIndicator").innerHTML = "0";
779 }
780
781 function dec2hex(d) {
782         var hch = '0123456789ABCDEF';
783         var a = d % 16;
784         var q = (d - a) / 16;
785         return hch.charAt(q) + hch.charAt(a);
786 }
787
788 function pbar_set(amount, max) {
789         debug('pbar_set('+amount+', '+max+')');
790
791         if (_pbar_width == null) {
792                 var _mc = element_id("mContainer");
793                 if (_pbar_width == null) _pbar_width = parseInt(_mc.offsetWidth ? (_mc.offsetWidth - 2) : 0) || null;
794                 if (_pbar_width == null) _pbar_width = parseInt(_mc.clientWidth ? (_mc.clientWidth + 2) : 0) || null;
795                 if (_pbar_width == null) _pbar_width = 0;
796         }
797
798         var pcnt = Math.floor(amount * 100 / max);
799         var p90 = 100 - _pbar_warn;
800         var pcol = pcnt - p90;
801         if (Math.round(pcnt) <= 100) {
802                 if (pcol < 0) pcol = 0;
803                 var e = element_id("submitBurner");
804                 debug('enable_button');
805                 e.disabled = false;
806                 var a = e.getAttributeNode('disabled') || null;
807                 if (a) e.removeAttributeNode(a);
808         } else {
809                 debug('disable button');
810                 pcol = _pbar_warn;
811                 var e = element_id("submitBurner");
812                 if (!e.disabled) e.disabled = true;
813         }
814         var col_g = Math.floor((_pbar_warn - pcol) * 255 / _pbar_warn);
815         var col = '#FF' + dec2hex(col_g) + '00';
816
817         //debug('pcol: '+pcol+' g:'+col_g+' _pbar_warn:'+ _pbar_warn + ' color: '+col);
818         element_id("gradient").style.backgroundColor = col;
819
820         element_id("progressIndicator").innerHTML = pcnt + '%';
821         //element_id("progressIndicator").innerHTML = amount;
822
823         element_id("mask").style.clip = 'rect(' + Array(
824                 '0px',
825                 element_id("mask").offsetWidth + 'px',
826                 element_id("mask").offsetHeight + 'px',
827                 Math.round(_pbar_width * amount / max) + 'px'
828         ).join(' ') + ')';
829 }
830
831 if (!self.body) self.body = new Object();
832 self.onload = self.document.onload = self.body.onload = function() {
833         //pbar_reset();
834         sumiraj();
835 };
836
837 // -->
838 </script>
839 <div id="fixedBox">
840
841 <input type="hidden" name="totalsize"/>
842 Size: <input type="text" name="totalsize_kb" size="7" readonly="readonly" style="text-align:right;" value="0" /> kB
843
844 <div id="mContainer">
845         <div id="gradient">&nbsp;</div>
846         <div id="mask">&nbsp;</div>
847         <div id="progressIndicator">0%</div>
848 </div>
849 <br/>
850
851 <div id="volumes">&nbsp;</div>
852
853 Note:
854 <textarea name="note" cols="10" rows="5" id="note"></textarea>
855
856 <input type="submit" id="submitBurner" value="Burn selected" name="submitBurner" />
857
858 </div>
859 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
860 no debug output yet
861 </div>
862 EOF3
863         $retHTML .= q{
864                         <input type="hidden" value="burn" name="action">
865                         <input type="hidden" value="results" name="search_results">
866                         <table style="fview" border="0" cellspacing="0" cellpadding="2">
867                         <tr class="tableheader">
868                         <td class="tableheader">
869                                 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
870                         </td>
871         } .
872                 sort_header($param, 'Share', 'share', 'center') .
873                 sort_header($param, '#', 'num', 'center') .
874         qq{
875                         <td align="center">Type</td>
876         } .
877                 sort_header($param, 'Date', 'date', 'center') .
878                 sort_header($param, 'Age/days', 'age', 'center') .
879                 sort_header($param, 'Size/Mb', 'size', 'center') .
880                 sort_header($param, 'gzip size/Kb', 'incsize', 'center') .
881         qq{
882                         <td align="center">medias</td></tr>
883         };
884
885         my @color = (' bgcolor="#e0e0e0"', '');
886
887         my $i = 0;
888         my $host = '';
889
890         foreach my $backup ( getBackupsNotBurned($param) ) {
891
892                 if ($host ne $backup->{'host'}) {
893                         $i++;
894                         $host = $backup->{'host'};
895                 }
896                 my $ftype = "";
897
898                 my $checkbox_key = $backup->{'hostid'}. '_' .$backup->{'backupnum'} . '_' . $backup->{'id'};
899
900                 $retHTML .=
901                         '<tr' . $color[$i %2 ] . '>
902                         <td class="fview">';
903
904                 if (($backup->{'inc_size'} || 0) > 0) {
905                         $retHTML .= '
906                         <input type="checkbox" name="fcb' . $checkbox_key . '" value="' . $checkbox_key . '" onClick="sumiraj(this);">';
907                 }
908
909                 my $img_url = $Conf{CgiImageDirURL};
910
911                 $retHTML .=
912                         '</td>' .
913                         '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
914                         '<td align="center">' . $backup->{'backupnum'} . '</td>' .
915                         '<td align="center">' . $backup->{'type'} . '</td>' .
916                         '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
917                         '<td align="center">' . $backup->{'age'} . '</td>' .
918                         '<td align="right">' . $backup->{'size'} . '</td>' .
919                         '<td align="right">' . sprintf("%0.1f", $backup->{'inc_size'} / 1024 ) .
920                         '<input type="hidden" id="fss'.$checkbox_key .'" value="'. $backup->{'inc_size_calc'} .'"></td>' .
921                         '<input type="hidden" id="prt'.$checkbox_key .'" value="'. $backup->{'volumes'} .'"></td>' .
922                         '<td align="left">' . ( qq{<img src="$img_url/icon-cd.gif" alt="media">} x $backup->{volumes} ) . '</td>' .
923
924                         "</tr>\n";
925         }
926
927         $retHTML .= "</table>";
928         $retHTML .= "</form>";
929       
930         return $retHTML;
931 }      
932
933 sub displayGrid($) {
934         my ($param) = @_;
935
936         my $offset = $param->{'offset'};
937         my $hilite = $param->{'search_filename'};
938
939         my $retHTML = "";
940  
941         my $start_t = time();
942
943         my ($results, $files);
944         if ($param->{'use_hest'} && length($hilite) > 0) {
945                 ($results, $files) = getFilesHyperEstraier($param);
946         } else {
947                 ($results, $files) = getFiles($param);
948         }
949
950         my $dur_t = time() - $start_t;
951         my $dur = sprintf("%0.4fs", $dur_t);
952
953         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
954
955         if ($results <= 0) {
956                 $retHTML .= qq{
957                         <p style="color: red;">No results found...</p>
958                 };
959                 return $retHTML;
960         } else {
961                 # DEBUG
962                 #use Data::Dumper;
963                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
964         }
965
966
967         $retHTML .= qq{
968         <div>
969         Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
970         </div>
971         <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
972                 <tr class="fviewheader"> 
973                 <td></td>
974         };
975
976         sub sort_header($$$$) {
977                 my ($param, $display, $name, $align) = @_;
978
979                 my ($sort_what, $sort_direction) = split(/_/,$param->{'sort'},2);
980
981                 my $old_sort = $param->{'sort'};
982
983                 my $html = qq{<td align="$align"};
984                 my $arrow = '';
985
986                 if (lc($sort_what) eq lc($name)) {
987                         my $direction = lc($sort_direction);
988
989                         # swap direction or fallback to default
990                         $direction =~ tr/ad/da/;
991                         $direction = 'a' unless ($direction =~ /[ad]/);
992
993                         $param->{'sort'} = $name . '_' . $direction;
994                         $html .= ' style="border: 1px solid #808080;"';
995                 
996                         # add unicode arrow for direction
997                         $arrow .= '&nbsp;';
998                         $arrow .= $direction eq 'a'  ?  '&#9650;'
999                                 : $direction eq 'd'  ?  '&#9660;'
1000                                 :                       ''
1001                                 ;
1002
1003                 } else {
1004                         $param->{'sort'} = $name . '_a';
1005                 }
1006
1007                 $html .= '><a href="' . page_uri($param) . '">' . $display . '</a>' . $arrow . '</td>';
1008                 $param->{'sort'} = $old_sort;
1009
1010                 return $html;
1011         }
1012
1013         $retHTML .=
1014                 sort_header($param, 'Share', 'share', 'center') .
1015                 sort_header($param, 'Type and Name', 'path', 'center') .
1016                 sort_header($param, '#', 'num', 'center') .
1017                 sort_header($param, 'Size', 'size', 'center') .
1018                 sort_header($param, 'Date', 'date', 'center');
1019
1020         $retHTML .= qq{
1021                 <td align="center">Media</td>
1022                 </tr>
1023         };
1024
1025         my $file;
1026
1027         sub hilite_html($$) {
1028                 my ($html, $search) = @_;
1029                 $html =~ s#($search)#<b>$1</b>#gis;
1030                 return $html;
1031         }
1032
1033         sub restore_link($$$$$$) {
1034                 my $type = shift;
1035                 my $action = 'RestoreFile';
1036                 $action = 'browse' if (lc($type) eq 'dir');
1037                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
1038         }
1039
1040         my $sth_archived;
1041         my %archived_cache;
1042
1043         sub check_archived($$$) {
1044                 my ($host, $share, $num) = @_;
1045
1046                 if (my $html = $archived_cache{"$host $share $num"}) {
1047                         return $html;
1048                 }
1049
1050                 $sth_archived ||= $dbh->prepare(qq{
1051                         select
1052                                 dvd_nr, note,
1053                                 count(archive_burned.copy) as copies
1054                         from archive
1055                         inner join archive_burned on archive_burned.archive_id = archive.id
1056                         inner join archive_backup on archive.id = archive_backup.archive_id
1057                         inner join backups on backups.id = archive_backup.backup_id
1058                         inner join hosts on hosts.id = backups.hostid
1059                         inner join shares on shares.id = backups.shareid
1060                         where hosts.name = ? and shares.name = ? and backups.num = ?
1061                         group by dvd_nr, note
1062                 });
1063
1064                 my @mediums;
1065
1066                 $sth_archived->execute($host, $share, $num);
1067                 while (my $row = $sth_archived->fetchrow_hashref()) {
1068                         push @mediums, '<abbr title="' .
1069                                 $row->{'note'} .
1070                                 ' [' . $row->{'copies'} . ']' .
1071                                 '">' .$row->{'dvd_nr'} .
1072                                 '</abbr>';
1073                 }
1074
1075                 my $html = join(", ",@mediums);
1076                 $archived_cache{"$host $share $num"} = $html;
1077                 return $html;
1078         }
1079
1080         my $i = $offset * $on_page;
1081
1082         foreach $file (@{ $files }) {
1083                 $i++;
1084
1085                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
1086                 $retHTML .= qq{<tr class="fviewborder">};
1087
1088                 $retHTML .= qq{<td class="fviewborder">$i</td>};
1089
1090                 $retHTML .=
1091                         qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
1092                         qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
1093                         qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
1094                         qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
1095                         qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
1096                         qq{<td class="fviewborder">} . check_archived( $file->{'hname'}, $file->{'sname'}, $file->{'backupnum'} ) . qq{</td>};
1097
1098                 $retHTML .= "</tr>";
1099         }
1100         $retHTML .= "</table>";
1101
1102         # all variables which has to be transfered
1103         foreach my $n (qw/search_day_from search_month_from search_year_from search_day_to search_month_to search_year_to search_backup_day_from search_backup_month_from search_backup_year_from search_backup_day_to search_backup_month_to search_backup_year_to search_filename offset/) {
1104                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
1105         }
1106
1107         my $del = '';
1108         my $max_page = int( $results / $on_page );
1109         my $page = 0;
1110
1111         sub page_uri($) {
1112                 my $param = shift || die "no param?";
1113
1114                 my $uri = $MyURL;
1115                 my $del = '?';
1116                 foreach my $k (keys %{ $param }) {
1117                         if ($param->{$k}) {
1118                                 $uri .= $del . $k . '=' . ${EscURI( $param->{$k} )};
1119                                 $del = '&';
1120                         }
1121                 }
1122                 return $uri;
1123         }
1124
1125         sub page_link($$$) {
1126                 my ($param,$page,$display) = @_;
1127
1128                 $param->{'offset'} = $page if (defined($page));
1129
1130                 my $html = '<a href = "' . page_uri($param) . '">' . $display . '</a>';
1131         }
1132
1133         $retHTML .= '<div style="text-align: center;">';
1134
1135         if ($offset > 0) {
1136                 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
1137         }
1138
1139         while ($page <= $max_page) {
1140                 if ($page == $offset) {
1141                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
1142                 } else {
1143                         $retHTML .= $del . page_link($param, $page, $page + 1);
1144                 }
1145
1146                 if ($page < $offset - $pager_pages && $page != 0) {
1147                         $retHTML .= " ... ";
1148                         $page = $offset - $pager_pages;
1149                         $del = '';
1150                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
1151                         $retHTML .= " ... ";
1152                         $page = $max_page;
1153                         $del = '';
1154                 } else {
1155                         $del = ' | ';
1156                         $page++;
1157                 }
1158         }
1159
1160         if ($offset < $max_page) {
1161                 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
1162         }
1163
1164         $retHTML .= "</div>";
1165
1166         return $retHTML;
1167 }
1168
1169 1;