1
0
mirror of https://github.com/haiwen/seahub.git synced 2025-09-03 07:55:36 +00:00

Update captcha

This commit is contained in:
zhengxie
2014-08-05 14:41:04 +08:00
parent 10ea577fb2
commit 2dd360e5e7
31 changed files with 339 additions and 100 deletions

View File

@@ -1,6 +1,6 @@
import re
VERSION = (0, 4, 0)
VERSION = (0, 4, 3)
def get_version(svn=False):
@@ -20,8 +20,8 @@ def pillow_required():
except ImportError:
try:
import Image
import ImageDraw
import ImageFont
import ImageDraw # NOQA
import ImageFont # NOQA
except ImportError:
return True

View File

@@ -1,6 +1,5 @@
import os
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
CAPTCHA_FONT_PATH = getattr(settings, 'CAPTCHA_FONT_PATH', os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'fonts/Vera.ttf')))
CAPTCHA_FONT_SIZE = getattr(settings, 'CAPTCHA_FONT_SIZE', 22)
@@ -19,9 +18,9 @@ CAPTCHA_IMAGE_BEFORE_FIELD = getattr(settings, 'CAPTCHA_IMAGE_BEFORE_FIELD', Tru
CAPTCHA_DICTIONARY_MIN_LENGTH = getattr(settings, 'CAPTCHA_DICTIONARY_MIN_LENGTH', 0)
CAPTCHA_DICTIONARY_MAX_LENGTH = getattr(settings, 'CAPTCHA_DICTIONARY_MAX_LENGTH', 99)
if CAPTCHA_IMAGE_BEFORE_FIELD:
CAPTCHA_OUTPUT_FORMAT = getattr(settings, 'CAPTCHA_OUTPUT_FORMAT', u'%(image)s %(hidden_field)s %(text_field)s')
CAPTCHA_OUTPUT_FORMAT = getattr(settings, 'CAPTCHA_OUTPUT_FORMAT', '%(image)s %(hidden_field)s %(text_field)s')
else:
CAPTCHA_OUTPUT_FORMAT = getattr(settings, 'CAPTCHA_OUTPUT_FORMAT', u'%(hidden_field)s %(text_field)s %(image)s')
CAPTCHA_OUTPUT_FORMAT = getattr(settings, 'CAPTCHA_OUTPUT_FORMAT', '%(hidden_field)s %(text_field)s %(image)s')
CAPTCHA_TEST_MODE = getattr(settings, 'CAPTCHA_TEST_MODE', getattr(settings, 'CATPCHA_TEST_MODE', False))

View File

@@ -1,12 +1,12 @@
from captcha.conf import settings
from django.conf import settings as django_settings
from captcha.models import CaptchaStore, get_safe_now
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch
from django.core.urlresolvers import reverse, NoReverseMatch
from django.forms import ValidationError
from django.forms.fields import CharField, MultiValueField
from django.forms.widgets import TextInput, MultiWidget, HiddenInput
from django.utils.translation import ugettext, ugettext_lazy
from six import u
class BaseCaptchaTextInput(MultiWidget):
@@ -38,14 +38,10 @@ class BaseCaptchaTextInput(MultiWidget):
key = CaptchaStore.generate_key()
# these can be used by format_output and render
self._value = [key, u'']
self._value = [key, u('')]
self._key = key
self.id_ = self.build_attrs(attrs).get('id', None)
def render(self, name, value, attrs=None):
#self.fetch_captcha_store(name, value, attrs)
return super(BaseCaptchaTextInput, self).render(name, self._value, attrs=attrs)
def id_for_label(self, id_):
if id_:
return id_ + '_1'
@@ -76,6 +72,7 @@ class CaptchaTextInput(BaseCaptchaTextInput):
def format_output(self, rendered_widgets):
hidden_field, text_field = rendered_widgets
text_field = text_field.replace('<input', '<input autocomplete="off"')
return self._args['output_format'] % {
'image': self.image_and_audio,
'hidden_field': hidden_field,
@@ -88,8 +85,6 @@ class CaptchaTextInput(BaseCaptchaTextInput):
self.image_and_audio = '<img src="%s" alt="captcha" class="captcha" />' % self.image_url()
if settings.CAPTCHA_FLITE_PATH:
self.image_and_audio = '<a href="%s" title="%s">%s</a>' % (self.audio_url(), ugettext('Play CAPTCHA as audio file'), self.image_and_audio)
return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs)

View File

@@ -2,6 +2,7 @@
import random
from captcha.conf import settings
from django.core.urlresolvers import reverse
from six import u
def math_challenge():
@@ -15,14 +16,14 @@ def math_challenge():
def random_char_challenge():
chars, ret = u'abcdefghijklmnopqrstuvwxyz', u''
chars, ret = u('abcdefghijklmnopqrstuvwxyz'), u('')
for i in range(settings.CAPTCHA_LENGTH):
ret += random.choice(chars)
return ret.upper(), ret
def unicode_challenge():
chars, ret = u'äàáëéèïíîöóòüúù', u''
chars, ret = u('äàáëéèïíîöóòüúù'), u('')
for i in range(settings.CAPTCHA_LENGTH):
ret += random.choice(chars)
return ret.upper(), ret
@@ -70,6 +71,10 @@ def noise_dots(draw, image):
return draw
def noise_null(draw, image):
return draw
def post_smooth(image):
try:
import ImageFilter

View File

@@ -0,0 +1,31 @@
# django-simple-captcha Bulgarian translation
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the django-simple-captcha package.
# Venelin Stoykov <vkstoykov@gmail.com>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.4.1\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-02-10 14:43+0200\n"
"PO-Revision-Date: 2014-02-10 15:00+0200\n"
"Last-Translator: Venelin Stoykov <vkstoykov@gmail.com>\n"
"Language-Team: bg <vkstoykov@gmail.com>\n"
"Language: bg\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
#: fields.py:91
msgid "Play CAPTCHA as audio file"
msgstr "Чуй текста като аудио файл"
#: fields.py:106 fields.py:135 tests/tests.py:99 tests/tests.py:239
#: tests/tests.py:246
msgid "Invalid CAPTCHA"
msgstr "Сгрешен текст"
#: tests/tests.py:125
msgid "This field is required."
msgstr "Това поле е задължително"

View File

@@ -11,7 +11,7 @@ msgstr ""
"PO-Revision-Date: 2012-10-09 07:08+0200\n"
"Last-Translator: Beda Kosata <bedrich.kosata@nic.cz>\n"
"Language-Team: Czech <>\n"
"Language: \n"
"Language: cs\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -11,6 +11,7 @@ msgstr ""
"PO-Revision-Date: 2013-07-16 12:10+0100\n"
"Last-Translator: Patrick Lauber <patrick.lauber@divio.ch>\n"
"Language-Team: DE <digi@treepy.com>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -0,0 +1,32 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-07-16 12:06+0200\n"
"PO-Revision-Date: 2014-05-20 21:22+0100\n"
"Last-Translator: https://github.com/dragosdobrota\n"
"Language-Team: es\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 1.6.5\n"
#: fields.py:90
msgid "Play CAPTCHA as audio file"
msgstr "Reproducir CAPTCHA de audio"
#: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239
#: tests/tests.py:246
msgid "Invalid CAPTCHA"
msgstr "CAPTCHA no válido"
#: tests/tests.py:125
msgid "This field is required."
msgstr "Este campo es obligatorio."

View File

@@ -3,7 +3,6 @@
# This file is distributed under the same license as the PACKAGE package.
# Patrick Samson <maxcom@laposte.net>, 2010.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.2.0\n"
@@ -11,8 +10,8 @@ msgstr ""
"POT-Creation-Date: 2012-07-25 11:44+0300\n"
"PO-Revision-Date: 2010-09-16 12:16+0200\n"
"Last-Translator: Patrick Samson <maxcom@laposte.net>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"Language-Team: fr <maxcom@laposte.net>\n"
"Language: fr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -3,7 +3,6 @@
# This file is distributed under the same license as the PACKAGE package.
# Arjuna Del Toso <stupidate@gmail.net>, 2012
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.6\n"
@@ -12,6 +11,8 @@ msgstr ""
"PO-Revision-Date: 2012-11-14 02:53+0000\n"
"Last-Translator: Arjuna Del Toso <stupidate@gmail.com>\n"
"MIME-Version: 1.0\n"
"Language-Team: Arjuna Del Toso <stupidate@gmail.com>\n"
"Language: it\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n>1;\n"

View File

@@ -0,0 +1,31 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Keisuke URAGO <bravo@resourcez.org>, 2014.
#
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-02-13 07:11+0900\n"
"PO-Revision-Date: 2014-02-13 07:11+0900\n"
"Last-Translator: Keisuke URAGO <bravo@resourcez.org>\n"
"Language-Team: Keisuke URAGO <bravo@resourcez.org>\n"
"Language: ja\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=1; plural=0;\n"
#: fields.py:90
msgid "Play CAPTCHA as audio file"
msgstr "CAPTCHAをオーディオで読み上げる"
#: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239
#: tests/tests.py:246
msgid "Invalid CAPTCHA"
msgstr "CAPTCHAの値が違っています"
#: tests/tests.py:125
msgid "This field is required."
msgstr "この項目は必須です"

View File

@@ -3,15 +3,16 @@
# This file is distributed under the same license as the PACKAGE package.
# Leon de Rijke <leon@cmsworks.nl>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-02-15 13:26+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: 2013-02-15 13:26+0100\n"
"Last-Translator: Leon de Rijke <leon@cmsworks.nl>\n"
"MIME-Version: 1.0\n"
"Language-Team: Leon de Rijke <leon@cmsworks.nl>\n"
"Language: nl\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"

View File

@@ -3,16 +3,15 @@
# This file is distributed under the same license as the django-simple-captcha package.
# Sławomir Zborowski <slawomir.zborowski@hotmail.com>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: django-simple-captcha 0.3.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-08-18 18:49+0200\n"
"PO-Revision-Date: 2013-08-18 18:52+0200\n"
"Last-Translator: Sławomir Zborowski <slawomir.zborowski@hotmail.com>\n"
"Language-Team: Polisch\n"
"Language: \n"
"Language: pl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -3,7 +3,6 @@
# This file is distributed under the same license as the PACKAGE package.
# Alisson Patricio <eu@alisson.net>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.7\n"
@@ -11,7 +10,8 @@ msgstr ""
"POT-Creation-Date: 2013-05-18 10:58-0300\n"
"PO-Revision-Date: 2013-05-18 13:12-0300\n"
"Last-Translator: Alisson Patricio <eu@alisson.net>\n"
"Language: Portuguese (Brazilian)\n"
"Language-Team: Alisson Patricio <eu@alisson.net>\n"
"Language: pt_br\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -3,16 +3,15 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Project-Id-Version: django-simple-captcha 0.3.7\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2012-07-25 11:17+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"PO-Revision-Date: 2012-07-25 11:17+0300\n"
"Last-Translator: \n"
"Language-Team: ru <LL@li.org>\n"
"Language: ru\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -0,0 +1,32 @@
# Slovak translation of django-simple-captcha.
# Copyright (C) 2013 THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# Pavol Otto <pavol.otto@nic.cz>, 2013.
#
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.7\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-10-15 17:16+0200\n"
"PO-Revision-Date: 2013-10-15 17:16+0200\n"
"Last-Translator: Pavol Otto <pavol.otto@nic.cz>\n"
"Language-Team: SK\n"
"Language: sk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
#: fields.py:90
msgid "Play CAPTCHA as audio file"
msgstr "Prehrať captchu ako audio súbor"
#: fields.py:105 fields.py:134 tests/tests.py:99 tests/tests.py:239
#: tests/tests.py:246
msgid "Invalid CAPTCHA"
msgstr "Neplatná CAPTCHA"
#: tests/tests.py:125
msgid "This field is required."
msgstr "Toto pole je povinné."

View File

@@ -3,7 +3,6 @@
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.6\n"
@@ -11,7 +10,8 @@ msgstr ""
"POT-Creation-Date: 2013-01-19 16:33-0200\n"
"PO-Revision-Date: 2013-01-19 20:52+0200\n"
"Last-Translator: Gokmen Gorgen <gokmen@alageek.com>\n"
"Language: \n"
"Language-Team: TR Gokmen Gorgen <gokmen@alageek.com>\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -0,0 +1,32 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-02-21 22:05+0200\n"
"PO-Revision-Date: 2014-02-21 22:05+0200\n"
"Last-Translator: @FuriousCoder\n"
"Language-Team: uk @FuriousCoder\n"
"Language: uk\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
#: fields.py:91
msgid "Play CAPTCHA as audio file"
msgstr "Відтворити CAPTCHA як аудіо файл."
#: fields.py:106 fields.py:135 tests/tests.py:99 tests/tests.py:239
#: tests/tests.py:246
msgid "Invalid CAPTCHA"
msgstr "Неправильна відповідь."
#: tests/tests.py:125
msgid "This field is required."
msgstr "Це поле є обов'язковим."

View File

@@ -3,15 +3,15 @@
# This file is distributed under the same license as the PACKAGE package.
# Ming Chen <c.ming@live.com>, 2013.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: django-simple-captcha 0.3.6\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-03-01 05:04+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"PO-Revision-Date: 2013-03-01 05:04+0800\n"
"Last-Translator: Ming Chen <c.ming@live.com>\n"
"Language: \n"
"Language-Team: zh_cn Ming Chen <c.ming@live.com>\n"
"Language: zh_cn\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

View File

@@ -11,15 +11,15 @@ class Command(BaseCommand):
verbose = int(options.get('verbosity'))
expired_keys = CaptchaStore.objects.filter(expiration__lte=get_safe_now()).count()
if verbose >= 1:
print "Currently %d expired hashkeys" % expired_keys
print("Currently %d expired hashkeys" % expired_keys)
try:
CaptchaStore.remove_expired()
except:
if verbose >= 1:
print "Unable to delete expired hashkeys."
print("Unable to delete expired hashkeys.")
sys.exit(1)
if verbose >= 1:
if expired_keys > 0:
print "%d expired hashkeys removed." % expired_keys
print("%d expired hashkeys removed." % expired_keys)
else:
print "No keys to remove."
print("No keys to remove.")

View File

@@ -1,39 +1,26 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'CaptchaStore'
db.create_table('captcha_captchastore', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('challenge', self.gf('django.db.models.fields.CharField')(max_length=32)),
('response', self.gf('django.db.models.fields.CharField')(max_length=32)),
('hashkey', self.gf('django.db.models.fields.CharField')(unique=True, max_length=40)),
('expiration', self.gf('django.db.models.fields.DateTimeField')()),
))
db.send_create_signal('captcha', ['CaptchaStore'])
from django.db import models, migrations
def backwards(self, orm):
class Migration(migrations.Migration):
# Deleting model 'CaptchaStore'
db.delete_table('captcha_captchastore')
dependencies = [
]
models = {
'captcha.captchastore': {
'Meta': {'object_name': 'CaptchaStore'},
'challenge': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'expiration': ('django.db.models.fields.DateTimeField', [], {}),
'hashkey': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'response': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['captcha']
operations = [
migrations.CreateModel(
name='CaptchaStore',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('challenge', models.CharField(max_length=32)),
('response', models.CharField(max_length=32)),
('hashkey', models.CharField(unique=True, max_length=40)),
('expiration', models.DateTimeField()),
],
options={
},
bases=(models.Model,),
),
]

View File

@@ -0,0 +1,21 @@
"""
Django migrations for django-simple-captcha app
This package does not contain South migrations. South migrations can be found
in the ``south_migrations`` package.
"""
SOUTH_ERROR_MESSAGE = """\n
For South support, customize the SOUTH_MIGRATION_MODULES setting like so:
SOUTH_MIGRATION_MODULES = {
'captcha': 'captcha.south_migrations',
}
"""
# Ensure the user is not using Django 1.6 or below with South
try:
from django.db import migrations # noqa
except ImportError:
from django.core.exceptions import ImproperlyConfigured
raise ImproperlyConfigured(SOUTH_ERROR_MESSAGE)

View File

@@ -40,10 +40,9 @@ class CaptchaStore(models.Model):
expiration = models.DateTimeField(blank=False)
def save(self, *args, **kwargs):
#import ipdb; ipdb.set_trace()
self.response = six.text_type(self.response).lower()
if not self.expiration:
#self.expiration = datetime.datetime.now() + datetime.timedelta(minutes=int(captcha_settings.CAPTCHA_TIMEOUT))
self.expiration = get_safe_now() + datetime.timedelta(minutes=int(captcha_settings.CAPTCHA_TIMEOUT))
if not self.hashkey:
key_ = unicodedata.normalize('NFKD', str(randrange(0, MAX_RANDOM_KEY)) + str(time.time()) + six.text_type(self.challenge)).encode('ascii', 'ignore') + unicodedata.normalize('NFKD', six.text_type(self.response)).encode('ascii', 'ignore')

View File

@@ -0,0 +1,39 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'CaptchaStore'
db.create_table('captcha_captchastore', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('challenge', self.gf('django.db.models.fields.CharField')(max_length=32)),
('response', self.gf('django.db.models.fields.CharField')(max_length=32)),
('hashkey', self.gf('django.db.models.fields.CharField')(unique=True, max_length=40)),
('expiration', self.gf('django.db.models.fields.DateTimeField')()),
))
db.send_create_signal('captcha', ['CaptchaStore'])
def backwards(self, orm):
# Deleting model 'CaptchaStore'
db.delete_table('captcha_captchastore')
models = {
'captcha.captchastore': {
'Meta': {'object_name': 'CaptchaStore'},
'challenge': ('django.db.models.fields.CharField', [], {'max_length': '32'}),
'expiration': ('django.db.models.fields.DateTimeField', [], {}),
'hashkey': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'response': ('django.db.models.fields.CharField', [], {'max_length': '32'})
}
}
complete_apps = ['captcha']

