Initial commit, mostly a copy of the askismet
[recaptcha_spam_plugin.git] / __init__.py
blob906713531ddb134187dac8bed8aa25ff0c6ffa41
1 # -*- coding: utf-8 -*-
2 from os.path import dirname, join
3 from urllib import urlopen
5 from werkzeug import escape, url_encode
7 import zine
8 from zine.api import *
9 from zine.widgets import Widget
10 from zine.views.admin import flash, render_admin_response
11 from zine.models import COMMENT_BLOCKED_SPAM, Comment
12 from zine.privileges import BLOG_ADMIN, require_privilege
13 from zine.utils.validators import ValidationError, check
14 from zine.utils.http import redirect_to
15 from zine.utils import forms
17 # Check do we have the recaptcha library installed
19 try:
20 import recaptcha
21 have_recaptcha = True
22 except ImportError:
23 have_recaptcha = False
25 USER_AGENT = 'Zine /%s | reCatpcha/1.11' % zine.__version__
26 #AKISMET_URL_BASE = 'rest.akismet.com'
27 #AKISMET_VERSION = '1.1'
28 TEMPLATES = join(dirname(__file__), 'templates')
29 #BLOCKED_MSG = 'blocked by akismet'
32 #: because we need the information about verified keys quite often
33 #: we store verified keys here.
34 _verified_keys = set()
37 class InvalidKey(ValueError):
38 """Raised with a message if the key is invalid."""
40 def __init__(self, message):
41 self.message = message
42 ValueError.__init__(self, message)
45 def is_valid_key(message=None, memorize=False):
46 """A validator that validates keys."""
47 if message is None:
48 message = _('The key is invalid.')
50 def validate(form, apikey):
51 blog_url = get_application().cfg['blog_url']
52 cachekey = (apikey, blog_url)
53 if cachekey in _verified_keys:
54 return
56 data = {'key': apikey, 'blog': blog_url}
57 resp = send_request(apikey, False, data, 'verify-key')
58 if resp is None:
59 raise ValidationError(_('Could not verify key because of a '
60 'server to server connection error.'))
61 elif resp != 'valid':
62 raise ValidationError(message)
63 if memorize:
64 _verified_keys.add(cachekey)
65 return validate
68 def get_akismet_key():
69 """Return the akismet key for the current application or
70 `None` if there is no key or the key is invalid.
71 """
72 app = get_application()
73 key = app.cfg['akismet_spam_filter/apikey']
74 if key and check(is_valid_key, key, memorize=True):
75 return key
78 class ConfigurationForm(forms.Form):
79 """The configuration form."""
80 api_key = forms.TextField(validators=[is_valid_key()])
83 def do_spamcheck(req, comment):
84 """Do spamchecking for all new comments."""
85 # something blocked the comment already. no need to check for
86 # spam then.
87 if comment.blocked:
88 return
90 key = get_akismet_key()
91 if key is None:
92 return
94 data = {
95 'key': apikey,
96 'blog': req.app['blog_url'],
97 'user_ip': comment.submitter_ip,
98 'user_agent': USER_AGENT,
99 'comment_type': 'comment',
100 'comment_author': comment.author,
101 'comment_author_email': comment.email,
102 'comment_author_url': comment.www,
103 'comment_content': comment.body
106 # if we have a request object for testing we can provide some
107 # more information for akismet.
108 if req is not None:
109 data['referrer'] = req.environ.get('HTTP_REFERER', '')
110 for key in 'SERVER_ADDR', 'SERVER_NAME', 'SERVER_PORT', \
111 'SERVER_SOFTWARE', 'HTTP_ACCEPT', 'REMOTE_ADDR':
112 data[key] = req.environ.get(key, '')
114 resp = send_request(apikey, True, data, 'comment-check')
115 if resp == 'true':
116 comment.status = COMMENT_BLOCKED_SPAM
117 comment.blocked_msg = BLOCKED_MSG
120 def add_akismet_link(req, navigation_bar):
121 """Add a button for akismet to the comments page."""
122 if req.user.has_privilege(BLOG_ADMIN):
123 for link_id, url, title, children in navigation_bar:
124 if link_id == 'options':
125 children.insert(-3, ('akismet_spam_filter',
126 url_for('akismet_spam_filter/config'),
127 _('Akismet')))
130 @require_privilege(BLOG_ADMIN)
131 def show_akismet_config(req):
132 """Show the akismet control panel."""
133 form = ConfigurationForm(initial=dict(
134 pubkey=req.app.cfg['akismet_spam_filter/pubkey']
135 privkey=req.app.cfg['akismet_spam_filter/privkey']
138 if req.method == 'POST' and form.validate(req.form):
139 if form.has_changed:
140 req.app.cfg.change_single('recaptcha_spam_filter/pubkey',
141 form['pubkey'])
142 req.app.cfg.change_single('recaptcha_spam_filter/privkey',
143 form['privkey'])
144 if form['api_key']:
145 flash(_('reCAPTCHA has been successfully enabled.'), 'ok')
146 else:
147 flash(_('reCAPTCHA disabled.'), 'ok')
148 return redirect_to('recaptcha_spam_filter/config')
149 return render_admin_response('admin/recaptcha_spam_filter.html',
150 'options.recaptcha_spam_filter',
151 form=form.as_widget())
154 #class AkismetBlockedCommentsCounterWidget(Widget):
155 # NAME = 'get_akismet_blocked_comments'
156 # TEMPLATE = 'akismet_widget.html'
158 # def __init__(self, show_title=False, title='Akismet Blocked Comments'):
159 # self.show_title = show_title
160 # self.title = title
161 # self.spam_comments = Comment.objects.filter(
162 # Comment.blocked_msg == BLOCKED_MSG).count()
164 # @staticmethod
165 # def get_display_name():
166 # return _('Comments Blocked by Akismet')
169 def setup(app, plugin):
170 if not have_recaptcha:
171 raise SetupError("The reCAPTCHA plugin requires the recaptcha python library")
172 app.add_config_var('recaptcha_spam_filter/pubkey',
173 forms.TextField(default=u''))
174 app.add_config_var('recaptcha_spam_filter/privkey',
175 forms.TextField(default=u''))
176 app.add_url_rule('/options/recaptcha', prefix='admin',
177 endpoint='recaptcha/config',
178 view=show_akismet_config)
179 app.connect_event('before-comment-saved', do_spamcheck)
180 app.connect_event('modify-admin-navigation-bar', add_akismet_link)
181 app.add_template_searchpath(TEMPLATES)
182 #app.add_widget(AkismetBlockedCommentsCounterWidget)