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