fixed limit by share while searching
[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
12 my $on_page = 100;
13 my $pager_pages = 10;
14
15 my $dsn = $Conf{SearchDSN};
16 my $db_user = $Conf{SearchUser} || '';
17
18 sub getUnits() {
19         my @ret;
20
21         my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
22         my $sth = $dbh->prepare(qq{ SELECT id, share FROM shares} );
23         $sth->execute();
24         push @ret, { 'id' => '', 'share' => '-'};       # dummy any
25
26         while ( my $row = $sth->fetchrow_hashref() ) {
27                 push @ret, $row;
28         }
29         $dbh->disconnect();
30         return @ret;
31 }
32
33 sub epoch_to_iso {
34         my $t = shift || return;
35         my $dt = DateTime->from_epoch( epoch => $t ) || return;
36         print STDERR "BUG: $t != " . $dt->epoch . "\n" unless ($t == $dt->epoch);
37         return $dt->ymd . ' ' . $dt->hms;
38 }
39
40 sub getWhere($) {
41         my ($param)    = @_;
42         my @conditions;
43
44         sub mk_epoch_date($$) {
45                 my ($name,$suffix) = @_;
46
47                 my $yyyy = $param->{ $name . '_year_' . $suffix} || return;
48                 my $mm .= $param->{ $name . '_month_' . $suffix} ||
49                         ( $suffix eq 'from' ? 1 : 12);
50                 my $dd .= $param->{ $name . '_day_' . $suffix} ||
51                         ( $suffix eq 'from' ? 1 : 31);
52                 my $dt = new DateTime(
53                         year => $yyyy,
54                         month => $mm,
55                         day => $dd
56                 );
57                 return $dt->epoch || 'NULL';
58         }
59
60         my $backup_from = mk_epoch_date('search_backup', 'from');
61         push @conditions, qq{ backups.date >= $backup_from } if ($backup_from);
62         my $backup_to = mk_epoch_date('search_backup', 'to');
63         push @conditions, qq{ backups.date <= $backup_to } if ($backup_to);
64
65         my $files_from = mk_epoch_date('search', 'from');
66         push @conditions, qq{ files.date >= $files_from } if ($files_from);
67         my $files_to = mk_epoch_date('search', 'to');
68         push @conditions, qq{ files.date <= $files_to } if ($files_to);
69
70         print STDERR "backup: $backup_from - $backup_to files: $files_from - $files_to cond:" . join(" | ",@conditions);
71     
72         push( @conditions, ' files.shareid = ' . $param->{'search_share'} ) if ($param->{'search_share'});
73
74         push (@conditions, " upper(files.name) LIKE upper('%".$param->{'search_filename'}."%')") if ($param->{'search_filename'});
75
76         return (
77                 join(" and ", @conditions),
78                 $files_from, $files_to,
79                 $backup_from, $backup_to
80         );
81 }
82
83
84 sub getFiles($$) {
85         my ($where, $offset) = @_;
86
87         my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
88
89         my $sql_cols = qq{
90                 files.id                        AS fid,
91                 hosts.name                      AS hname,
92                 shares.name                     AS sname,
93                 shares.share                    AS sharename,
94                 files.backupNum                 AS backupNum,
95                 files.name                      AS filename,
96                 files.path                      AS filepath,
97                 shares.share||files.fullpath    AS networkPath,
98                 files.date                      AS date,
99                 files.type                      AS filetype,
100                 files.size                      AS size,
101                 dvds.name                       AS dvd
102         };
103
104         my $sql_from = qq{
105                 FROM files 
106                         INNER JOIN shares       ON files.shareID=shares.ID
107                         INNER JOIN hosts        ON hosts.ID = shares.hostID
108                         INNER JOIN backups      ON backups.num = files.backupNum and backups.hostID = hosts.ID
109         };
110
111         my $sql_dvd_from = qq{
112                         LEFT  JOIN dvds         ON dvds.ID = files.dvdid
113         };
114
115         my $sql_where;
116         $sql_where = " WHERE ". $where if ($where);
117
118         my $sql_order = qq{
119                 ORDER BY files.id
120                 LIMIT $on_page
121                 OFFSET ?
122         };
123
124         my $sql_count = qq{ select count(files.id) $sql_from $sql_where };
125         my $sql_results = qq{ select $sql_cols $sql_from $sql_dvd_from $sql_where $sql_order };
126
127         $offset ||= 0;
128         $offset = ($offset * $on_page);
129
130         my $sth = $dbh->prepare($sql_count);
131         $sth->execute();
132         my ($results) = $sth->fetchrow_array();
133
134         $sth = $dbh->prepare($sql_results);
135         $sth->execute( $offset );
136
137         if ($sth->rows != $results) {
138                 my $bug = "$0 BUG: [[ $sql_count ]] = $results while [[ $sql_results ]] = " . $sth->rows;
139                 $bug =~ s/\s+/ /gs;
140                 print STDERR "$bug\n";
141         }
142
143         my @ret;
144       
145         while (my $row = $sth->fetchrow_hashref()) {
146                 push(@ret, { 
147                         'hname'         => $row->{'hname'}, 
148                         'sname'         => $row->{'sname'},
149                         'sharename'     => $row->{'sharename'},
150                         'backupno'      => $row->{'backupnum'},
151                         'fname'         => $row->{'filename'},
152                         'fpath'         => $row->{'filepath'},
153                         'networkpath'   => $row->{'networkpath'},
154                         'date'          => $row->{'date'},
155                         'type'          => $row->{'filetype'},
156                         'size'          => $row->{'size'},
157                         'id'            => $row->{'fid'},
158                         'dvd'           => $row->{'dvd'}
159                 });
160         }
161      
162         $sth->finish();
163         $dbh->disconnect();
164         return ($results, \@ret);
165 }
166
167 sub getBackupsNotBurned() {
168
169         my $dbh = DBI->connect($dsn, $db_user, "", { RaiseError => 1, AutoCommit => 1 } );
170         my $sql = q{ 
171         SELECT
172                 hosts.ID                AS hostid,
173                 min(hosts.name)         AS host,
174                 backups.num             AS backupno,
175                 min(backups.type)       AS type,
176                 min(backups.date)       AS date
177         FROM backups, shares, files, hosts
178         WHERE 
179                 backups.num     = files.backupNum       AND
180                 shares.ID       = files.shareID         AND         
181                 backups.hostID  = shares.hostID         AND
182                 hosts.ID        = backups.hostID        AND
183                 files.dvdid     IS NULL
184         GROUP BY 
185                 backups.hostID, backups.num, hosts.id
186         ORDER BY min(backups.date)
187         };
188         my $sth = $dbh->prepare( $sql );
189         my @ret;
190         $sth->execute();
191
192         while ( my $row = $sth->fetchrow_hashref() ) {      
193                 push(@ret, { 
194                          'host'         => $row->{'host'},
195                          'hostid'       => $row->{'hostid'},
196                          'backupno'     => $row->{'backupno'},
197                          'type'         => $row->{'type'},
198                          'date'         => $row->{'date'},
199                          'age'          => sprintf("%0.1f", ( (time() - $row->{'date'}) / 86400 ) ),
200                        }
201                 );
202         }
203       
204         return @ret;      
205 }
206
207 sub displayBackupsGrid()
208   {
209       my $retHTML = "";
210       my $addForm = 1;
211       
212       if ($addForm) {
213
214             $retHTML .= <<EOF3;
215 <script language="javascript" type="text/javascript">
216 <!--
217
218     function checkAll(location)
219     {
220       for (var i=0;i<document.forma.elements.length;i++)
221       {
222         var e = document.forma.elements[i];
223         if ((e.checked || !e.checked) && e.name != \'all\') {
224             if (eval("document.forma."+location+".checked")) {
225                 e.checked = true;
226             } else {
227                 e.checked = false;
228             }
229         }
230       }
231     }
232 //-->
233 </script>      
234 EOF3
235               $retHTML .= q{<form name="forma" method="GET" action="}."$MyURL"."?action=burn\"";
236               $retHTML.= q{<input type="hidden" value="burn" name="action">};
237               $retHTML .= q{<input type="hidden" value="results" name="search_results">};
238         }
239         $retHTML .= qq{<table style="fview"><tr>};
240
241         if ($addForm) {
242             $retHTML .= "<td class=\"tableheader\"><input type=\"checkbox\" name=\"allFiles\" onClick=\"checkAll('allFiles');\"></td>";
243         }
244         $retHTML .=  qq{
245                 <td class="tableheader">Host</td>
246                 <td class="tableheader">Backup no</td>
247                 <td class="tableheader">Type</td>
248                 <td class="tableheader">date</td>
249                 <td class="tableheader">age/days</td>
250                 </tr>
251         };
252
253         my @backups = getBackupsNotBurned();
254         my $backup;
255
256         if ($addForm) {
257                 $retHTML .= qq{
258                         <tr><td colspan=7 style="tableheader">
259                         <input type="submit" value="Burn selected backups on medium" name="submitBurner">
260                         </td></tr>
261                 };
262         }
263
264         foreach $backup(@backups) {
265
266                 my $ftype = "";
267             
268                 $retHTML .= "<tr>";
269                 if ($addForm) {
270                         $retHTML .= '<td class="fview"><input type="checkbox" name="fcb' .
271                                 $backup->{'hostid'}.'_'.$backup->{'backupno'} . 
272                                 '" value="' . $backup->{'hostid'}.'_'.$backup->{'backupno'} .
273                                 '"></td>';
274                 }           
275             
276                 $retHTML .= '<td class="fviewborder">' . $backup->{'host'} . '</td>' .
277                         '<td class="fviewborder">' . $backup->{'backupno'} . '</td>' .
278                         '<td class="fviewborder">' . $backup->{'type'} . '</td>' .
279                         '<td class="fviewborder">' . epoch_to_iso( $backup->{'date'} ) . '</td>' .
280                         '<td class="fviewborder">' . $backup->{'age'} . '</td>' .
281                         '</tr>';
282         }
283
284         $retHTML .= "</table>";
285
286         if ($addForm) {
287                 $retHTML .= "</form>";
288         }
289       
290         return $retHTML;
291 }      
292
293 sub displayGrid($$$$) {
294         my ($where, $addForm, $offset, $hilite) = @_;
295         my $retHTML = "";
296  
297         my $start_t = time();
298
299         my ($results, $files) = getFiles($where, $offset);
300
301         my $dur_t = time() - $start_t;
302         my $dur = sprintf("%0.4fs", $dur_t);
303
304         my ($from, $to) = (($offset * $on_page) + 1, ($offset * $on_page) + $on_page);
305
306         if ($results <= 0) {
307                 $retHTML .= qq{
308                         <p style="color: red;">No results found...</p>
309                 };
310                 return $retHTML;
311         } else {
312                 # DEBUG
313                 #use Data::Dumper;
314                 #$retHTML .= '<pre>' . Dumper($files) . '</pre>';
315         }
316
317
318         if ($addForm) {
319                 $retHTML .= qq{<form name="forma" method="GET" action="$MyURL">};
320                 $retHTML.= qq{<input type="hidden" value="search" name="action">};
321                 $retHTML .= qq{<input type="hidden" value="results" name="search_results">};
322         }
323
324
325         $retHTML .= qq{
326         <br/>Found <b>$results files</b> showing <b>$from - $to</b> (took $dur)
327         <table style="fview" width="100%">
328                 <tr> 
329                 <td class="tableheader">Share</td>
330                 <td class="tableheader">Name</td>
331                 <td class="tableheader">Type</td>
332                 <td class="tableheader">#</td>
333                 <td class="tableheader">Size</td>
334                 <td class="tableheader">Date</td>
335                 <td class="tableheader">Media</td>
336                 </tr>
337         };
338
339         my $file;
340
341         sub hilite_html($$) {
342                 my ($html, $search) = @_;
343                 $html =~ s#($search)#<b>$1</b>#gis;
344                 return $html;
345         }
346
347         sub restore_link($$$$$$) {
348                 my $type = shift;
349                 my $action = 'RestoreFile';
350                 $action = 'browse' if (lc($type) eq 'dir');
351                 return sprintf(qq{<a href="?action=%s&host=%s&num=%d&share=%s&dir=%s">%s</a>}, $action, @_);
352         }
353
354         foreach $file (@{ $files }) {
355                 my $typeStr  = BackupPC::Attrib::fileType2Text(undef, $file->{'type'});
356                 $retHTML .= "<tr>";
357
358                 foreach my $v ((
359                         $file->{'sharename'},
360                         qq{<img src="$Conf{CgiImageDirURL}/icon-$typeStr.gif" align="center">&nbsp;} . hilite_html( $file->{'fpath'}, $hilite ),
361                         $typeStr,
362                         restore_link( $typeStr, $file->{'hname'}, $file->{'backupno'}, $file->{'sname'}, $file->{'fpath'}, $file->{'backupno'} ),
363                         $file->{'size'},
364                         epoch_to_iso( $file->{'date'} ),
365                         $file->{'dvd'}
366                 )) {
367                         $retHTML .= qq{<td class="fviewborder">$v</td>};
368                 }
369
370                 $retHTML .= "</tr>";
371         }
372         $retHTML .= "</table>";
373
374         # all variables which has to be transfered
375         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/) {
376                 $retHTML .= qq{<INPUT TYPE="hidden" NAME="$n" VALUE="$In{$n}">\n};
377         }
378
379         my $del = '';
380         my $max_page = int( $results / $on_page );
381         my $page = 0;
382
383         my $link_fmt = '<a href = "#" onclick="document.forma.offset.value=%d;document.forma.submit();">%s</a>';
384
385         $retHTML .= '<div style="text-align: center;">';
386
387         if ($offset > 0) {
388                 $retHTML .= sprintf($link_fmt, $offset - 1, '&lt;&lt;') . ' ';
389         }
390
391         while ($page <= $max_page) {
392                 if ($page == $offset) {
393                         $retHTML .= $del . '<b>' . ($page + 1) . '</b>';
394                 } else {
395                         $retHTML .= $del . sprintf($link_fmt, $page, $page + 1);
396                 }
397
398                 if ($page < $offset - $pager_pages && $page != 0) {
399                         $retHTML .= " ... ";
400                         $page = $offset - $pager_pages;
401                         $del = '';
402                 } elsif ($page > $offset + $pager_pages && $page != $max_page) {
403                         $retHTML .= " ... ";
404                         $page = $max_page;
405                         $del = '';
406                 } else {
407                         $del = ' | ';
408                         $page++;
409                 }
410         }
411
412         if ($offset < $max_page) {
413                 $retHTML .= ' ' . sprintf($link_fmt, $offset + 1, '&gt;&gt;');
414         }
415
416         $retHTML .= "</div>";
417
418         $retHTML .= "</form>" if ($addForm);
419
420         return $retHTML;
421 }
422
423 1;