Changeset 93

Show
Ignore:
Timestamp:
06/23/05 12:00:26 (3 years ago)
Author:
ogawa
Message:

Initial development version of Tagwire Plugin.
There're lots of changes from AllKeywords?.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • tagwire/trunk/tagwire.pl

    r92 r93  
    1 # A plugin for adding keywords-handling tags 
     1# Tagwire Plugin (aka AllKeywords Plugin) 
     2# a plugin for listing and handling "tags" 
    23# 
    34# $Id$ 
     
    89# Copyright (c) 2005 Hirotaka Ogawa 
    910 
    10 package MT::Plugin::AllKeywords
     11package MT::Plugin::Tagwire
    1112use strict; 
    1213use MT::Template::Context; 
     
    1617    require MT::Plugin; 
    1718    $plugin = new MT::Plugin(); 
    18     $plugin->name("AllKeywords Plugin"); 
    19     $plugin->description("MTAllKeywords tags for listing and handling blog-wide tags and entry tags. Version 0.14"); 
    20     $plugin->doc_link("http://as-is.net/hacks/2005/03/allkeywords_plugin.html"); 
     19    $plugin->name("Tagwire Plugin"); 
     20    $plugin->description("A plugin for listing and handling blog-wide tags and entry tags."); 
     21#    $plugin->doc_link("http://as-is.net/hacks/2005/03/allkeywords_plugin.html"); 
    2122    MT->add_plugin($plugin); 
    2223}; 
    2324 
    24 MT::Template::Context->add_container_tag('AllKeywords' => \&all_keywords); 
    25 MT::Template::Context->add_container_tag('EntryAllKeywords' => \&entry_all_keywords); 
    26 MT::Template::Context->add_tag('AllKeyword' => \&all_keyword); 
    27 MT::Template::Context->add_tag('AllKeywordCount' => \&all_keyword_count); 
    28 MT::Template::Context->add_tag('AllKeywordsTotal' => \&all_keywords_total); 
    29 MT::Template::Context->add_tag('AllKeywordsTotalSum' => \&all_keywords_total_sum); 
     25if (MT->can('add_callback')) { 
     26  my $mt = MT->instance; 
     27  MT->add_callback((ref $mt eq 'MT::App::CMS' ? 'AppPostEntrySave' : 'MT::Entry::post_save'), 
     28                   10, $plugin, \&update_indexes); 
     29
     30 
     31my $FORCE_REFRESH = 0; 
     32sub update_indexes { 
     33    my ($eh, $app, $entry) = @_; 
     34    return unless $plugin; 
     35    require MT::Entry; 
     36    my $blog_id = $entry->blog_id; 
     37    require MT::PluginData; 
     38    my $pd = MT::PluginData->load({ plugin => $plugin->name, 
     39                                    key => $blog_id }); 
     40    my (%eindex, %tindex); 
     41    my $data; 
     42    if (!$pd || $FORCE_REFRESH) { 
     43        $pd = new MT::PluginData(); 
     44        $pd->plugin($plugin->name); 
     45        $pd->key($blog_id); 
     46        $data = $pd->data() || {}; 
     47        my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
     48                                          status => MT::Entry::RELEASE() }); 
     49        while (my $e = $iter->()) { 
     50            my @tags = split_tags($e->keywords, 1) or next; 
     51            $eindex{$e->id} = { tags => \@tags, 
     52                                created_on => $e->created_on }; 
     53        } 
     54    } else { 
     55        $data = $pd->data() || {}; 
     56        my $entry_id = $entry->id; 
     57        %eindex = %{$data->{eindex}}; 
     58        delete $eindex{$entry_id} if exists $eindex{$entry_id}; 
     59        if ($entry->status == MT::Entry::RELEASE()) { 
     60            my @tags = split_tags($entry->keywords, 1); 
     61            $eindex{$entry_id} = { tags => \@tags, 
     62                                   created_on => $entry->created_on }; 
     63        } 
     64    } 
     65    foreach my $eid (keys %eindex) { 
     66        map { push @{$tindex{$_}}, $eid } @{$eindex{$eid}->{tags}}; 
     67    } 
     68    $data->{eindex} = \%eindex; 
     69    $data->{tindex} = \%tindex; 
     70    $pd->data($data); 
     71    $pd->save or die $pd->errstr; 
     72
     73 
     74MT::Template::Context->add_container_tag('Tags' => \&tags); 
     75MT::Template::Context->add_container_tag('EntryTags' => \&entry_tags); 
     76MT::Template::Context->add_tag('Tag' => \&tag); 
     77MT::Template::Context->add_tag('TagCount' => \&tag_count); 
     78MT::Template::Context->add_tag('TagsTotal' => \&tags_total); 
     79MT::Template::Context->add_tag('TagsTotalSum' => \&tags_total_sum); 
    3080MT::Template::Context->add_container_tag('EntriesWithKeywords' => \&entries_with_keywords); 
    3181MT::Template::Context->add_container_tag('MostRelatedEntries' => \&most_related_entries); 
     82 
     83# For compatibility (this plugin formely named AllKeywords Plugin) 
     84MT::Template::Context->add_container_tag('AllKeywords' => \&tags); 
     85MT::Template::Context->add_container_tag('EntryAllKeywords' => \&entry_tags); 
     86MT::Template::Context->add_tag('AllKeyword' => \&tag); 
     87MT::Template::Context->add_tag('AllKeywordCount' => \&tag_count); 
     88MT::Template::Context->add_tag('AllKeywordsTotal' => \&tags_total); 
     89MT::Template::Context->add_tag('AllKeywordsTotalSum' => \&tags_total_sum); 
    3290 
    3391sub split_args { 
    3492    my ($string, $delimiter, $case_sensitive) = @_; 
    3593    return unless $string; 
    36     my @keywords; 
     94    my @tags; 
    3795    $string =~ s/(^\s+|\s+$)//g; 
    3896    $string = lc $string unless $case_sensitive; 
     
    4098    return split(/\s+/, $string) unless $delimiter; 
    4199 
    42     foreach my $keyword (split($delimiter, $string)) { 
    43         $keyword =~ s/(^\s+|\s+$)//g; 
    44         push @keywords, $keyword if $keyword
    45     } 
    46     @keywords; 
    47 } 
    48  
    49 sub split_keywords { 
     100    foreach my $tag (split($delimiter, $string)) { 
     101        $tag =~ s/(^\s+|\s+$)//g; 
     102        push @tags, $tag if $tag
     103    } 
     104    @tags; 
     105} 
     106 
     107sub split_tags { 
    50108    my ($string, $case_sensitive) = @_; 
    51109    return unless $string; 
    52     my @keywords; 
     110    my @tags; 
    53111    $string =~ s/(^\s+|\s+$)//g; 
    54112    $string = lc $string unless $case_sensitive; 
    55113 
    56114    if ($string =~ m/[;,|]/) { 
    57         # keywords separated by non-whitespaces 
     115        # tags separated by non-whitespaces 
    58116        while ($string =~ m/(\[[^]]+\]|"[^"]+"|'[^']+'|[^;,|]+)/g) { 
    59             my $keyword = $1; 
    60             $keyword =~ s/(^[\["'\s;,|]+|[\]"'\s;,|]+$)//g; 
    61             push @keywords, $keyword if $keyword
    62         } 
    63     } else { 
    64         # keywords separated by whitespaces 
     117            my $tag = $1; 
     118            $tag =~ s/(^[\["'\s;,|]+|[\]"'\s;,|]+$)//g; 
     119            push @tags, $tag if $tag
     120        } 
     121    } else { 
     122        # tags separated by whitespaces 
    65123        while ($string =~ m/(\[[^]]+\]|"[^"]+"|'[^']+'|[^\s]+)/g) { 
    66             my $keyword = $1; 
    67             $keyword =~ s/(^[\["'\s]+|[\]"'\s]+$)//g; 
    68             push @keywords, $keyword if $keyword; 
    69         } 
    70     } 
    71     @keywords; 
    72 
    73  
    74 sub all_keywords { 
     124            my $tag = $1; 
     125            $tag =~ s/(^[\["'\s]+|[\]"'\s]+$)//g; 
     126            push @tags, $tag if $tag; 
     127        } 
     128    } 
     129    @tags; 
     130
     131 
     132use MT::Request; 
     133sub get_pdata { 
     134    return undef unless $plugin; 
     135    my ($blog_id) = @_; 
     136    my $r = MT::Request->instance; 
     137    my $cache = $r->cache('Tagwire::Cache'); 
     138    return $cache->{data} if $cache && ($cache->{blog_id} == $blog_id); 
     139    $r->cache('Tagwire::Cache', undef); 
     140     
     141    my $data; 
     142    eval { 
     143        require MT::PluginData; 
     144        my $pd = MT::PluginData->load({ plugin => $plugin->name, 
     145                                        key => $blog_id }); 
     146        $data = $pd->data() if $pd; 
     147    }; 
     148    if ($data) { 
     149        $cache->{blog_id} = $blog_id; 
     150        $cache->{data} = $data; 
     151        $r->cache('Tagwire::Cache', $cache); 
     152    } 
     153    $data; 
     154
     155 
     156sub tags { 
    75157    my ($ctx, $args, $cond) = @_; 
    76158 
    77     # sort_by option (keyword/keyword-case/count, default = keyword
     159    # sort_by option (tag/tag-case/count, default = tag
    78160    my $sort_by = $args->{sort_by} || 'keyword'; 
    79161    # sort_order option (ascend/descend, default = ascend) 
     
    86168 
    87169    my $blog_id = $ctx->stash('blog_id'); 
    88     require MT::Entry; 
    89     my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
    90                                       status => MT::Entry::RELEASE() }); 
    91     my %all_keywords = (); 
    92     while (my $e = $iter->()) { 
    93         next unless $e->keywords; 
    94         my @keywords = split_keywords($e->keywords, $case_sensitive); 
    95         foreach my $keyword (@keywords) { 
    96             if (exists($all_keywords{$keyword})) { 
    97                 $all_keywords{$keyword}++; 
    98             } else { 
    99                 $all_keywords{$keyword} = 1; 
     170    my %tags = (); 
     171 
     172    my $data = get_pdata($blog_id); 
     173    if ($data) { 
     174        my %tindex = %{$data->{tindex}}; 
     175        if ($case_sensitive) { 
     176            map { $tags{$_} = scalar(@{$tindex{$_}}) } keys %tindex; 
     177        } else { 
     178            map { $tags{lc $_} += scalar(@{$tindex{$_}}) } keys %tindex; 
     179        } 
     180    } else { 
     181        require MT::Entry; 
     182        my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
     183                                          status => MT::Entry::RELEASE() }); 
     184        while (my $e = $iter->()) { 
     185            next unless $e->keywords; 
     186            my @etags = split_tags($e->keywords, $case_sensitive); 
     187            foreach my $etag (@etags) { 
     188                $tags{$etag} = exists $tags{$etag} ? $tags{$etag} + 1 : 1; 
    100189            } 
    101190        } 
     
    103192 
    104193    my @list; 
    105     if ($sort_by eq 'keyword') { 
     194    if ($sort_by eq 'tag' || $sort_by eq 'keyword' ) { 
    106195        @list = $sort_order eq 'ascend' ? 
    107             sort { lc $a cmp lc $b } keys %all_keywords : 
    108             sort { lc $b cmp lc $a } keys %all_keywords; 
    109     } elsif ($sort_by eq 'keyword-case') { 
     196            sort { lc $a cmp lc $b } keys %tags : 
     197            sort { lc $b cmp lc $a } keys %tags; 
     198    } elsif ($sort_by eq 'tag-case' || $sort_by eq 'keyword-case') { 
    110199        @list = $sort_order eq 'ascend' ? 
    111             sort keys %all_keywords : 
    112             sort reverse keys %all_keywords; 
     200            sort keys %tags : 
     201            sort reverse keys %tags; 
    113202    } else { 
    114203        @list = $sort_order eq 'ascend' ? 
    115             sort { $all_keywords{$a} <=> $all_keywords{$b} } keys %all_keywords : 
    116             sort { $all_keywords{$b} <=> $all_keywords{$a} } keys %all_keywords; 
    117     } 
    118  
    119     $ctx->stash('all_keywords_total', scalar(@list)); 
     204            sort { $tags{$a} <=> $tags{$b} } keys %tags : 
     205            sort { $tags{$b} <=> $tags{$a} } keys %tags; 
     206    } 
     207 
     208    $ctx->stash('Tagwire::tags_total', scalar @list); 
    120209 
    121210    my $total_sum = 0; 
    122211    foreach (@list) { 
    123         $total_sum += $all_keywords{$_}; 
    124     } 
    125     $ctx->stash('all_keywords_total_sum', $total_sum); 
     212        $total_sum += $tags{$_}; 
     213    } 
     214    $ctx->stash('Tagwire::tags_total_sum', $total_sum); 
    126215 
    127216    my @res; 
     
    131220    foreach (@list) { 
    132221        last if $lastn && $i >= $lastn; 
    133         $ctx->stash('all_keyword', $case_sensitive ? $_ : ucfirst $_); 
    134         $ctx->stash('all_keyword_count', $all_keywords{$_}); 
     222        $ctx->stash('Tagwire::tag', $case_sensitive ? $_ : ucfirst $_); 
     223        $ctx->stash('Tagwire::tag_count', $tags{$_}); 
    135224        defined(my $out = $builder->build($ctx, $tokens)) 
    136225            or return $ctx->error($ctx->errstr); 
     
    142231} 
    143232 
    144 sub entry_all_keywords { 
     233sub entry_tags { 
    145234    my ($ctx, $args, $cond) = @_; 
    146235    my $e = $ctx->stash('entry') 
    147         or return $ctx->_no_entry_error('MTEntryAllKeywords'); 
     236        or return $ctx->_no_entry_error('MTEntryTas'); 
    148237    return '' unless $e->keywords; 
    149238 
     
    152241        $args->{case_sensitive} : 1; 
    153242 
    154     my @keywords = split_keywords($e->keywords, $case_sensitive); 
    155     my $total = scalar(@keywords); 
    156     $ctx->stash('all_keywords_total', $total); 
    157     $ctx->stash('all_keywords_total_sum', $total); 
     243    my @tags = split_tags($e->keywords, $case_sensitive); 
     244    my $total = scalar(@tags); 
     245    $ctx->stash('Tagwire::tags_total', $total); 
     246    $ctx->stash('Tagwire::tags_total_sum', $total); 
    158247 
    159248    my @res; 
    160249    my $builder = $ctx->stash('builder'); 
    161250    my $tokens = $ctx->stash('tokens'); 
    162     foreach (@keywords) { 
    163         $ctx->stash('all_keyword', $case_sensitive ? $_ : ucfirst $_); 
    164         $ctx->stash('all_keyword_count', 1); 
     251    foreach (@tags) { 
     252        $ctx->stash('Tagwire::tag', $case_sensitive ? $_ : ucfirst $_); 
     253        $ctx->stash('Tagwire::tag_count', 1); 
    165254        defined(my $out = $builder->build($ctx, $tokens)) 
    166255            or return $ctx->error($ctx->errstr); 
     
    171260} 
    172261 
    173 sub all_keyword
    174     $_[0]->stash('all_keyword'); 
    175 } 
    176  
    177 sub all_keyword_count { 
    178     $_[0]->stash('all_keyword_count'); 
    179 } 
    180  
    181 sub all_keywords_total { 
    182     $_[0]->stash('all_keywords_total'); 
    183 } 
    184  
    185 sub all_keywords_total_sum { 
    186     $_[0]->stash('all_keywords_total_sum'); 
     262sub tag
     263    $_[0]->stash('Tagwire::tag'); 
     264} 
     265 
     266sub tag_count { 
     267    $_[0]->stash('Tagwire::tag_count'); 
     268} 
     269 
     270sub tags_total { 
     271    $_[0]->stash('Tagwire::tags_total'); 
     272} 
     273 
     274sub tags_total_sum { 
     275    $_[0]->stash('Tagwire::tags_total_sum'); 
    187276} 
    188277 
     
    194283    # delimiter option (default = space characters) 
    195284    my $delimiter = $args->{delimiter} || ''; 
    196     # case_sensitive option (0/1, default = 0) 
    197     my $case_sensitive = $args->{case_sensitive} || 0; 
     285    # case_sensitive option (0/1, default = 1) 
     286    my $case_sensitive = defined $args->{case_sensitive} ? 
     287        $args->{case_sensitive} : 1; 
    198288    # sort_by option (title/status/created_on/modified_on/author_id/excerpt, default = created_on) 
    199289    my $sort_by = $args->{sort_by} || 'created_on'; 
     
    204294 
    205295    my $blog_id = $ctx->stash('blog_id'); 
    206     require MT::Entry; 
    207     my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
    208                                       status => MT::Entry::RELEASE() }, 
    209                                     { sort => $sort_by, 
    210                                       direction => $sort_order }); 
    211296    my @entries; 
    212297    my @patterns = split_args($keywords, $delimiter, $case_sensitive); 
    213     my $i = 0; 
    214     while (my $e = $iter->()) { 
    215         last if $lastn && $i >= $lastn; 
    216         next unless $e->keywords; 
    217         my @keywords = split_keywords($e->keywords, $case_sensitive); 
    218         my $check = 1; 
    219         foreach my $pattern (@patterns) { 
    220             unless (scalar grep { $_ eq $pattern } @keywords) { 
    221                 $check = 0; 
    222                 last; 
    223             } 
    224         } 
    225         if ($check) { 
    226             push @entries, $e; 
    227             $i++; 
    228         } 
    229     } 
    230     return '' unless @entries; 
    231  
    232     my $res = ''; 
    233     my $tokens = $ctx->stash('tokens'); 
    234     my $builder = $ctx->stash('builder'); 
    235     $i = 0; 
    236     for my $e (@entries) { 
    237         local $ctx->{__stash}{entry} = $e; 
    238         local $ctx->{current_timestamp} = $e->created_on; 
    239         local $ctx->{modification_timestamp} = $e->modified_on; 
    240         my $out = $builder->build($ctx, $tokens, { 
    241             %$cond, 
    242             EntryIfExtended => $e->text_more ? 1 : 0, 
    243             EntryIfAllowComments => $e->allow_comments, 
    244             EntryIfCommentsOpen => $e->allow_comments && $e->allow_comments eq '1', 
    245             EntryIfAllowPings => $e->allow_pings, 
    246             EntriesHeader => !$i, 
    247             EntriesFooter => !defined $entries[$i+1] 
    248             }); 
    249         return $ctx->error($ctx->errstr) unless defined $out; 
    250         $res .= $out; 
    251         $i++; 
    252     } 
    253     $res; 
    254 
    255  
    256 sub most_related_entries { 
    257     my ($ctx, $args, $cond) = @_; 
    258     my $entry = $ctx->stash('entry') 
    259         or return $ctx->_no_entry_error('MTMostRelatedEntries'); 
    260     return '' unless $entry->keywords; 
    261  
    262     # case_sensitive option (0/1, default = 0) 
    263     my $case_sensitive = $args->{case_sensitive} || 0; 
    264     # lastn option (default = 0, no cutoff) 
    265     my $lastn = $args->{lastn} || 0; 
    266  
    267     my @patterns = split_keywords($entry->keywords, $case_sensitive); 
    268  
    269     my $blog_id = $ctx->stash('blog_id'); 
    270     require MT::Entry; 
    271     my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
    272                                       status => MT::Entry::RELEASE() }, 
    273                                     { sort => 'created_on', 
    274                                       direction => 'descend' }); 
    275     my @entries; 
    276     my %matches = (); 
    277     while (my $e = $iter->()) { 
    278         next unless $e->keywords; 
    279         next if $e->id == $entry->id; 
    280         my @keywords = split_keywords($e->keywords, $case_sensitive); 
    281         my $count = 0; 
    282         foreach my $pattern (@patterns) { 
    283             $count++ if (scalar grep { $_ eq $pattern } @keywords); 
    284         } 
    285         if ($count) { 
    286             push @entries, $e; 
    287             $matches{ $e->id } = $count; 
    288         } 
    289     } 
    290     return '' unless @entries; 
    291  
    292     @entries = sort { $matches{$b->id} <=> $matches{$a->id} } @entries; 
    293     splice(@entries, $lastn) if $lastn && (scalar @entries > $lastn); 
     298 
     299    my $data = get_pdata($blog_id); 
     300    if ($data) { 
     301        my %tindex = %{$data->{tindex}}; 
     302        my %eindex = %{$data->{eindex}}; 
     303        my %match; 
     304        if ($case_sensitive) { 
     305            foreach my $tag (@patterns) { 
     306                foreach my $eid (@{$tindex{$tag}}) { 
     307                    $match{$eid} = exists $match{$eid} ? 
     308                        $match{$eid} + 1 : 1; 
     309                } 
     310            } 
     311        } else { 
     312            foreach my $tag (@patterns) { 
     313                foreach my $mtag (grep { lc $_ == $tag } keys %tindex) { 
     314                    foreach my $eid (@{$tindex{$mtag}}) { 
     315                        $match{$eid} = exists $match{$eid} ? 
     316                            $match{$eid} + 1 : 1; 
     317                    } 
     318                } 
     319            } 
     320        } 
     321        my $count = scalar @patterns; 
     322        my @eids = grep { $match{$_} == $count } keys %match or return; 
     323        @eids = sort { $eindex{$b}->{created_on} <=> $eindex{$a}->{created_on} } @eids; 
     324        require MT::Entry; 
     325        map { push @entries, MT::Entry->load($_) } @eids; 
     326        # The above code is sticked to "created_on", "descend". 
     327    } else { 
     328        require MT::Entry; 
     329        my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
     330                                          status => MT::Entry::RELEASE() }, 
     331                                        { sort => $sort_by, 
     332                                          direction => $sort_order }); 
     333        my $i = 0; 
     334        while (my $e = $iter->()) { 
     335            last if $lastn && $i >= $lastn; 
     336            next unless $e->keywords; 
     337            my @tags = split_tags($e->keywords, $case_sensitive); 
     338            my $check = 1; 
     339            foreach my $pattern (@patterns) { 
     340                unless (scalar grep { $_ eq $pattern } @tags) { 
     341                    $check = 0; 
     342                    last; 
     343                } 
     344            } 
     345            if ($check) { 
     346                push @entries, $e; 
     347                $i++; 
     348            } 
     349        } 
     350        return '' unless @entries; 
     351    } 
    294352 
    295353    my $res = ''; 
     
    317375} 
    318376 
     377sub most_related_entries { 
     378    my ($ctx, $args, $cond) = @_; 
     379    my $entry = $ctx->stash('entry') 
     380        or return $ctx->_no_entry_error('MTMostRelatedEntries'); 
     381    return '' unless $entry->keywords; 
     382 
     383    # case_sensitive option (0/1, default = 1) 
     384    my $case_sensitive = defined $args->{case_sensitive} ? 
     385        $args->{case_sensitive} : 1; 
     386    # lastn option (default = 0, no cutoff) 
     387    my $lastn = $args->{lastn} || 0; 
     388 
     389    my @patterns = split_tags($entry->keywords, $case_sensitive) 
     390        or return ''; 
     391 
     392    my $blog_id = $ctx->stash('blog_id'); 
     393    my @entries; 
     394    my %match = (); 
     395 
     396    my $data = get_pdata($blog_id); 
     397    if ($data) { 
     398        my %tindex = %{$data->{tindex}}; 
     399        my %eindex = %{$data->{eindex}}; 
     400        my %match; 
     401        if ($case_sensitive) { 
     402            foreach my $tag (@patterns) { 
     403                foreach my $eid (@{$tindex{$tag}}) { 
     404                    next if $eid == $entry->id; 
     405                    $match{$eid} = exists $match{$eid} ? 
     406                        $match{$eid} + 1 : 1; 
     407                } 
     408            } 
     409        } else { 
     410            foreach my $tag (@patterns) { 
     411                foreach my $mtag (grep { lc $_ == $tag } keys %tindex) { 
     412                    foreach my $eid (@{$tindex{$mtag}}) { 
     413                        next if $eid == $entry->id; 
     414                        $match{$eid} = exists $match{$eid} ? 
     415                            $match{$eid} + 1 : 1; 
     416                    } 
     417                } 
     418            } 
     419        } 
     420        my @eids = keys %match or return ''; 
     421        @eids = sort { $eindex{$b}->{created_on} <=> $eindex{$a}->{created_on} } @eids; 
     422        @eids = sort { $match{$b} <=> $match{$a} } @eids; 
     423        splice(@eids, $lastn) if $lastn && (scalar @eids > $lastn); 
     424        require MT::Entry; 
     425        map { push @entries, MT::Entry->load($_) } @eids; 
     426        return '' unless @entries; 
     427    } else { 
     428        require MT::Entry; 
     429        my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
     430                                          status => MT::Entry::RELEASE() }, 
     431                                        { sort => 'created_on', 
     432                                          direction => 'descend' }); 
     433        while (my $e = $iter->()) { 
     434            next unless $e->keywords; 
     435            next if $e->id == $entry->id; 
     436            my @tags = split_tags($e->keywords, $case_sensitive); 
     437            my $count = 0; 
     438            foreach my $pattern (@patterns) { 
     439                $count++ if (scalar grep { $_ eq $pattern } @tags); 
     440            } 
     441            if ($count) { 
     442                push @entries, $e; 
     443                $match{ $e->id } = $count; 
     444            } 
     445        } 
     446        return '' unless @entries; 
     447 
     448        @entries = sort { $match{$b->id} <=> $match{$a->id} } @entries; 
     449        splice(@entries, $lastn) if $lastn && (scalar @entries > $lastn); 
     450    } 
     451 
     452    my $res = ''; 
     453    my $tokens = $ctx->stash('tokens'); 
     454    my $builder = $ctx->stash('builder'); 
     455    my $i = 0; 
     456    for my $e (@entries) { 
     457        local $ctx->{__stash}{entry} = $e; 
     458        local $ctx->{current_timestamp} = $e->created_on; 
     459        local $ctx->{modification_timestamp} = $e->modified_on; 
     460        my $out = $builder->build($ctx, $tokens, { 
     461            %$cond, 
     462            EntryIfExtended => $e->text_more ? 1 : 0, 
     463            EntryIfAllowComments => $e->allow_comments, 
     464            EntryIfCommentsOpen => $e->allow_comments && $e->allow_comments eq '1', 
     465            EntryIfAllowPings => $e->allow_pings, 
     466            EntriesHeader => !$i, 
     467            EntriesFooter => !defined $entries[$i+1] 
     468            }); 
     469        return $ctx->error($ctx->errstr) unless defined $out; 
     470        $res .= $out; 
     471        $i++; 
     472    } 
     473    $res; 
     474} 
     475 
    319476eval { 
    320477    require MT::XSearch; 
    321     MT::XSearch->add_search_plugin('AllKeywords', { 
    322         label => 'Keyword Search', 
    323         description => 'Keyword Search plugin for MT-XSearch', 
     478    MT::XSearch->add_search_plugin('Tagwire', { 
     479        label => 'Tag(Keyword) Search', 
     480        description => 'Tag(Keyword) Search plugin for MT-XSearch', 
    324481        on_execute => \&xsearch_on_execute, 
    325482        on_stash => \&xsearch_on_stash }); 
     
    340497    my $sort_order = $args->{sort_order} || 'descend'; 
    341498    my $delimiter = $args->{delimiter} || ''; 
    342     my $case_sensitive = $args->{case_sensitive} || 0; 
    343  
     499    my $case_sensitive = defined $args->{case_sensitive} ? 
     500        $args->{case_sensitive} : 1; 
     501 
     502    my @results; 
    344503    my @patterns = split_args($args->{search}, $delimiter, $case_sensitive); 
    345     my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
    346                                       status => MT::Entry::RELEASE() }, 
    347                                     { sort => $sort_by,  
    348                                       direction => $sort_order }); 
    349     my @results; 
    350     while (my $e = $iter->()) { 
    351         next unless $e->keywords; 
    352         my @keywords = split_keywords($e->keywords, $case_sensitive); 
    353         my $check = 1; 
    354         foreach my $pattern (@patterns) { 
    355             unless (scalar grep { $_ eq $pattern } @keywords) { 
    356                 $check = 0; 
    357                 last; 
    358             } 
    359         } 
    360         push @results, $e if $check; 
     504 
     505    my $data = get_pdata($blog_id); 
     506    if ($data) { 
     507        my %tindex = %{$data->{tindex}}; 
     508        my %eindex = %{$data->{eindex}}; 
     509        my %match; 
     510        if ($case_sensitive) { 
     511            foreach my $tag (@patterns) { 
     512                foreach my $eid (@{$tindex{$tag}}) { 
     513                    $match{$eid} = exists $match{$eid} ? 
     514                        $match{$eid} + 1 : 1; 
     515                } 
     516            } 
     517        } else { 
     518            foreach my $tag (@patterns) { 
     519                foreach my $mtag (grep { lc $_ == $tag } keys %tindex) { 
     520                    foreach my $eid (@{$tindex{$mtag}}) { 
     521                        $match{$eid} = exists $match{$eid} ? 
     522                            $match{$eid} + 1 : 1; 
     523                    } 
     524                } 
     525            } 
     526        } 
     527        my $count = scalar @patterns; 
     528        my @eids = grep { $match{$_} == $count } keys %match 
     529            or return; 
     530        @eids = sort { $eindex{$b}->{created_on} <=> $eindex{$a}->{created_on} } @eids; 
     531        require MT::Entry; 
     532        map { push @results, MT::Entry->load($_) } @eids; 
     533        # The above code is sticked to "created_on", "descend". 
     534    } else { 
     535        my $iter = MT::Entry->load_iter({ blog_id => $blog_id, 
     536                                          status => MT::Entry::RELEASE() }, 
     537                                        { sort => $sort_by,  
     538                                          direction => $sort_order }); 
     539        while (my $e = $iter->()) { 
     540            next unless $e->keywords; 
     541            my @tags = split_tags($e->keywords, $case_sensitive) 
     542                or next; 
     543            my $check = 1; 
     544            foreach my $pattern (@patterns) { 
     545                unless (scalar grep { $_ eq $pattern } @tags) { 
     546                    $check = 0; 
     547                    last; 
     548                } 
     549            } 
     550            push @results, $e if $check; 
     551        } 
    361552    } 
    362553    \@results;