root/branches/release-40/plugins/TypePadAntiSpam/TypePadAntiSpam.pl @ 2611

Revision 2611, 12.3 kB (checked in by bchoate, 18 months ago)

Fixes for older MT releases. BugId:80017 - pre_save callbacks for MT 3.x. BugId:80020 - count tag fix for MT 3.x, 4.0, 4.1.

Line 
1# TypePad AntiSpam Plugin for Movable Type
2# Copyright (C) 2008, Six Apart, Ltd.
3# Derived from Tim Appnel's MT Akismet plugin for Movable Type.
4# This program is distributed under the terms of the
5# GNU General Public License, version 2.
6#
7# $Id: TypePadAntiSpam.pl 82073 2008-05-29 02:58:04Z fyoshimatsu $
8
9package MT::Plugin::TypePadAntiSpam;
10
11use strict;
12
13use base qw( MT::Plugin );
14our $VERSION = '1.0';
15
16my $plugin;
17{
18    my $settings = [
19        ['api_key', { Scope => 'system'} ],
20        ['weight',  { Default => 1, Scope => 'blog'}],
21        ['service_host',  { Default => 'api.antispam.typepad.com', Scope => 'system'}],
22    ];
23    my $about = {
24        name                   => 'TypePad AntiSpam',
25        id                     => 'typepadantispam',
26        key                    => __PACKAGE__,
27        author_name            => 'Six Apart Ltd.',
28        author_link            => 'http://www.sixapart.com/',
29        version                => $VERSION,
30        blog_config_template   => 'config.tmpl',
31        system_config_template => 'system.tmpl',
32        settings               => MT::PluginSettings->new($settings),
33        l10n_class             => 'TypePadAntiSpam::L10N',
34        registry => {
35            callbacks => {
36                handle_spam => \&handle_junk,
37                handle_ham => \&handle_not_junk,
38                'MT::Comment::pre_save' => \&pre_save_obj,
39                'MT::TBPing::pre_save' => \&pre_save_obj,
40            },
41            junk_filters => {
42                'TypePadAntiSpam' => {
43                    label => 'TypePad AntiSpam',
44                    code => \&typepadantispam_score,
45                },
46            },
47            tags => {
48                function => {
49                    TypePadAntiSpamCounter => \&_hdlr_tpas_counter,
50                },
51            },
52            widgets => {
53                typepadantispam => {
54                    label      => 'TypePad AntiSpam',
55                    template   => 'stats_widget.tmpl',
56                    handler    => \&stats_widget, 
57                    set        => 'sidebar',
58                    singular   => 1,
59                    order      => 2.1,
60                    condition  => sub {
61                        return $plugin->api_key ? 1 : 0;
62                    },
63                },
64            },
65        },
66    };
67    $plugin = __PACKAGE__->new($about);
68}
69MT->add_plugin($plugin);
70if (MT->version_number < 4) {
71    MT->add_callback('HandleJunk',    5, $plugin, \&handle_junk);
72    MT->add_callback('HandleNotJunk', 5, $plugin, \&handle_not_junk);
73    MT->add_callback('MT::Comment::pre_save', 5, $plugin, \&pre_save_obj);
74    MT->add_callback('MT::TBPing::pre_save', 5, $plugin, \&pre_save_obj);
75    MT->register_junk_filter({
76        name => 'TypePadAntiSpam',
77        code => \&typepadantispam_score
78    });
79    require MT::Template::Context;
80    MT::Template::Context->add_tag( TypePadAntiSpamCounter => \&_hdlr_tpas_counter );
81}
82
83#--- plugin handlers
84
85sub instance {
86    return $plugin;
87}
88
89sub description {
90    my $plugin = shift;
91    my $app = MT->instance;
92    my $blog;
93    if ($app->isa('MT::App::CMS')) {
94        $blog = $app->blog;
95    }
96    my $sys_blocked = $plugin->blocked();
97    my $desc = '<p>' . $plugin->translate('TypePad AntiSpam is a free service from Six Apart that helps protect your blog from comment and TrackBack spam. The TypePad AntiSpam plugin will send every comment or TrackBack submitted to your blog to the service for evaluation, and Movable Type will filter items if TypePad AntiSpam determines it is spam. If you discover that TypePad AntiSpam incorrectly classifies an item, simply change its classification by marking it as "Spam" or "Not Spam" from the Manage Comments screen, and TypePad AntiSpam will learn from your actions. Over time the service will improve based on reports from its users, so take care when marking items as "Spam" or "Not Spam."') . '</p>';
98
99    if ($blog) {
100        my $blog_blocked = $blog ? $plugin->blocked($blog) : 0;
101        $desc .= '<p>' . $plugin->translate('So far, TypePad AntiSpam has blocked [quant,_1,message,messages] for this blog, and [quant,_2,message,messages] system-wide.', $blog_blocked, $sys_blocked) . '</p>';
102    } else {
103        $desc .= '<p>' . $plugin->translate('So far, TypePad AntiSpam has blocked [quant,_1,message,messages] system-wide.', $sys_blocked) . '</p>';
104    }
105    return $desc;
106}
107
108sub _hdlr_tpas_counter {
109    my ($ctx, $args, $cond) = @_;
110    my $blog = $ctx->stash('blog');
111    if ($ctx->can('count_format')) {
112        return $ctx->count_format($plugin->blocked($blog), $args);
113    } else {
114        return $plugin->blocked($blog) || 0;
115    }
116}
117
118sub save_config {
119    my $plugin = shift;
120    my ($args, $scope) = @_;
121
122    my $app = MT->instance;
123
124    $scope ||= 'system';
125    if ( $scope eq 'system' ) {
126        my $existing_api_key = $plugin->api_key || '';
127        my $new_api_key = $args->{api_key} || '';
128        if ( ($new_api_key ne '') && ( $new_api_key ne $existing_api_key ) ) {
129            # user assigned a new API key
130            $plugin->require_tpas;
131            local $MT::TypePadAntiSpam::SERVICE_HOST = $args->{service_host}
132                if $args->{service_host};
133            my $url = $app->base . $app->mt_uri;
134            my $res = MT::TypePadAntiSpam->verify( $new_api_key, $url );
135            if ( $res->http_status >= 500 ) {
136                return $plugin->error($plugin->translate("Failed to verify your TypePad AntiSpam API key: [_1]", $res->http_response->message));
137            } elsif ( $res->status != 1 ) {
138                return $plugin->error($plugin->translate("The TypePad AntiSpam API key provided is invalid."));
139            }
140        }
141    }
142
143    my $result = $plugin->SUPER::save_config(@_);
144    return $result if MT->version_number < 4;
145
146    my $user = $app->user;
147    my $blog_id = $app->blog->id if $app->blog;
148
149    my $widget_store = $user->widgets();
150    if ($widget_store && %$widget_store) {
151        my $sys_db = $widget_store->{'dashboard:system'} ||= default_widgets();
152        my $blog_db = $widget_store->{'dashboard:blog:' . $blog_id} ||= default_widgets()
153            if $blog_id;
154
155        my $changed = 0;
156        if ($blog_id && (!exists $blog_db->{'typepadantispam'})) {
157            $blog_db->{'typepadantispam'} =
158                { order => 2.1,  # following mt shortcuts, if shown
159                    set => 'sidebar' };
160            $changed++;
161        }
162        if (!exists $sys_db->{'typepadantispam'}) {
163            $sys_db->{'typepadantispam'} =
164                { order => 2.1,  # following mt shortcuts, if shown
165                    set => 'sidebar' };
166            $changed++;
167        }
168        if ($changed) {
169            $user->widgets( $widget_store );
170            $user->save;
171        }
172    }
173
174    return $result;
175}
176
177sub default_widgets {
178    # FIXME: This should come from the app
179    return {
180        'blog_stats' =>
181          { param => { tab => 'entry' }, order => 1, set => 'main' },
182        'this_is_you-1' => { order => 1, set => 'sidebar' },
183        'mt_shortcuts'  => { order => 2, set => 'sidebar' },
184        'mt_news'       => { order => 3, set => 'sidebar' },
185    };
186}
187
188sub stats_widget {
189    my $app = shift;
190    my ( $tmpl, $param ) = @_;
191    my $blog = $app->blog;
192    if ($blog) {
193        $param->{'blog_blocked'} = $plugin->blocked($blog) || 0;
194    }
195    $param->{'system_blocked'} = $plugin->blocked();
196    $param->{'language'} = lc substr( MT->instance->current_language, 0, 2 );
197    $param->{'use_ssl'} = $app->is_secure;
198    $param->{'api_key'} = $plugin->api_key;
199    return;
200}
201
202sub pre_save_obj {
203    my ($cb, $obj) = @_;
204    if (!$obj->id && $obj->is_junk && ($obj->junk_log =~ m/TypePad AntiSpam says spam/)) {
205        # this was junked due in part to TypePad AntiSpam
206        if (my $blog = $obj->blog) {
207            $plugin->blocked( $blog, $plugin->blocked($blog) + 1 );
208        }
209        # now increment total block count:
210        $plugin->blocked( undef, $plugin->blocked() + 1 );
211    }
212}
213
214sub handle_junk {
215    my ($cb, $app, $thing) = @_;
216    $plugin->require_tpas;
217    my $key    = is_valid_key($thing)       or return;
218    my $sig    = package_signature($thing)  or return;
219    MT::TypePadAntiSpam->submit_spam($sig, $key);
220}
221
222sub handle_not_junk {
223    my ($cb, $app, $thing) = @_;
224    $plugin->require_tpas;
225    my $key    = is_valid_key($thing)       or return;
226    my $sig    = package_signature($thing)  or return;
227    MT::TypePadAntiSpam->submit_ham($sig, $key);
228}
229
230sub typepadantispam_score {
231    my $thing = shift;
232    $plugin->require_tpas;
233    my $key    = is_valid_key($thing)
234        or return MT::JunkFilter::ABSTAIN();
235    my $sig    = package_signature($thing, 1)
236        or return MT::JunkFilter::ABSTAIN();
237    my $res = MT::TypePadAntiSpam->check($sig, $key);
238    my $http_resp = $res->http_response;
239    if ( $http_resp && ! $http_resp->is_success ) {
240        MT->log("TypePad AntiSpam error: " . $http_resp->message);
241    } elsif ( !$http_resp ) {
242        if ( MT::TypePadAntiSpam->errstr ) {
243            MT->log("TypePad AntiSpam error: " . MT::TypePadAntiSpam->errstr );
244        }
245    }
246    return MT::JunkFilter::ABSTAIN()
247        unless $res && $res->http_response->is_success;
248    my $weight = $plugin->get_config_value('weight',
249        'blog:' . $thing->blog_id) || 1;
250    my ($score, $grade) = $res->status
251                        ? ($weight, 'ham')
252                        : (-$weight, 'spam');
253    ($score, ["TypePad AntiSpam says $grade"]);
254}
255
256#--- utility
257
258sub is_valid_key {
259    my $thing = shift;
260    my $r     = MT->request;
261    unless ($r->stash('MT::Plugin::TypePadAntiSpam::api_key')) {
262        my $key = $plugin->api_key || return;
263        $r->stash('MT::Plugin::TypePadAntiSpam::api_key', $key);
264    }
265    $r->stash('MT::Plugin::TypePadAntiSpam::api_key');
266}
267
268sub require_tpas {
269    my $plugin = shift;
270    require MT::TypePadAntiSpam;
271
272    my $host = $plugin->get_config_value('service_host');
273    if ($host) {
274        $MT::TypePadAntiSpam::SERVICE_HOST = $host;
275    }
276}
277
278sub package_signature {
279    my $thing = shift;
280    my ($include_referrer) = @_;
281    my $sig   = MT::TypePadAntiSpam::Signature->new;
282    if ($include_referrer) {
283        $sig->user_agent($ENV{HTTP_USER_AGENT});
284        $sig->referrer($ENV{HTTP_REFERER});
285    }
286    $sig->user_ip($thing->ip);
287    $sig->blog(cache('B' . $thing->blog_id));
288    if (ref $thing eq 'MT::Comment') {
289        my $entry = $thing->entry;
290        $sig->article_date(MT->version_number < 4 ? $entry->created_on : $entry->authored_on);
291        $sig->permalink($entry->permalink);
292        $sig->comment_type('comment');
293        $sig->comment_author($thing->author);
294        $sig->comment_author_email($thing->email);
295        $sig->comment_author_url($thing->url);
296        $sig->comment_content($thing->text);
297    } elsif (ref $thing eq 'MT::TBPing') {
298        my $p = $thing->parent;
299        if ($p->isa('MT::Entry')) {
300            $sig->article_date(MT->version_number < 4 ? $p->created_on : $p->authored_on);
301            $sig->permalink($p->permalink);
302        }
303        $sig->comment_type('trackback');
304        $sig->comment_author($thing->blog_name);
305        $sig->comment_author_url($thing->source_url);
306        $sig->comment_content(join "\n", $thing->title, $thing->excerpt);
307    } else {
308        return;    # don't know what this is.
309    }
310    $sig;
311}
312
313sub cache {
314    my $id    = shift;
315    my $cache = MT->request->stash('MT::Plugin::TypePadAntiSpam::permalinks');
316    unless ($cache) {
317        $cache = {};
318        MT->request->stash('MT::Plugin::TypePadAntiSpam::permalinks', $cache);
319    }
320    unless ($cache->{$id}) {
321        if ($id =~ /^B/) {
322            my $b = MT::Blog->load(substr($id, 1)) or return;
323            $cache->{$id} = $b->site_url;
324        } else {
325            require MT::Entry;
326            my $e = MT::Entry->load($id) or return;
327            $cache->{$id} = $e->permalink;
328        }
329    }
330    $cache->{$id};
331}
332
333sub api_key {
334    my $plugin = shift;
335    my $blog = shift;
336    if (@_) {
337        my $key = shift;
338        $plugin->set_config_value('api_key', $key);
339    } else {
340        return $plugin->get_config_value('api_key');
341    }
342}
343
344sub blocked {
345    my $plugin = shift;
346    my $blog = shift;
347    my $blog_id = (ref($blog) && $blog->isa('MT::Blog')) ? $blog->id : $blog;
348    my $blocked = $plugin->get_config_value('blocked', $blog_id ? 'blog:' . $blog_id : 'system');
349    return $blocked || 0 unless @_;
350    my ($count) = @_;
351    $plugin->set_config_value('blocked', $count, $blog_id ? 'blog:' . $blog_id : 'system');
352    return $count;
353}
354
3551;
Note: See TracBrowser for help on using the browser.