/[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 122 - (hide annotations)
Fri Sep 16 14:11:00 2005 UTC (18 years, 8 months ago) by dpavlin
File size: 15422 byte(s)
1000x speedup (60 s -> 60 ms)

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 122 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 dpavlin 122 INNER JOIN shares ON backups.shareID=shares.ID
315     INNER JOIN hosts ON backups.hostID = hosts.ID
316     LEFT OUTER JOIN archive_backup ON archive_backup.backup_id = backups.id AND archive_backup.backup_id IS NULL
317     WHERE backups.size > 0
318     GROUP BY
319     backups.hostID,
320     hosts.name,
321     shares.name,
322     backups.num,
323     backups.shareid,
324     backups.id,
325     backups.type,
326     backups.date,
327     backups.size
328 iklaric 121 ORDER BY backups.date
329 dpavlin 53 };
330     my $sth = $dbh->prepare( $sql );
331     my @ret;
332     $sth->execute();
333 dpavlin 4
334 dpavlin 66 while ( my $row = $sth->fetchrow_hashref() ) {
335     $row->{'age'} = sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) );
336     $row->{'size'} = sprintf("%0.2f", $row->{'size'} / 1024 / 1024);
337 dpavlin 109 my (undef,undef,undef,undef,undef,undef,undef,$fs_size,undef,undef,undef,undef,undef) =
338     stat( $Conf{InstallDir}.'/'.$Conf{GzipTempDir}.'/'.
339     getGzipName($row->{'host'}, $row->{share}, $row->{'backupnum'}));
340     $row->{'fs_size'} = $fs_size;
341 dpavlin 66 push @ret, $row;
342 dpavlin 4 }
343    
344 dpavlin 53 return @ret;
345     }
346 dpavlin 4
347     sub displayBackupsGrid()
348     {
349     my $retHTML = "";
350    
351 dpavlin 102 $retHTML .= <<EOF3;
352 dpavlin 4 <script language="javascript" type="text/javascript">
353     <!--
354    
355     function checkAll(location)
356     {
357     for (var i=0;i<document.forma.elements.length;i++)
358     {
359     var e = document.forma.elements[i];
360     if ((e.checked || !e.checked) && e.name != \'all\') {
361     if (eval("document.forma."+location+".checked")) {
362     e.checked = true;
363     } else {
364     e.checked = false;
365     }
366     }
367     }
368     }
369 iklaric 121
370     function sumiraj()
371     {
372     var suma = 0;
373     for (var i = 0; i < document.forma.elements.length; i++)
374     {
375     var e = document.forma.elements[i];
376     if ((e.checked || !e.checked) && e.name != \'all\')
377     {
378     if (e.checked)
379     {
380     var ret = e.name.match("fcb(.*)");
381     suma += parseInt(eval("document.forma.fss"+ret[1]+".value"));
382    
383     }
384     }
385     }
386     document.forma.totalsize.value = suma;
387     return suma;
388     }
389 dpavlin 4 //-->
390     </script>
391     EOF3
392 dpavlin 102 $retHTML .= q{
393 iklaric 121 <form name="forma" method="GET" action=};
394     $retHTML .= "\"".$MyURL."\"";
395     $retHTML .= q{?action=burn>
396     <input type="hidden" value="burn" name="action">
397     <input type="hidden" value="results" name="search_results">
398     <table style="fview" border="0" cellspacing="0" cellpadding="2">
399     <tr class="tableheader">
400     <td class="tableheader">
401     <input type="checkbox" name="allFiles" onClick="checkAll('allFiles');">
402     </td>
403     <td align="center">Share</td>
404     <td align="center">Backup no</td>
405     <td align="center">Type</td>
406     <td align="center">date</td>
407     <td align="center">age/days</td>
408     <td align="center">size/MB</td>
409     <td align="center">gzip size</td>
410     </tr>
411 dpavlin 102
412 iklaric 121 <tr><td colspan=7 style="tableheader">
413     <input type="submit" value="Burn selected backups on medium" name="submitBurner">
414     </td></tr>
415 dpavlin 58 };
416 dpavlin 4
417 dpavlin 102 my @color = (' bgcolor="#e0e0e0"', '');
418 dpavlin 31
419 dpavlin 102 my $i = 0;
420     my $host = '';
421 dpavlin 31
422 dpavlin 102 foreach my $backup ( getBackupsNotBurned() ) {
423 dpavlin 31
424 dpavlin 102 if ($host ne $backup->{'host'}) {
425     $i++;
426     $host = $backup->{'host'};
427     }
428 dpavlin 31 my $ftype = "";
429 dpavlin 4
430 dpavlin 102 $retHTML .= "<tr" . $color[$i %2 ] . ">";
431     $retHTML .= '<td class="fview"><input type="checkbox" name="fcb' .
432 iklaric 121 $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
433     '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupnum'} .
434     '" onClick="sumiraj();"></td>';
435 dpavlin 4
436 dpavlin 102 $retHTML .=
437     '<td align="right">' . $backup->{'host'} . ':' . $backup->{'share'} . '</td>' .
438     '<td align="center">' . $backup->{'backupnum'} . '</td>' .
439     '<td align="center">' . $backup->{'type'} . '</td>' .
440     '<td align="center">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
441     '<td align="center">' . $backup->{'age'} . '</td>' .
442     '<td align="right">' . $backup->{'size'} . '</td>' .
443 iklaric 121 '<td align="right">' . $backup->{'fs_size'} .
444     '<input type="hidden" name="fss'.$backup->{'hostid'}.'_'.$backup->{'backupnum'} . '"'.
445     'value="'. $backup->{'fs_size'} .'"'.'</td>' .
446 dpavlin 102 "</tr>\n";
447    
448    
449 dpavlin 4 }
450 dpavlin 31
451     $retHTML .= "</table>";
452 iklaric 121 $retHTML .= "total gzip size:<input type=\"text\" name=\"totalsize\"><br>";
453     $retHTML .= "Note:<input type=\"text\" name=\"note\">";
454 dpavlin 102 $retHTML .= "</form>";
455 dpavlin 4
456 dpavlin 31 return $retHTML;
457     }
458 dpavlin 4
459 dpavlin 86 sub displayGrid($) {
460     my ($param) = @_;
461 dpavlin 83
462     my $offset = $param->{'offset'};
463     my $hilite = $param->{'search_filename'};
464    
465 dpavlin 17 my $retHTML = "";
466    
467 dpavlin 55 my $start_t = time();
468    
469 dpavlin 86 my ($results, $files);
470 dpavlin 88 if ($param->{'use_hest'} && length($hilite) > 0) {
471 dpavlin 87 ($results, $files) = getFilesHyperEstraier($param);
472 dpavlin 86 } else {
473 dpavlin 87 ($results, $files) = getFiles($param);
474 dpavlin 86 }
475 dpavlin 31
476 dpavlin 55 my $dur_t = time() - $start_t;
477     my $dur = sprintf("%0.4fs", $dur_t);
478    
479 dpavlin 31 my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
480    
481 dpavlin 59 if ($results <= 0) {
482     $retHTML .= qq{
483     <p style="color: red;">No results found...</p>
484     };
485     return $retHTML;
486     } else {
487     # DEBUG
488     #use Data::Dumper;
489     #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
490     }
491    
492    
493 dpavlin 17 $retHTML .= qq{
494 dpavlin 79 <div>
495     Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
496     </div>
497     <table style="fview" width="100%" border="0" cellpadding="2" cellspacing="0">
498     <tr class="fviewheader">
499 dpavlin 87 <td></td>
500 dpavlin 79 <td align="center">Share</td>
501     <td align="center">Type and Name</td>
502     <td align="center">#</td>
503     <td align="center">Size</td>
504     <td align="center">Date</td>
505     <td align="center">Media</td>
506 dpavlin 17 </tr>
507     };
508 dpavlin 31
509 dpavlin 17 my $file;
510 dpavlin 4
511 dpavlin 17 sub hilite_html($$) {
512     my ($html, $search) = @_;
513     $html =~ s#($search)#<b>$1</b>#gis;
514     return $html;
515 dpavlin 4 }
516 dpavlin 9
517 dpavlin 26 sub restore_link($$$$$$) {
518     my $type = shift;
519     my $action = 'RestoreFile';
520     $action = 'browse' if (lc($type) eq 'dir');
521     return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
522     }
523    
524 dpavlin 87 my $i = $offset * $on_page;
525    
526 dpavlin 31 foreach $file (@{ $files }) {
527 dpavlin 87 $i++;
528    
529 dpavlin 24 my $typeStr = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
530 dpavlin 79 $retHTML .= qq{<tr class="fviewborder">};
531 dpavlin 9
532 dpavlin 88 $retHTML .= qq{<td class="fviewborder">$i</td>};
533 dpavlin 87
534 dpavlin 79 $retHTML .=
535 dpavlin 86 qq{<td class="fviewborder" align="right">} . $file->{'hname'} . ':' . $file->{'sname'} . qq{</td>} .
536     qq{<td class="fviewborder"><img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" alt="$typeStr" align="middle">&nbsp;} . hilite_html( $file->{'filepath'}, $hilite ) . qq{</td>} .
537     qq{<td class="fviewborder" align="center">} . restore_link( $typeStr, ${EscURI( $file->{'hname'} )}, $file->{'backupnum'}, ${EscURI( $file->{'sname'})}, ${EscURI( $file->{'filepath'} )}, $file->{'backupnum'} ) . qq{</td>} .
538 dpavlin 79 qq{<td class="fviewborder" align="right">} . $file->{'size'} . qq{</td>} .
539     qq{<td class="fviewborder">} . epoch_to_iso( $file->{'date'} ) . qq{</td>} .
540 dpavlin 87 qq{<td class="fviewborder">} . '?' . qq{</td>};
541 dpavlin 9
542 dpavlin 17 $retHTML .= "</tr>";
543     }
544     $retHTML .= "</table>";
545    
546 dpavlin 31 # all variables which has to be transfered
547     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/) {
548     $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
549     }
550 dpavlin 17
551 dpavlin 31 my $del = '';
552     my $max_page = int( $results / $on_page );
553     my $page = 0;
554    
555 dpavlin 85 sub page_link($$$) {
556     my ($param,$page,$display) = @_;
557 dpavlin 31
558 dpavlin 85 $param->{'offset'} = $page;
559    
560     my $html = '<a href = "' . $MyURL;
561     my $del = '?';
562     foreach my $k (keys %{ $param }) {
563     if ($param->{$k}) {
564     $html .= $del . $k . '=' . ${EscURI( $param->{$k} )};
565     $del = '&';
566     }
567     }
568     $html .= '">' . $display . '</a>';
569     }
570    
571 dpavlin 31 $retHTML .= '<div style="text-align: center;">';
572    
573     if ($offset > 0) {
574 dpavlin 85 $retHTML .= page_link($param, $offset - 1, '&lt;&lt;') . ' ';
575 dpavlin 31 }
576    
577     while ($page <= $max_page) {
578     if ($page == $offset) {
579     $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
580     } else {
581 dpavlin 85 $retHTML .= $del . page_link($param, $page, $page + 1);
582 dpavlin 17 }
583 dpavlin 31
584     if ($page < $offset - $pager_pages && $page != 0) {
585     $retHTML .= " ... ";
586     $page = $offset - $pager_pages;
587     $del = '';
588     } elsif ($page > $offset + $pager_pages && $page != $max_page) {
589     $retHTML .= " ... ";
590     $page = $max_page;
591     $del = '';
592     } else {
593     $del = ' | ';
594     $page++;
595     }
596 dpavlin 17 }
597    
598 dpavlin 31 if ($offset < $max_page) {
599 dpavlin 85 $retHTML .= ' ' . page_link($param, $offset + 1, '&gt;&gt;');
600 dpavlin 31 }
601    
602     $retHTML .= "</div>";
603    
604 dpavlin 17 return $retHTML;
605     }
606 dpavlin 4
607     1;

  ViewVC Help
Powered by ViewVC 1.1.26