| 1 | # A plugin for adding keywords-handling tags |
|---|
| 2 | # |
|---|
| 3 | # Release 0.11 (Mar 20, 2005) |
|---|
| 4 | # |
|---|
| 5 | # This software is provided as-is. You may use it for commercial or |
|---|
| 6 | # personal use. If you distribute it, please keep this notice intact. |
|---|
| 7 | # |
|---|
| 8 | # Copyright (c) 2005 Hirotaka Ogawa |
|---|
| 9 | |
|---|
| 10 | use strict; |
|---|
| 11 | use MT::Template::Context; |
|---|
| 12 | |
|---|
| 13 | eval("use Storable;"); |
|---|
| 14 | if (!$@ && MT->can('add_plugin')) { |
|---|
| 15 | require MT::Plugin; |
|---|
| 16 | my $plugin = new MT::Plugin(); |
|---|
| 17 | $plugin->name("AllKeywords Plugin 0.11"); |
|---|
| 18 | $plugin->description("Add MTAllKeywords tags for listing blog-wide keywords and entry keywords"); |
|---|
| 19 | $plugin->doc_link("http://as-is.net/hacks/2005/03/allkeywords_plugin.html"); |
|---|
| 20 | MT->add_plugin($plugin); |
|---|
| 21 | } |
|---|
| 22 | |
|---|
| 23 | MT::Template::Context->add_container_tag('AllKeywords' => \&all_keywords); |
|---|
| 24 | MT::Template::Context->add_container_tag('EntryAllKeywords' => \&entry_all_keywords); |
|---|
| 25 | MT::Template::Context->add_tag('AllKeyword' => \&all_keyword); |
|---|
| 26 | MT::Template::Context->add_tag('AllKeywordCount' => \&all_keyword_count); |
|---|
| 27 | MT::Template::Context->add_tag('AllKeywordsTotal' => \&all_keywords_total); |
|---|
| 28 | MT::Template::Context->add_tag('AllKeywordsTotalSum' => \&all_keywords_total_sum); |
|---|
| 29 | MT::Template::Context->add_container_tag('EntriesWithKeywords' => \&entries_with_keywords); |
|---|
| 30 | MT::Template::Context->add_container_tag('MostRelatedEntries' => \&most_related_entries); |
|---|
| 31 | |
|---|
| 32 | sub all_keywords { |
|---|
| 33 | my ($ctx, $args, $cond) = @_; |
|---|
| 34 | |
|---|
| 35 | # sort_by option (keyword/count, default = keyword) |
|---|
| 36 | my $sort_by = $args->{sort_by} || 'keyword'; |
|---|
| 37 | # sort_order option (ascend/descend, default = ascend) |
|---|
| 38 | my $sort_order = $args->{sort_order} || 'ascend'; |
|---|
| 39 | # lastn option (default = 0, no cutoff) |
|---|
| 40 | my $lastn = $args->{lastn} || 0; |
|---|
| 41 | # delimiter option (default = space characters) |
|---|
| 42 | my $delimiter = $args->{delimiter} || ''; |
|---|
| 43 | # case_sensitive option (0/1, default = 1) |
|---|
| 44 | my $case_sensitive = $args->{case_sensitive} || 1; |
|---|
| 45 | |
|---|
| 46 | my $blog_id = $ctx->stash('blog_id'); |
|---|
| 47 | require MT::Entry; |
|---|
| 48 | my $iter = MT::Entry->load_iter({ blog_id => $blog_id, |
|---|
| 49 | status => MT::Entry::RELEASE() }); |
|---|
| 50 | my %all_keywords = (); |
|---|
| 51 | while (my $e = $iter->()) { |
|---|
| 52 | next unless $e->keywords; |
|---|
| 53 | my $e_keywords = $case_sensitive ? $e->keywords : lc $e->keywords; |
|---|
| 54 | my @keywords = $delimiter ? |
|---|
| 55 | split($delimiter, $e_keywords) : split(/\s+/, $e_keywords); |
|---|
| 56 | foreach my $keyword (@keywords) { |
|---|
| 57 | if (exists($all_keywords{$keyword})) { |
|---|
| 58 | $all_keywords{$keyword}++; |
|---|
| 59 | } else { |
|---|
| 60 | $all_keywords{$keyword} = 1; |
|---|
| 61 | } |
|---|
| 62 | } |
|---|
| 63 | } |
|---|
| 64 | my $res = ''; |
|---|
| 65 | my $builder = $ctx->stash('builder'); |
|---|
| 66 | my $tokens = $ctx->stash('tokens'); |
|---|
| 67 | |
|---|
| 68 | my @list; |
|---|
| 69 | if ($sort_by eq 'keyword') { |
|---|
| 70 | @list = $sort_order eq 'ascend' ? |
|---|
| 71 | sort { $a cmp $b } keys %all_keywords : |
|---|
| 72 | sort { $b cmp $a } keys %all_keywords; |
|---|
| 73 | } else { |
|---|
| 74 | @list = $sort_order eq 'ascend' ? |
|---|
| 75 | sort { $all_keywords{$a} <=> $all_keywords{$b} } keys %all_keywords : |
|---|
| 76 | sort { $all_keywords{$b} <=> $all_keywords{$a} } keys %all_keywords; |
|---|
| 77 | } |
|---|
| 78 | |
|---|
| 79 | $ctx->stash('all_keywords_total', scalar(@list)); |
|---|
| 80 | |
|---|
| 81 | my $total_sum = 0; |
|---|
| 82 | foreach (@list) { |
|---|
| 83 | $total_sum += $all_keywords{$_}; |
|---|
| 84 | } |
|---|
| 85 | $ctx->stash('all_keywords_total_sum', $total_sum); |
|---|
| 86 | |
|---|
| 87 | my $i = 0; |
|---|
| 88 | foreach (@list) { |
|---|
| 89 | last if $lastn && $i >= $lastn; |
|---|
| 90 | $ctx->stash('all_keyword', $case_sensitive ? $_ : ucfirst $_); |
|---|
| 91 | $ctx->stash('all_keyword_count', $all_keywords{$_}); |
|---|
| 92 | defined(my $out = $builder->build($ctx, $tokens)) |
|---|
| 93 | or return $ctx->error($ctx->errstr); |
|---|
| 94 | $res .= $out; |
|---|
| 95 | $i++; |
|---|
| 96 | } |
|---|
| 97 | $res; |
|---|
| 98 | } |
|---|
| 99 | |
|---|
| 100 | sub entry_all_keywords { |
|---|
| 101 | my ($ctx, $args, $cond) = @_; |
|---|
| 102 | my $e = $ctx->stash('entry') |
|---|
| 103 | or return $ctx->_no_entry_error('MTEntryAllKeywords'); |
|---|
| 104 | return '' unless $e->keywords; |
|---|
| 105 | |
|---|
| 106 | # delimiter option (default = space characters) |
|---|
| 107 | my $delimiter = $args->{delimiter} || ''; |
|---|
| 108 | # case_sensitive option (0/1, default = 1) |
|---|
| 109 | my $case_sensitive = $args->{case_sensitive} || 1; |
|---|
| 110 | |
|---|
| 111 | my $e_keywords = $case_sensitive ? $e->keywords : lc $e->keywords; |
|---|
| 112 | my @keywords = $delimiter ? |
|---|
| 113 | split($delimiter, $e_keywords) : split(/\s+/, $e_keywords); |
|---|
| 114 | my $res = ''; |
|---|
| 115 | my $builder = $ctx->stash('builder'); |
|---|
| 116 | my $tokens = $ctx->stash('tokens'); |
|---|
| 117 | my $total = scalar(@keywords); |
|---|
| 118 | $ctx->stash('all_keywords_total', $total); |
|---|
| 119 | $ctx->stash('all_keywords_total_sum', $total); |
|---|
| 120 | foreach (@keywords) { |
|---|
| 121 | $ctx->stash('all_keyword', $case_sensitive ? $_ : ucfirst $_); |
|---|
| 122 | $ctx->stash('all_keyword_count', 1); |
|---|
| 123 | defined(my $out = $builder->build($ctx, $tokens)) |
|---|
| 124 | or return $ctx->error($ctx->errstr); |
|---|
| 125 | $res .= $out; |
|---|
| 126 | } |
|---|
| 127 | $res; |
|---|
| 128 | } |
|---|
| 129 | |
|---|
| 130 | sub all_keyword { |
|---|
| 131 | $_[0]->stash('all_keyword'); |
|---|
| 132 | } |
|---|
| 133 | |
|---|
| 134 | sub all_keyword_count { |
|---|
| 135 | $_[0]->stash('all_keyword_count'); |
|---|
| 136 | } |
|---|
| 137 | |
|---|
| 138 | sub all_keywords_total { |
|---|
| 139 | $_[0]->stash('all_keywords_total'); |
|---|
| 140 | } |
|---|
| 141 | |
|---|
| 142 | sub all_keywords_total_sum { |
|---|
| 143 | $_[0]->stash('all_keywords_total_sum'); |
|---|
| 144 | } |
|---|
| 145 | |
|---|
| 146 | sub entries_with_keywords { |
|---|
| 147 | my ($ctx, $args, $cond) = @_; |
|---|
| 148 | |
|---|
| 149 | # keywords option (must be specified) |
|---|
| 150 | my $keywords = $args->{keywords} or return ''; |
|---|
| 151 | # delimiter option (default = space characters) |
|---|
| 152 | my $delimiter = $args->{delimiter} || ''; |
|---|
| 153 | # case_sensitive option (0/1, default = 0) |
|---|
| 154 | my $case_sensitive = $args->{case_sensitive} || 0; |
|---|
| 155 | # sort_by option (title/status/created_on/modified_on/author_id/excerpt, default = created_on) |
|---|
| 156 | my $sort_by = $args->{sort_by} || 'created_on'; |
|---|
| 157 | # sort_order option (ascend/descend, default = ascend) |
|---|
| 158 | my $sort_order = $args->{sort_order} || 'descend'; |
|---|
| 159 | # lastn option (default = 0, no cutoff) |
|---|
| 160 | my $lastn = $args->{lastn} || 0; |
|---|
| 161 | |
|---|
| 162 | my $blog_id = $ctx->stash('blog_id'); |
|---|
| 163 | require MT::Entry; |
|---|
| 164 | my $iter = MT::Entry->load_iter({ blog_id => $blog_id, |
|---|
| 165 | status => MT::Entry::RELEASE() }, |
|---|
| 166 | { sort => $sort_by, |
|---|
| 167 | direction => $sort_order }); |
|---|
| 168 | my @entries; |
|---|
| 169 | $keywords = lc $keywords unless $case_sensitive; |
|---|
| 170 | my @patterns = $delimiter ? |
|---|
| 171 | split($delimiter, $keywords) : split(/\s+/, $keywords); |
|---|
| 172 | my $i = 0; |
|---|
| 173 | while (my $e = $iter->()) { |
|---|
| 174 | last if $lastn && $i >= $lastn; |
|---|
| 175 | next unless $e->keywords; |
|---|
| 176 | my $e_keywords = $case_sensitive ? $e->keywords : lc $e->keywords; |
|---|
| 177 | my @keywords = $delimiter ? |
|---|
| 178 | split($delimiter, $e_keywords) : split(/\s+/, $e_keywords); |
|---|
| 179 | my $check = 1; |
|---|
| 180 | foreach my $pattern (@patterns) { |
|---|
| 181 | unless (scalar grep { $_ eq $pattern } @keywords) { |
|---|
| 182 | $check = 0; |
|---|
| 183 | last; |
|---|
| 184 | } |
|---|
| 185 | } |
|---|
| 186 | if ($check) { |
|---|
| 187 | push @entries, $e; |
|---|
| 188 | $i++; |
|---|
| 189 | } |
|---|
| 190 | } |
|---|
| 191 | return '' unless @entries; |
|---|
| 192 | |
|---|
| 193 | my $res = ''; |
|---|
| 194 | my $tokens = $ctx->stash('tokens'); |
|---|
| 195 | my $builder = $ctx->stash('builder'); |
|---|
| 196 | $i = 0; |
|---|
| 197 | for my $e (@entries) { |
|---|
| 198 | local $ctx->{__stash}{entry} = $e; |
|---|
| 199 | local $ctx->{current_timestamp} = $e->created_on; |
|---|
| 200 | local $ctx->{modification_timestamp} = $e->modified_on; |
|---|
| 201 | my $out = $builder->build($ctx, $tokens, { |
|---|
| 202 | %$cond, |
|---|
| 203 | EntryIfExtended => $e->text_more ? 1 : 0, |
|---|
| 204 | EntryIfAllowComments => $e->allow_comments, |
|---|
| 205 | EntryIfCommentsOpen => $e->allow_comments && $e->allow_comments eq '1', |
|---|
| 206 | EntryIfAllowPings => $e->allow_pings, |
|---|
| 207 | EntriesHeader => !$i, |
|---|
| 208 | EntriesFooter => !defined $entries[$i+1] |
|---|
| 209 | }); |
|---|
| 210 | return $ctx->error($ctx->errstr) unless defined $out; |
|---|
| 211 | $res .= $out; |
|---|
| 212 | $i++; |
|---|
| 213 | } |
|---|
| 214 | $res; |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | sub most_related_entries { |
|---|
| 218 | my ($ctx, $args, $cond) = @_; |
|---|
| 219 | my $entry = $ctx->stash('entry') |
|---|
| 220 | or return $ctx->_no_entry_error('MTMostRelatedEntries'); |
|---|
| 221 | return '' unless $entry->keywords; |
|---|
| 222 | |
|---|
| 223 | # delimiter option (default = space characters) |
|---|
| 224 | my $delimiter = $args->{delimiter} || ''; |
|---|
| 225 | # case_sensitive option (0/1, default = 0) |
|---|
| 226 | my $case_sensitive = $args->{case_sensitive} || 0; |
|---|
| 227 | # lastn option (default = 0, no cutoff) |
|---|
| 228 | my $lastn = $args->{lastn} || 0; |
|---|
| 229 | |
|---|
| 230 | my $entry_keywords = $case_sensitive ? $entry->keywords : lc $entry->keywords; |
|---|
| 231 | my @patterns = $delimiter ? |
|---|
| 232 | split($delimiter, $entry_keywords) : split(/\s+/, $entry_keywords); |
|---|
| 233 | |
|---|
| 234 | my $blog_id = $ctx->stash('blog_id'); |
|---|
| 235 | require MT::Entry; |
|---|
| 236 | my $iter = MT::Entry->load_iter({ blog_id => $blog_id, |
|---|
| 237 | status => MT::Entry::RELEASE() }, |
|---|
| 238 | { sort => 'created_on', |
|---|
| 239 | direction => 'descend' }); |
|---|
| 240 | my @entries; |
|---|
| 241 | my %matches = (); |
|---|
| 242 | while (my $e = $iter->()) { |
|---|
| 243 | next unless $e->keywords; |
|---|
| 244 | next if $e->id == $entry->id; |
|---|
| 245 | my $e_keywords = $case_sensitive ? $e->keywords : lc $e->keywords; |
|---|
| 246 | my @keywords = $delimiter ? |
|---|
| 247 | split($delimiter, $e_keywords) : split(/\s+/, $e_keywords); |
|---|
| 248 | my $count = 0; |
|---|
| 249 | foreach my $pattern (@patterns) { |
|---|
| 250 | $count++ if (scalar grep { $_ eq $pattern } @keywords); |
|---|
| 251 | } |
|---|
| 252 | if ($count) { |
|---|
| 253 | push @entries, $e; |
|---|
| 254 | $matches{ $e->id } = $count; |
|---|
| 255 | } |
|---|
| 256 | } |
|---|
| 257 | return '' unless @entries; |
|---|
| 258 | |
|---|
| 259 | @entries = sort { $matches{$b->id} <=> $matches{$a->id} } @entries; |
|---|
| 260 | splice(@entries, $lastn) if $lastn && (scalar @entries > $lastn); |
|---|
| 261 | |
|---|
| 262 | my $res = ''; |
|---|
| 263 | my $tokens = $ctx->stash('tokens'); |
|---|
| 264 | my $builder = $ctx->stash('builder'); |
|---|
| 265 | my $i = 0; |
|---|
| 266 | for my $e (@entries) { |
|---|
| 267 | local $ctx->{__stash}{entry} = $e; |
|---|
| 268 | local $ctx->{current_timestamp} = $e->created_on; |
|---|
| 269 | local $ctx->{modification_timestamp} = $e->modified_on; |
|---|
| 270 | my $out = $builder->build($ctx, $tokens, { |
|---|
| 271 | %$cond, |
|---|
| 272 | EntryIfExtended => $e->text_more ? 1 : 0, |
|---|
| 273 | EntryIfAllowComments => $e->allow_comments, |
|---|
| 274 | EntryIfCommentsOpen => $e->allow_comments && $e->allow_comments eq '1', |
|---|
| 275 | EntryIfAllowPings => $e->allow_pings, |
|---|
| 276 | EntriesHeader => !$i, |
|---|
| 277 | EntriesFooter => !defined $entries[$i+1] |
|---|
| 278 | }); |
|---|
| 279 | return $ctx->error($ctx->errstr) unless defined $out; |
|---|
| 280 | $res .= $out; |
|---|
| 281 | $i++; |
|---|
| 282 | } |
|---|
| 283 | $res; |
|---|
| 284 | } |
|---|
| 285 | |
|---|
| 286 | 1; |
|---|