remove extraneous blank line causing Excel exports to fail
[freeside.git] / httemplate / search / elements / search.html
1 %  # options example...  
2 %  # (everything not commented required is optional)
3 %  #
4 %  # # basic options, required
5 %  # 'title'         => 'Page title',
6 %  # 
7 %  # 'name_singular' => 'item',  #singular name for the records returned
8 %  #    #OR#                     # (preferred, will be pluralized automatically)
9 %  # 'name'          => 'items', #plural name for the records returned
10 %  #                             # (deprecated, will be singularlized
11 %  #                             #  simplisticly)
12 %  #
13 %  # # some HTML callbacks...
14 %  # 'menubar'          => '', #menubar arrayref
15 %  # 'html_init'        => '', #after the header/menubar and before the pager
16 %  # 'html_form'        => '', #after the pager, right before the results
17 %  #                           # (only shown if there are results)
18 %  #                           # (use this for any form-opening tag rather than
19 %  #                           #  html_init, to avoid a nested form)
20 %  # 'html_foot'        => '', #at the bottom
21 %  # 'html_posttotal'   => '', #at the bottom
22 %  #                           # (these three can be strings or coderefs)
23 %  # 
24 %  #
25 %  # #literal SQL query string or qsearch hashref, required
26 %  # 'query'       => {
27 %  #                    'table'     => 'tablename',
28 %  #                    #everything else is optional...
29 %  #                    'hashref'   => { 'field' => 'value',
30 %  #                                     'field' => { 'op'    => '<',
31 %  #                                                  'value' => '54',
32 %  #                                                },
33 %  #                                   },
34 %  #                    'select'    => '*',
35 %  #                    'addl_from' => '', #'LEFT JOIN othertable USING ( key )',
36 %  #                    'extra_sql' => '', #'AND otherstuff', #'WHERE onlystuff',
37 %  #                    'order_by'  => 'ORDER BY something',
38 %  #
39 %  #                  },
40 %  #                  # "select * from tablename";
41 %  #
42 %  # #required unless 'query' is an SQL query string (shouldn't be...)
43 %  # 'count_query' => 'SELECT COUNT(*) FROM tablename',
44 %  #
45 %  # 'count_addl' => [], #additional count fields listref of sprintf strings
46 %  #                     # [ $money_char.'%.2f total paid', ],
47 %  #
48 %  # #listref of column labels, <TH>
49 %  # #required unless 'query' is an SQL query string
50 %  # # (if not specified the database column names will be used)
51 %  # 'header'      => [ '#', 'Item' ],
52 %  #
53 %  # 'disable_download' => '', # set true to hide the CSV/Excel download links
54 %  # 'disable_nonefound' => '', # set true to disable the "No matching Xs found"
55 %  #                            # message
56 %  #
57 %  # 'disableable' => 1,  # set true if this table has a "disabled" field, to
58 %  #                      # hide disabled records & have "show disabled" links
59 %  # 'disabled_statuspos' => 3, #optional position (starting from 0) to insert
60 %  #                            #a Status column when showing disabled records
61 %  #                            #(query needs to be a qsearch hashref and
62 %  #                            # header & fields need to be defined)
63 %  # 'agent_virt' => 1, # set true if this search should be agent-virtualized
64 %  # 'agent_null_right' => 'Access Right', #opt. right to view global records
65 %  # 'agent_pos' => 3, #optional position (starting from 0) to insert
66 %  #                   #an Agent column 
67 %  #                   #(query needs to be a qsearch hashref and
68 %  #                   # header & fields need to be defined)
69 %  #
70 %  # #listref - each item is a literal column name (or method) or coderef
71 %  # #if not specified all columns will be shown
72 %  # 'fields'      => [
73 %  #                    'column',
74 %  #                    sub { my $row = shift; $row->column; },
75 %  #                  ],
76 %  #
77 %  # #listref of column footers
78 %  # 'footer'      => [],
79 %  # 
80 %  # #listref - each item is the empty string, or a listref of ...
81 %  # 'links'       =>
82 %  #
83 %  #
84 %  # 'align'       => 'lrc.', #one letter for each column, left/right/center/none
85 %  #                          # can also pass a listref with full values:
86 %  #                          # [ 'left', 'right', 'center', '' ]
87 %  #
88 %  # #listrefs...
89 %  # #currently only HTML, maybe eventually Excel too
90 %  # 'color'       => [],
91 %  # 'size'        => [],
92 %  # 'style'       => [],
93 %  # 
94 %  # #redirect if there's only one item...
95 %  # # listref of URL base and column name (or method)
96 %  # # or a coderef that returns the same
97 %  # 'redirect' =>
98 %  #
99 %  # #set to 1 (or column position for "disabled" status col) to enable
100 %  # #"show disabled/hide disabled" links
101 %  # #(can't be used with a literal query)
102 %  # 'disableable' => 1,
103 %
104 %  my(%opt) = @_;
105 %  #warn join(' / ', map { "$_ => $opt{$_}" } keys %opt ). "\n";
106 %
107 %  my $curuser = $FS::CurrentUser::CurrentUser;
108 %
109 %  my %align = (
110 %    'l' => 'left',
111 %    'r' => 'right',
112 %    'c' => 'center',
113 %    ' ' => '',
114 %    '.' => '',
115 %  );
116 %  $opt{align} = [ map $align{$_}, split(//, $opt{align}) ],
117 %    unless !$opt{align} || ref($opt{align});
118 %
119 %  if ( $opt{'agent_virt'} ) {
120 %
121 %    my $agentnums_sql = $curuser->agentnums_sql(
122 %                          'null_right' => $opt{'agent_null_right'}
123 %                        );
124 %
125 %    $opt{'query'}{'extra_sql'} .=
126 %      ( $opt{'query'}       =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
127 %      $agentnums_sql;
128 %    $opt{'count_query'} .=
129 %      ( $opt{'count_query'} =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
130 %      $agentnums_sql;
131 %
132 %    if ( $opt{'agent_pos'} || $opt{'agent_pos'} eq '0'
133 %         and scalar($curuser->agentnums) > 1           ) {
134 %      #false laziness w/statuspos above
135 %      my $pos = $opt{'agent_pos'};
136 %
137 %      foreach my $att (qw( align style color size )) {
138 %        $opt{$att} ||= [ map '', @{ $opt{'fields'} } ];
139 %      }
140 %
141 %      splice @{ $opt{'header'} }, $pos, 0, 'Agent'; 
142 %      splice @{ $opt{'align'}  }, $pos, 0, 'c'; 
143 %      splice @{ $opt{'style'}  }, $pos, 0, ''; 
144 %      splice @{ $opt{'size'}   }, $pos, 0, ''; 
145 %      splice @{ $opt{'fields'} }, $pos, 0,
146 %        sub { $_[0]->agentnum ? $_[0]->agent->agent : '(global)'; };
147 %      splice @{ $opt{'color'}  }, $pos, 0, '';
148 %      splice @{ $opt{'links'}  }, $pos, 0, '' #[ 'agent link?', 'agentnum' ]
149 %        if $opt{'links'};
150 %
151 %    }
152 %
153 %  }
154 %
155 %  if ( $opt{'disableable'} ) {
156 %  
157 %    unless ( $cgi->param('showdisabled') ) { #modify searches
158 %  
159 %      $opt{'query'}{'hashref'}{'disabled'} = '';
160 %      $opt{'query'}{'extra_sql'} =~ s/^\s*WHERE/ AND/i;
161 %  
162 %      $opt{'count_query'} .=
163 %        ( $opt{'count_query'} =~ /WHERE/i ? ' AND ' : ' WHERE ' ).
164 %        "( disabled = '' OR disabled IS NULL )";
165 %
166 %    } elsif (    $opt{'disabled_statuspos'}
167 %              || $opt{'disabled_statuspos'} eq '0' ) { #add status column
168 %
169 %      my $pos = $opt{'disabled_statuspos'};
170 %
171 %      foreach my $att (qw( align style color size )) {
172 %        $opt{$att} ||= [ map '', @{ $opt{'fields'} } ];
173 %      }
174 %
175 %      splice @{ $opt{'header'} }, $pos, 0, 'Status'; 
176 %      splice @{ $opt{'align'}  }, $pos, 0, 'c'; 
177 %      splice @{ $opt{'style'}  }, $pos, 0, 'b'; 
178 %      splice @{ $opt{'size'}   }, $pos, 0, ''; 
179 %      splice @{ $opt{'fields'} }, $pos, 0,
180 %        sub { shift->disabled ? 'DISABLED' : 'Active'; };
181 %      splice @{ $opt{'color'}  }, $pos, 0,
182 %        sub { shift->disabled ? 'FF0000'   : '00CC00'; };
183 %      splice @{ $opt{'links'}  }, $pos, 0, ''
184 %        if $opt{'links'};
185 %    }
186 %  
187 %    #add show/hide disabled links
188 %    my $items = $opt{'name'} || PL($opt{'name_singular'});
189 %    if ( $cgi->param('showdisabled') ) {
190 %      $cgi->param('showdisabled', 0);
191 %      $opt{'html_posttotal'} .=
192 %        '( <a href="'. $cgi->self_url. qq!">hide disabled $items</a> )!;
193 %      $cgi->param('showdisabled', 1);
194 %    } else {
195 %      $cgi->param('showdisabled', 1);
196 %      $opt{'html_posttotal'} .=
197 %        '( <a href="'. $cgi->self_url. qq!">show disabled $items</a> )!;
198 %      $cgi->param('showdisabled', 0);
199 %    }
200 %  
201 %  }
202 %
203 %  my $type = '';
204 %  my $limit = '';
205 %  my($confmax, $maxrecords, $total, $offset, $count_arrayref);
206 %
207 %  if ( $cgi->param('_type') =~ /^(csv|\w*\.xls)$/ ) {
208 %  
209 %    $type = $1;
210 %
211 %  } else { #setup some pagination things if we're in html mode
212 %
213 %    unless (exists($opt{count_query}) && length($opt{count_query})) {
214 %      ( $opt{count_query} = $opt{query} ) =~
215 %        s/^\s*SELECT\s*(.*?)\s+FROM\s/SELECT COUNT(*) FROM /i; #silly vim:/
216 %    }
217 %
218 %    if ( $opt{disableable} && ! $cgi->param('showdisabled') ) {
219 %      $opt{count_query} .=
220 %        ( ( $opt{count_query} =~ /WHERE/i ) ? ' AND ' : ' WHERE ' ).
221 %        "( disabled = '' OR disabled IS NULL )";
222 %    }
223 %
224 %    my $conf = new FS::Conf;
225 %    $confmax = $conf->config('maxsearchrecordsperpage');
226 %    if ( $cgi->param('maxrecords') =~ /^(\d+)$/ ) {
227 %      $maxrecords = $1;
228 %    } else {
229 %      $maxrecords ||= $confmax;
230 %    }
231 %
232 %    $limit = $maxrecords ? "LIMIT $maxrecords" : '';
233 %
234 %    $offset = $cgi->param('offset') || 0;
235 %    $limit .= " OFFSET $offset" if $offset;
236 %
237 %    my $count_sth = dbh->prepare($opt{'count_query'})
238 %      or die "Error preparing $opt{'count_query'}: ". dbh->errstr;
239 %    $count_sth->execute
240 %      or die "Error executing $opt{'count_query'}: ". $count_sth->errstr;
241 %    $count_arrayref = $count_sth->fetchrow_arrayref;
242 %    $total = $count_arrayref->[0];
243 %
244 %  }
245 %
246 %  # run the query
247 %
248 %  my $header = $opt{header};
249 %  my $rows;
250 %  if ( ref($opt{query}) ) {
251 %
252 %    if ( $opt{disableable} && ! $cgi->param('showdisabled') ) {
253 %      #%search = ( 'disabled' => '' );
254 %      $opt{'query'}->{'hashref'}->{'disabled'} = '';
255 %      $opt{'query'}->{'extra_sql'} =~ s/^\s*WHERE/ AND/i;
256 %    }
257 %
258 %    #eval "use FS::$opt{'query'};";
259 %    $rows = [ qsearch({
260 %      'select'    => $opt{'query'}->{'select'},
261 %      'table'     => $opt{'query'}->{'table'}, 
262 %      'addl_from' => (exists($opt{'query'}->{'addl_from'}) ? $opt{'query'}->{'addl_from'} : ''),
263 %      'hashref'   => $opt{'query'}->{'hashref'} || {}, 
264 %      'extra_sql' => $opt{'query'}->{'extra_sql'},
265 %      'order_by'  => $opt{'query'}->{'order_by'}. " $limit",
266 %    }) ];
267 %  } else {
268 %    my $sth = dbh->prepare("$opt{'query'} $limit")
269 %      or die "Error preparing $opt{'query'}: ". dbh->errstr;
270 %    $sth->execute
271 %      or die "Error executing $opt{'query'}: ". $sth->errstr;
272 %
273 %    #can get # of rows without fetching them all?
274 %    $rows = $sth->fetchall_arrayref;
275 %
276 %    $header ||= $sth->{NAME};
277 %  }
278 %
279 %  if ( $type eq 'csv' ) {
280 %
281 %    #http_header('Content-Type' => 'text/comma-separated-values' ); #IE chokes
282 %    http_header('Content-Type' => 'text/plain' );
283 %
284 %    my $csv = new Text::CSV_XS { 'always_quote' => 1,
285 %                                 'eol'          => "\n", #"\015\012", #"\012"
286 %                               };
287 %
288 %    $csv->combine(@$header); #or die $csv->status;
289 %    
290 <% $csv->string %>
291 %
292 %
293 %    foreach my $row ( @$rows ) {
294 %
295 %      if ( $opt{'fields'} ) {
296 %
297 %        my @line = ();
298 %
299 %        foreach my $field ( @{$opt{'fields'}} ) {
300 %          if ( ref($field) eq 'CODE' ) {
301 %            push @line, map {
302 %                              ref($_) eq 'ARRAY'
303 %                                ? '(N/A)' #unimplemented
304 %                                : $_;
305 %                            }
306 %                            &{$field}($row);
307 %          } else {
308 %            push @line, $row->$field();
309 %          }
310 %        }
311 %
312 %        $csv->combine(@line); #or die $csv->status;
313 %
314 %      } else {
315 %        $csv->combine(@$row); #or die $csv->status;
316 %      }
317 %
318 %      
319 <% $csv->string %>
320 %
321 %
322 %    }
323 %
324 %  #} elsif ( $type eq 'excel' ) {
325 %  } elsif ( $type =~ /\.xls$/ ) {
326 %
327 %    #http_header('Content-Type' => 'application/excel' ); #eww
328 %    http_header('Content-Type' => 'application/vnd.ms-excel' );
329 %    #http_header('Content-Type' => 'application/msexcel' ); #alas
330 %
331 %    my $data = '';
332 %    my $XLS = new IO::Scalar \$data;
333 %    my $workbook = Spreadsheet::WriteExcel->new($XLS)
334 %      or die "Error opening .xls file: $!";
335 %
336 %    my $worksheet = $workbook->add_worksheet(substr($opt{'title'},0,31));
337 %
338 %    my($r,$c) = (0,0);
339 %
340 %    $worksheet->write($r, $c++, $_) foreach @$header;
341 %
342 %    foreach my $row ( @$rows ) {
343 %      $r++;
344 %      $c = 0;
345 %
346 %      if ( $opt{'fields'} ) {
347 %
348 %        #my $links = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
349 %        #my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
350 %
351 %        foreach my $field ( @{$opt{'fields'}} ) {
352 %          #my $align = $aligns ? shift @$aligns : '';
353 %          #$align = " ALIGN=$align" if $align;
354 %          #my $a = '';
355 %          #if ( $links ) {
356 %          #  my $link = shift @$links;
357 %          #  $link = &{$link}($row) if ref($link) eq 'CODE';
358 %          #  if ( $link ) {
359 %          #    my( $url, $method ) = @{$link};
360 %          #    if ( ref($method) eq 'CODE' ) {
361 %          #      $a = $url. &{$method}($row);
362 %          #    } else {
363 %          #      $a = $url. $row->$method();
364 %          #    }
365 %          #    $a = qq(<A HREF="$a">);
366 %          #  }
367 %          #}
368 %          if ( ref($field) eq 'CODE' ) {
369 %            foreach my $value ( &{$field}($row) ) {
370 %              if ( ref($value) eq 'ARRAY' ) { 
371 %                $worksheet->write($r, $c++, '(N/A)' ); #unimplemented
372 %              } else {
373 %                $worksheet->write($r, $c++, $value );
374 %              }
375 %            }
376 %          } else {
377 %            $worksheet->write($r, $c++, $row->$field() );
378 %          }
379 %        }
380 %
381 %      } else {
382 %        $worksheet->write($r, $c++, $_) foreach @$row;
383 %      }
384 %
385 %    }
386 %
387 %    $workbook->close();# or die "Error creating .xls file: $!";
388 %
389 %    http_header('Content-Length' => length($data) );
390 %    
391 <% $data %>
392 %
393 %
394 %  } else { # regular HTML
395 %
396 %    if ( exists($opt{'redirect'}) && scalar(@$rows) == 1 && $total == 1 ) {
397 %      my $redirect = $opt{'redirect'};
398 %      $redirect = &{$redirect}($rows->[0]) if ref($redirect) eq 'CODE';
399 %      my( $url, $method ) = @$redirect;
400 %      redirect( $url. $rows->[0]->$method() );
401 %    } else {
402 %      if ( $opt{'name_singular'} ) {
403 %        $opt{'name'} = PL($opt{'name_singular'});
404 %      }
405 %      ( my $xlsname = $opt{'name'} ) =~ s/\W//g;
406 %      if ( $total == 1 ) {
407 %        if ( $opt{'name_singular'} ) {
408 %          $opt{'name'} = $opt{'name_singular'}
409 %        } else {
410 %          #$opt{'name'} =~ s/s$// if $total == 1;
411 %          $opt{'name'} =~ s/((s)e)?s$/$2/ if $total == 1;
412 %        }
413 %      }                                               
414 %
415 %      my @menubar = ();
416 %      if ( $opt{'menubar'} ) {
417 %        @menubar = @{ $opt{'menubar'} };
418 %      #} else {
419 %      #  @menubar = ( 'Main menu' => $p );
420 %      }
421
422   <% include( '/elements/header.html', $opt{'title'},
423                  include( '/elements/menubar.html', @menubar )
424              )
425   %>
426   <% defined($opt{'html_init'}) 
427         ? ( ref($opt{'html_init'})
428               ? &{$opt{'html_init'}}()
429               : $opt{'html_init'}
430           )
431         : ''
432   %>
433 %  
434 % unless ( $total ) { 
435 % unless ( $opt{'disable_nonefound'} ) { 
436
437       No matching <% $opt{'name'} %> found.<BR>
438 % } 
439 % } else { 
440
441     <TABLE>
442       <TR>
443
444         <TD VALIGN="bottom">
445
446           <FORM>
447
448           <% $total %> total <% $opt{'name'} %>
449
450 %         if ( $confmax && $total > $confmax ) {
451 %           $cgi->delete('maxrecords');
452 %           $cgi->param('_dummy', 1);
453
454 %#           ( show <SELECT NAME="maxrecords" onChange="this.form.submit();">
455             ( show <SELECT NAME="maxrecords" onChange="window.location = '<% $cgi->self_url %>;maxrecords=' + this.options[this.selectedIndex].value;">
456
457 %             foreach my $max ( map { $_ * $confmax } qw( 1 5 10 25 ) ) {
458                 <OPTION VALUE="<% $max %>" <% ( $maxrecords == $max ) ? 'SELECTED' : '' %>><% $max %></OPTION>
459 %             }
460
461             </SELECT> per page )
462
463 %           $cgi->param('maxrecords', $maxrecords);
464 %         }
465
466           <% defined($opt{'html_posttotal'}) 
467                 ? ( ref($opt{'html_posttotal'})
468                       ? &{$opt{'html_posttotal'}}()
469                       : $opt{'html_posttotal'}
470                   )
471                 : ''
472           %>
473           <BR>
474
475 % if ( $opt{'count_addl'} ) { 
476 %   my $n=0; foreach my $count ( @{$opt{'count_addl'}} ) { 
477
478       <% sprintf( $count, $count_arrayref->[++$n] ) %><BR>
479
480 %   } 
481 % } 
482           </FORM>
483
484         </TD>
485
486 % unless ( $opt{'disable_download'} ) { 
487
488           <TD ALIGN="right">
489 % $cgi->param('_type', "$xlsname.xls" ); 
490
491             Download full results<BR>
492             as <A HREF="<% $cgi->self_url %>">Excel spreadsheet</A><BR>
493 % $cgi->param('_type', 'csv'); 
494
495             as <A HREF="<% $cgi->self_url %>">CSV file</A>
496           </TD>
497 % $cgi->param('_type', "html" ); 
498 % } 
499
500       </TR>
501       <TR>
502         <TD COLSPAN=2>
503
504             <% my $pager = include ( '/elements/pager.html',
505                            'offset'     => $offset,
506                            'num_rows'   => scalar(@$rows),
507                            'total'      => $total,
508                            'maxrecords' => $maxrecords,
509             ) %>
510
511             <% defined($opt{'html_form'}) 
512                  ? ( ref($opt{'html_form'})
513                        ? &{$opt{'html_form'}}()
514                        : $opt{'html_form'}
515                    )
516                  : ''
517             %>
518
519             <% include('/elements/table-grid.html') %>
520
521               <TR>
522
523 %                 foreach my $header ( @$header ) { 
524
525                    <TH CLASS="grid" BGCOLOR="#cccccc"><% $header %></TH>
526 % } 
527
528               </TR>
529 % my $bgcolor1 = '#eeeeee';
530 %                 my $bgcolor2 = '#ffffff';
531 %                 my $bgcolor;
532 %                 foreach my $row ( @$rows ) {
533 %                   if ( $bgcolor eq $bgcolor1 ) {
534 %                     $bgcolor = $bgcolor2;
535 %                   } else {
536 %                     $bgcolor = $bgcolor1;
537 %                   }
538 %              
539
540                    <TR>
541 % if ( $opt{'fields'} ) {
542 %
543 %                        my $links  = $opt{'links'} ? [ @{$opt{'links'}} ] : '';
544 %                        my $aligns = $opt{'align'} ? [ @{$opt{'align'}} ] : '';
545 %                        my $colors = $opt{'color'} ? [ @{$opt{'color'}} ] : [];
546 %                        my $sizes  = $opt{'size'}  ? [ @{$opt{'size'}}  ] : [];
547 %                        my $styles = $opt{'style'} ? [ @{$opt{'style'}} ] : [];
548 %
549 %                        foreach my $field (
550 %
551 %                          map {
552 %                                if ( ref($_) eq 'ARRAY' ) {
553 %
554 %                                  my $tableref = $_;
555 %
556 %                                  '<TABLE CLASS="inv" CELLSPACING=0 CELLPADDING=0 WIDTH="100%">'.
557 %
558 %                                  join('', map {
559 %
560 %                                    my $rowref = $_;
561 %
562 %                                    '<tr>'.
563 %
564 %                                    join('', map {
565 %
566 %                                      my $e = $_;
567 %
568 %                                      '<TD '.
569 %                                        join(' ', map {
570 %                                          uc($_).'="'. $e->{$_}. '"';
571 %                                        }
572 %                                        grep exists($e->{$_}),
573 %                                             qw( align bgcolor colspan rowspan
574 %                                                 style valign width )
575 %                                        ).
576 %                                      '>'.
577 %
578 %                                      ( $e->{'link'}
579 %                                          ? '<A HREF="'. $e->{'link'}. '">'
580 %                                          : ''
581 %                                      ).
582 %                                      ( $e->{'size'}
583 %                                         ? '<FONT SIZE="'.uc($e->{'size'}).'">'
584 %                                         : ''
585 %                                      ).
586 %                                      ( $e->{'data_style'}
587 %                                          ? '<'. uc($e->{'data_style'}). '>'
588 %                                          : ''
589 %                                      ).
590 %                                      $e->{'data'}.
591 %                                      ( $e->{'data_style'}
592 %                                          ? '</'. uc($e->{'data_style'}). '>'
593 %                                          : ''
594 %                                      ).
595 %                                      ( $e->{'size'} ? '</FONT>' : '' ).
596 %                                      ( $e->{'link'} ? '</A>'    : '' ).
597 %                                      '</td>';
598 %
599 %                                    } @$rowref ).
600 %
601 %                                    '</tr>';
602 %                                  } @$tableref ).
603 %
604 %                                  '</table>';
605 %
606 %                                } else {
607 %                                  $_;
608 %                                }
609 %                              }
610 %
611 %                          map {
612 %                                if ( ref($_) eq 'CODE' ) {
613 %                                  &{$_}($row);
614 %                                } else {
615 %                                  $row->$_();
616 %                                }
617 %                              }
618 %                          @{$opt{'fields'}}
619 %
620 %                        ) {
621 %
622 %                          my $class = ( $field =~ /^<TABLE/i ) ? 'inv' : 'grid';
623 %
624 %                          my $align = $aligns ? shift @$aligns : '';
625 %                          $align = " ALIGN=$align" if $align;
626 %
627 %                          my $a = '';
628 %                          if ( $links ) {
629 %                            my $link = shift @$links;
630 %                            $link = &{$link}($row) if ref($link) eq 'CODE';
631 %                            if ( $link ) {
632 %                              my( $url, $method ) = @{$link};
633 %                              if ( ref($method) eq 'CODE' ) {
634 %                                $a = $url. &{$method}($row);
635 %                              } else {
636 %                                $a = $url. $row->$method();
637 %                              }
638 %                              $a = qq(<A HREF="$a">);
639 %                            }
640 %                          }
641 %
642 %                          my $font = '';
643 %                          my $color = shift @$colors;
644 %                          $color = &{$color}($row) if ref($color) eq 'CODE';
645 %                          my $size = shift @$sizes;
646 %                          $size = &{$size}($row) if ref($size) eq 'CODE';
647 %                          if ( $color || $size ) {
648 %                            $font = '<FONT '.
649 %                                    ( $color ? "COLOR=#$color "   : '' ).
650 %                                    ( $size  ? qq(SIZE="$size" )  : '' ).
651 %                                    '>';
652 %                          }
653 %
654 %                          my($s, $es) = ( '', '' );
655 %                          my $style = shift @$styles;
656 %                          $style = &{$style}($row) if ref($style) eq 'CODE';
657 %                          if ( $style ) {
658 %                            $s = join( '', map "<$_>", split('', $style) );
659 %                            $es = join( '', map "</$_>", split('', $style) );
660 %                          }
661 %
662 %                       
663
664                        <TD CLASS="<% $class %>" BGCOLOR="<% $bgcolor %>"<% $align %>><% $font %><% $a %><% $s %><% $field %><% $es %><% $a ? '</A>' : '' %><% $font ? '</FONT>' : '' %></TD>
665 % } 
666 % } else { 
667 % foreach ( @$row ) { 
668
669                           <TD CLASS="grid" BGCOLOR="$bgcolor"><% $_ %></TD>
670 % } 
671 % } 
672
673                    </TR>
674 % } 
675 % if ( $opt{'footer'} ) { 
676
677                 <TR>
678 % foreach my $footer ( @{ $opt{'footer'} } ) { 
679
680                      <TD CLASS="grid" BGCOLOR="#dddddd" STYLE="border-top: dashed 1px black;"><i><% $footer %></i></TH>
681 % } 
682
683                 </TR>
684 % } 
685
686             
687             </TABLE>
688             <% $pager %>
689   
690           </TD>
691         </TR>
692       </TABLE>
693 % } 
694
695   <% defined($opt{'html_foot'}) 
696         ? ( ref($opt{'html_foot'})
697               ? &{$opt{'html_foot'}}()
698               : $opt{'html_foot'}
699           )
700         : ''
701   %>
702   <% include( '/elements/footer.html' ) %>
703 % } 
704 % }