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