r8502@llin: dpavlin | 2005-10-13 19:05:29 +0200
[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
124 sub getFiles($) {
125         my ($param) = @_;
126
127         my $offset = $param->{'offset'} || 0;
128         $offset *= $on_page;
129
130         my $dbh = get_dbh();
131
132         my $sql_cols = qq{
133                 files.id                        AS fid,
134                 hosts.name                      AS hname,
135                 shares.name                     AS sname,
136                 files.backupnum                 AS backupnum,
137                 files.path                      AS filepath,
138                 files.date                      AS date,
139                 files.type                      AS type,
140                 files.size                      AS size
141         };
142
143         my $sql_from = qq{
144                 FROM files 
145                         INNER JOIN shares       ON files.shareID=shares.ID
146                         INNER JOIN hosts        ON hosts.ID = shares.hostID
147                         INNER JOIN backups      ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
148         };
149
150         my $sql_where;
151         my $where = getWhere($param);
152         $sql_where = " WHERE ". $where if ($where);
153
154         my $sql_order = qq{
155                 ORDER BY files.date
156                 LIMIT $on_page
157                 OFFSET ?
158         };
159
160         my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
161         my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
162
163         my $sth = $dbh->prepare($sql_count);
164         $sth->execute();
165         my ($results) = $sth->fetchrow_array();
166
167         $sth = $dbh->prepare($sql_results);
168         $sth->execute( $offset );
169
170         if ($sth->rows != $results) {
171                 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
172                 $bug =~ s/\s+/ /gs;
173                 print STDERR "$bug\n";
174         }
175
176         my @ret;
177       
178         while (my $row = $sth->fetchrow_hashref()) {
179                 push @ret, $row;
180         }
181      
182         $sth->finish();
183         return ($results, \@ret);
184 }
185
186 sub getHyperEstraier_url($) {
187         my ($use_hest) = @_;
188
189         return unless $use_hest;
190
191         use HyperEstraier;
192         my ($index_path, $index_node_url);
193
194         if ($use_hest =~ m#^http://#) {
195                 $index_node_url = $use_hest;
196         } else {
197                 $index_path = $TopDir . '/' . $index_path;
198                 $index_path =~ s#//#/#g;
199         }
200         return ($index_path, $index_node_url);
201 }
202
203 sub getFilesHyperEstraier($) {
204         my ($param) = @_;
205
206         my $offset = $param->{'offset'} || 0;
207         $offset *= $on_page;
208
209         die "no index_path?" unless ($hest_index_path);
210
211         use HyperEstraier;
212
213         my ($index_path, $index_node_url) = getHyperEstraier_url($hest_index_path);
214
215         # open the database
216         my $db;
217         if ($index_path) {
218                 $db = HyperEstraier::Database->new();
219                 $db->open($index_path, $HyperEstraier::ESTDBREADER);
220         } elsif ($index_node_url) {
221                 $db ||= HyperEstraier::Node->new($index_node_url);
222                 $db->set_auth('admin', 'admin');
223         } else {
224                 die "BUG: unimplemented";
225         }
226
227         # create a search condition object
228         my $cond = HyperEstraier::Condition->new();
229
230         my $q = $param->{'search_filename'};
231         my $shareid = $param->{'search_share'};
232
233         if (length($q) > 0) {
234                 # exact match
235                 $cond->add_attr("filepath ISTRINC $q");
236
237                 $q =~ s/(.)/$1 /g;
238                 # set the search phrase to the search condition object
239                 $cond->set_phrase($q);
240         }
241
242         my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
243
244         $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
245         $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
246
247         $cond->add_attr("date NUMGE $files_from") if ($files_from);
248         $cond->add_attr("date NUMLE $files_to") if ($files_to);
249
250         $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
251
252 #       $cond->set_max( $offset + $on_page );
253         $cond->set_options( $HyperEstraier::Condition::SURE );
254         $cond->set_order( 'date NUMA' );
255
256         # get the result of search
257         my @res;
258         my ($result, $hits);
259
260         if ($index_path) {
261                 $result = $db->search($cond, 0);
262                 $hits = $result->size;
263         } elsif ($index_node_url) {
264                 $result = $db->search($cond, 0);
265                 $hits = $result->doc_num;
266         } else {
267                 die "BUG: unimplemented";
268         }
269
270         # for each document in result
271         for my $i ($offset .. ($offset + $on_page - 1)) {
272                 last if ($i >= $hits);
273
274                 my $doc;
275                 if ($index_path) {
276                         my $id = $result->get($i);
277                         $doc = $db->get_doc($id, 0);
278                 } elsif ($index_node_url) {
279                         $doc = $result->get_doc($i);
280                 } else {
281                         die "BUG: unimplemented";
282                 }
283
284                 my $row;
285                 foreach my $c (qw/fid hname sname backupnum fiilename filepath date type size/) {
286                         $row->{$c} = $doc->attr($c);
287                 }
288                 push @res, $row;
289         }
290
291         return ($hits, \@res);
292 }
293
294 sub getGzipName($$$)
295 {
296         my ($host, $share, $backupnum) = @_;
297         my $ret = $Conf{GzipSchema};
298         
299         $share =~ s/\//_/g;
300         $ret =~ s/\\h/$host/ge;
301         $ret =~ s/\\s/$share/ge;
302         $ret =~ s/\\n/$backupnum/ge;
303
304         $ret =~ s/__+/_/g;
305
306         return $ret;
307         
308 }
309
310 sub get_tgz_size_by_name($) {
311         my $name = shift;
312
313         my $tgz = $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.$name;
314
315         my $size = -1;
316
317         if (-f $tgz) {
318                 $size = (stat($tgz))[7];
319         } elsif (-d $tgz) {
320                 opendir(my $dir, $tgz) || die "can't opendir $tgz: $!";
321                 my @parts = grep { !/^\./ && -f "$tgz/$_" } readdir($dir);
322                 $size = 0;
323                 foreach my $part (@parts) {
324                         $size += (stat("$tgz/$part"))[7] || die "can't stat $tgz/$part: $!";
325                 }
326                 closedir $dir;
327         }
328
329         return $size;
330 }
331
332 sub getGzipSize($$)
333 {
334         my ($hostID, $backupNum) = @_;
335         my $sql;
336         my $dbh = get_dbh();
337         
338         $sql = q{ 
339                                 SELECT hosts.name  as host,
340                                            shares.name as share,
341                                            backups.num as backupnum
342                                 FROM hosts, backups, shares
343                                 WHERE shares.id=backups.shareid AND
344                                           hosts.id =backups.hostid AND
345                                           hosts.id=? AND
346                                           backups.num=?
347                         };
348         my $sth = $dbh->prepare($sql);
349         $sth->execute($hostID, $backupNum);
350
351         my $row = $sth->fetchrow_hashref();
352
353         return get_tgz_size_by_name(
354                 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'})
355         );
356 }
357
358 sub getBackupsNotBurned() {
359
360         my $dbh = get_dbh();
361
362         my $sql = q{
363                 SELECT 
364                         backups.hostID AS hostID,
365                         hosts.name AS host,
366                         shares.name AS share,
367                         backups.num AS backupnum,
368                         backups.type AS type,
369                         backups.date AS date,
370                         backups.size AS size,
371                         backups.id AS id,
372                         backups.inc_size AS inc_size
373                 FROM backups 
374                 INNER JOIN shares       ON backups.shareID=shares.ID
375                 INNER JOIN hosts        ON backups.hostID = hosts.ID
376                 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id 
377                 WHERE backups.inc_size > 0 AND backups.inc_deleted is false AND archive_backup.backup_id IS NULL
378                 GROUP BY
379                         backups.hostID,
380                         hosts.name,
381                         shares.name,
382                         backups.num,
383                         backups.shareid,
384                         backups.id,
385                         backups.type,
386                         backups.date,
387                         backups.size,
388                         backups.inc_size
389                 ORDER BY backups.date
390         };
391         my $sth = $dbh->prepare( $sql );
392         my @ret;
393         $sth->execute();
394
395         while ( my $row = $sth->fetchrow_hashref() ) {
396                 $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
397                 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
398
399                 # do some cluster calculation (approximate) and convert to kB
400                 $row->{'inc_size'} = int(($row->{'inc_size'} + 1023 ) / ( 2 * 1024 ) * 2);
401                 push @ret, $row;
402         }
403       
404         return @ret;      
405 }
406
407 sub displayBackupsGrid() {
408
409         my $retHTML .= q{
410                 <form id="forma" method="POST" action="}.$MyURL.q{?action=burn">
411         };
412
413         $retHTML .= <<'EOF3';
414 <style type="text/css">
415 <!--
416 DIV#fixedBox {
417         position: absolute;
418         top: 50em;
419         left: -24%;
420         padding: 0.5em;
421         width: 20%;
422         background-color: #E0F0E0;
423         border: 1px solid #00C000;
424 }
425
426 DIV#fixedBox, DIV#fixedBox INPUT, DIV#fixedBox TEXTAREA {
427         font-size: 10pt;
428 }
429
430 FORM>DIV#fixedBox {
431         position: fixed !important;
432         left: 0.5em !important;
433         top: auto !important;
434         bottom: 1em !important;
435         width: 15% !important;
436 }
437
438 DIV#fixedBox INPUT[type=text], DIV#fixedBox TEXTAREA {
439         border: 1px solid #00C000;
440 }
441
442 DIV#fixedBox #note {
443         display: block;
444         width: 100%;
445 }
446
447 DIV#fixedBox #submitBurner {
448         display: block;
449         width: 100%;
450         margin-top: 0.5em;
451         cursor: pointer;
452 }
453
454 * HTML {
455         overflow-y: hidden;
456 }
457
458 * HTML BODY {
459         overflow-y: auto;
460         height: 100%;
461         font-size: 100%;
462 }
463
464 * HTML DIV#fixedBox {
465         position: absolute;
466 }
467
468 #mContainer, #gradient, #mask, #progressIndicator {
469         display: block;
470         width: 100%;
471         font-size: 10pt;
472         font-weight: bold;
473         text-align: center;
474         vertical-align: middle;
475         padding: 1px;
476 }
477
478 #gradient, #mask, #progressIndicator {
479         left: 0;
480         border-width: 1px;
481         border-style: solid;
482         border-color: #000000;
483         color: #404040;
484         margin: 0.4em;
485         position: absolute;
486         margin-left: -1px;
487         margin-top: -1px;
488         margin-bottom: -1px;
489         overflow: hidden;
490 }
491
492 #mContainer {
493         display: block;
494         position: relative;
495         padding: 0px;
496         margin-top: 0.4em;
497         margin-bottom: 0.5em;
498 }
499
500 #gradient {
501         z-index: 1;
502         background-color: #FFFF00;
503 }
504
505 #mask {
506         z-index: 2;
507         background-color: #FFFFFF;
508 }
509
510 #progressIndicator {
511         z-index: 3;
512         background-color: transparent;
513 }
514 -->
515 </style>
516 <script type="text/javascript">
517 <!--
518
519 var debug_div = null;
520 EOF3
521
522         # take maximum archive size from configuration
523         $retHTML .= 'var media_size = '. $Conf{MaxArchiveSize} .';';
524
525         $retHTML .= <<'EOF3';
526
527 function debug(msg) {
528 //      return; // Disable debugging
529
530         if (! debug_div) debug_div = document.getElementById('debug');
531
532         // this will create debug div if it doesn't exist.
533         if (! debug_div) {
534                 debug_div = document.createElement('div');
535                 if (document.body) document.body.appendChild(debug_div);
536                 else debug_div = null;
537         }
538         if (debug_div) {
539                 debug_div.appendChild(document.createTextNode(msg));
540                 debug_div.appendChild(document.createElement("br"));
541         }
542 }
543
544
545 var element_id_cache = Array();
546
547 function element_id(name,element) {
548         if (! element_id_cache[name]) {
549                 element_id_cache[name] = self.document.getElementById(name);
550         }
551         return element_id_cache[name];
552 }
553
554 function checkAll(location) {
555         var f = element_id('forma') || null;
556         if (!f) return false;
557
558         var len = f.elements.length;
559         var check_all = element_id('allFiles');
560         var suma = check_all.checked ? (parseInt(f.elements['totalsize'].value) || 0) : 0;
561
562         for (var i = 0; i < len; i++) {
563                 var e = f.elements[i];
564                 if (e.name != 'all' && e.name.substr(0, 3) == 'fcb') {
565                         if (check_all.checked) {
566                                 if (e.checked) continue;
567                                 var el = element_id("fss" + e.name.substr(3));
568                                 var size = parseInt(el.value) || 0;
569                                 debug('suma: '+suma+' size: '+size);
570                                 if ((suma + size) < media_size) {
571                                         suma += size;
572                                         e.checked = true;
573                                 } else {
574                                         break;
575                                 }
576                         } else {
577                                 e.checked = false;
578                         }
579                 }
580         }
581         update_sum(suma);
582 }
583
584 function update_sum(suma) {
585         element_id('forma').elements['totalsize'].value = suma;
586         pbar_set(suma, media_size);
587         debug('total size: ' + suma);
588 }
589
590 function sumiraj(e) {
591         var suma = parseInt(element_id('forma').elements['totalsize'].value) || 0;
592         var len = element_id('forma').elements.length;
593         if (e) {
594                 var size = parseInt( element_id("fss" + e.name.substr(3)).value);
595                 if (e.checked) {
596                         suma += size;
597                 } else {
598                         suma -= size;
599                 }
600         } else {
601                 suma = 0;
602                 for (var i = 0; i < len; i++) {
603                         var e = element_id('forma').elements[i];
604                         if (e.name != 'all' && e.checked && e.name.substr(0,3) == 'fcb') {
605                                 var el = element_id("fss" + e.name.substr(3));
606                                 if (el && el.value) suma += parseInt(el.value) || 0;
607                         }
608                 }
609         }
610         update_sum(suma);
611         return suma;
612 }
613
614 /* progress bar */
615
616 var _pbar_width = null;
617 var _pbar_warn = 10;    // change color in last 10%
618
619 function pbar_reset() {
620         element_id("mask").style.left = "0px";
621         _pbar_width = element_id("mContainer").offsetWidth - 2;
622         element_id("mask").style.width = _pbar_width + "px";
623         element_id("mask").style.display = "block";
624         element_id("progressIndicator").style.zIndex  = 10;
625         element_id("progressIndicator").innerHTML = "0";
626 }
627
628 function dec2hex(d) {
629         var hch = '0123456789ABCDEF';
630         var a = d % 16;
631         var q = (d - a) / 16;
632         return hch.charAt(q) + hch.charAt(a);
633 }
634
635 function pbar_set(amount, max) {
636         debug('pbar_set('+amount+', '+max+')');
637
638         if (_pbar_width == null) {
639                 var _mc = element_id("mContainer");
640                 if (_pbar_width == null) _pbar_width = parseInt(_mc.offsetWidth ? (_mc.offsetWidth - 2) : 0) || null;
641                 if (_pbar_width == null) _pbar_width = parseInt(_mc.clientWidth ? (_mc.clientWidth + 2) : 0) || null;
642                 if (_pbar_width == null) _pbar_width = 0;
643         }
644
645         var pcnt = Math.floor(amount * 100 / max);
646         var p90 = 100 - _pbar_warn;
647         var pcol = pcnt - p90;
648         if (Math.round(pcnt) <= 100) {
649                 if (pcol < 0) pcol = 0;
650                 var e = element_id("submitBurner");
651                 debug('enable_button');
652                 e.disabled = false;
653                 var a = e.getAttributeNode('disabled') || null;
654                 if (a) e.removeAttributeNode(a);
655         } else {
656                 debug('disable button');
657                 pcol = _pbar_warn;
658                 var e = element_id("submitBurner");
659                 if (!e.disabled) e.disabled = true;
660         }
661         var col_g = Math.floor((_pbar_warn - pcol) * 255 / _pbar_warn);
662         var col = '#FF' + dec2hex(col_g) + '00';
663
664         //debug('pcol: '+pcol+' g:'+col_g+' _pbar_warn:'+ _pbar_warn + ' color: '+col);
665         element_id("gradient").style.backgroundColor = col;
666
667         element_id("progressIndicator").innerHTML = pcnt + '%';
668         //element_id("progressIndicator").innerHTML = amount;
669
670         element_id("mask").style.clip = 'rect(' + Array(
671                 '0px',
672                 element_id("mask").offsetWidth + 'px',
673                 element_id("mask").offsetHeight + 'px',
674                 Math.round(_pbar_width * amount / max) + 'px'
675         ).join(' ') + ')';
676 }
677
678 if (!self.body) self.body = new Object();
679 self.onload = self.document.onload = self.body.onload = function() {
680         //pbar_reset();
681         sumiraj();
682 };
683
684 // -->
685 </script>
686 <div id="fixedBox">
687
688 Size: <input type="text" name="totalsize" size="7" readonly="readonly" style="text-align:right;" value="0" /> kB
689
690 <div id="mContainer">
691         <div id="gradient">&nbsp;</div>
692         <div id="mask">&nbsp;</div>
693         <div id="progressIndicator">0%</div>
694 </div>
695 <br/>
696
697 Note:
698 <textarea name="note" cols="10" rows="5" id="note"></textarea>
699
700 <input type="submit" id="submitBurner" value="Burn selected" name="submitBurner" />
701
702 </div>
703 <!--
704 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
705 no debug output yet
706 </div>
707 -->
708 EOF3
709         $retHTML .= q{
710                         <input type="hidden" value="burn" name="action">
711                         <input type="hidden" value="results" name="search_results">
712                         <table style="fview" border="0" cellspacing="0" cellpadding="2">
713                         <tr class="tableheader">
714                         <td class="tableheader">
715                                 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
716                         </td>
717                         <td align="center">Share</td>
718                         <td align="center">Backup no</td>
719                         <td align="center">Type</td>
720                         <td align="center">date</td>
721                         <td align="center">age/days</td>
722                         <td align="center">size/MB</td>
723                         <td align="center">gzip size/kB</td>
724                         </tr>
725
726         };
727
728         my @color = (' bgcolor="#e0e0e0"', '');
729
730         my $i = 0;
731         my $host = '';
732
733         foreach my $backup ( getBackupsNotBurned() ) {
734
735                 if ($host ne $backup->{'host'}) {
736                         $i++;
737                         $host = $backup->{'host'};
738                 }
739                 my $ftype = "";
740
741                 my $checkbox_key = $backup->{'hostid'}. '_' .$backup->{'backupnum'} . '_' . $backup->{'id'};
742
743                 $retHTML .=
744                         '<tr' . $color[$i %2 ] . '>
745                         <td class="fview">';
746
747                 if (($backup->{'inc_size'} || 0) > 0) {
748                         $retHTML .= '
749                         <input type="checkbox" name="fcb' . $checkbox_key . '" value="' . $checkbox_key . '" onClick="sumiraj(this);">';
750                 }
751
752                 $retHTML .=
753                         '</td>' .
754                         '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
755                         '<td align="center">' . $backup->{'backupnum'} . '</td>' .
756                         '<td align="center">' . $backup->{'type'} . '</td>' .
757                         '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
758                         '<td align="center">' . $backup->{'age'} . '</td>' .
759                         '<td align="right">' . $backup->{'size'} . '</td>' .
760                         '<td align="right">' . $backup->{'inc_size'} .
761                         '<input type="hidden" iD="fss'.$checkbox_key .'" value="'. $backup->{'inc_size'} .'"></td>' .
762
763                         "</tr>\n";
764         }
765
766         $retHTML .= "</table>";
767         $retHTML .= "</form>";
768       
769         return $retHTML;
770 }      
771
772 sub displayGrid($) {
773         my ($param) = @_;
774
775         my $offset = $param->{'offset'};
776         my $hilite = $param->{'search_filename'};
777
778         my $retHTML = "";
779  
780         my $start_t = time();
781
782         my ($results, $files);
783         if ($param->{'use_hest'} && length($hilite) > 0) {
784                 ($results, $files) = getFilesHyperEstraier($param);
785         } else {
786                 ($results, $files) = getFiles($param);
787         }
788
789         my $dur_t = time() - $start_t;
790         my $dur = sprintf("%0.4fs", $dur_t);
791
792         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
793
794         if ($results <= 0) {
795                 $retHTML .= qq{
796                         <p style="color: red;">No results found...</p>
797                 };
798                 return $retHTML;
799         } else {
800                 # DEBUG
801                 #use Data::Dumper;
802                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
803         }
804
805
806         $retHTML .= qq{
807         <div>
808         Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
809         </div>
810         <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
811                 <tr class="fviewheader"> 
812                 <td></td>
813                 <td align="center">Share</td>
814                 <td align="center">Type and Name</td>
815                 <td align="center">#</td>
816                 <td align="center">Size</td>
817                 <td align="center">Date</td>
818                 <td align="center">Media</td>
819                 </tr>
820         };
821
822         my $file;
823
824         sub hilite_html($$) {
825                 my ($html, $search) = @_;
826                 $html =~ s#($search)#<b>$1</b>#gis;
827                 return $html;
828         }
829
830         sub restore_link($$$$$$) {
831                 my $type = shift;
832                 my $action = 'RestoreFile';
833                 $action = 'browse' if (lc($type) eq 'dir');
834                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
835         }
836
837         my $i = $offset * $on_page;
838
839         foreach $file (@{ $files }) {
840                 $i++;
841
842                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
843                 $retHTML .= qq{<tr class="fviewborder">};
844
845                 $retHTML .= qq{<td class="fviewborder">$i</td>};
846
847                 $retHTML .=
848                         qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
849                         qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
850                         qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
851                         qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
852                         qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
853                         qq{<td class="fviewborder">} . '?' . qq{</td>};
854
855                 $retHTML .= "</tr>";
856         }
857         $retHTML .= "</table>";
858
859         # all variables which has to be transfered
860         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/) {
861                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
862         }
863
864         my $del = '';
865         my $max_page = int( $results / $on_page );
866         my $page = 0;
867
868         sub page_link($$$) {
869                 my ($param,$page,$display) = @_;
870
871                 $param->{'offset'} = $page;
872
873                 my $html = '<a href = "' . $MyURL;
874                 my $del = '?';
875                 foreach my $k (keys %{ $param }) {
876                         if ($param->{$k}) {
877                                 $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
878                                 $del = '&';
879                         }
880                 }
881                 $html .= '">' . $display . '</a>';
882         }
883
884         $retHTML .= '<div style="text-align: center;">';
885
886         if ($offset > 0) {
887                 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
888         }
889
890         while ($page <= $max_page) {
891                 if ($page == $offset) {
892                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
893                 } else {
894                         $retHTML .= $del . page_link($param, $page, $page + 1);
895                 }
896
897                 if ($page < $offset - $pager_pages && $page != 0) {
898                         $retHTML .= " ... ";
899                         $page = $offset - $pager_pages;
900                         $del = '';
901                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
902                         $retHTML .= " ... ";
903                         $page = $max_page;
904                         $del = '';
905                 } else {
906                         $del = ' | ';
907                         $page++;
908                 }
909         }
910
911         if ($offset < $max_page) {
912                 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
913         }
914
915         $retHTML .= "</div>";
916
917         return $retHTML;
918 }
919
920 1;