root/branches/release-36/lib/MT/CMS/Search.pm @ 2081

Revision 2081, 27.0 kB (checked in by bchoate, 19 months ago)

Limit replace operation to checked items. BugId:69487

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