diff options
-rw-r--r-- | askbot/conf/external_keys.py | 22 | ||||
-rw-r--r-- | askbot/doc/source/optional-modules.rst | 8 | ||||
-rw-r--r-- | askbot/utils/decorators.py | 58 | ||||
-rw-r--r-- | askbot/utils/functions.py | 8 | ||||
-rw-r--r-- | askbot/views/writers.py | 20 |
5 files changed, 108 insertions, 8 deletions
diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index 15ad2903..a652d7fb 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -73,6 +73,28 @@ settings.register( ) ) + + +settings.register( + livesettings.BooleanValue( + EXTERNAL_KEYS, + 'USE_AKISMET', + description=_('Enable Akismet spam detection(keys below are required)'), + default=False, + help_text = _( + 'To get an Akismet key please visit <a href="https://akismet.com/signup/">Akismet site</a>' + ) + ) +) + +settings.register( + livesettings.StringValue( + EXTERNAL_KEYS, + 'AKISMET_API_KEY', + description=_('Akismet key for spam detection') + ) +) + settings.register( livesettings.StringValue( EXTERNAL_KEYS, diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst index 164f2f8c..c18c6aa2 100644 --- a/askbot/doc/source/optional-modules.rst +++ b/askbot/doc/source/optional-modules.rst @@ -136,3 +136,11 @@ To enable authentication for self hosted wordpress sites(wordpress.com blogs wil * Upload an icon for display in the login area. After doing this steps you should be able to login with your self hosted wordpress site user/password combination. + +Akismet spam detection tool +=========================== + +To enable Akismet for spam detection you will need to install `akismet <http://pypi.python.org/pypi/akismet/0.2.0>`_ from pypi and in the live settins for +external keys activate click on "Enable Akismet for spam detection" and enter the Akismet keys below. To get an Akismet key signup into `Akismet and select your plan. <https://akismet.com/signup/>`_ + +Currently it will just block every spam positive content of being posted to the site, including, questions, answers and comments. diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py index f2c86cd5..6dbf021c 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -8,13 +8,17 @@ import logging from django.conf import settings from django.core import exceptions as django_exceptions from django.core import urlresolvers +from django.core.urlresolvers import reverse +from django.core.exceptions import ImproperlyConfigured from django.http import HttpResponse, HttpResponseForbidden, Http404 from django.http import HttpResponseRedirect from django.utils import simplejson from django.utils.translation import ugettext as _ +from django.utils.encoding import smart_str from askbot import exceptions as askbot_exceptions from askbot.conf import settings as askbot_settings from askbot.utils import url_utils +from askbot import get_version def auto_now_timestamp(func): """decorator that will automatically set @@ -33,12 +37,12 @@ def auto_now_timestamp(func): def ajax_login_required(view_func): @functools.wraps(view_func) - def wrap(request,*args,**kwargs): + def wrap(request, *args, **kwargs): if request.user.is_authenticated(): - return view_func(request,*args,**kwargs) + return view_func(request, *args, **kwargs) else: json = simplejson.dumps({'login_required':True}) - return HttpResponseForbidden(json,mimetype='application/json') + return HttpResponseForbidden(json, mimetype='application/json') return wrap @@ -74,14 +78,13 @@ def post_only(view_func): return view_func(request, *args, **kwargs) return wrapper - def ajax_only(view_func): @functools.wraps(view_func) - def wrapper(request,*args,**kwargs): + def wrapper(request, *args, **kwargs): if not request.is_ajax(): raise Http404 try: - data = view_func(request,*args,**kwargs) + data = view_func(request, *args, **kwargs) except Exception, e: message = unicode(e) if message == '': @@ -99,7 +102,7 @@ def ajax_only(view_func): else: data['success'] = 1 json = simplejson.dumps(data) - return HttpResponse(json,mimetype='application/json') + return HttpResponse(json, mimetype='application/json') return wrapper def check_authorization_to_post(func_or_message): @@ -166,3 +169,44 @@ def profile(log_file): return _inner return _outer + +def check_spam(field): + '''Decorator to check if there is spam in the form''' + + def decorator(view_func): + @functools.wraps(view_func) + def wrapper(request, *args, **kwargs): + + if askbot_settings.USE_AKISMET and askbot_settings.AKISMET_API_KEY == "": + raise ImproperlyConfigured('You have not set AKISMET_API_KEY') + + if askbot_settings.USE_AKISMET and request.method == "POST": + comment = smart_str(request.POST[field]) + data = {'user_ip': request.META["REMOTE_ADDR"], + 'user_agent': request.environ['HTTP_USER_AGENT'], + 'comment_author': smart_str(request.user.username), + } + if request.user.is_authenticated(): + data.update({'comment_author_email': request.user.email}) + + from akismet import Akismet + api = Akismet(askbot_settings.AKISMET_API_KEY, + smart_str(askbot_settings.APP_URL), + "Askbot/%s" % get_version()) + + if api.comment_check(comment, data, build_data=False): + logging.debug('Spam detected in %s post at: %s' % + (request.user.username, datetime.datetime.now())) + spam_message = _("Spam was detected on your post, sorry \ + for that if you are not a spammer") + if request.is_ajax(): + return HttpResponseForbidden(spam_message, + mimetype="application/json") + else: + request.user.message_set.create(message=spam_message) + return HttpResponseRedirect(reverse('index')) + + return view_func(request, *args, **kwargs) + return wrapper + + return decorator diff --git a/askbot/utils/functions.py b/askbot/utils/functions.py index a56ed897..3906bb9e 100644 --- a/askbot/utils/functions.py +++ b/askbot/utils/functions.py @@ -2,6 +2,7 @@ import re import datetime from django.utils.translation import ugettext as _ from django.utils.translation import ungettext +from django.contrib.auth.models import User def get_from_dict_or_object(source, key): try: @@ -126,3 +127,10 @@ def setup_paginator(context): "pages_outside_trailing_range": pages_outside_trailing_range, "extend_url" : extend_url } + +def get_admin(): + '''Returns an admin users, usefull for raising flags''' + try: + return User.objects.filter(is_superuser=True)[0] + except: + raise Exception('there is no admin users') diff --git a/askbot/views/writers.py b/askbot/views/writers.py index a2540a90..a4ec0bc1 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -189,6 +189,7 @@ def import_data(request): #@login_required #actually you can post anonymously, but then must register @csrf.csrf_protect @decorators.check_authorization_to_post(_('Please log in to ask questions')) +@decorators.check_spam('text') def ask(request):#view used to ask a new question """a view to ask a new question gives space for q title, body, tags and checkbox for to post as wiki @@ -240,6 +241,18 @@ def ask(request):#view used to ask a new question ) question.save() return HttpResponseRedirect(url_utils.get_login_url()) + else: + form = forms.AskForm(request.POST) + if 'title' in request.GET: + #normally this title is inherited from search query + #but it is possible to ask with a parameter title in the url query + form.initial['title'] = request.GET['title'] + else: + #attempt to extract title from previous search query + search_state = request.session.get('search_state',None) + if search_state: + query = search_state.query + form.initial['title'] = query else: #this branch is for the initial load of ask form form = forms.AskForm() @@ -319,6 +332,7 @@ def retag_question(request, id): @login_required @csrf.csrf_protect +@decorators.check_spam('text') def edit_question(request, id): """edit question view """ @@ -406,6 +420,7 @@ def edit_question(request, id): @login_required @csrf.csrf_protect +@decorators.check_spam('text') def edit_answer(request, id): answer = get_object_or_404(models.Answer, id=id) try: @@ -464,6 +479,7 @@ def edit_answer(request, id): #todo: rename this function to post_new_answer @decorators.check_authorization_to_post(_('Please log in to answer questions')) +@decorators.check_spam('text') def answer(request, id):#process a new answer """view that posts new answer @@ -548,6 +564,7 @@ def __generate_comments_json(obj, user):#non-view generates json data for the po data = simplejson.dumps(json_comments) return HttpResponse(data, mimetype="application/json") +@decorators.check_spam('comment') def post_comments(request):#generic ajax handler to load comments to an object # only support get post comments by ajax now user = request.user @@ -587,6 +604,7 @@ def post_comments(request):#generic ajax handler to load comments to an object raise Http404 @decorators.ajax_only +@decorators.check_spam('text') def edit_comment(request): if request.user.is_authenticated(): comment_id = int(request.POST['comment_id']) @@ -609,7 +627,7 @@ def edit_comment(request): 'user_id': comment.user.id, 'is_deletable': is_deletable, 'is_editable': is_editable, - 'score': comment.score, + 'score': comment.score, 'voted': comment.is_upvoted_by(request.user), } else: |