root/branches/release-34/lib/MT/CMS/Search.pm @ 1823

Revision 1823, 26.6 kB (checked in by takayama, 20 months ago)

Fixed BugId:67959
* Added check for result of object loading

  • Property svn:keywords set to Id Revision
Line 
1package MT::CMS::Search;
2
3use strict;
4
5sub core_search_apis {
6    my $app = shift;
7    my $q       = $app->param;
8    my $blog_id = $q->param('blog_id');
9    my $author  = $app->user;
10    my @perms;
11    if ( !$blog_id ) {
12        if ( !$author->is_superuser() ) {
13            require MT::Permission;
14            @perms = MT::Permission->load( { author_id => $author->id } );
15        }
16    }
17    else {
18        @perms = ( $app->permissions )
19          or return $app->error( $app->translate("No permissions") );
20    }
21    return {
22        'entry' => {
23            'order' => 100,
24            'permission' => 'create_post,publish_post,edit_all_posts',
25            'handler' => '$Core::MT::CMS::Entry::build_entry_table',
26            'label' => 'Entries',
27            'perm_check' => sub {
28                grep { $_->can_edit_entry( $_[0], $author ) } @perms;
29            },
30            'search_cols' => {
31                'title' => sub { $app->translate('Title') },
32                'text' => sub { $app->translate('Entry Body') },
33                'text_more' => sub { $app->translate('Extended Entry') },
34                'keywords' => sub { $app->translate('Keywords') },
35                'excerpt' => sub { $app->translate('Excerpt') },
36                'basename' => sub { $app->translate('Basename') },
37            },
38            'replace_cols'       => [qw(title text text_more keywords excerpt)],
39            'can_replace'        => 1,
40            'can_search_by_date' => 1,
41            'date_column'        => 'authored_on',
42        },
43        'comment' => {
44            'order' => 200,
45            'permission' => 'publish_post,create_post,edit_all_posts,manage_feedback',
46            'handler' => '$Core::MT::CMS::Comment::build_comment_table',
47            'label' => 'Comments',
48            'perm_check' => sub {
49                require MT::Entry;
50                my $entry = MT::Entry->load( $_[0]->entry_id );
51                grep { $_->can_edit_entry( $entry, $author ) } @perms;
52            },
53            'search_cols' => {
54                'url' => sub { $app->translate('URL') },
55                'text' => sub { $app->translate('Comment Text') },
56                'email' => sub { $app->translate('Email Address') },
57                'ip' => sub { $app->translate('IP Address') },
58                'author' => sub { $app->translate('Name') },
59            },
60            'replace_cols'       => [qw(text)],
61            'can_replace'        => 1,
62            'can_search_by_date' => 1,
63        },
64        'ping' => {
65            'order' => 300,
66            'permission' => 'create_post,publish_post,edit_all_posts,manage_feedback',
67            'label' => 'TrackBacks',
68            'handler' => '$Core::MT::CMS::TrackBack::build_ping_table',
69            'perm_check' => sub {
70                my $ping = shift;
71                my $tb   = MT::Trackback->load( $ping->tb_id )
72                    or return undef;
73                if ( $tb->entry_id ) {
74                    require MT::Entry;
75                    my $entry = MT::Entry->load( $tb->entry_id );
76                    return
77                      grep { $_->can_edit_entry( $entry, $author ) } @perms;
78                }
79                elsif ( $tb->category_id ) {
80                    return grep { $_->can_edit_categories } @perms;
81                }
82            },
83            'search_cols' => {
84                'title' => sub { $app->translate('Title') },
85                'excerpt' => sub { $app->translate('Excerpt') },
86                'source_url' => sub { $app->translate('Source URL') },
87                'ip' => sub { $app->translate('IP Address') },
88                'blog_name' => sub { $app->translate('Blog Name') },
89            },
90            'replace_cols'       => [qw(title excerpt)],
91            'can_replace'        => 1,
92            'can_search_by_date' => 1,
93        },
94        'page' => {
95            'order' => 400,
96            'permission' => 'manage_pages',
97            'label' => 'Pages',
98            'handler' => '$Core::MT::CMS::Entry::build_entry_table',
99            'perm_check' => sub {
100                grep { $_->can_manage_pages( $_[0], $author ) } @perms;
101            },
102            'search_cols' => {
103                'title' => sub { $app->translate('Title') },
104                'text' => sub { $app->translate('Page Body') },
105                'text_more' => sub { $app->translate('Extended Page') },
106                'keywords' => sub { $app->translate('Keywords') },
107                'excerpt' => sub { $app->translate('Excerpt') },
108                'basename' => sub { $app->translate('Basename') },
109            },
110            'replace_cols'       => [qw(title text text_more keywords excerpt)],
111            'can_replace'        => 1,
112            'can_search_by_date' => 1,
113            'date_column'        => 'authored_on',
114            'results_table_template' => '<mt:include name="include/entry_table.tmpl">',
115        },
116        'template' => {
117            'order'         => 500,
118            'permission'    => 'edit_templates',
119            'handler' => '$Core::MT::CMS::Template::build_template_table',
120            'label'         => 'Templates',
121            'perm_check' => sub {
122                my ($obj) = @_;
123
124                # are there any perms that match this object and
125                # allow template editing?
126                my @check = grep {
127                         $_->blog_id == $obj->blog_id
128                      && $_->can_edit_templates
129                } @perms;
130                return @check;
131
132            },
133            'search_cols' => {
134                'name' => sub { $app->translate('Template Name') },
135                'text' => sub { $app->translate('Text') },
136                'linked_file' => sub { $app->translate('Linked Filename') },
137                'outfile' => sub { $app->translate('Output Filename') },
138            },
139            'replace_cols'       => [qw(name text linked_file outfile)],
140            'can_replace'        => 1,
141            'can_search_by_date' => 0,
142        },
143        'asset' => {
144            'order' => 600,
145            'permission' => 'manage_assets',
146            'label' => 'Assets',
147            'handler' => '$Core::MT::CMS::Asset::build_asset_table',
148            'perm_check' => sub {
149                1;
150            },
151            'search_cols' => {
152                'file_name' => sub { $app->translate('Filename') },
153                'description' => sub { $app->translate('Description') },
154                'label' => sub { $app->translate('Label') },
155            },
156            'replace_cols'       => [],
157            'can_replace'        => 0,
158            'can_search_by_date' => 1,
159            'setup_terms_args'   => sub {
160                my ($terms, $args, $blog_id) = @_;
161                $terms->{class} = '*';
162                $terms->{blog_id} = $blog_id if $blog_id;
163            }
164        },
165        'log' => {
166            'order' => 700,
167            'permission'        => "view_blog_log",
168            'system_permission' => "view_log",
169            'handler' => '$Core::MT::CMS::Log::build_log_table',
170            'label' => 'Activity Log',
171            'perm_check' => sub {
172                my ($obj) = @_;
173                return 1 if $author->can_view_log;
174                my $perm = $author->permissions( $obj->blog_id );
175                return $perm->can_view_blog_log;
176            },
177            'search_cols' => {
178                'ip' => sub { $app->translate('Log Message') },
179                'message' => sub { $app->translate('IP Address') },
180            },
181            'can_replace'        => 0,
182            'can_search_by_date' => 1,
183            'setup_terms_args'   => sub {
184                my ($terms, $args, $blog_id) = @_;
185                $terms->{class} = '*';
186                $terms->{blog_id} = $blog_id if $blog_id;
187            }
188        },
189        'author' => {
190            'order' => 800,
191            'system_permission' => 'administer',
192            'label' => 'Users',
193            'handler' => '$Core::MT::CMS::User::build_author_table',
194            'perm_check' => sub {
195                return 1 if $author->is_superuser;
196                if ($blog_id) {
197                    my $perm = $author->permissions($blog_id);
198                    return $perm->can_administer_blog;
199                }
200                return 0;
201            },
202            'search_cols' => {
203                'name'     => sub { $app->translate('Username') },
204                'nickname' => sub { $app->translate('Display Name') },
205                'email'    => sub { $app->translate('Email Address') },
206                'url'      => sub { $app->translate('URL') },
207            },
208            'can_replace'        => 0,
209            'can_search_by_date' => 0,
210            'setup_terms_args'   => sub {
211                my ($terms, $args, $blog_id) = @_;
212                if ($blog_id) {
213                    $args->{'join'} =
214                      MT::Permission->join_on( 'author_id',
215                        { blog_id => $blog_id } );
216                }
217                else {
218                    $terms->{'type'} = MT::Author::AUTHOR();
219                }
220            },
221            'results_table_template' => '
222<mt:if name="blog_id">
223    <mt:include name="include/member_table.tmpl">
224<mt:else>
225    <mt:include name="include/author_table.tmpl">
226</mt:if>',
227        },
228        'blog' => {
229            'order' => 900,
230            'system_permission' => 'administer',
231            'label' => 'Blogs',
232            'handler' => '$Core::MT::CMS::Blog::build_blog_table',
233            'perm_check' => sub {
234                return 1 if $author->is_superuser;
235                my ($obj) = @_;
236                my $perm = $author->permissions( $obj->id );
237                $perm
238                  && ( $perm->can_administer_blog || $perm->can_edit_config );
239            },
240            'search_cols' => {
241                'name' => sub { $app->translate('Name') },
242                'site_url' => sub { $app->translate('Site URL') },
243                'site_path' => sub { $app->translate('Site Root') },
244                'description' => sub { $app->translate('Description') },
245            },
246            'replace_cols'       => [qw(name site_url site_path description)],
247            'can_replace'        => $author->is_superuser(),
248            'can_search_by_date' => 0,
249            'view'               => 'system',
250            'setup_terms_args'   => sub {
251                my ($terms, $args, $blog_id) = @_;
252                $args->{sort}      = 'name';
253                $args->{direction} = 'ascend';
254            }
255        }
256    };
257
258}
259
260sub search_replace {
261    my $app = shift;
262    my $param = do_search_replace($app, @_) or return;
263    my $blog_id = $app->param('blog_id');
264    $app->add_breadcrumb( $app->translate('Search & Replace') );
265    $param->{nav_search}   = 1;
266    $param->{screen_class} = "search-replace";
267    $param->{screen_id}    = "search-replace";
268    $param->{search_tabs}  = $app->search_apis($blog_id ? 'blog' : 'system');
269    $param->{entry_type}  = $app->param('entry_type');
270    my $tmpl = $app->load_tmpl( 'search_replace.tmpl', $param );
271    my $placeholder = $tmpl->getElementById('search_results');
272    $placeholder->innerHTML(delete $param->{results_template});
273    return $tmpl;
274}
275
276sub do_search_replace {
277    my $app     = shift;
278    my $q       = $app->param;
279    my $blog_id = $q->param('blog_id');
280    my $author  = $app->user;
281
282    my $search_api = $app->registry("search_apis");
283
284    my (
285        $search,        $replace,     $do_replace,    $case,
286        $is_regex,      $is_limited,  $type,          $is_junk,
287        $is_dateranged, $ids,         $datefrom_year, $datefrom_month,
288        $datefrom_day,  $dateto_year, $dateto_month,  $dateto_day,
289        $from,          $to,          $show_all,      $do_search,
290        $orig_search,   $quicksearch
291      )
292      = map scalar $q->param($_),
293      qw( search replace do_replace case is_regex is_limited _type is_junk is_dateranged replace_ids datefrom_year datefrom_month datefrom_day dateto_year dateto_month dateto_day from to show_all do_search orig_search quicksearch );
294
295
296    if ( !$type || ( 'category' eq $type ) || ( 'folder' eq $type ) ) {
297        $type = 'entry';
298    }
299   
300    foreach my $obj_type (qw( role association )) {
301        if ( $type eq $obj_type ) {
302            $type = 'author';
303        }
304    }
305
306    $replace && ( $app->validate_magic() or return );
307    $search = $orig_search if $do_replace;    # for safety's sake
308    my $list_pref = $app->list_pref($type);
309
310    $app->assert( $search_api->{$type}, "Invalid request." ) or return;
311
312    # force action bars to top and bottom
313    $list_pref->{"bar"}                     = 'both';
314    $list_pref->{"position_actions_both"}   = 1;
315    $list_pref->{"position_actions_top"}    = 1;
316    $list_pref->{"position_actions_bottom"} = 1;
317    $list_pref->{"view"}                    = 'compact';
318    $list_pref->{"view_compact"}            = 1;
319    my ( @cols, $datefrom, $dateto, $date_col );
320    $do_replace    = 0 unless $search_api->{$type}{can_replace};
321    $is_dateranged = 0 unless $search_api->{$type}{can_search_by_date};
322    my @ids;
323
324    if ($ids) {
325        @ids = split /,/, $ids;
326    }
327    if ($is_limited) {
328        @cols = $q->param('search_cols');
329        my %search_api_cols =
330          map { $_ => 1 } keys %{ $search_api->{$type}{search_cols} };
331        if ( @cols && ( $cols[0] =~ /,/ ) ) {
332            @cols = split /,/, $cols[0];
333        }
334        @cols = grep { $search_api_cols{$_} } @cols;
335    }
336    else {
337        @cols = keys %{ $search_api->{$type}->{search_cols} };
338    }
339    my $quicksearch_id;
340    if ($quicksearch && ($search || '') ne '' && $search !~ m{ \D }xms) {
341        $quicksearch_id = $search;
342        unshift @cols, 'id';
343    }
344    foreach (
345        $datefrom_year, $datefrom_month, $datefrom_day,
346        $dateto_year,   $dateto_month,   $dateto_day
347      )
348    {
349        s!\D!!g if $_;
350    }
351    if ($is_dateranged) {
352        $datefrom = sprintf( "%04d%02d%02d",
353            $datefrom_year, $datefrom_month, $datefrom_day );
354        $dateto =
355          sprintf( "%04d%02d%02d", $dateto_year, $dateto_month, $dateto_day );
356        if ( ( $datefrom eq '00000000' ) && ( $dateto eq '00000000' ) ) {
357            $is_dateranged = 0;
358        }
359        else {
360            if (   !is_valid_date( $datefrom . '000000' )
361                || !is_valid_date( $dateto . '000000' ) )
362            {
363                return $app->error(
364                    $app->translate(
365                        "Invalid date(s) specified for date range.")
366                );
367            }
368        }
369    }
370    elsif ( $from && $to ) {
371        $is_dateranged = 1;
372        s!\D!!g foreach ( $from, $to );
373        $datefrom = substr( $from, 0, 8 );
374        $dateto   = substr( $to,   0, 8 );
375    }
376    my $tab = $q->param('tab') || 'entry';
377    ## Sometimes we need to pass in the search columns like 'title,text', so
378    ## we look for a comma (not a valid character in a column name) and split
379    ## on it if it's there.
380    if ( ($search || '') ne '' ) {
381        my $enc = $app->charset;
382        $search = MT::I18N::encode_text( $search, 'utf-8', $enc )
383          if ( $enc !~ m/utf-?8/i )
384          && ( 'dialog_grant_role' eq $app->param('__mode') );
385        $search = quotemeta($search) unless $is_regex;
386        $search = '(?i)' . $search   unless $case;
387    }
388    my ( @to_save, @data );
389    my $api   = $search_api->{$type};
390    my $class = $app->model($api->{object_type} || $type);
391    my %param = %$list_pref;
392    my $limit = $q->param('limit') || 125;    # FIXME: mt.cfg setting?
393    my $matches;
394    $date_col = $api->{date_column} || 'created_on';
395    if ( ( $do_search && $search ne '' ) || $show_all || $do_replace ) {
396        my %terms;
397        my %args;
398        ## we need to search all user/group for 'grant permissions',
399        ## if $blog_id is specified. it affects the setup_terms_args.
400        if ( $app->param('__mode') eq 'dialog_grant_role' ) {
401            $blog_id = 0;
402        }
403        if (exists $api->{setup_terms_args}) {
404            my $code = $app->handler_to_coderef($api->{setup_terms_args});
405            $code->(\%terms, \%args, $blog_id);
406        }
407        else {
408            %terms = $blog_id ? ( blog_id => $blog_id ) : ();
409            if ( $type ne 'template' ) {
410                %args = ( 'sort' => $date_col, direction => 'descend' );
411            }
412        }
413        if ( $class->has_column('junk_status') ) {
414            if ($is_junk) {
415                $terms{junk_status} = -1;
416            }
417            else {
418                $terms{junk_status} = [ 0, 1 ];
419            }
420        }
421        if ($is_dateranged) {
422            $args{range_incl}{$date_col} = 1;
423            if ( $datefrom gt $dateto ) {
424                $terms{$date_col} =
425                  [ $dateto . '000000', $datefrom . '235959' ];
426            }
427            else {
428                $terms{$date_col} =
429                  [ $datefrom . '000000', $dateto . '235959' ];
430            }
431        }
432        my $iter;
433        if ($do_replace) {
434            $iter = sub {
435                if ( my $id = pop @ids ) {
436                    $class->load($id);
437                }
438            };
439        }
440        if ( $blog_id || ($type eq 'blog') ) {
441            $iter = $class->load_iter( \%terms, \%args ) or die $class->errstr;
442        }
443        else {
444
445            my @streams;
446            if ( $author->is_superuser ) {
447                @streams = ( { iter => $class->load_iter( \%terms, \%args ) } );
448            } 
449            else {
450                # Get an iter for each accessible blog
451                my @perms = $app->model('permission')->load(
452                    { blog_id => '0', author_id => $author->id },
453                    { not => { blog_id => 1 } },
454                );
455                if (@perms) {
456                    @streams = map {
457                        {
458                            iter => $class->load_iter(
459                                {
460                                    blog_id => $_->blog_id,
461                                    %terms
462                                },
463                                \%args
464                            )
465                        }
466                    } @perms;
467                }
468            }
469
470            # Pull out the head of each iterator
471            # Next: effectively mergesort the various iterators
472            # To call the iterator n times takes time in O(bn)
473            #   with 'b' the number of blogs
474            # we expect to hit the iterator l/p times where 'p' is the
475            #   prob. of the search term appearing and 'l' is $limit
476            $_->{head} = $_->{iter}->() foreach @streams;
477            if ( $type ne 'template' ) {
478                $iter = sub {
479
480                    # find the head with greatest created_on
481                    my $which = \$streams[0];
482                    foreach my $iter (@streams) {
483                        next
484                          if !exists $iter->{head}
485                          || !$which
486                          || !${$which}->{head}
487                          || !defined( $iter->{head} );
488                        if ( $iter->{head}->created_on >
489                            ${$which}->{head}->created_on )
490                        {
491                            $which = \$iter;
492                        }
493                    }
494
495                    # Advance the chosen one
496                    my $result = ${$which}->{head};
497                    ${$which}->{head} = ${$which}->{iter}->() if $result;
498                    $result;
499                };
500            }
501            else {
502                $iter = sub {
503                    return undef unless @streams;
504
505                    # find the head with greatest created_on
506                    my $which = \$streams[0];
507                    while ( @streams && ( !defined ${$which}->{head} ) ) {
508                        shift @streams;
509                        last unless @streams;
510                        $which = \$streams[0];
511                    }
512                    my $result = ${$which}->{head};
513                    ${$which}->{head} = ${$which}->{iter}->() if $result;
514                    $result;
515                };
516            }
517        }
518        my $i = 1;
519        my %replace_cols;
520        if ($do_replace) {
521            %replace_cols = map { $_ => 1 } @{ $api->{replace_cols} };
522        }
523
524        my $re;
525        if (($search || '') ne '') {
526            $re = eval { qr/$search/ };
527            if ( my $err = $@ ) {
528                return $app->error(
529                    $app->translate( "Error in search expression: [_1]", $@ ) );
530            }
531        }
532        while ( my $obj = $iter->() ) {
533            next unless $author->is_superuser || $api->{perm_check}->($obj);
534            my $match = 0;
535            unless ($show_all) {
536                for my $col (@cols) {
537                    next if $do_replace && !$replace_cols{$col};
538                    my $text = $obj->column($col);
539                    $text = '' unless defined $text;
540                    if ($do_replace) {
541                        if ( $text =~ s!$re!$replace!g ) {
542                            $match++;
543                            $obj->$col($text);
544                        }
545                    }
546                    else {
547                        $match = $search ne '' ? $text =~ m!$re! : 1;
548                        last if $match;
549                    }
550                }
551            }
552            if ( $match || $show_all ) {
553                push @to_save, $obj if $do_replace && !$show_all;
554                push @data, $obj;
555            }
556            last if ( $limit ne 'all' ) && @data > $limit;
557        }
558        if (@data) {
559            $param{have_results} = 1;
560
561            # We got one extra to see if there were more
562            if ( ( $limit ne 'all' ) && @data > $limit ) {
563                $param{have_more} = 1;
564                pop @data;
565            }
566            $matches = @data;
567        }
568        else {
569            $matches = 0;
570        }
571    }
572    my $replace_count = 0;
573    for my $obj (@to_save) {
574        $replace_count++;
575        $obj->save
576          or return $app->error(
577            $app->translate( "Saving object failed: [_2]", $obj->errstr ) );
578    }
579    if (@data) {
580        if ($quicksearch) {
581            my $obj;
582            if (1 == scalar @data) {
583                ($obj) = @data;
584            }
585            elsif (defined $quicksearch_id) {
586                ($obj) = grep { $_->id == $quicksearch_id } @data;
587            }
588
589            if ($obj) {
590                my %args = (
591                    _type         => $type,
592                    id            => $obj->id,
593                    search_result => 1,
594                );
595                $args{blog_id} = $obj->blog_id
596                    if $obj->has_column('blog_id');
597                my $mode = 'view';
598                if ($type eq 'blog') {
599                    $args{blog_id} = delete $args{id};
600                    $mode = 'cfg_prefs';
601                }
602                return $app->redirect($app->uri(
603                    mode => $mode,
604                    args => \%args,
605                ));
606            }
607        }
608
609        if (my $meth = $search_api->{$type}{handler}) {
610            $meth = $app->handler_to_coderef($meth);
611            $meth->($app, items => \@data, param => \%param, type => $type );
612        } else {
613            my $meth = 'build_' . $type . '_table';
614            if ( $app->can($meth) ) {
615                $app->$meth( items => \@data, param => \%param, type => $type );
616            }
617            else {
618                my @objects;
619                push @objects, { object => $_ } for @data;
620                $param{object_loop} = \@objects;
621            }
622        }
623        $param{object_type} = $type;
624        if ( exists $api->{results_table_template} ) {
625            $param{results_template} = $api->{results_table_template};
626        }
627        else {
628            $param{results_template} = _default_results_table_template($app, $type, 1, $class->class_label_plural);
629        }
630    }
631    else {
632        if ( exists $api->{no_results_template} ) {
633            $param{results_template} = $api->{no_results_template};
634        }
635        else {
636            $param{results_template} = _default_results_table_template($app, $type, 0, $class->class_label_plural);
637        }
638    }
639    if ($is_dateranged) {
640        ( $datefrom_year, $datefrom_month, $datefrom_day ) =
641          $datefrom =~ m/^(\d\d\d\d)(\d\d)(\d\d)/;
642        ( $dateto_year, $dateto_month, $dateto_day ) =
643          $dateto =~ m/^(\d\d\d\d)(\d\d)(\d\d)/;
644    }
645    my %res = (
646        error => $q->param('error') || '',
647        limit => $limit,
648        limit_all => $limit eq 'all',
649        count_matches  => $matches,
650        replace_count  => $replace_count,
651        "search_$type" => 1,
652        search_label   => $class->class_label_plural,
653        object_label_plural => $class->class_label_plural,
654        object_type    => $type,
655        search         => ($do_replace ? $q->param('orig_search')
656        : $q->param('search')) || '',
657        searched => (
658            $do_replace ? $q->param('orig_search')
659            : ( $do_search && $q->param('search') ne '' )
660          )
661          || $show_all,
662        replace            => $replace,
663        do_replace         => $do_replace,
664        case               => $case,
665        datefrom_year      => $datefrom_year,
666        datefrom_month     => $datefrom_month,
667        datefrom_day       => $datefrom_day,
668        dateto_year        => $dateto_year,
669        dateto_month       => $dateto_month,
670        dateto_day         => $dateto_day,
671        is_regex           => $is_regex,
672        is_limited         => $is_limited,
673        is_dateranged      => $is_dateranged,
674        is_junk            => $is_junk,
675        can_search_junk    => ( $type eq 'comment' || $type eq 'ping' ),
676        can_replace        => $search_api->{$type}{can_replace},
677        can_search_by_date => $search_api->{$type}{can_search_by_date},
678        quick_search       => 0,
679        "tab_$tab"         => 1,
680        %param
681    );
682    $res{'tab_junk'} = 1 if $is_junk;
683   
684    my $search_cols = $search_api->{$type}{search_cols};
685    my %cols = map { $_ => 1 } @cols;
686    my @search_cols;
687    for my $field (keys %$search_cols) {
688        my %search_field;
689        $search_field{field}    = $field;
690        $search_field{selected} = 1 if exists($cols{$field});
691        $search_field{label}    = 'CODE' eq ref($search_cols->{$field})
692          ? $search_cols->{$field}->()
693          : $app->translate($search_cols->{$field});
694        push @search_cols, \%search_field;
695    }
696    $res{'search_cols'} = \@search_cols;
697    \%res;
698}
699
700sub _default_results_table_template {
701    my $app = shift;
702    my ($type, $results, $plural) = @_;
703    if ($results) {
704        return "<mt:include name=\"include/${type}_table.tmpl\">";
705    }
706    else {
707        return <<TMPL;   
708        <mtapp:statusmsg
709                id="no-$plural"
710                class="info">
711                <__trans phrase="No [_1] were found that match the given criteria." params="$plural">
712            </mtapp:statusmsg>
713        </mt:if>
714TMPL
715    }
716}
717
7181;
Note: See TracBrowser for help on using the browser.