View File

@@ -1 +1 @@
from .tests import CaptchaCase, trivial_challenge
from .tests import CaptchaCase, trivial_challenge # NOQA

View File

@@ -12,6 +12,7 @@ import json
import re
import six
import os
from six import u
class CaptchaCase(TestCase):
@@ -42,7 +43,6 @@ class CaptchaCase(TestCase):
settings.CAPTCHA_WORDS_DICTIONARY = self.__current_settings_dictionary
settings.CAPTCHA_PUNCTUATION = self.__current_settings_punctuation
def __extract_hash_and_response(self, r):
hash_ = re.findall(r'value="([0-9a-f]+)"', str(r.content))[0]
response = CaptchaStore.objects.get(hashkey=hash_).response
@@ -175,7 +175,7 @@ class CaptchaCase(TestCase):
def testOutputFormat(self):
for urlname in ('captcha-test', 'captcha-test-model-form'):
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s<p>Hello, captcha world</p>%(hidden_field)s%(text_field)s'
settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s<p>Hello, captcha world</p>%(hidden_field)s%(text_field)s')
r = self.client.get(reverse(urlname))
self.assertEqual(r.status_code, 200)
self.assertTrue('<p>Hello, captcha world</p>' in str(r.content))
@@ -186,7 +186,7 @@ class CaptchaCase(TestCase):
# we turn on DEBUG because CAPTCHA_OUTPUT_FORMAT is only checked debug
django_settings.DEBUG = True
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s'
settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s')
try:
self.client.get(reverse(urlname))
self.fail()
@@ -195,14 +195,14 @@ class CaptchaCase(TestCase):
django_settings.DEBUG = __current_settings_debug
def testPerFormFormat(self):
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s testCustomFormatString %(hidden_field)s %(text_field)s'
settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s testCustomFormatString %(hidden_field)s %(text_field)s')
r = self.client.get(reverse('captcha-test'))
self.assertTrue('testCustomFormatString' in str(r.content))
r = self.client.get(reverse('test_per_form_format'))
self.assertTrue('testPerFieldCustomFormatString' in str(r.content))
def testIssue31ProperLabel(self):
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s %(hidden_field)s %(text_field)s'
settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s %(hidden_field)s %(text_field)s')
r = self.client.get(reverse('captcha-test'))
self.assertTrue('<label for="id_captcha_1"' in str(r.content))
@@ -226,12 +226,12 @@ class CaptchaCase(TestCase):
This test covers a default django field and widget behavior
It not assert anything. If something is wrong it will raise a error!
"""
settings.CAPTCHA_OUTPUT_FORMAT = u'%(image)s %(hidden_field)s %(text_field)s'
settings.CAPTCHA_OUTPUT_FORMAT = u('%(image)s %(hidden_field)s %(text_field)s')
widget = CaptchaTextInput(attrs={'class': 'required'})
CaptchaField(widget=widget)
def testTestMode_Issue15(self):
__current_test_mode_setting = settings.CAPTCHA_TEST_MODE
__current_test_mode_setting = settings.CAPTCHA_TEST_MODE
settings.CAPTCHA_TEST_MODE = False
r = self.client.get(reverse('captcha-test'))
self.assertEqual(r.status_code, 200)
@@ -270,11 +270,30 @@ class CaptchaCase(TestCase):
self.assertEqual(r.status_code, 200)
hash_, response = self.__extract_hash_and_response(r)
r = self.client.post(reverse('captcha-test-non-required'), dict(captcha_0=hash_, captcha_1=response, subject='xxx', sender='asasd@asdasd.com'))
self.assertEqual(r.status_code, 200)
self.assertTrue(str(r.content).find('Form validated') > 0)
def test_autocomplete_off(self):
r = self.client.get(reverse('captcha-test'))
self.assertTrue('<input autocomplete="off" ' in six.text_type(r.content))
def test_autocomplete_not_on_hidden_input(self):
r = self.client.get(reverse('captcha-test'))
self.assertFalse('autocomplete="off" type="hidden" name="captcha_0"' in six.text_type(r.content))
self.assertFalse('autocomplete="off" id="id_captcha_0" name="captcha_0" type="hidden"' in six.text_type(r.content))
def test_transparent_background(self):
__current_test_mode_setting = settings.CAPTCHA_BACKGROUND_COLOR
settings.CAPTCHA_BACKGROUND_COLOR = "transparent"
for key in [store.hashkey for store in six.itervalues(self.stores)]:
response = self.client.get(reverse('captcha-image', kwargs=dict(key=key)))
self.assertEqual(response.status_code, 200)
self.assertTrue(response.has_header('content-type'))
self.assertEqual(response._headers.get('content-type'), ('Content-Type', 'image/png'))
settings.CAPTCHA_BACKGROUND_COLOR = __current_test_mode_setting
def trivial_challenge():
return 'trivial', 'trivial'

View File

@@ -3,7 +3,8 @@ try:
except ImportError:
from django.conf.urls.defaults import url, patterns, include
urlpatterns = patterns('',
urlpatterns = patterns(
'',
url(r'test/$', 'captcha.tests.views.test', name='captcha-test'),
url(r'test-modelform/$', 'captcha.tests.views.test_model_form', name='captcha-test-model-form'),
url(r'test2/$', 'captcha.tests.views.test_custom_error_message', name='captcha-test-custom-error-message'),

View File

@@ -3,7 +3,7 @@ from captcha.fields import CaptchaField
from django.template import RequestContext, loader
from django.http import HttpResponse
from django.contrib.auth.models import User
from six import u
TEST_TEMPLATE = r'''
{% load url from future %}
@@ -30,6 +30,7 @@ TEST_TEMPLATE = r'''
</html>
'''
def _test(request, form_class):
passed = False
if request.POST:
@@ -80,8 +81,10 @@ def test_per_form_format(request):
help_text='asdasd',
error_messages=dict(invalid='TEST CUSTOM ERROR MESSAGE'),
output_format=(
u'%(image)s testPerFieldCustomFormatString '
u'%(hidden_field)s %(text_field)s'
u(
'%(image)s testPerFieldCustomFormatString '
'%(hidden_field)s %(text_field)s'
)
)
)
return _test(request, CaptchaTestFormatForm)

View File

@@ -3,7 +3,8 @@ try:
except ImportError:
from django.conf.urls.defaults import patterns, url
urlpatterns = patterns('captcha.views',
urlpatterns = patterns(
'captcha.views',
url(r'image/(?P<key>\w+)/$', 'captcha_image', name='captcha-image', kwargs={'scale': 1}),
url(r'image/(?P<key>\w+)@2/$', 'captcha_image', name='captcha-image-2x', kwargs={'scale': 2}),
url(r'audio/(?P<key>\w+)/$', 'captcha_audio', name='captcha-audio'),

View File

@@ -1,4 +1,4 @@
from captcha.conf import settings
from captcha.conf import settings
from captcha.helpers import captcha_image_url
from captcha.models import CaptchaStore
from django.http import HttpResponse, Http404
@@ -27,6 +27,15 @@ except ImportError:
from django.utils import simplejson as json
NON_DIGITS_RX = re.compile('[^\d]')
# Distance of the drawn text from the top of the captcha image
from_top = 4
def getsize(font, text):
if hasattr(font, 'getoffset'):
return [x + y for x, y in zip(font.getsize(text), font.getoffset(text))]
else:
return font.getsize(text)
def captcha_image(request, key, scale=1):
@@ -38,9 +47,12 @@ def captcha_image(request, key, scale=1):
else:
font = ImageFont.load(settings.CAPTCHA_FONT_PATH)
size = font.getsize(text)
size = (size[0] * 2, int(size[1] * 1.2))
image = Image.new('RGB', size, settings.CAPTCHA_BACKGROUND_COLOR)
size = getsize(font, text)
size = (size[0] * 2, int(size[1] * 1.4))
if settings.CAPTCHA_BACKGROUND_COLOR == "transparent":
image = Image.new('RGBA', size)
else:
image = Image.new('RGB', size, settings.CAPTCHA_BACKGROUND_COLOR)
try:
PIL_VERSION = int(NON_DIGITS_RX.sub('', Image.VERSION))
@@ -56,7 +68,7 @@ def captcha_image(request, key, scale=1):
charlist.append(char)
for char in charlist:
fgimage = Image.new('RGB', size, settings.CAPTCHA_FOREGROUND_COLOR)
charimage = Image.new('L', font.getsize(' %s ' % char), '#000000')
charimage = Image.new('L', getsize(font, ' %s ' % char), '#000000')
chardraw = ImageDraw.Draw(charimage)
chardraw.text((0, 0), ' %s ' % char, font=font, fill='#ffffff')
if settings.CAPTCHA_LETTER_ROTATION:
@@ -67,7 +79,7 @@ def captcha_image(request, key, scale=1):
charimage = charimage.crop(charimage.getbbox())
maskimage = Image.new('L', size)
maskimage.paste(charimage, (xpos, 4, xpos + charimage.size[0], 4 + charimage.size[1]))
maskimage.paste(charimage, (xpos, from_top, xpos + charimage.size[0], from_top + charimage.size[1]))
size = maskimage.size
image = Image.composite(fgimage, image, maskimage)
xpos = xpos + 2 + charimage.size[0]