📄 search.pm
字号:
if ($app->{searchparam}{CommentSearchCutoff} && $app->{searchparam}{CommentSearchCutoff} != 9999999) { my @ago = MT::Util::offset_time_list(time - 3600 * 24 * $app->{searchparam}{CommentSearchCutoff}); my $ago = sprintf "%04d%02d%02d%02d%02d%02d", $ago[5]+1900, $ago[4]+1, @ago[3,2,1,0]; $args{'join'}->[2]{created_on} = [ $ago ]; $args{'join'}->[3]{range} = { created_on => 1 }; } elsif ($app->{searchparam}{MaxResults} && $app->{searchparam}{MaxResults} != 9999999) { $args{limit} = $app->{searchparam}{MaxResults}; } my $iter = MT::Entry->load_iter({ status => MT::Entry::RELEASE() }, \%args); my %blogs; my $include = $app->{searchparam}{IncludeBlogs}; while (my $entry = $iter->()) { next unless $include->{ $entry->blog_id }; my $blog = $blogs{ $entry->blog_id } || MT::Blog->load($entry->blog_id); $app->_store_hit_data($blog, $entry); } 1;}sub _set_form_elements { my($app, $tmpl) = @_; ## Fill in user-defined template with proper form settings. if ($app->{searchparam}{Type} eq 'newcomments') { if ($app->{searchparam}{CommentSearchCutoff}) { $tmpl =~ s/(<select name="CommentSearchCutoff">.*<option value="$app->{searchparam}{CommentSearchCutoff}")/$1 selected="selected"/si; } else { $tmpl =~ s/(<select name="CommentSearchCutoff">.*<option value="9999999")/$1 selected="selected"/si; } } else { if ($app->{searchparam}{SearchCutoff}) { $tmpl =~ s/(<select name="SearchCutoff">.*<option value="$app->{searchparam}{SearchCutoff}")/$1 selected="selected"/si; } else { $tmpl =~ s/(<select name="SearchCutoff">.*<option value="9999999")/$1 selected="selected"/si; } if ($app->{searchparam}{CaseSearch}) { $tmpl =~ s/(<input type="checkbox"[^>]+name="CaseSearch")/$1 checked="checked"/g; } if ($app->{searchparam}{RegexSearch}) { $tmpl =~ s/(<input type="checkbox"[^>]+name="RegexSearch")/$1 checked="checked"/g; } $tmpl =~ s/(<input type="radio"[^>]+?$app->{searchparam}{SearchElement}\")/$1 checked="checked"/g; for my $type (qw( IncludeBlogs ExcludeBlogs )) { for my $blog_id (keys %{ $app->{searchparam}{$type} }) { $tmpl =~ s/(<input type="checkbox"[^>]+?$type" value="$blog_id")/$1 checked="checked"/g; #" } } } if ($app->{searchparam}{MaxResults}) { $tmpl =~ s/(<select name="MaxResults">.*<option value="$app->{searchparam}{MaxResults}")/$1 selected="selected"/si; } else { $tmpl =~ s/(<select name="MaxResults">.*<option value="9999999")/$1 selected="selected"/si; } $tmpl;}sub is_a_match { my($app, $txt) = @_; if ($app->{searchparam}{RegexSearch}) { return unless $txt =~ m/$app->{search_string}/; } else { my $casemod = $app->{searchparam}{CaseSearch} ? '' : '(?i)'; for (@{$app->{searchparam}{search_keys}{AND}}) { return unless $txt =~ /$casemod$_/; } for (@{$app->{searchparam}{search_keys}{NOT}}) { return if $txt =~ /$casemod$_/; } } 1;}sub query_parse { my $app = shift; return unless $app->{search_string}; local $_ = $app->{search_string}; s/^\s//; # Remove leading whitespace s/\s$//; # Remove trailing whitespace s/\s+AND\s+/ /g; # Remove AND because it's implied s/\s{2,}/ /g; # Remove contiguous spaces my @search_keys; my @tokens = split; while (my $atom = shift @tokens) { my($type); if ($atom eq 'NOT' || $atom eq 'AND') { $type = $atom; $atom = shift @tokens; $atom = find_phrase($atom, \@tokens) if $atom =~ /^\"/; } elsif ($atom eq 'OR') { $atom = shift @tokens; $atom = find_phrase($atom, \@tokens) if $atom =~ /^\"/; ## OR new atom with last atom $search_keys[-1]{atom} = '(?:' . $search_keys[-1]{atom} .'|' . quotemeta($atom) . ')'; next; } elsif ($atom =~ /^-(.*)/) { $type = 'NOT'; $atom = $1; $atom = find_phrase($atom, \@tokens) if $atom =~ /^\"/; } else { $type = 'AND'; $atom = find_phrase($atom, \@tokens) if $atom =~ /^\"/; } push @search_keys, { atom => quotemeta($atom), type => $type }; } $app->{searchparam}{search_keys} = \@search_keys; $app->query_optimize;}sub find_phrase { my($atom, $tokenref) = @_; while (my $next = shift @$tokenref) { $atom = $atom . ' ' . $next; last if $atom =~ /\"$/; } $atom =~ s/^"(.*)"$/$1/; $atom;}sub query_optimize { my $app = shift; ## Sort keys longest to shortest for search efficiency. $app->{searchparam}{search_keys} = [ reverse sort { length($a->{atom}) <=> length($b->{atom}) } @{ $app->{searchparam}{search_keys} } ]; ## Sort keys by contents. Any ORs immediately get a lower priority. my %terms; for my $key (@{ $app->{searchparam}{search_keys} }) { if ($key->{atom} =~ /\(.*\|.*\)/) { push(@{ $terms{$key->{type}}{low} }, $key); } else { push(@{ $terms{$key->{type}}{high} }, $key); } } ## Final priority: AND long, AND short, AND with OR (long/short), ## NOT long/short ## This should give us the most efficient search in that it is ## searching for the harder-to-match keys first. my %regex; for my $type (qw( AND NOT )) { for my $pri (qw( high low )) { for my $obj (@{ $terms{$type}{$pri} }) { push(@{ $regex{$type} }, $obj->{atom}); } } } $app->{searchparam}{search_keys} = \%regex;}sub _search_hit { my($app, $entry) = @_; my @text_elements; if ($app->{searchparam}{SearchElement} ne 'comments') { @text_elements = ($entry->title, $entry->text, $entry->text_more, $entry->keywords); } if ($app->{searchparam}{SearchElement} ne 'entries') { my $comments = $entry->comments; for my $comment (@$comments) { push @text_elements, $comment->text, $comment->author, $comment->url; } } return 1 if $app->is_a_match(join("\n", map $_ || '', @text_elements));}sub _store_hit_data { my $app = shift; my($blog, $entry, $banner_seen) = @_; my %result_data = (blog => $blog); ## Need to create entry excerpt here, because we can't rely on ## the user's per-blog setting. unless ($entry->excerpt) { $entry->excerpt($entry->get_excerpt($app->{searchparam}{ExcerptWords})); } $result_data{entry} = $entry; if ($app->{searchparam}{Type} eq 'newcomments') { push @{ $app->{results} }, \%result_data; } else { push(@{ $app->{results}{ $blog->name } }, \%result_data); }}package MT::App::Search::Context;use MT::Template::Context;@MT::App::Search::Context::ISA = qw( MT::Template::Context );sub init { my $ctx = shift; $ctx->SUPER::init(@_); $ctx->register_handler(EntryEditLink => \&_hdlr_entry_edit_link); $ctx->register_handler(SearchResults => [ \&_hdlr_results, 1 ]); $ctx->register_handler(SearchResultCount => \&_hdlr_result_count); $ctx->register_handler(SearchString => \&_hdlr_search_string); $ctx->register_handler(NoSearchResults => [ \&MT::Template::Context::_hdlr_pass_tokens, 1 ]); $ctx->register_handler(NoSearch => [ \&MT::Template::Context::_hdlr_pass_tokens, 1 ]); $ctx->register_handler(BlogResultHeader => [ \&MT::Template::Context::_hdlr_pass_tokens, 1 ]); $ctx->register_handler(BlogResultFooter => [ \&MT::Template::Context::_hdlr_pass_tokens, 1 ]); $ctx;}sub _hdlr_search_string { $_[0]->stash('search_string') || '' }sub _hdlr_result_count { my $results = $_[0]->stash('results'); $results && ref($results) eq 'ARRAY' ? scalar @$results : 0;}sub _hdlr_results { my($ctx, $args) = @_; ## If there are no results, return the empty string, knowing ## that the handler for <MTNoSearchResults> will fill in the ## no results message. my $results = $ctx->stash('results') or return ''; my $output = ''; my $build = $ctx->stash('builder'); my $tokens = $ctx->stash('tokens'); for my $res (@$results) { $ctx->stash('entry', $res->{entry}); local $ctx->{__stash}{blog} = $res->{blog}; $ctx->stash('result', $res); local $ctx->{current_timestamp} = $res->{entry}->created_on; defined(my $out = $build->build($ctx, $tokens, { BlogResultHeader => $res->{blogheader} ? 1 : 0 } )) or return $ctx->error( $build->errstr ); $output .= $out; } $output;}sub _hdlr_entry_edit_link { my($ctx, $args) = @_; my $user = $ctx->stash('user') or return ''; my $entry = $ctx->stash('entry') or return $ctx->error(MT->translate( 'You used an [_1] tag outside of the proper context.', '<$MTEntryEditLink$>' )); my $blog_id = $entry->blog_id; my $cfg = MT::ConfigMgr->instance; my $url = $cfg->AdminCGIPath || $cfg->CGIPath; $url .= '/' unless $url =~ m!/$!; require MT::Permission; my $perms = MT::Permission->load({ author_id => $user->id, blog_id => $blog_id }); return '' unless $perms && $perms->can_edit_entry($entry, $user); sprintf q([<a href="%s%s?__mode=view&_type=entry&id=%d&blog_id=%d">Edit</a>]), $url, $cfg->AdminScript, $entry->id, $blog_id;}1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -