/[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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 121 - (hide annotations)
Thu Sep 15 13:54:29 2005 UTC (18 years, 8 months ago) by iklaric
File size: 15121 byte(s)
 r124@klaxLaptop:  klax | 2005-09-15 15:46:19 +0200
 - first step of workflow is done.

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

  ViewVC Help
Powered by ViewVC 1.1.26