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