summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--askbot/conf/external_keys.py22
-rw-r--r--askbot/doc/source/optional-modules.rst8
-rw-r--r--askbot/utils/decorators.py58
-rw-r--r--askbot/utils/functions.py8
-rw-r--r--askbot/views/writers.py20
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: