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

Revision 1866, 26.6 kB (checked in by bchoate, 20 months ago)

Changes to store binary state to junk_status column. BugId:79280

  • 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            require MT::Comment;
415            if ($is_junk) {
416                $terms{junk_status} = MT::Comment::JUNK();
417            }
418            else {
419                $terms{junk_status} = MT::Comment::NOT_JUNK();
420            }
421        }
422        if ($is_dateranged) {
423            $args{range_incl}{$date_col} = 1;
424            if ( $datefrom gt $dateto ) {
425                $terms{$date_col} =
426                  [ $dateto . '000000', $datefrom . '235959' ];
427            }
428            else {
429                $terms{$date_col} =
430                  [ $datefrom . '000000', $dateto . '235959' ];
431            }
432        }
433        my $iter;
434        if ($do_replace) {
435            $iter = sub {
436                if ( my $id = pop @ids ) {
437                    $class->load($id);
438                }
439            };
440        }
441        if ( $blog_id || ($type eq 'blog') ) {
442            $iter = $class->load_iter( \%terms, \%args ) or die $class->errstr;
443        }
444        else {
445
446            my @streams;
447            if ( $author->is_superuser ) {
448                @streams = ( { iter => $class->load_iter( \%terms, \%args ) } );
449            } 
450            else {
451                # Get an iter for each accessible blog
452                my @perms = $app->model('permission')->load(
453                    { blog_id => '0', author_id => $author->id },
454                    { not => { blog_id => 1 } },
455                );
456                if (@perms) {
457                    @streams = map {
458                        {
459                            iter => $class->load_iter(
460                                {
461                                    blog_id => $_->blog_id,
462                                    %terms
463                                },
464                                \%args
465                            )
466                        }
467                    } @perms;
468                }
469            }
470
471            # Pull out the head of each iterator
472            # Next: effectively mergesort the various iterators
473            # To call the iterator n times takes time in O(bn)
474            #   with 'b' the number of blogs
475            # we expect to hit the iterator l/p times where 'p' is the
476            #   prob. of the search term appearing and 'l' is $limit
477            $_->{head} = $_->{iter}->() foreach @streams;
478            if ( $type ne 'template' ) {
479                $iter = sub {
480
481                    # find the head with greatest created_on
482                    my $which = \$streams[0];
483                    foreach my $iter (@streams) {
484                        next
485                          if !exists $iter->{head}
486                          || !$which
487                          || !${$which}->{head}
488                          || !defined( $iter->{head} );
489                        if ( $iter->{head}->created_on >
490                            ${$which}->{head}->created_on )
491                        {
492                            $which = \$iter;
493                        }
494                    }
495
496                    # Advance the chosen one
497                    my $result = ${$which}->{head};
498                    ${$which}->{head} = ${$which}->{iter}->() if $result;
499                    $result;
500                };
501            }
502            else {
503                $iter = sub {
504                    return undef unless @streams;
505
506                    # find the head with greatest created_on
507                    my $which = \$streams[0];
508                    while ( @streams && ( !defined ${$which}->{head} ) ) {
509                        shift @streams;
510                        last unless @streams;
511                        $which = \$streams[0];
512                    }
513                    my $result = ${$which}->{head};
514                    ${$which}->{head} = ${$which}->{iter}->() if $result;
515                    $result;
516                };
517            }
518        }
519        my $i = 1;
520        my %replace_cols;
521        if ($do_replace) {
522            %replace_cols = map { $_ => 1 } @{ $api->{replace_cols} };
523        }
524
525        my $re;
526        if (($search || '') ne '') {
527            $re = eval { qr/$search/ };
528            if ( my $err = $@ ) {
529                return $app->error(
530                    $app->translate( "Error in search expression: [_1]", $@ ) );
531            }
532        }
533        while ( my $obj = $iter->() ) {
534            next unless $author->is_superuser || $api->{perm_check}->($obj);
535            my $match = 0;
536            unless ($show_all) {
537                for my $col (@cols) {
538                    next if $do_replace && !$replace_cols{$col};
539                    my $text = $obj->column($col);
540                    $text = '' unless defined $text;
541                    if ($do_replace) {
542                        if ( $text =~ s!$re!$replace!g ) {
543                            $match++;
544                            $obj->$col($text);
545                        }
546                    }
547                    else {
548                        $match = $search ne '' ? $text =~ m!$re! : 1;
549                        last if $match;
550                    }
551                }
552            }
553            if ( $match || $show_all ) {
554                push @to_save, $obj if $do_replace && !$show_all;
555                push @data, $obj;
556            }
557            last if ( $limit ne 'all' ) && @data > $limit;
558        }
559        if (@data) {
560            $param{have_results} = 1;
561
562            # We got one extra to see if there were more
563            if ( ( $limit ne 'all' ) && @data > $limit ) {
564                $param{have_more} = 1;
565                pop @data;
566            }
567            $matches = @data;
568        }
569        else {
570            $matches = 0;
571        }
572    }
573    my $replace_count = 0;
574    for my $obj (@to_save) {
575        $replace_count++;
576        $obj->save
577          or return $app->error(
578            $app->translate( "Saving object failed: [_2]", $obj->errstr ) );
579    }
580    if (@data) {
581        if ($quicksearch) {
582            my $obj;
583            if (1 == scalar @data) {
584                ($obj) = @data;
585            }
586            elsif (defined $quicksearch_id) {
587                ($obj) = grep { $_->id == $quicksearch_id } @data;
588            }
589
590            if ($obj) {
591                my %args = (
592                    _type         => $type,
593                    id            => $obj->id,
594                    search_result => 1,
595                );
596                $args{blog_id} = $obj->blog_id
597                    if $obj->has_column('blog_id');
598                my $mode = 'view';
599                if ($type eq 'blog') {
600                    $args{blog_id} = delete $args{id};
601                    $mode = 'cfg_prefs';
602                }
603                return $app->redirect($app->uri(
604                    mode => $mode,
605                    args => \%args,
606                ));
607            }
608        }
609
610        if (my $meth = $search_api->{$type}{handler}) {
611            $meth = $app->handler_to_coderef($meth);
612            $meth->($app, items => \@data, param => \%param, type => $type );
613        } else {
614            my $meth = 'build_' . $type . '_table';
615            if ( $app->can($meth) ) {
616                $app->$meth( items => \@data, param => \%param, type => $type );
617            }
618            else {
619                my @objects;
620                push @objects, { object => $_ } for @data;
621                $param{object_loop} = \@objects;
622            }
623        }
624        $param{object_type} = $type;
625        if ( exists $api->{results_table_template} ) {
626            $param{results_template} = $api->{results_table_template};
627        }
628        else {
629            $param{results_template} = _default_results_table_template($app, $type, 1, $class->class_label_plural);
630        }
631    }
632    else {
633        if ( exists $api->{no_results_template} ) {
634            $param{results_template} = $api->{no_results_template};
635        }
636        else {
637            $param{results_template} = _default_results_table_template($app, $type, 0, $class->class_label_plural);
638        }
639    }
640    if ($is_dateranged) {
641        ( $datefrom_year, $datefrom_month, $datefrom_day ) =
642          $datefrom =~ m/^(\d\d\d\d)(\d\d)(\d\d)/;
643        ( $dateto_year, $dateto_month, $dateto_day ) =
644          $dateto =~ m/^(\d\d\d\d)(\d\d)(\d\d)/;
645    }
646    my %res = (
647        error => $q->param('error') || '',
648        limit => $limit,
649        limit_all => $limit eq 'all',
650        count_matches  => $matches,
651        replace_count  => $replace_count,
652        "search_$type" => 1,
653        search_label   => $class->class_label_plural,
654        object_label_plural => $class->class_label_plural,
655        object_type    => $type,
656        search         => ($do_replace ? $q->param('orig_search')
657        : $q->param('search')) || '',
658        searched => (
659            $do_replace ? $q->param('orig_search')
660            : ( $do_search && $q->param('search') ne '' )
661          )
662          || $show_all,
663        replace            => $replace,
664        do_replace         => $do_replace,
665        case               => $case,
666        datefrom_year      => $datefrom_year,
667        datefrom_month     => $datefrom_month,
668        datefrom_day       => $datefrom_day,
669        dateto_year        => $dateto_year,
670        dateto_month       => $dateto_month,
671        dateto_day         => $dateto_day,
672        is_regex           => $is_regex,
673        is_limited         => $is_limited,
674        is_dateranged      => $is_dateranged,
675        is_junk            => $is_junk,
676        can_search_junk    => ( $type eq 'comment' || $type eq 'ping' ),
677        can_replace        => $search_api->{$type}{can_replace},
678        can_search_by_date => $search_api->{$type}{can_search_by_date},
679        quick_search       => 0,
680        "tab_$tab"         => 1,
681        %param
682    );
683    $res{'tab_junk'} = 1 if $is_junk;
684   
685    my $search_cols = $search_api->{$type}{search_cols};
686    my %cols = map { $_ => 1 } @cols;
687    my @search_cols;
688    for my $field (keys %$search_cols) {
689        my %search_field;
690        $search_field{field}    = $field;
691        $search_field{selected} = 1 if exists($cols{$field});
692        $search_field{label}    = 'CODE' eq ref($search_cols->{$field})
693          ? $search_cols->{$field}->()
694          : $app->translate($search_cols->{$field});
695        push @search_cols, \%search_field;
696    }
697    $res{'search_cols'} = \@search_cols;
698    \%res;
699}
700
701sub _default_results_table_template {
702    my $app = shift;
703    my ($type, $results, $plural) = @_;
704    if ($results) {
705        return "<mt:include name=\"include/${type}_table.tmpl\">";
706    }
707    else {
708        return <<TMPL;   
709        <mtapp:statusmsg
710                id="no-$plural"
711                class="info">
712                <__trans phrase="No [_1] were found that match the given criteria." params="$plural">
713            </mtapp:statusmsg>
714        </mt:if>
715TMPL
716    }
717}
718
7191;
Note: See TracBrowser for help on using the browser.