/[BackupPC]/trunk/lib/BackupPC/SearchLib.pm
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/lib/BackupPC/SearchLib.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 127 - (show annotations)
Thu Sep 22 09:27:17 2005 UTC (18 years, 7 months ago) by dpavlin
File size: 17057 byte(s)
make div with fixed position for current size, note and submit button

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
12 my $on_page = 100;
13 my $pager_pages = 10;
14
15 my $dsn = $Conf{SearchDSN};
16 my $db_user = $Conf{SearchUser} || '';
17
18 my $hest_index_path = $Conf{HyperEstraierIndex};
19
20 my $dbh;
21
22 sub get_dbh {
23 $dbh ||= DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
24 return $dbh;
25 }
26
27 sub getUnits() {
28 my @ret;
29
30 my $dbh = get_dbh();
31 my $sth = $dbh->prepare(qq{
32 SELECT
33 shares.id as id,
34 hosts.name || ':' || shares.name as share
35 FROM shares
36 JOIN hosts on hostid = hosts.id
37 ORDER BY share
38 } );
39 $sth->execute();
40 push @ret, { 'id' => '', 'share' => '-'}; # dummy any
41
42 while ( my $row = $sth->fetchrow_hashref() ) {
43 push @ret, $row;
44 }
45 return @ret;
46 }
47
48 sub epoch_to_iso {
49 my $t = shift || return;
50 my $iso = BackupPC::Lib::timeStamp(undef, $t);
51 $iso =~ s/\s/ /g;
52 return $iso;
53 }
54
55 sub dates_from_form($) {
56 my $param = shift || return;
57
58 sub mk_epoch_date($$) {
59 my ($name,$suffix) = @_;
60
61 my $yyyy = $param->{ $name . '_year_' . $suffix} || return undef;
62 my $mm .= $param->{ $name . '_month_' . $suffix} ||
63 ( $suffix eq 'from' ? 1 : 12);
64 my $dd .= $param->{ $name . '_day_' . $suffix} ||
65 ( $suffix eq 'from' ? 1 : 31);
66
67 $yyyy =~ s/\D//g;
68 $mm =~ s/\D//g;
69 $dd =~ s/\D//g;
70
71 my $dt = new DateTime(
72 year => $yyyy,
73 month => $mm,
74 day => $dd
75 );
76 print STDERR "mk_epoch_date($name,$suffix) [$yyyy-$mm-$dd] = " . $dt->ymd . " " . $dt->hms . "\n";
77 return $dt->epoch || 'NULL';
78 }
79
80 my @ret = (
81 mk_epoch_date('search_backup', 'from'),
82 mk_epoch_date('search_backup', 'to'),
83 mk_epoch_date('search', 'from'),
84 mk_epoch_date('search', 'to'),
85 );
86
87 return @ret;
88
89 }
90
91
92 sub getWhere($) {
93 my $param = shift || return;
94
95 my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
96
97 my @conditions;
98 push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
99 push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
100 push @conditions, qq{ files.date >= $files_from } if ($files_from);
101 push @conditions, qq{ files.date <= $files_to } if ($files_to);
102
103 print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" | ",@conditions);
104
105 push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
106 push (@conditions, " upper(files.path) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
107
108 return join(" and ", @conditions);
109 }
110
111
112 sub getFiles($) {
113 my ($param) = @_;
114
115 my $offset = $param->{'offset'} || 0;
116 $offset *= $on_page;
117
118 my $dbh = get_dbh();
119
120 my $sql_cols = qq{
121 files.id AS fid,
122 hosts.name AS hname,
123 shares.name AS sname,
124 files.backupnum AS backupnum,
125 files.path AS filepath,
126 files.date AS date,
127 files.type AS type,
128 files.size AS size
129 };
130
131 my $sql_from = qq{
132 FROM files
133 INNER JOIN shares ON files.shareID=shares.ID
134 INNER JOIN hosts ON hosts.ID = shares.hostID
135 INNER JOIN backups ON backups.num = files.backupnum and backups.hostID = hosts.ID AND backups.shareID = files.shareID
136 };
137
138 my $sql_where;
139 my $where = getWhere($param);
140 $sql_where = " WHERE ". $where if ($where);
141
142 my $sql_order = qq{
143 ORDER BY files.date
144 LIMIT $on_page
145 OFFSET ?
146 };
147
148 my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
149 my $sql_results = qq{ select $sql_cols $sql_from $sql_where $sql_order };
150
151 my $sth = $dbh->prepare($sql_count);
152 $sth->execute();
153 my ($results) = $sth->fetchrow_array();
154
155 $sth = $dbh->prepare($sql_results);
156 $sth->execute( $offset );
157
158 if ($sth->rows != $results) {
159 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
160 $bug =~ s/\s+/ /gs;
161 print STDERR "$bug\n";
162 }
163
164 my @ret;
165
166 while (my $row = $sth->fetchrow_hashref()) {
167 push @ret, $row;
168 }
169
170 $sth->finish();
171 return ($results, \@ret);
172 }
173
174 sub getHyperEstraier_url($) {
175 my ($use_hest) = @_;
176
177 return unless $use_hest;
178
179 use HyperEstraier;
180 my ($index_path, $index_node_url);
181
182 if ($use_hest =~ m#^http://#) {
183 $index_node_url = $use_hest;
184 } else {
185 $index_path = $TopDir . '/' . $index_path;
186 $index_path =~ s#//#/#g;
187 }
188 return ($index_path, $index_node_url);
189 }
190
191 sub getFilesHyperEstraier($) {
192 my ($param) = @_;
193
194 my $offset = $param->{'offset'} || 0;
195 $offset *= $on_page;
196
197 die "no index_path?" unless ($hest_index_path);
198
199 use HyperEstraier;
200
201 my ($index_path, $index_node_url) = getHyperEstraier_url($hest_index_path);
202
203 # open the database
204 my $db;
205 if ($index_path) {
206 $db = HyperEstraier::Database->new();
207 $db->open($index_path, $HyperEstraier::ESTDBREADER);
208 } elsif ($index_node_url) {
209 $db ||= HyperEstraier::Node->new($index_node_url);
210 $db->set_auth('admin', 'admin');
211 } else {
212 die "BUG: unimplemented";
213 }
214
215 # create a search condition object
216 my $cond = HyperEstraier::Condition->new();
217
218 my $q = $param->{'search_filename'};
219 my $shareid = $param->{'search_share'};
220
221 if (length($q) > 0) {
222 # exact match
223 $cond->add_attr("filepath ISTRINC $q");
224
225 $q =~ s/(.)/$1 /g;
226 # set the search phrase to the search condition object
227 $cond->set_phrase($q);
228 }
229
230 my ($backup_from, $backup_to, $files_from, $files_to) = dates_from_form($param);
231
232 $cond->add_attr("backup_date NUMGE $backup_from") if ($backup_from);
233 $cond->add_attr("backup_date NUMLE $backup_to") if ($backup_to);
234
235 $cond->add_attr("date NUMGE $files_from") if ($files_from);
236 $cond->add_attr("date NUMLE $files_to") if ($files_to);
237
238 $cond->add_attr("shareid NUMEQ $shareid") if ($shareid);
239
240 # $cond->set_max( $offset + $on_page );
241 $cond->set_options( $HyperEstraier::Condition::SURE );
242 $cond->set_order( 'date NUMA' );
243
244 # get the result of search
245 my @res;
246 my ($result, $hits);
247
248 if ($index_path) {
249 $result = $db->search($cond, 0);
250 $hits = $result->size;
251 } elsif ($index_node_url) {
252 $result = $db->search($cond, 0);
253 $hits = $result->doc_num;
254 } else {
255 die "BUG: unimplemented";
256 }
257
258 # for each document in result
259 for my $i ($offset .. ($offset + $on_page - 1)) {
260 last if ($i >= $hits);
261
262 my $doc;
263 if ($index_path) {
264 my $id = $result->get($i);
265 $doc = $db->get_doc($id, 0);
266 } elsif ($index_node_url) {
267 $doc = $result->get_doc($i);
268 } else {
269 die "BUG: unimplemented";
270 }
271
272 my $row;
273 foreach my $c (qw/fid hname sname backupnum fiilename filepath date type size/) {
274 $row->{$c} = $doc->attr($c);
275 }
276 push @res, $row;
277 }
278
279 return ($hits, \@res);
280 }
281
282 sub getGzipName($$$)
283 {
284 my ($host, $share, $backupnum) = @_;
285 my $ret = $Conf{GzipSchema};
286
287 $share =~ s/\//_/g;
288 $ret =~ s/\\h/$host/ge;
289 $ret =~ s/\\s/$share/ge;
290 $ret =~ s/\\n/$backupnum/ge;
291
292 return $ret;
293
294 }
295
296 sub getBackupsNotBurned() {
297
298 my $dbh = get_dbh();
299
300 my $sql = q{
301 SELECT
302 backups.hostID AS hostID,
303 hosts.name AS host,
304 shares.name AS share,
305 backups.id AS backupnum,
306 backups.type AS type,
307 backups.date AS date,
308 backups.size AS size
309 FROM backups
310 INNER JOIN shares ON backups.shareID=shares.ID
311 INNER JOIN hosts ON backups.hostID = hosts.ID
312 LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id AND archive_backup.backup_id IS NULL
313 WHERE backups.size > 0
314 GROUP BY
315 backups.hostID,
316 hosts.name,
317 shares.name,
318 backups.num,
319 backups.shareid,
320 backups.id,
321 backups.type,
322 backups.date,
323 backups.size
324 ORDER BY backups.date
325 };
326 my $sth = $dbh->prepare( $sql );
327 my @ret;
328 $sth->execute();
329
330 while ( my $row = $sth->fetchrow_hashref() ) {
331 $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
332 $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
333 my (undef,undef,undef,undef,undef,undef,undef,$fs_size,undef,undef,undef,undef,undef) =
334 stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
335 getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
336 $row->{'fs_size'} = $fs_size;
337 push @ret, $row;
338 }
339
340 return @ret;
341 }
342
343 sub displayBackupsGrid() {
344
345 my $retHTML .= q{
346 <form id="forma" method="POST" action=};
347 $retHTML .= "\"".$MyURL."\"";
348 $retHTML .= q{?action=burn>
349 };
350
351 $retHTML .= <<'EOF3';
352 <style>
353 <!--
354
355 div#fixedBox
356 {
357 position: absolute;
358 bottom: 1em;
359 left: 0.5em;
360 padding: 0.5em;
361 width: 10em;
362 background: #e0f0e0;
363 border: 1px solid #00ff00;
364 }
365 @media screen
366 {
367 div#fixedBox
368 {
369 position: fixed;
370 }
371 /* Don't do this at home */
372 * html
373 {
374 overflow-y: hidden;
375 }
376 * html body
377 {
378 overflow-y: auto;
379 height: 100%;
380 padding: 0 1em 0 12em;
381 font-size: 100%;
382 }
383 * html div#fixedBox
384 {
385 position: absolute;
386 }
387 /* All done. */
388 }
389
390 -->
391 </style>
392 <script language="javascript" type="text/javascript">
393 <!--
394
395 var debug_div = null;
396
397 function debug(msg) {
398 // return; // Disable debugging
399
400 if (! debug_div) debug_div = document.getElementById('debug');
401
402 // this will create debug div if it doesn't exist.
403 if (! debug_div) {
404 debug_div = document.createElement('div');
405 if (document.body) document.body.appendChild(debug_div);
406 else debug_div = null;
407 }
408 if (debug_div) {
409 debug_div.appendChild(document.createTextNode(msg));
410 debug_div.appendChild(document.createElement("br"));
411 }
412 }
413
414
415 var element_id_cache = Array();
416
417 function element_id(name,element) {
418 if (! element_id_cache[name]) {
419 element_id_cache[name] = self.document.getElementById(name);
420 }
421 return element_id_cache[name];
422 }
423
424 function checkAll(location) {
425 var len = element_id('forma').elements.length;
426 var check_all = element_id('allFiles');
427
428 for (var i = 0; i < len; i++) {
429
430 var e = element_id('forma').elements[i];
431 if ((e.checked || !e.checked) && e.name != 'all') {
432 if (check_all.checked) {
433 e.checked = true;
434 } else {
435 e.checked = false;
436 }
437 }
438 }
439
440 sumiraj();
441 }
442
443 function sumiraj(e) {
444 var suma = parseInt(element_id('forma').totalsize.value) || 0;
445 var len = element_id('forma').elements.length;
446 if (e) {
447 var size = parseInt( element_id("fss" + e.name.substr(3)).value );
448 if (e.checked) {
449 suma += size;
450 } else {
451 suma -= size;
452 }
453 } else {
454 suma = 0;
455 for (var i = 0; i < len; i++) {
456 var e = element_id('forma').elements[i];
457 if (e.name != 'all' && e.checked && e.name.substr(0,3) == 'fcb') {
458 var el = element_id("fss" + e.name.substr(3));
459 if (el && el.value) suma += parseInt(el.value) || 0;
460 }
461 }
462 }
463 element_id('forma').totalsize.value = suma;
464 debug('total size: '+suma);
465 return suma;
466 }
467
468 if (!self.body) self.body = new Object();
469 self.onload = self.document.onload = self.body.onload = function() {
470 sumiraj();
471 }
472
473 //-->
474 </script>
475 <div id="fixedBox">
476
477 Size:
478 <input type="text" name="totalsize" size="7" readonly>
479 <br/>
480 Note:
481 <br/>
482 <textarea name="note" cols="10" rows="5">
483 </textarea>
484 <br/>
485 <input type="submit" value="Burn selected" name="submitBurner">
486
487 </div>
488 <div id="debug" style="float: right; width: 10em; border: 1px #ff0000 solid; background-color: #ffe0e0; -moz-opacity: 0.7;">
489 no debug output yet
490 </div>
491 EOF3
492 $retHTML .= q{
493 <input type="hidden" value="burn" name="action">
494 <input type="hidden" value="results" name="search_results">
495 <table style="fview" border="0" cellspacing="0" cellpadding="2">
496 <tr class="tableheader">
497 <td class="tableheader">
498 <input type="checkbox" name="allFiles" id="allFiles" onClick="checkAll('allFiles');">
499 </td>
500 <td align="center">Share</td>
501 <td align="center">Backup no</td>
502 <td align="center">Type</td>
503 <td align="center">date</td>
504 <td align="center">age/days</td>
505 <td align="center">size/MB</td>
506 <td align="center">gzip size</td>
507 </tr>
508
509 };
510
511 my @color = (' bgcolor="#e0e0e0"', '');
512
513 my $i = 0;
514 my $host = '';
515
516 foreach my $backup ( getBackupsNotBurned() ) {
517
518 if ($host ne $backup->{'host'}) {
519 $i++;
520 $host = $backup->{'host'};
521 }
522 my $ftype = "";
523
524 $retHTML .=
525 '<tr' . $color[$i %2 ] . '>
526 <td class="fview">';
527 # FIXME
528 #$backup->{'fs_size'} = int($backup->{'size'} * 1024);
529 if (($backup->{'fs_size'} || 0) > 0) {
530 $retHTML .= '
531 <input type="checkbox" name="fcb' .
532 $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
533 '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
534 '" onClick="sumiraj(this);">';
535 }
536 $retHTML .=
537 '</td>' .
538 '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
539 '<td align="center">' . $backup->{'backupnum'} . '</td>' .
540 '<td align="center">' . $backup->{'type'} . '</td>' .
541 '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
542 '<td align="center">' . $backup->{'age'} . '</td>' .
543 '<td align="right">' . $backup->{'size'} . '</td>' .
544 '<td align="right">' . $backup->{'fs_size'} .
545 '<input type="hidden" iD="fss'.$backup->{'hostid'}.'_'.$backup->{'backupnum'} . '" value="'. $backup->{'fs_size'} .'"></td>' .
546
547 "</tr>\n";
548 }
549
550 $retHTML .= "</table>";
551 $retHTML .= "</form>";
552
553 return $retHTML;
554 }
555
556 sub displayGrid($) {
557 my ($param) = @_;
558
559 my $offset = $param->{'offset'};
560 my $hilite = $param->{'search_filename'};
561
562 my $retHTML = "";
563
564 my $start_t = time();
565
566 my ($results, $files);
567 if ($param->{'use_hest'} && length($hilite) > 0) {
568 ($results, $files) = getFilesHyperEstraier($param);
569 } else {
570 ($results, $files) = getFiles($param);
571 }
572
573 my $dur_t = time() - $start_t;
574 my $dur = sprintf("%0.4fs", $dur_t);
575
576 my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
577
578 if ($results <= 0) {
579 $retHTML .= qq{
580 <p style="color: red;">No results found...</p>
581 };
582 return $retHTML;
583 } else {
584 # DEBUG
585 #use Data::Dumper;
586 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
587 }
588
589
590 $retHTML .= qq{
591 <div>
592 Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
593 </div>
594 <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
595 <tr class="fviewheader">
596 <td></td>
597 <td align="center">Share</td>
598 <td align="center">Type and Name</td>
599 <td align="center">#</td>
600 <td align="center">Size</td>
601 <td align="center">Date</td>
602 <td align="center">Media</td>
603 </tr>
604 };
605
606 my $file;
607
608 sub hilite_html($$) {
609 my ($html, $search) = @_;
610 $html =~ s#($search)#<b>$1</b>#gis;
611 return $html;
612 }
613
614 sub restore_link($$$$$$) {
615 my $type = shift;
616 my $action = 'RestoreFile';
617 $action = 'browse' if (lc($type) eq 'dir');
618 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
619 }
620
621 my $i = $offset * $on_page;
622
623 foreach $file (@{ $files }) {
624 $i++;
625
626 my $typeStr = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
627 $retHTML .= qq{<tr class="fviewborder">};
628
629 $retHTML .= qq{<td class="fviewborder">$i</td>};
630
631 $retHTML .=
632 qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
633 qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
634 qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
635 qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
636 qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
637 qq{<td class="fviewborder">} . '?' . qq{</td>};
638
639 $retHTML .= "</tr>";
640 }
641 $retHTML .= "</table>";
642
643 # all variables which has to be transfered
644 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/) {
645 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
646 }
647
648 my $del = '';
649 my $max_page = int( $results / $on_page );
650 my $page = 0;
651
652 sub page_link($$$) {
653 my ($param,$page,$display) = @_;
654
655 $param->{'offset'} = $page;
656
657 my $html = '<a href = "' . $MyURL;
658 my $del = '?';
659 foreach my $k (keys %{ $param }) {
660 if ($param->{$k}) {
661 $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
662 $del = '&';
663 }
664 }
665 $html .= '">' . $display . '</a>';
666 }
667
668 $retHTML .= '<div style="text-align: center;">';
669
670 if ($offset > 0) {
671 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
672 }
673
674 while ($page <= $max_page) {
675 if ($page == $offset) {
676 $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
677 } else {
678 $retHTML .= $del . page_link($param, $page, $page + 1);
679 }
680
681 if ($page < $offset - $pager_pages && $page != 0) {
682 $retHTML .= " ... ";
683 $page = $offset - $pager_pages;
684 $del = '';
685 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
686 $retHTML .= " ... ";
687 $page = $max_page;
688 $del = '';
689 } else {
690 $del = ' | ';
691 $page++;
692 }
693 }
694
695 if ($offset < $max_page) {
696 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
697 }
698
699 $retHTML .= "</div>";
700
701 return $retHTML;
702 }
703
704 1;

  ViewVC Help
Powered by ViewVC 1.1.26