From 468bd2543c715070abed0adf8f2f6f8008ad43ab Mon Sep 17 00:00:00 2001 From: Adolfo Fitoria Date: Fri, 9 Sep 2011 17:09:28 -0300 Subject: added akismet support --- askbot/conf/external_keys.py | 19 +++++++++++++++++++ askbot/utils/decorators.py | 32 +++++++++++++++++++++++++++++++- askbot/utils/functions.py | 8 ++++++++ askbot/views/writers.py | 20 +++++++++++++++++++- 4 files changed, 77 insertions(+), 2 deletions(-) diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index 15ad2903..a2dc3094 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -73,6 +73,25 @@ settings.register( ) ) + + +settings.register( + livesettings.BooleanValue( + EXTERNAL_KEYS, + 'USE_AKISMET', + description=_('Enable Akismet spam detection(keys below are required)'), + default=False + ) +) + +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/utils/decorators.py b/askbot/utils/decorators.py index f2c86cd5..352c5497 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -8,13 +8,16 @@ 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.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 @@ -74,7 +77,6 @@ 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): @@ -166,3 +168,31 @@ 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 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): + #if True: + request.user.message_set.create(message=_("Spam was detected on your post, sorry for that if you are not a spammer")) + 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: -- cgit v1.2.3-1-g7c22 From e0a3ec541f37436319ea5da3f499deacb5980725 Mon Sep 17 00:00:00 2001 From: Adolfo Fitoria Date: Mon, 12 Sep 2011 09:47:51 -0300 Subject: Finished spam detection with akismet, added documentation about the topic --- askbot/conf/external_keys.py | 5 ++++- askbot/doc/source/optional-modules.rst | 8 ++++++++ askbot/utils/decorators.py | 36 +++++++++++++++++++++++----------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index a2dc3094..a652d7fb 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -80,7 +80,10 @@ settings.register( EXTERNAL_KEYS, 'USE_AKISMET', description=_('Enable Akismet spam detection(keys below are required)'), - default=False + default=False, + help_text = _( + 'To get an Akismet key please visit Akismet site' + ) ) ) 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 `_ 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. `_ + +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 352c5497..6dbf021c 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -9,6 +9,7 @@ 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 @@ -36,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 @@ -79,11 +80,11 @@ def post_only(view_func): 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 == '': @@ -101,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): @@ -175,7 +176,11 @@ def check_spam(field): def decorator(view_func): @functools.wraps(view_func) def wrapper(request, *args, **kwargs): - if askbot_settings.USE_AKISMET and request.method=="POST": + + 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'], @@ -185,12 +190,21 @@ def check_spam(field): 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()) + 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): - #if True: - request.user.message_set.create(message=_("Spam was detected on your post, sorry for that if you are not a spammer")) - return HttpResponseRedirect(reverse('index')) + 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 -- cgit v1.2.3-1-g7c22 From 30c3f79e66d9fcf3c8e392c398dbf4bfc4a89133 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Wed, 14 Sep 2011 13:24:44 -0300 Subject: fixed paginator floating issues --- askbot/skins/default/templates/macros.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/askbot/skins/default/templates/macros.html b/askbot/skins/default/templates/macros.html index 36ab9692..7ddbd70f 100644 --- a/askbot/skins/default/templates/macros.html +++ b/askbot/skins/default/templates/macros.html @@ -138,8 +138,7 @@ {%- macro paginator(p, position='left') -%}{# p is paginator context dictionary #} {% spaceless %} {% if p.is_paginated %} -
-
+
{% if p.has_previous %} « {% trans %}previous{% endtrans %} @@ -169,7 +168,6 @@ {% trans %}next page{% endtrans %} » {% endif %}
-
{% endif %} {% endspaceless %} {%- endmacro -%} -- cgit v1.2.3-1-g7c22 From 4f2f40eb73294c143014f50292601cdf7a4fd17b Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Wed, 14 Sep 2011 15:54:37 -0300 Subject: maintenance edits - moved more external urls to consts and pylinted a number of files --- askbot/conf/external_keys.py | 52 ++++++++++++++-------------- askbot/const/__init__.py | 62 ++++++++++++++++++++-------------- askbot/doc/source/optional-modules.rst | 8 ----- askbot/startup_procedures.py | 1 + askbot/utils/decorators.py | 28 +++++++++------ askbot/utils/functions.py | 16 ++++++--- askbot/views/writers.py | 4 +-- askbot_requirements.txt | 1 + setup.py | 1 + 9 files changed, 99 insertions(+), 74 deletions(-) diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index a652d7fb..c5138be0 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -1,6 +1,7 @@ """ External service key settings """ +from askbot import const from askbot.conf.settings_wrapper import settings from askbot.deps import livesettings from django.utils.translation import ugettext as _ @@ -8,7 +9,8 @@ from django.conf import settings as django_settings EXTERNAL_KEYS = livesettings.ConfigurationGroup( 'EXTERNAL_KEYS', - _('Keys to connect the site with external services like Facebook, etc.') + _('Keys to connect the site with external ' + 'services like Facebook, etc.') ) settings.register( @@ -19,11 +21,12 @@ settings.register( help_text=_( 'This key helps google index your site ' 'please obtain is at ' - '' + '' 'google webmasters tools site' - ) % {'google_webmasters_tools_url': - 'https://www.google.com/webmasters/tools/home?hl=' \ - + django_settings.LANGUAGE_CODE} + ) % { + 'url': const.DEPENDENCY_URLS['google-webmaster-tools'], + 'lang': django_settings.LANGUAGE_CODE, + } ) ) @@ -33,12 +36,12 @@ settings.register( 'GOOGLE_ANALYTICS_KEY', description=_('Google Analytics key'), help_text=_( - 'Obtain is at ' - 'Google Analytics site, if you ' - 'wish to use Google Analytics to monitor ' - 'your site' - ) % {'ga_site':'http://www.google.com/intl/%s/analytics/' \ - % django_settings.LANGUAGE_CODE } + 'Obtain is at ' + 'Google Analytics site, if you ' + 'wish to use Google Analytics to monitor ' + 'your site' + ) % {'url': 'http://www.google.com/intl/%s/analytics/' \ + % django_settings.LANGUAGE_CODE } ) ) @@ -68,13 +71,11 @@ settings.register( 'Recaptcha is a tool that helps distinguish ' 'real people from annoying spam robots. ' 'Please get this and a public key at ' - 'the http://google.com/recaptcha' - ) + 'the %(url)s' + ) % {'url': const.DEPENDENCY_URLS['recaptcha']} ) ) - - settings.register( livesettings.BooleanValue( EXTERNAL_KEYS, @@ -82,8 +83,9 @@ settings.register( description=_('Enable Akismet spam detection(keys below are required)'), default=False, help_text = _( - 'To get an Akismet key please visit Akismet site' - ) + 'To get an Akismet key please visit ' + 'Akismet site' + ) % {'url': const.DEPENDENCY_URLS['akismet']} ) ) @@ -104,9 +106,9 @@ settings.register( 'Facebook API key and Facebook secret ' 'allow to use Facebook Connect login method ' 'at your site. Please obtain these keys ' - 'at ' + 'at ' 'facebook create app site' - ) + ) % {'url': const.DEPENDENCY_URLS['facebook-apps']} ) ) @@ -124,9 +126,9 @@ settings.register( 'TWITTER_KEY', description=_('Twitter consumer key'), help_text=_( - 'Please register your forum at ' + 'Please register your forum at ' 'twitter applications site' - ), + ) % {'url': const.DEPENDENCY_URLS['twitter-apps']}, ) ) @@ -145,9 +147,9 @@ settings.register( 'LINKEDIN_KEY', description=_('LinkedIn consumer key'), help_text=_( - 'Please register your forum at ' + 'Please register your forum at ' 'LinkedIn developer site' - ), + ) % {'url': const.DEPENDENCY_URLS['linkedin-apps']}, ) ) @@ -166,9 +168,9 @@ settings.register( 'IDENTICA_KEY', description=_('ident.ca consumer key'), help_text=_( - 'Please register your forum at ' + 'Please register your forum at ' 'Identi.ca applications site' - ), + ) % {'url': const.DEPENDENCY_URLS['identica-apps']}, ) ) diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py index e62dd2cb..d1449dd8 100644 --- a/askbot/const/__init__.py +++ b/askbot/const/__init__.py @@ -1,10 +1,11 @@ # encoding:utf-8 -from django.utils.translation import ugettext as _ -import re """ All constants could be used in other modules -For reasons that models, views can't have unicode text in this project, all unicode text go here. +For reasons that models, views can't have unicode +text in this project, all unicode text go here. """ +from django.utils.translation import ugettext as _ +import re CLOSE_REASONS = ( (1, _('duplicate question')), (2, _('question is off-topic or not relevant')), @@ -48,7 +49,8 @@ POST_SORT_METHODS = ( ('relevance-desc', _('relevance')), ) #todo: add assertion here that all sort methods are unique -#because they are keys to the hash used in implementations of Q.run_advanced_search +#because they are keys to the hash used in implementations +#of Q.run_advanced_search DEFAULT_POST_SORT_METHOD = 'activity-desc' POST_SCOPE_LIST = ( @@ -91,28 +93,28 @@ TAG_REGEX = r'^[%s]+$' % TAG_CHARS TAG_SPLIT_REGEX = r'[ ,]+' EMAIL_REGEX = re.compile(r'\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b', re.I) -TYPE_ACTIVITY_ASK_QUESTION=1 -TYPE_ACTIVITY_ANSWER=2 -TYPE_ACTIVITY_COMMENT_QUESTION=3 -TYPE_ACTIVITY_COMMENT_ANSWER=4 -TYPE_ACTIVITY_UPDATE_QUESTION=5 -TYPE_ACTIVITY_UPDATE_ANSWER=6 -TYPE_ACTIVITY_PRIZE=7 -TYPE_ACTIVITY_MARK_ANSWER=8 -TYPE_ACTIVITY_VOTE_UP=9 -TYPE_ACTIVITY_VOTE_DOWN=10 -TYPE_ACTIVITY_CANCEL_VOTE=11 -TYPE_ACTIVITY_DELETE_QUESTION=12 -TYPE_ACTIVITY_DELETE_ANSWER=13 -TYPE_ACTIVITY_MARK_OFFENSIVE=14 -TYPE_ACTIVITY_UPDATE_TAGS=15 -TYPE_ACTIVITY_FAVORITE=16 +TYPE_ACTIVITY_ASK_QUESTION = 1 +TYPE_ACTIVITY_ANSWER = 2 +TYPE_ACTIVITY_COMMENT_QUESTION = 3 +TYPE_ACTIVITY_COMMENT_ANSWER = 4 +TYPE_ACTIVITY_UPDATE_QUESTION = 5 +TYPE_ACTIVITY_UPDATE_ANSWER = 6 +TYPE_ACTIVITY_PRIZE = 7 +TYPE_ACTIVITY_MARK_ANSWER = 8 +TYPE_ACTIVITY_VOTE_UP = 9 +TYPE_ACTIVITY_VOTE_DOWN = 10 +TYPE_ACTIVITY_CANCEL_VOTE = 11 +TYPE_ACTIVITY_DELETE_QUESTION = 12 +TYPE_ACTIVITY_DELETE_ANSWER = 13 +TYPE_ACTIVITY_MARK_OFFENSIVE = 14 +TYPE_ACTIVITY_UPDATE_TAGS = 15 +TYPE_ACTIVITY_FAVORITE = 16 TYPE_ACTIVITY_USER_FULL_UPDATED = 17 TYPE_ACTIVITY_EMAIL_UPDATE_SENT = 18 TYPE_ACTIVITY_MENTION = 19 TYPE_ACTIVITY_UNANSWERED_REMINDER_SENT = 20 -#TYPE_ACTIVITY_EDIT_QUESTION=17 -#TYPE_ACTIVITY_EDIT_ANSWER=18 +#TYPE_ACTIVITY_EDIT_QUESTION = 17 +#TYPE_ACTIVITY_EDIT_ANSWER = 18 #todo: rename this to TYPE_ACTIVITY_CHOICES TYPE_ACTIVITY = ( @@ -250,11 +252,21 @@ DEFAULT_USER_STATUS = 'w' #number of items to show in user views USER_VIEW_DATA_SIZE = 50 +#not really dependency, but external links, which it would +#be nice to test for correctness from time to time DEPENDENCY_URLS = { - 'mathjax': 'http://www.mathjax.org/resources/docs/?installation.html', + 'akismet': 'https://akismet.com/signup/', + 'cc-by-sa': 'http://creativecommons.org/licenses/by-sa/3.0/legalcode', + 'embedding-video': \ + 'http://askbot.org/doc/optional-modules.html#embedding-video', 'favicon': 'http://en.wikipedia.org/wiki/Favicon', - 'embedding-video': 'http://askbot.org/doc/optional-modules.html#embedding-video', - 'cc-by-sa': 'http://creativecommons.org/licenses/by-sa/3.0/legalcode' + 'facebook-apps': 'http://www.facebook.com/developers/createapp.php', + 'google-webmaster-tools': 'https://www.google.com/webmasters/tools/home', + 'identica-apps': 'http://identi.ca/settings/oauthapps', + 'linkedin-apps': 'https://www.linkedin.com/secure/developer', + 'mathjax': 'http://www.mathjax.org/resources/docs/?installation.html', + 'recaptcha': 'http://google.com/recaptcha', + 'twitter-apps': 'http://dev.twitter.com/apps/', } PASSWORD_MIN_LENGTH = 8 diff --git a/askbot/doc/source/optional-modules.rst b/askbot/doc/source/optional-modules.rst index c18c6aa2..164f2f8c 100644 --- a/askbot/doc/source/optional-modules.rst +++ b/askbot/doc/source/optional-modules.rst @@ -136,11 +136,3 @@ 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 `_ 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. `_ - -Currently it will just block every spam positive content of being posted to the site, including, questions, answers and comments. diff --git a/askbot/startup_procedures.py b/askbot/startup_procedures.py index 4fb41eb6..cc7f86c0 100644 --- a/askbot/startup_procedures.py +++ b/askbot/startup_procedures.py @@ -136,6 +136,7 @@ def try_import(module_name, pypi_package_name): def test_modules(): """tests presence of required modules""" + try_import('akismet', 'akismet') try_import('recaptcha_works', 'django-recaptcha-works') def test_postgres(): diff --git a/askbot/utils/decorators.py b/askbot/utils/decorators.py index 6dbf021c..29e92645 100644 --- a/askbot/utils/decorators.py +++ b/askbot/utils/decorators.py @@ -7,7 +7,6 @@ import inspect 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 @@ -190,18 +189,27 @@ def check_spam(field): 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()) + 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") + 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 if this is a mistake' + ) if request.is_ajax(): - return HttpResponseForbidden(spam_message, - mimetype="application/json") + return HttpResponseForbidden( + spam_message, + mimetype="application/json" + ) else: request.user.message_set.create(message=spam_message) return HttpResponseRedirect(reverse('index')) diff --git a/askbot/utils/functions.py b/askbot/utils/functions.py index 3906bb9e..d31d9027 100644 --- a/askbot/utils/functions.py +++ b/askbot/utils/functions.py @@ -8,7 +8,7 @@ def get_from_dict_or_object(source, key): try: return source[key] except: - return getattr(source,key) + return getattr(source, key) def is_iterable(thing): @@ -54,7 +54,7 @@ def not_a_robot_request(request): return False -def diff_date(date, limen=2, use_on_prefix = False): +def diff_date(date, use_on_prefix = False): now = datetime.datetime.now()#datetime(*time.localtime()[0:6])#??? diff = now - date days = diff.days @@ -75,9 +75,17 @@ def diff_date(date, limen=2, use_on_prefix = False): elif days == 1: return _('yesterday') elif minutes >= 60: - return ungettext('%(hr)d hour ago','%(hr)d hours ago',hours) % {'hr':hours} + return ungettext( + '%(hr)d hour ago', + '%(hr)d hours ago', + hours + ) % {'hr':hours} else: - return ungettext('%(min)d min ago','%(min)d mins ago',minutes) % {'min':minutes} + return ungettext( + '%(min)d min ago', + '%(min)d mins ago', + minutes + ) % {'min':minutes} #todo: this function may need to be removed to simplify the paginator functionality LEADING_PAGE_RANGE_DISPLAYED = TRAILING_PAGE_RANGE_DISPLAYED = 5 diff --git a/askbot/views/writers.py b/askbot/views/writers.py index a4ec0bc1..fcc98761 100644 --- a/askbot/views/writers.py +++ b/askbot/views/writers.py @@ -249,7 +249,7 @@ def ask(request):#view used to ask a new question form.initial['title'] = request.GET['title'] else: #attempt to extract title from previous search query - search_state = request.session.get('search_state',None) + search_state = request.session.get('search_state', None) if search_state: query = search_state.query form.initial['title'] = query @@ -262,7 +262,7 @@ def ask(request):#view used to ask a new question form.initial['title'] = request.GET['title'] else: #attempt to extract title from previous search query - search_state = request.session.get('search_state',None) + search_state = request.session.get('search_state', None) if search_state: query = search_state.query form.initial['title'] = query diff --git a/askbot_requirements.txt b/askbot_requirements.txt index 70bc5874..a1d3b603 100644 --- a/askbot_requirements.txt +++ b/askbot_requirements.txt @@ -1,3 +1,4 @@ +akismet django>=1.1.2 Jinja2 Coffin>=0.3 diff --git a/setup.py b/setup.py index d319d761..71ed2abb 100644 --- a/setup.py +++ b/setup.py @@ -7,6 +7,7 @@ import sys #you might want to install django-debug-toolbar as well install_requires = [ + 'akismet', 'django>=1.1.2', 'Jinja2', 'Coffin>=0.3', -- cgit v1.2.3-1-g7c22 From 9f39c00579d5f71e1afe81c70b419f6f7bb441cc Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Wed, 14 Sep 2011 16:21:19 -0300 Subject: moved akismet settings to "Spam and moderation" --- askbot/conf/__init__.py | 1 + askbot/conf/external_keys.py | 25 +------------------------ askbot/conf/spam_and_moderation.py | 32 ++++++++++++++++++++++++++++++++ askbot/doc/source/changelog.rst | 1 + 4 files changed, 35 insertions(+), 24 deletions(-) create mode 100644 askbot/conf/spam_and_moderation.py diff --git a/askbot/conf/__init__.py b/askbot/conf/__init__.py index d51be698..1d2d7240 100644 --- a/askbot/conf/__init__.py +++ b/askbot/conf/__init__.py @@ -13,6 +13,7 @@ import askbot.conf.skin_general_settings import askbot.conf.sidebar_main import askbot.conf.sidebar_question import askbot.conf.sidebar_profile +import askbot.conf.spam_and_moderation import askbot.conf.user_settings import askbot.conf.markup import askbot.conf.social_sharing diff --git a/askbot/conf/external_keys.py b/askbot/conf/external_keys.py index c5138be0..8912b0ff 100644 --- a/askbot/conf/external_keys.py +++ b/askbot/conf/external_keys.py @@ -1,6 +1,4 @@ -""" -External service key settings -""" +"""External service key settings""" from askbot import const from askbot.conf.settings_wrapper import settings from askbot.deps import livesettings @@ -76,27 +74,6 @@ 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 ' - 'Akismet site' - ) % {'url': const.DEPENDENCY_URLS['akismet']} - ) -) - -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/conf/spam_and_moderation.py b/askbot/conf/spam_and_moderation.py new file mode 100644 index 00000000..375fbdd5 --- /dev/null +++ b/askbot/conf/spam_and_moderation.py @@ -0,0 +1,32 @@ +"""Settings for content moderation and spam control""" +from django.utils.translation import ugettext as _ +from askbot import const +from askbot.deps import livesettings +from askbot.conf.settings_wrapper import settings + +SPAM_AND_MODERATION = livesettings.ConfigurationGroup( + 'SPAM_AND_MODERATION', + _('Spam control and content moderation') + ) + +settings.register( + livesettings.BooleanValue( + SPAM_AND_MODERATION, + 'USE_AKISMET', + description=_('Enable Akismet spam detection(keys below are required)'), + default=False, + help_text = _( + 'To get an Akismet key please visit ' + 'Akismet site' + ) % {'url': const.DEPENDENCY_URLS['akismet']} + ) +) + +settings.register( + livesettings.StringValue( + SPAM_AND_MODERATION, + 'AKISMET_API_KEY', + description=_('Akismet key for spam detection') + ) +) + diff --git a/askbot/doc/source/changelog.rst b/askbot/doc/source/changelog.rst index ade7421c..c1eaa158 100644 --- a/askbot/doc/source/changelog.rst +++ b/askbot/doc/source/changelog.rst @@ -3,6 +3,7 @@ Changes in Askbot Development version ------------------- +* Added support for Akismet spam detection service (Adolfo Fitoria) * Added noscript message (Arun SAG) * Support for url shortening with TinyUrl on link sharing (Rtnpro) * Allowed logging in with password and email in the place of login name (Evgeny) -- cgit v1.2.3-1-g7c22 From 2464b782aacd1bd7083b5169a520008631f69c03 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Thu, 15 Sep 2011 12:09:32 -0300 Subject: partial fix of login problem when password form is always on --- askbot/skins/default/media/jquery-openid/jquery.openid.js | 15 +++++++++++++++ .../templates/authopenid/providers_javascript.html | 7 +------ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/askbot/skins/default/media/jquery-openid/jquery.openid.js b/askbot/skins/default/media/jquery-openid/jquery.openid.js index 4af89369..7ba9adce 100644 --- a/askbot/skins/default/media/jquery-openid/jquery.openid.js +++ b/askbot/skins/default/media/jquery-openid/jquery.openid.js @@ -298,9 +298,21 @@ $.fn.authenticator = function() { }; var start_password_login_or_change = function(){ + //called upon clicking on one of the password login buttons reset_form(); set_provider_name($(this)); var provider_name = $(this).attr('name'); + return setup_password_login_or_change(provider_name); + }; + + var init_always_visible_password_login = function(){ + reset_form(); + //will break wordpress and ldap + provider_name_input.val('local'); + setup_password_login_or_change('local'); + }; + + var setup_password_login_or_change = function(provider_name){ var token_name = extra_token_name[provider_name] var password_action_input = $('input[name=password_action]'); if (userIsAuthenticated === true){ @@ -427,6 +439,9 @@ $.fn.authenticator = function() { }; setup_default_handlers(); + if (askbot['settings']['signin_always_show_local_login'] === true){ + init_always_visible_password_login(); + } clear_password_fields(); return this; }; diff --git a/askbot/skins/default/templates/authopenid/providers_javascript.html b/askbot/skins/default/templates/authopenid/providers_javascript.html index 85e55e9f..0fe72eb3 100644 --- a/askbot/skins/default/templates/authopenid/providers_javascript.html +++ b/askbot/skins/default/templates/authopenid/providers_javascript.html @@ -34,13 +34,8 @@ {% else %} var userIsAuthenticated = false; {% endif %} - $("body").authenticator(); - {% if settings.SIGNIN_ALWAYS_SHOW_LOCAL_LOGIN %} - {% if settings.SIGNIN_LOCAL_ENABLED %} - $('input.password').remove(); - {% endif %} - {%endif%} askbot['settings']['signin_always_show_local_login'] = {% if settings.SIGNIN_ALWAYS_SHOW_LOCAL_LOGIN %}true{% else %}false{% endif %}; + $("body").authenticator(); {% if settings.FACEBOOK_KEY and settings.FACEBOOK_SECRET %}
-- cgit v1.2.3-1-g7c22 From b15c131f8f1806359d3e9e956f561b2a0a248569 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sat, 17 Sep 2011 11:37:57 -0300 Subject: removed incorrect localization of a message --- askbot/locale/en/LC_MESSAGES/django.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/askbot/locale/en/LC_MESSAGES/django.po b/askbot/locale/en/LC_MESSAGES/django.po index 5bb75eca..a1548a93 100644 --- a/askbot/locale/en/LC_MESSAGES/django.po +++ b/askbot/locale/en/LC_MESSAGES/django.po @@ -499,7 +499,7 @@ msgstr "" #: conf/email.py:22 msgid "Prefix for the email subject line" -msgstr "Welcome to the Q&A forum" +msgstr "" #: conf/email.py:24 msgid "" -- cgit v1.2.3-1-g7c22 From 094b354a0725c0d75c1565541e5fed50f9950ed8 Mon Sep 17 00:00:00 2001 From: Evgeny Fadeev Date: Sat, 17 Sep 2011 13:18:14 -0300 Subject: added url explaining how to enable cookies in the browser --- askbot/const/__init__.py | 1 + askbot/context.py | 4 +++- askbot/skins/default/templates/blocks/bottom_scripts.html | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/askbot/const/__init__.py b/askbot/const/__init__.py index d1449dd8..73c71800 100644 --- a/askbot/const/__init__.py +++ b/askbot/const/__init__.py @@ -263,6 +263,7 @@ DEPENDENCY_URLS = { 'facebook-apps': 'http://www.facebook.com/developers/createapp.php', 'google-webmaster-tools': 'https://www.google.com/webmasters/tools/home', 'identica-apps': 'http://identi.ca/settings/oauthapps', + 'noscript': 'https://www.google.com/support/bin/answer.py?answer=23852', 'linkedin-apps': 'https://www.linkedin.com/secure/developer', 'mathjax': 'http://www.mathjax.org/resources/docs/?installation.html', 'recaptcha': 'http://google.com/recaptcha', diff --git a/askbot/context.py b/askbot/context.py index 5a174585..6dc38f79 100644 --- a/askbot/context.py +++ b/askbot/context.py @@ -5,6 +5,7 @@ and the application available for the templates from django.conf import settings import askbot from askbot import api +from askbot import const from askbot.conf import settings as askbot_settings from askbot.skins.loaders import get_skin from askbot.utils import url_utils @@ -22,5 +23,6 @@ def application_settings(request): return { 'settings': my_settings, 'skin': get_skin(request), - 'moderation_items': api.get_info_on_moderation_items(request.user) + 'moderation_items': api.get_info_on_moderation_items(request.user), + 'noscript_url': const.DEPENDENCY_URLS['noscript'], } diff --git a/askbot/skins/default/templates/blocks/bottom_scripts.html b/askbot/skins/default/templates/blocks/bottom_scripts.html index 6fbd6adc..771c13a4 100644 --- a/askbot/skins/default/templates/blocks/bottom_scripts.html +++ b/askbot/skins/default/templates/blocks/bottom_scripts.html @@ -4,7 +4,7 @@ #}