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