r11640@llin: dpavlin | 2005-12-12 18:07:17 +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 getBackupsNotBurned($) {
424
425         my $param = shift;
426         my $dbh = get_dbh();
427
428         my $order = getSort('burn', 'sql', $param->{'sort'});
429
430 print STDERR "## sort=". ($param->{'sort'} || 'no sort param') . " burn sql order: $order\n";
431
432         my $sql = qq{
433                 SELECT 
434                         backups.hostID AS hostID,
435                         hosts.name AS host,
436                         shares.name AS share,
437                         backups.num AS backupnum,
438                         backups.type AS type,
439                         backups.date AS date,
440                         date_part('epoch',now()) - backups.date as age,
441                         backups.size AS size,
442                         backups.id AS id,
443                         backups.inc_size AS inc_size,
444                         backups.parts AS parts
445                 FROM backups 
446                 INNER JOIN shares       ON backups.shareID=shares.ID
447                 INNER JOIN hosts        ON backups.hostID = hosts.ID
448                 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id 
449                 WHERE backups.inc_size > 0 AND backups.inc_deleted is false AND archive_backup.backup_id IS NULL
450                 GROUP BY
451                         backups.hostID,
452                         hosts.name,
453                         shares.name,
454                         backups.num,
455                         backups.shareid,
456                         backups.id,
457                         backups.type,
458                         backups.date,
459                         backups.size,
460                         backups.inc_size,
461                         backups.parts
462                 ORDER BY $order
463         };
464         my $sth = $dbh->prepare( $sql );
465         my @ret;
466         $sth->execute();
467
468         while ( my $row = $sth->fetchrow_hashref() ) {
469                 $row->{'age'} = sprintf("%0.1f", ( $row->{'age'} / 86400 ) );
470                 #$row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
471                 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
472
473                 # do some cluster calculation (approximate) and convert to kB
474                 $row->{'inc_size'} = int(($row->{'inc_size'} + 1023 ) / ( 2 * 1024 ) * 2);
475                 push @ret, $row;
476         }
477       
478         return @ret;      
479 }
480
481 sub displayBackupsGrid($) {
482
483         my $param = shift;
484
485         my $max_archive_size = $Conf{MaxArchiveSize} || die "no MaxArchiveSize";
486         my $max_archive_file_size = $Conf{MaxArchiveFileSize}  || die "no MaxFileInSize";
487
488         my $retHTML .= q{
489                 <form id="forma" method="POST" action="}.$MyURL.q{?action=burn">
490         };
491
492         $retHTML .= <<'EOF3';
493 <style type="text/css">
494 <!--
495 DIV#fixedBox {
496         position: absolute;
497         top: 50em;
498         left: -24%;
499         padding: 0.5em;
500         width: 20%;
501         background-color: #E0F0E0;
502         border: 1px solid #00C000;
503 }
504
505 DIV#fixedBox, DIV#fixedBox INPUT, DIV#fixedBox TEXTAREA {
506         font-size: 10pt;
507 }
508
509 FORM>DIV#fixedBox {
510         position: fixed !important;
511         left: 0.5em !important;
512         top: auto !important;
513         bottom: 1em !important;
514         width: 15% !important;
515 }
516
517 DIV#fixedBox INPUT[type=text], DIV#fixedBox TEXTAREA {
518         border: 1px solid #00C000;
519 }
520
521 DIV#fixedBox #note {
522         display: block;
523         width: 100%;
524 }
525
526 DIV#fixedBox #submitBurner {
527         display: block;
528         width: 100%;
529         margin-top: 0.5em;
530         cursor: pointer;
531 }
532
533 * HTML {
534         overflow-y: hidden;
535 }
536
537 * HTML BODY {
538         overflow-y: auto;
539         height: 100%;
540         font-size: 100%;
541 }
542
543 * HTML DIV#fixedBox {
544         position: absolute;
545 }
546
547 #mContainer, #gradient, #mask, #progressIndicator {
548         display: block;
549         width: 100%;
550         font-size: 10pt;
551         font-weight: bold;
552         text-align: center;
553         vertical-align: middle;
554         padding: 1px;
555 }
556
557 #gradient, #mask, #progressIndicator {
558         left: 0;
559         border-width: 1px;
560         border-style: solid;
561         border-color: #000000;
562         color: #404040;
563         margin: 0.4em;
564         position: absolute;
565         margin-left: -1px;
566         margin-top: -1px;
567         margin-bottom: -1px;
568         overflow: hidden;
569 }
570
571 #mContainer {
572         display: block;
573         position: relative;
574         padding: 0px;
575         margin-top: 0.4em;
576         margin-bottom: 0.5em;
577 }
578
579 #gradient {
580         z-index: 1;
581         background-color: #FFFF00;
582 }
583
584 #mask {
585         z-index: 2;
586         background-color: #FFFFFF;
587 }
588
589 #progressIndicator {
590         z-index: 3;
591         background-color: transparent;
592 }
593
594 #parts {
595         padding: 0.4em;
596         display: none;
597         width: 100%;
598         font-size: 80%;
599         color: #ff0000;
600         text-align: center;
601 }
602 -->
603 </style>
604 <script type="text/javascript">
605 <!--
606
607 var debug_div = null;
608 EOF3
609
610         # take maximum archive size from configuration
611         $retHTML .= qq{
612 var media_size = $max_archive_size ;
613 var max_file_size = $max_archive_file_size;
614
615 };
616
617         $retHTML .= <<'EOF3';
618
619 function debug(msg) {
620         return; // Disable debugging
621
622         if (! debug_div) debug_div = document.getElementById('debug');
623
624         // this will create debug div if it doesn't exist.
625         if (! debug_div) {
626                 debug_div = document.createElement('div');
627                 if (document.body) document.body.appendChild(debug_div);
628                 else debug_div = null;
629         }
630         if (debug_div) {
631                 debug_div.appendChild(document.createTextNode(msg));
632                 debug_div.appendChild(document.createElement("br"));
633         }
634 }
635
636
637 var element_id_cache = Array();
638
639 function element_id(name,element) {
640         if (! element_id_cache[name]) {
641                 element_id_cache[name] = self.document.getElementById(name);
642         }
643         return element_id_cache[name];
644 }
645
646 function checkAll(location) {
647         var f = element_id('forma') || null;
648         if (!f) return false;
649
650         var len = f.elements.length;
651         var check_all = element_id('allFiles');
652         var suma = check_all.checked ? (parseInt(f.elements['totalsize'].value) || 0) : 0;
653
654         for (var i = 0; i < len; i++) {
655                 var e = f.elements[i];
656                 if (e.name != 'all' && e.name.substr(0, 3) == 'fcb') {
657                         if (check_all.checked) {
658                                 if (e.checked) continue;
659                                 var el = element_id("fss" + e.name.substr(3));
660                                 var size = parseInt(el.value) || 0;
661                                 debug('suma: '+suma+' size: '+size);
662                                 if ((suma + size) < media_size) {
663                                         suma += size;
664                                         e.checked = true;
665                                 } else {
666                                         break;
667                                 }
668                         } else {
669                                 e.checked = false;
670                         }
671                 }
672         }
673         update_sum(suma);
674 }
675
676 function update_sum(suma, suma_disp) {
677         if (! suma_disp) suma_disp = suma;
678         element_id('forma').elements['totalsize'].value = suma_disp;
679         pbar_set(suma, media_size);
680         debug('total size: ' + suma);
681 }
682
683 function sumiraj(e) {
684         var suma = parseInt(element_id('forma').elements['totalsize'].value) || 0;
685         var len = element_id('forma').elements.length;
686         if (e) {
687                 var size = parseInt( element_id("fss" + e.name.substr(3)).value);
688                 if (e.checked) {
689                         suma += size;
690                 } else {
691                         suma -= size;
692                 }
693
694                 var parts = parseInt( element_id("prt" + e.name.substr(3)).value);
695                 if (suma > max_file_size && suma == size && parts > 1) {
696                         element_id("parts").innerHTML = "This will take "+parts+" mediums!";
697                         element_id("parts").style.display = 'block';
698                         update_sum(media_size, suma);
699                         suma = media_size;
700                         return suma;
701                 } else {
702                         element_id("parts").style.display = 'none';
703                 }
704
705                 if (suma < 0) suma = 0;
706         } else {
707                 suma = 0;
708                 for (var i = 0; i < len; i++) {
709                         var e = element_id('forma').elements[i];
710                         if (e.name != 'all' && e.checked && e.name.substr(0,3) == 'fcb') {
711                                 var el = element_id("fss" + e.name.substr(3));
712                                 if (el && el.value) suma += parseInt(el.value) || 0;
713                         }
714                 }
715         }
716         update_sum(suma);
717         return suma;
718 }
719
720 /* progress bar */
721
722 var _pbar_width = null;
723 var _pbar_warn = 10;    // change color in last 10%
724
725 function pbar_reset() {
726         element_id("mask").style.left = "0px";
727         _pbar_width = element_id("mContainer").offsetWidth - 2;
728         element_id("mask").style.width = _pbar_width + "px";
729         element_id("mask").style.display = "block";
730         element_id("progressIndicator").style.zIndex  = 10;
731         element_id("progressIndicator").innerHTML = "0";
732 }
733
734 function dec2hex(d) {
735         var hch = '0123456789ABCDEF';
736         var a = d % 16;
737         var q = (d - a) / 16;
738         return hch.charAt(q) + hch.charAt(a);
739 }
740
741 function pbar_set(amount, max) {
742         debug('pbar_set('+amount+', '+max+')');
743
744         if (_pbar_width == null) {
745                 var _mc = element_id("mContainer");
746                 if (_pbar_width == null) _pbar_width = parseInt(_mc.offsetWidth ? (_mc.offsetWidth - 2) : 0) || null;
747                 if (_pbar_width == null) _pbar_width = parseInt(_mc.clientWidth ? (_mc.clientWidth + 2) : 0) || null;
748                 if (_pbar_width == null) _pbar_width = 0;
749         }
750
751         var pcnt = Math.floor(amount * 100 / max);
752         var p90 = 100 - _pbar_warn;
753         var pcol = pcnt - p90;
754         if (Math.round(pcnt) <= 100) {
755                 if (pcol < 0) pcol = 0;
756                 var e = element_id("submitBurner");
757                 debug('enable_button');
758                 e.disabled = false;
759                 var a = e.getAttributeNode('disabled') || null;
760                 if (a) e.removeAttributeNode(a);
761         } else {
762                 debug('disable button');
763                 pcol = _pbar_warn;
764                 var e = element_id("submitBurner");
765                 if (!e.disabled) e.disabled = true;
766         }
767         var col_g = Math.floor((_pbar_warn - pcol) * 255 / _pbar_warn);
768         var col = '#FF' + dec2hex(col_g) + '00';
769
770         //debug('pcol: '+pcol+' g:'+col_g+' _pbar_warn:'+ _pbar_warn + ' color: '+col);
771         element_id("gradient").style.backgroundColor = col;
772
773         element_id("progressIndicator").innerHTML = pcnt + '%';
774         //element_id("progressIndicator").innerHTML = amount;
775
776         element_id("mask").style.clip = 'rect(' + Array(
777                 '0px',
778                 element_id("mask").offsetWidth + 'px',
779                 element_id("mask").offsetHeight + 'px',
780                 Math.round(_pbar_width * amount / max) + 'px'
781         ).join(' ') + ')';
782 }
783
784 if (!self.body) self.body = new Object();
785 self.onload = self.document.onload = self.body.onload = function() {
786         //pbar_reset();
787         sumiraj();
788 };
789
790 // -->
791 </script>
792 <div id="fixedBox">
793
794 Size: <input type="text" name="totalsize" size="7" readonly="readonly" style="text-align:right;" value="0" /> kB
795
796 <div id="mContainer">
797         <div id="gradient">&nbsp;</div>
798         <div id="mask">&nbsp;</div>
799         <div id="progressIndicator">0%</div>
800 </div>
801 <br/>
802
803 <div id="parts">&nbsp;</div>
804
805 Note:
806 <textarea name="note" cols="10" rows="5" id="note"></textarea>
807
808 <input type="submit" id="submitBurner" value="Burn selected" name="submitBurner" />
809
810 </div>
811 <!--
812 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
813 no debug output yet
814 </div>
815 -->
816 EOF3
817         $retHTML .= q{
818                         <input type="hidden" value="burn" name="action">
819                         <input type="hidden" value="results" name="search_results">
820                         <table style="fview" border="0" cellspacing="0" cellpadding="2">
821                         <tr class="tableheader">
822                         <td class="tableheader">
823                                 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
824                         </td>
825         } .
826                 sort_header($param, 'Share', 'share', 'center') .
827                 sort_header($param, '#', 'num', 'center') .
828         qq{
829                         <td align="center">Type</td>
830         } .
831                 sort_header($param, 'Date', 'date', 'center') .
832                 sort_header($param, 'Age/days', 'age', 'center') .
833                 sort_header($param, 'Size/Mb', 'size', 'center') .
834                 sort_header($param, 'gzip size/Kb', 'incsize', 'center') .
835         qq{
836                         </tr>
837         };
838
839         my @color = (' bgcolor="#e0e0e0"', '');
840
841         my $i = 0;
842         my $host = '';
843
844         foreach my $backup ( getBackupsNotBurned($param) ) {
845
846                 if ($host ne $backup->{'host'}) {
847                         $i++;
848                         $host = $backup->{'host'};
849                 }
850                 my $ftype = "";
851
852                 my $checkbox_key = $backup->{'hostid'}. '_' .$backup->{'backupnum'} . '_' . $backup->{'id'};
853
854                 $retHTML .=
855                         '<tr' . $color[$i %2 ] . '>
856                         <td class="fview">';
857
858                 if (($backup->{'inc_size'} || 0) > 0) {
859                         $retHTML .= '
860                         <input type="checkbox" name="fcb' . $checkbox_key . '" value="' . $checkbox_key . '" onClick="sumiraj(this);">';
861                 }
862
863                 $retHTML .=
864                         '</td>' .
865                         '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
866                         '<td align="center">' . $backup->{'backupnum'} . '</td>' .
867                         '<td align="center">' . $backup->{'type'} . '</td>' .
868                         '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
869                         '<td align="center">' . $backup->{'age'} . '</td>' .
870                         '<td align="right">' . $backup->{'size'} . '</td>' .
871                         '<td align="right">' . $backup->{'inc_size'} .
872                         '<input type="hidden" id="fss'.$checkbox_key .'" value="'. $backup->{'inc_size'} .'"></td>' .
873                         '<input type="hidden" id="prt'.$checkbox_key .'" value="'. $backup->{'parts'} .'"></td>' .
874
875                         "</tr>\n";
876         }
877
878         $retHTML .= "</table>";
879         $retHTML .= "</form>";
880       
881         return $retHTML;
882 }      
883
884 sub displayGrid($) {
885         my ($param) = @_;
886
887         my $offset = $param->{'offset'};
888         my $hilite = $param->{'search_filename'};
889
890         my $retHTML = "";
891  
892         my $start_t = time();
893
894         my ($results, $files);
895         if ($param->{'use_hest'} && length($hilite) > 0) {
896                 ($results, $files) = getFilesHyperEstraier($param);
897         } else {
898                 ($results, $files) = getFiles($param);
899         }
900
901         my $dur_t = time() - $start_t;
902         my $dur = sprintf("%0.4fs", $dur_t);
903
904         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
905
906         if ($results <= 0) {
907                 $retHTML .= qq{
908                         <p style="color: red;">No results found...</p>
909                 };
910                 return $retHTML;
911         } else {
912                 # DEBUG
913                 #use Data::Dumper;
914                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
915         }
916
917
918         $retHTML .= qq{
919         <div>
920         Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
921         </div>
922         <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
923                 <tr class="fviewheader"> 
924                 <td></td>
925         };
926
927         sub sort_header($$$$) {
928                 my ($param, $display, $name, $align) = @_;
929
930                 my ($sort_what, $sort_direction) = split(/_/,$param->{'sort'},2);
931
932                 my $old_sort = $param->{'sort'};
933
934                 my $html = qq{<td align="$align"};
935                 my $arrow = '';
936
937                 if (lc($sort_what) eq lc($name)) {
938                         my $direction = lc($sort_direction);
939
940                         # swap direction or fallback to default
941                         $direction =~ tr/ad/da/;
942                         $direction = 'a' unless ($direction =~ /[ad]/);
943
944                         $param->{'sort'} = $name . '_' . $direction;
945                         $html .= ' style="border: 1px solid #808080;"';
946                 
947                         # add unicode arrow for direction
948                         $arrow .= '&nbsp;';
949                         $arrow .= $direction eq 'a'  ?  '&#9650;'
950                                 : $direction eq 'd'  ?  '&#9660;'
951                                 :                       ''
952                                 ;
953
954                 } else {
955                         $param->{'sort'} = $name . '_a';
956                 }
957
958                 $html .= '><a href="' . page_uri($param) . '">' . $display . '</a>' . $arrow . '</td>';
959                 $param->{'sort'} = $old_sort;
960
961                 return $html;
962         }
963
964         $retHTML .=
965                 sort_header($param, 'Share', 'share', 'center') .
966                 sort_header($param, 'Type and Name', 'path', 'center') .
967                 sort_header($param, '#', 'num', 'center') .
968                 sort_header($param, 'Size', 'size', 'center') .
969                 sort_header($param, 'Date', 'date', 'center');
970
971         $retHTML .= qq{
972                 <td align="center">Media</td>
973                 </tr>
974         };
975
976         my $file;
977
978         sub hilite_html($$) {
979                 my ($html, $search) = @_;
980                 $html =~ s#($search)#<b>$1</b>#gis;
981                 return $html;
982         }
983
984         sub restore_link($$$$$$) {
985                 my $type = shift;
986                 my $action = 'RestoreFile';
987                 $action = 'browse' if (lc($type) eq 'dir');
988                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
989         }
990
991         my $sth_archived;
992         my %archived_cache;
993
994         sub check_archived($$$) {
995                 my ($host, $share, $num) = @_;
996
997                 if (my $html = $archived_cache{"$host $share $num"}) {
998                         return $html;
999                 }
1000
1001                 $sth_archived ||= $dbh->prepare(qq{
1002                         select
1003                                 dvd_nr, note,
1004                                 count(archive_burned.copy) as copies
1005                         from archive
1006                         inner join archive_burned on archive_burned.archive_id = archive.id
1007                         inner join archive_backup on archive.id = archive_backup.archive_id
1008                         inner join backups on backups.id = archive_backup.backup_id
1009                         inner join hosts on hosts.id = backups.hostid
1010                         inner join shares on shares.id = backups.shareid
1011                         where hosts.name = ? and shares.name = ? and backups.num = ?
1012                         group by dvd_nr, note
1013                 });
1014
1015                 my @mediums;
1016
1017                 $sth_archived->execute($host, $share, $num);
1018                 while (my $row = $sth_archived->fetchrow_hashref()) {
1019                         push @mediums, '<abbr title="' .
1020                                 $row->{'note'} .
1021                                 ' [' . $row->{'copies'} . ']' .
1022                                 '">' .$row->{'dvd_nr'} .
1023                                 '</abbr>';
1024                 }
1025
1026                 my $html = join(", ",@mediums);
1027                 $archived_cache{"$host $share $num"} = $html;
1028                 return $html;
1029         }
1030
1031         my $i = $offset * $on_page;
1032
1033         foreach $file (@{ $files }) {
1034                 $i++;
1035
1036                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
1037                 $retHTML .= qq{<tr class="fviewborder">};
1038
1039                 $retHTML .= qq{<td class="fviewborder">$i</td>};
1040
1041                 $retHTML .=
1042                         qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
1043                         qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
1044                         qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
1045                         qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
1046                         qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
1047                         qq{<td class="fviewborder">} . check_archived( $file->{'hname'}, $file->{'sname'}, $file->{'backupnum'} ) . qq{</td>};
1048
1049                 $retHTML .= "</tr>";
1050         }
1051         $retHTML .= "</table>";
1052
1053         # all variables which has to be transfered
1054         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/) {
1055                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
1056         }
1057
1058         my $del = '';
1059         my $max_page = int( $results / $on_page );
1060         my $page = 0;
1061
1062         sub page_uri($) {
1063                 my $param = shift || die "no param?";
1064
1065                 my $uri = $MyURL;
1066                 my $del = '?';
1067                 foreach my $k (keys %{ $param }) {
1068                         if ($param->{$k}) {
1069                                 $uri .= $del . $k . '=' . ${EscURI( $param->{$k} )};
1070                                 $del = '&';
1071                         }
1072                 }
1073                 return $uri;
1074         }
1075
1076         sub page_link($$$) {
1077                 my ($param,$page,$display) = @_;
1078
1079                 $param->{'offset'} = $page if (defined($page));
1080
1081                 my $html = '<a href = "' . page_uri($param) . '">' . $display . '</a>';
1082         }
1083
1084         $retHTML .= '<div style="text-align: center;">';
1085
1086         if ($offset > 0) {
1087                 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
1088         }
1089
1090         while ($page <= $max_page) {
1091                 if ($page == $offset) {
1092                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
1093                 } else {
1094                         $retHTML .= $del . page_link($param, $page, $page + 1);
1095                 }
1096
1097                 if ($page < $offset - $pager_pages && $page != 0) {
1098                         $retHTML .= " ... ";
1099                         $page = $offset - $pager_pages;
1100                         $del = '';
1101                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
1102                         $retHTML .= " ... ";
1103                         $page = $max_page;
1104                         $del = '';
1105                 } else {
1106                         $del = ' | ';
1107                         $page++;
1108                 }
1109         }
1110
1111         if ($offset < $max_page) {
1112                 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
1113         }
1114
1115         $retHTML .= "</div>";
1116
1117         return $retHTML;
1118 }
1119
1120 1;