Skip to content

Commit

Permalink
Merge branch 'feature/spam-checkers-on-linkbacks' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Fantomas42 committed Mar 26, 2017
2 parents 89f17d9 + d1c4a0e commit 3dcf7f9
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 16 deletions.
8 changes: 6 additions & 2 deletions docs/topics/spam_checker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Spam Checker

.. versionadded:: 0.9

Spam protection is mandatory when you want to let your users to comment
Spam protection is mandatory when you want to let your users react on
your entries.

Originally Zinnia provided a only one type of spam protection with the
Expand All @@ -18,7 +18,7 @@ service may be a little bit risky.
Now Akismet has been moved in a dedicated module and the moderation system
let you choose the spam checkers to use. With this new feature you can now
write a custom spam checker corresponding to your needs and use it for
moderation your comments.
moderating your comments, trackbacks and linkbacks.

We can imagine for example that you want to authorize comments from
a white-list of IPs, it's possible by writing a backend.
Expand All @@ -35,6 +35,10 @@ Configuration example: ::

.. seealso:: :setting:`ZINNIA_SPAM_CHECKER_BACKENDS`

.. versionchanged:: 0.19

The spam protection now also apply on incoming trackbacks and linkbacks.

.. _builtin-spam-checkers:

Built-in spam checkers
Expand Down
5 changes: 4 additions & 1 deletion zinnia/spam_checker/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,13 @@ def get_spam_checker(backend_path):


def check_is_spam(content, content_object, request,
backends=SPAM_CHECKER_BACKENDS):
backends=None):
"""
Return True if the content is a spam, else False.
"""
if backends is None:
backends = SPAM_CHECKER_BACKENDS

for backend_path in backends:
spam_checker = get_spam_checker(backend_path)
if spam_checker is not None:
Expand Down
22 changes: 22 additions & 0 deletions zinnia/tests/test_pingback.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def setUp(self):
import zinnia.xmlrpc.pingback
self.original_urlopen = zinnia.xmlrpc.pingback.urlopen
zinnia.xmlrpc.pingback.urlopen = self.fake_urlopen
# Set up a stub around zinnia.spam_checker
import zinnia.spam_checker
self.original_scb = zinnia.spam_checker.SPAM_CHECKER_BACKENDS
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = []
# Preparing site
self.site = Site.objects.get_current()
# Creating tests entries
Expand Down Expand Up @@ -115,6 +119,8 @@ def tearDown(self):
import zinnia.xmlrpc.pingback
zinnia.xmlrpc.pingback.urlopen = self.original_urlopen
shortener_settings.URL_SHORTENER_BACKEND = self.original_shortener
import zinnia.spam_checker
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = self.original_scb

def test_generate_pingback_content(self):
soup = BeautifulSoup(self.second_entry.content, 'html.parser')
Expand Down Expand Up @@ -216,6 +222,22 @@ def test_pingback_ping_on_entry_without_author(self):
self.assertTrue(self.second_entry.title in
self.first_entry.pingbacks[0].user_name)

def test_pingback_ping_spam_checker(self):
import zinnia.spam_checker
original_scb = zinnia.spam_checker.SPAM_CHECKER_BACKENDS
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = (
'zinnia.spam_checker.backends.all_is_spam',
)
target = 'http://%s%s' % (
self.site.domain, self.first_entry.get_absolute_url())
source = 'http://%s%s' % (
self.site.domain, self.second_entry.get_absolute_url())
self.first_entry.pingback_enabled = True
self.first_entry.save()
response = self.server.pingback.ping(source, target)
self.assertEqual(response, 51)
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = original_scb

def test_pingback_extensions_get_pingbacks(self):
target = 'http://%s%s' % (
self.site.domain, self.first_entry.get_absolute_url())
Expand Down
34 changes: 34 additions & 0 deletions zinnia/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,11 @@ def test_zinnia_sitemap(self):
def test_zinnia_trackback(self):
# Clear the cache of user flagger to avoid error on MySQL
get_user_flagger.cache_clear()
# Disable spam-checkers
import zinnia.spam_checker
original_scb = zinnia.spam_checker.SPAM_CHECKER_BACKENDS
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = []

response = self.client.post('/trackback/404/')
trackback_url = '/trackback/%s/' % self.first_entry.pk
self.assertEqual(response.status_code, 404)
Expand Down Expand Up @@ -648,15 +653,44 @@ def test_zinnia_trackback(self):
{'url': 'http://example.com'})
self.assertEqual(response.context['error'],
'Trackback is already registered')
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = original_scb

def test_zinnia_trackback_on_entry_without_author(self):
# Clear the cache of user flagger to avoid error on MySQL
get_user_flagger.cache_clear()
# Disable spam-checkers
import zinnia.spam_checker
original_scb = zinnia.spam_checker.SPAM_CHECKER_BACKENDS
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = []

self.first_entry.authors.clear()
response = self.client.post('/trackback/%s/' % self.first_entry.pk,
{'url': 'http://example.com'})
self.assertEqual(response['Content-Type'], 'text/xml')
self.assertEqual('error' in response.context, False)
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = original_scb

