This commit was generated by cvs2svn to compensate for changes in r4888,
[freeside.git] / httemplate / search / elements / search.html
1 %
2 %
3 %  # options example...  
4 %  # (everything not commented required is optional)
5 %  #
6 %  # # basic options, required
7 %  # 'title'         => 'Page title',
8 %  # 
9 %  # 'name_singular' => 'item',  #singular name for the records returned
10 %  #    #OR#                     # (preferred, will be pluralized automatically)
11 %  # 'name'          => 'items', #plural name for the records returned
12 %  #                             # (deprecated, will be singularlized
13 %  #                             #  simplisticly)
14 %  #
15 %  # # some HTML callbacks...
16 %  # 'menubar'          => '', #menubar arrayref
17 %  # 'html_init'        => '', #after the header/menubar and before the pager
18 %  # 'html_foot'        => '', #at the bottom
19 %  # 'html_posttotal'   => '', #at the bottom
20 %  #                           # (these three can be strings or coderefs)
21 %  # 
22 %  #
23 %  # #literal SQL query string or qsearch hashref, required
24 %  # 'query'       => {
25 %  #                    'table'     => 'tablename',
26 %  #                    #everything else is optional...
27 %  #                    'hashref'   => { 'field' => 'value',
28 %  #                                     'field' => { 'op'    => '<',
29 %  #                                                  'value' => '54',
30 %  #                                                },
31 %  #                                   },
32 %  #                    'select'    => '*',
33 %  #                    'addl_from' => '', #'LEFT JOIN othertable USING ( key )',
34 %  #                    'extra_sql' => '', #'AND otherstuff', #'WHERE onlystuff',
35 %  #                    
36 %  #
37 %  #                  },
38 %  #                  # "select * from tablename";
39 %  #
40 %  # #required unless 'query' is an SQL query string (shouldn't be...)
41 %  # 'count_query' => 'SELECT COUNT(*) FROM tablename',
42 %  #
43 %  # 'count_addl' => [], #additional count fields listref of sprintf strings
44 %  #                     # [ $money_char.'%.2f total paid', ],
45 %  #
46 %  # #listref of column labels, <TH>
47 %  # #required unless 'query' is an SQL query string
48 %  # # (if not specified the database column names will be used)
49 %  # 'header'      => [ '#', 'Item' ],
50 %  #
51 %  # 'disable_download' => '', # set true to hide the CSV/Excel download links
52 %  # 'disable_nonefound' => '', # set true to disable the "No matching Xs found"
53 %  #                            # message
54 %  #
55 %  # #listref - each item is a literal column name (or method) or coderef
56 %  # #if not specified all columns will be shown
57 %  # 'fields'      => [
58 %  #                    'column',
59 %  #                    sub { my $row = shift; $row->column; },
60 %  #                  ],
61 %  #
62 %  # #listref of column footers
63 %  # 'footer'      => [],
64 %  # 
65 %  # #listref - each item is the empty string, or a listref of ...
66 %  # 'links'       =>
67 %  #
68 %  #
69 %  # 'align'       => 'lrc.', #one letter for each column, left/right/center/none
70 %  #                          # can also pass a listref with full values:
71 %  #                          # [ 'left', 'right', 'center', '' ]
72 %  #
73 %  # #listrefs...
74 %  # #currently only HTML, maybe eventually Excel too
75 %  # 'color'       => [],
76 %  # 'size'        => [],
77 %  # 'style'       => [],
78 %  # 
79 %  # #redirect if there's only one item...
80 %  # # listref of URL base and column name (or method)
81 %  # # or a coderef that returns the same
82 %  # 'redirect' =>
83 %
84 %  my $DEBUG = 0;
85 %
86 %  my(%opt) = @_;
87 %  #warn join(' / ', map { "$_ => $opt{$_}" } keys %opt ). "\n";
88 %
89 %  my %align = (
90 %    'l' => 'left',
91 %    'r' => 'right',
92 %    'c' => 'center',
93 %    ' ' => '',
94 %    '.' => '',
95 %  );
96 %  $opt{align} = [ map $align{$_}, split(//, $opt{align}) ],
97 %    unless !$opt{align} || ref($opt{align});
98 %
99 %  my $type = '';
100 %  my $limit = '';
101 %  my($maxrecords, $total, $offset, $count_arrayref);
102 %
103 %  if ( $cgi->param('_type') =~ /^(csv|\w*\.xls)$/ ) {
104 %  
105 %    $type = $1;
106 %
107 %  } else { #setup some pagination things if we're in html mode
108 %
109 %    unless (exists($opt{'count_query'}) && length($opt{'count_query'})) {
110 %      ( $opt{'count_query'} = $opt{'query'} ) =~
111 %        s/^\s*SELECT\s*(.*?)\s+FROM\s/SELECT COUNT(*) FROM /i;
112 %    }
113 %
114 %    my $conf = new FS::Conf;
115 %    $maxrecords = $conf->config('maxsearchrecordsperpage');
116 %
117 %    $limit = $maxrecords ? "LIMIT $maxrecords" : '';
118 %
119 %    $offset = $cgi->param('offset') || 0;
120 %    $limit .= " OFFSET $offset" if $offset;
121 %
122 %    my $count_sth = dbh->prepare($opt{'count_query'})
123 %      or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
124 %    $count_sth->execute
125 %      or die "Error executing $opt{'count_query'}: ". $count_sth->errstr;
126 %    $count_arrayref = $count_sth->fetchrow_arrayref;
127 %    $total = $count_arrayref->[0];
128 %
129 %  }
130 %
131 %  # run the query
132 %
133 %  my $header = $opt{'header'};
134 %  my $rows;
135 %  if ( ref($opt{'query'}) ) {
136 %
137 %    #eval "use FS::$opt{'query'};";
138 %    $rows = [ qsearch(
139 %      $opt{'query'}->{'table'}, 
140 %      $opt{'query'}->{'hashref'} || {}, 
141 %      $opt{'query'}->{'select'},
142 %      $opt{'query'}->{'extra_sql'}. " $limit",
143 %      '',
144 %      (exists($opt{'query'}->{'addl_from'}) ? $opt{'query'}->{'addl_from'} : '')
145 %    ) ];
146 %
147 %  } else {
148 %
149 %    my $sth = dbh->prepare("$opt{'query'} $limit")
150 %      or die "Error preparing $opt{'query'}: ". dbh->errstr;
151 %    $sth->execute
152 %      or die "Error executing $opt{'query'}: ". $sth->errstr;
153 %
154 %    #can get # of rows without fetching them all?
155 %    $rows = $sth->fetchall_arrayref;
156 %
157 %    $header ||= $sth->{NAME};
158 %
159 %  }
160 %
161 %  warn scalar(@$rows). ' rows returned from '.
162 %       ( ref($opt{'query'}) ? 'qsearch query' : 'literal SQL query' )
163 %    if $DEBUG || $opt{'debug'};
164 %
165 %  # display the results - csv, xls or html
166 %
167 %  if ( $type eq 'csv' ) {
168 %
169 %    #http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
170 %    http_header('Content-Type' => 'text/plain' );
171 %
172 %    my $csv = new Text::CSV_XS { 'always_quote' => 1,
173 %                                 'eol'          => "\n", #"\015\012", #"\012"
174 %                               };
175 %
176 %    $csv->combine(@$header); #or die $csv->status;
177 %    
178 <% $csv->string %>
179 %
180 %
181 %    foreach my $row ( @$rows ) {
182 %
183 %      if ( $opt{'fields'} ) {
184 %
185 %        my @line = ();
186 %
187 %        foreach my $field ( @{$opt{'fields'}} ) {
188 %          if ( ref($field) eq 'CODE' ) {
189 %            push @line, map {
190 %                              ref($_) eq 'ARRAY'
191 %                                ? '(N/A)' #unimplemented
192 %                                : $_;
193 %                            }
194 %                            &{$field}($row);
195 %          } else {
196 %            push @line, $row->$field();
197 %          }
198 %        }
199 %
200 %        $csv->combine(@line); #or die $csv->status;
201 %
202 %      } else {
203 %        $csv->combine(@$row); #or die $csv->status;
204 %      }
205 %
206 %      
207 <% $csv->string %>
208 %
209 %
210 %    }
211 %
212 %  #} elsif ( $type eq 'excel' ) {
213 %  } elsif ( $type =~ /\.xls$/ ) {
214 %
215 %    #http_header('Content-Type' => 'application/excel' ); #eww
216 %    http_header('Content-Type' => 'application/vnd.ms-excel' );
217 %    #http_header('Content-Type' => 'application/msexcel' ); #alas
218 %
219 %    my $data = '';
220 %    my $XLS = new IO::Scalar \$data;
221 %    my $workbook = Spreadsheet::WriteExcel->new($XLS)
222 %      or die "Error opening .xls file: $!";
223 %
224 %    my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
225 %
226 %    my($r,$c) = (0,0);
227 %
228 %    $worksheet->write($r, $c++, $_) foreach @$header;
229 %
230 %    foreach my $row ( @$rows ) {
231 %      $r++;
232 %      $c = 0;
233 %
234 %      if ( $opt{'fields'} ) {
235 %
236 %        #my $links = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
237 %        #my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
238 %
239 %        foreach my $field ( @{$opt{'fields'}} ) {
240 %          #my $align = $aligns ? shift @$aligns : '';
241 %          #$align = " ALIGN=$align" if $align;
242 %          #my $a = '';
243 %          #if ( $links ) {
244 %          #  my $link = shift @$links;
245 %          #  $link = &{$link}($row) if ref($link) eq 'CODE';
246 %          #  if ( $link ) {
247 %          #    my( $url, $method ) = @{$link};
248 %          #    if ( ref($method) eq 'CODE' ) {
249 %          #      $a = $url. &{$method}($row);
250 %          #    } else {
251 %          #      $a = $url. $row->$method();
252 %          #    }
253 %          #    $a = qq(<A HREF="$a">);
254 %          #  }
255 %          #}
256 %          if ( ref($field) eq 'CODE' ) {
257 %            foreach my $value ( &{$field}($row) ) {
258 %              if ( ref($value) eq 'ARRAY' ) { 
259 %                $worksheet->write($r, $c++, '(N/A)' ); #unimplemented
260 %              } else {
261 %                $worksheet->write($r, $c++, $value );
262 %              }
263 %            }
264 %          } else {
265 %            $worksheet->write($r, $c++, $row->$field() );
266 %          }
267 %        }
268 %
269 %      } else {
270 %        $worksheet->write($r, $c++, $_) foreach @$row;
271 %      }
272 %
273 %    }
274 %
275 %    $workbook->close();# or die "Error creating .xls file: $!";
276 %
277 %    http_header('Content-Length' => length($data) );
278 %    
279 <% $data %>
280 %
281 %
282 %  } else { # regular HTML
283 %
284 %    if ( exists($opt{'redirect'}) && scalar(@$rows) == 1 && $total == 1 ) {
285 %      my $redirect = $opt{'redirect'};
286 %      $redirect = &{$redirect}($rows->[0]) if ref($redirect) eq 'CODE';
287 %      my( $url, $method ) = @$redirect;
288 %      redirect( $url. $rows->[0]->$method() );
289 %    } else {
290 %      if ( $opt{'name_singular'} ) {
291 %        $opt{'name'} = PL($opt{'name_singular'});
292 %      }
293 %      ( my $xlsname = $opt{'name'} ) =~ s/\W//g;
294 %      if ( $total == 1 ) {
295 %        if ( $opt{'name_singular'} ) {
296 %          $opt{'name'} = $opt{'name_singular'}
297 %        } else {
298 %          #$opt{'name'} =~ s/s$// if $total == 1;
299 %          $opt{'name'} =~ s/((s)e)?s$/$2/ if $total == 1;
300 %        }
301 %      }                                               
302 %
303 %      my @menubar = ();
304 %      if ( $opt{'menubar'} ) {
305 %        @menubar = @{ $opt{'menubar'} };
306 %      } else {
307 %        @menubar = ( 'Main menu' => $p );
308 %      }
309 %
310 %
311 %  
312
313   <% include( '/elements/header.html', $opt{'title'},
314                  include( '/elements/menubar.html', @menubar )
315              )
316   %>
317   <% defined($opt{'html_init'}) 
318         ? ( ref($opt{'html_init'})
319               ? &{$opt{'html_init'}}()
320               : $opt{'html_init'}
321           )
322         : ''
323   %>
324 % my $pager = include ( '/elements/pager.html',
325 %                             'offset'     => $offset,
326 %                             'num_rows'   => scalar(@$rows),
327 %                             'total'      => $total,
328 %                             'maxrecords' => $maxrecords,
329 %                         );
330 %  
331 % unless ( $total ) { 
332 % unless ( $opt{'disable_nonefound'} ) { 
333
334       No matching <% $opt{'name'} %> found.<BR>
335 % } 
336 % } else { 
337
338   
339     <TABLE>
340       <TR>
341         <TD VALIGN="bottom">
342           <% $total %> total <% $opt{'name'} %>
343           <% defined($opt{'html_posttotal'}) 
344                 ? ( ref($opt{'html_posttotal'})
345                       ? &{$opt{'html_posttotal'}}()
346                       : $opt{'html_posttotal'}
347                   )
348                 : ''
349           %>
350           <BR>
351 % if ( $opt{'count_addl'} ) { 
352 % my $n=0; foreach my $count ( @{$opt{'count_addl'}} ) { 
353
354               <% sprintf( $count, $count_arrayref->[++$n] ) %><BR>
355 % } 
356 % } 
357
358         </TD>
359 % unless ( $opt{'disable_download'} ) { 
360
361           <TD ALIGN="right">
362 % $cgi->param('_type', "$xlsname.xls" ); 
363
364             Download full results<BR>
365             as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR>
366 % $cgi->param('_type', 'csv'); 
367
368             as <A HREF="<% $cgi->self_url %>">CSV file</A>
369           </TD>
370 % } 
371
372       </TR>
373       <TR>
374         <TD COLSPAN=2>
375             <% $pager %>
376
377             <% include('/elements/table-grid.html') %>
378
379               <TR>
380
381 %                 foreach my $header ( @$header ) { 
382
383                    <TH CLASS="grid" BGCOLOR="#cccccc"><% $header %></TH>
384 % } 
385
386               </TR>
387 % my $bgcolor1 = '#eeeeee';
388 %                 my $bgcolor2 = '#ffffff';
389 %                 my $bgcolor;
390 %                 foreach my $row ( @$rows ) {
391 %                   if ( $bgcolor eq $bgcolor1 ) {
392 %                     $bgcolor = $bgcolor2;
393 %                   } else {
394 %                     $bgcolor = $bgcolor1;
395 %                   }
396 %              
397
398                    <TR>
399 % if ( $opt{'fields'} ) {
400 %
401 %                        my $links  = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
402 %                        my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
403 %                        my $colors = $opt{'color'} ? [ @{$opt{'color'}} ] : [];
404 %                        my $sizes  = $opt{'size'}  ? [ @{$opt{'size'}}  ] : [];
405 %                        my $styles = $opt{'style'} ? [ @{$opt{'style'}} ] : [];
406 %
407 %                        foreach my $field (
408 %
409 %                          map {
410 %                                if ( ref($_) eq 'ARRAY' ) {
411 %
412 %                                  my $tableref = $_;
413 %
414 %                                  '<TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0>'.
415 %
416 %                                  join('', map {
417 %
418 %                                    my $rowref = $_;
419 %
420 %                                    '<tr>'.
421 %
422 %                                    join('', map {
423 %
424 %                                      my $element = $_;
425 %
426 %                                      '<TD'.
427 %                                      ( $element->{'align'}
428 %                                          ? ' ALIGN="'. $element->{'align'}. '"'
429 %                                          : ''
430 %                                      ). '>'.
431 %                                      ( $element->{'link'}
432 %                                          ? '<A HREF="'. $element->{'link'}.'">'
433 %                                          : ''
434 %                                      ).
435 %                                      $element->{'data'}.
436 %                                      ( $element->{'link'}
437 %                                          ? '</A>'
438 %                                          : ''
439 %                                      ).
440 %                                      '</td>';
441 %
442 %                                    } @$rowref ).
443 %
444 %                                    '</tr>';
445 %                                  } @$tableref ).
446 %
447 %                                  '</table>';
448 %
449 %                                } else {
450 %                                  $_;
451 %                                }
452 %                              }
453 %
454 %                          map {
455 %                                if ( ref($_) eq 'CODE' ) {
456 %                                  &{$_}($row);
457 %                                } else {
458 %                                  $row->$_();
459 %                                }
460 %                              }
461 %                          @{$opt{'fields'}}
462 %
463 %                        ) {
464 %
465 %                          my $class = ( $field =~ /^<TABLE/i ) ? 'inv' : 'grid';
466 %
467 %                          my $align = $aligns ? shift @$aligns : '';
468 %                          $align = " ALIGN=$align" if $align;
469 %
470 %                          my $a = '';
471 %                          if ( $links ) {
472 %                            my $link = shift @$links;
473 %                            $link = &{$link}($row) if ref($link) eq 'CODE';
474 %                            if ( $link ) {
475 %                              my( $url, $method ) = @{$link};
476 %                              if ( ref($method) eq 'CODE' ) {
477 %                                $a = $url. &{$method}($row);
478 %                              } else {
479 %                                $a = $url. $row->$method();
480 %                              }
481 %                              $a = qq(<A HREF="$a">);
482 %                            }
483 %                          }
484 %
485 %                          my $font = '';
486 %                          my $color = shift @$colors;
487 %                          $color = &{$color}($row) if ref($color) eq 'CODE';
488 %                          my $size = shift @$sizes;
489 %                          $size = &{$size}($row) if ref($size) eq 'CODE';
490 %                          if ( $color || $size ) {
491 %                            $font = '<FONT '.
492 %                                    ( $color ? "COLOR=#$color "   : '' ).
493 %                                    ( $size  ? qq(SIZE="$size" )  : '' ).
494 %                                    '>';
495 %                          }
496 %
497 %                          my($s, $es) = ( '', '' );
498 %                          my $style = shift @$styles;
499 %                          $style = &{$style}($row) if ref($style) eq 'CODE';
500 %                          if ( $style ) {
501 %                            $s = join( '', map "<$_>", split('', $style) );
502 %                            $es = join( '', map "</$_>", split('', $style) );
503 %                          }
504 %
505 %                       
506
507                        <TD CLASS="<% $class %>" BGCOLOR="<% $bgcolor %>"<% $align %>><% $font %><% $a %><% $s %><% $field %><% $es %><% $a ? '</A>' : '' %><% $font ? '</FONT>' : '' %></TD>
508 % } 
509 % } else { 
510 % foreach ( @$row ) { 
511
512                           <TD CLASS="grid" BGCOLOR="<% $bgcolor %>"><% $_ %></TD>
513 % } 
514 % } 
515
516                    </TR>
517 % } 
518 % if ( $opt{'footer'} ) { 
519
520                 <TR>
521 % foreach my $footer ( @{ $opt{'footer'} } ) { 
522
523                      <TD CLASS="grid" BGCOLOR="#dddddd" STYLE="border-top: dashed 1px black;"><i><% $footer %></i></TH>
524 % } 
525
526                 </TR>
527 % } 
528
529             
530             </TABLE>
531             <% $pager %>
532   
533           </TD>
534         </TR>
535       </TABLE>
536 % } 
537
538   <% defined($opt{'html_foot'}) 
539         ? ( ref($opt{'html_foot'})
540               ? &{$opt{'html_foot'}}()
541               : $opt{'html_foot'}
542           )
543         : ''
544   %>
545   <% include( '/elements/footer.html' ) %>
546 % } 
547 % } 
548