def test_zinnia_trackback_spam_check(self):
# Clear the cache of user flagger to avoid error on MySQL
get_user_flagger.cache_clear()
import zinnia.spam_checker
original_scb = zinnia.spam_checker.SPAM_CHECKER_BACKENDS
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = (
'zinnia.spam_checker.backends.all_is_spam',
)
response = self.client.post('/trackback/%s/' % self.first_entry.pk,
{'url': 'http://example.com',
'excerpt': 'Spam'})
self.assertEqual(response['Content-Type'], 'text/xml')
self.assertEqual(response.context['error'],
'Trackback considered like spam')
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = []
response = self.client.post('/trackback/%s/' % self.first_entry.pk,
{'url': 'http://example.com',
'excerpt': 'Spam'})
self.assertEqual(response['Content-Type'], 'text/xml')
self.assertEqual('error' in response.context, False)
zinnia.spam_checker.SPAM_CHECKER_BACKENDS = original_scb

def test_capabilities(self):
self.check_capabilities('/humans.txt', 'text/plain', 0)
Expand Down
28 changes: 21 additions & 7 deletions zinnia/views/trackback.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from django.contrib.sites.models import Site
from django.http import HttpResponsePermanentRedirect
from django.shortcuts import get_object_or_404
from django.utils import timezone
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import csrf_exempt
from django.views.generic.base import TemplateView
Expand All @@ -14,6 +13,7 @@
from zinnia.flags import get_user_flagger
from zinnia.models.entry import Entry
from zinnia.signals import trackback_was_posted
from zinnia.spam_checker import check_is_spam


class EntryTrackback(TemplateView):
Expand Down Expand Up @@ -66,12 +66,26 @@ def post(self, request, *args, **kwargs):
blog_name = request.POST.get('blog_name') or title
ip_address = request.META.get('REMOTE_ADDR', None)

trackback, created = comments.get_model().objects.get_or_create(
content_type=ContentType.objects.get_for_model(Entry),
object_pk=entry.pk, site=site, user_url=url,
user_name=blog_name, ip_address=ip_address,
defaults={'comment': excerpt,
'submit_date': timezone.now()})
trackback_klass = comments.get_model()
trackback_datas = {
'content_type': ContentType.objects.get_for_model(Entry),
'object_pk': entry.pk,
'site': site,
'user_url': url,
'user_name': blog_name,
'ip_address': ip_address,
'comment': excerpt
}

trackback = trackback_klass(**trackback_datas)
if check_is_spam(trackback, entry, request):
return self.render_to_response(
{'error': 'Trackback considered like spam'})

trackback_defaults = {'comment': trackback_datas.pop('comment')}
trackback, created = trackback_klass.objects.get_or_create(
defaults=trackback_defaults,
**trackback_datas)
if created:
trackback.flags.create(user=get_user_flagger(), flag=TRACKBACK)
trackback_was_posted.send(trackback.__class__,
Expand Down
30 changes: 24 additions & 6 deletions zinnia/xmlrpc/pingback.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from django.core.urlresolvers import Resolver404
from django.core.urlresolvers import resolve
from django.utils import six
from django.utils import timezone
from django.utils.html import strip_tags
from django.utils.translation import ugettext as _

Expand All @@ -30,13 +29,19 @@
from zinnia.models.entry import Entry
from zinnia.settings import PINGBACK_CONTENT_LENGTH
from zinnia.signals import pingback_was_posted
from zinnia.spam_checker import check_is_spam

UNDEFINED_ERROR = 0
SOURCE_DOES_NOT_EXIST = 16
SOURCE_DOES_NOT_LINK = 17
TARGET_DOES_NOT_EXIST = 32
TARGET_IS_NOT_PINGABLE = 33
PINGBACK_ALREADY_REGISTERED = 48
PINGBACK_IS_SPAM = 51


class FakeRequest(object):
META = {}


def generate_pingback_content(soup, target, max_length, trunc_char='...'):
Expand Down Expand Up @@ -117,11 +122,24 @@ def pingback_ping(source, target):
description = generate_pingback_content(soup, target,
PINGBACK_CONTENT_LENGTH)

pingback, created = comments.get_model().objects.get_or_create(
content_type=ContentType.objects.get_for_model(Entry),
object_pk=entry.pk, user_url=source, site=site,
defaults={'comment': description, 'user_name': title,
'submit_date': timezone.now()})
pingback_klass = comments.get_model()
pingback_datas = {
'content_type': ContentType.objects.get_for_model(Entry),
'object_pk': entry.pk,
'site': site,
'user_url': source,
'user_name': title,
'comment': description
}
pingback = pingback_klass(**pingback_datas)
if check_is_spam(pingback, entry, FakeRequest()):
return PINGBACK_IS_SPAM

pingback_defaults = {'comment': pingback_datas.pop('comment'),
'user_name': pingback_datas.pop('user_name')}
pingback, created = pingback_klass.objects.get_or_create(
defaults=pingback_defaults,
**pingback_datas)
if created:
pingback.flags.create(user=get_user_flagger(), flag=PINGBACK)
pingback_was_posted.send(pingback.__class__,
Expand Down

0 comments on commit 3dcf7f9

Please sign in to comment.