diff --git a/CHANGES b/CHANGES index b59ea302..91e0dc10 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,12 @@ Version History =============== + +Version 0.6.0 +------------- +* Only Django versions 4.2 or above are now supported +* Removed the old rendering methods that were deprecated in 2017. + Version 0.5.18 -------------- * Fix some typos in documentation (#210, thanks @stweil) diff --git a/captcha/__init__.py b/captcha/__init__.py index b0eaf290..bca8d0b0 100644 --- a/captcha/__init__.py +++ b/captcha/__init__.py @@ -1,4 +1,4 @@ -VERSION = (0, 5, 18) +VERSION = (0, 6, 0) def get_version(): diff --git a/captcha/conf/settings.py b/captcha/conf/settings.py index f31cdd07..7cbbfeb4 100644 --- a/captcha/conf/settings.py +++ b/captcha/conf/settings.py @@ -1,5 +1,4 @@ import os -import warnings from django.conf import settings @@ -36,24 +35,6 @@ CAPTCHA_DICTIONARY_MIN_LENGTH = getattr(settings, "CAPTCHA_DICTIONARY_MIN_LENGTH", 0) CAPTCHA_DICTIONARY_MAX_LENGTH = getattr(settings, "CAPTCHA_DICTIONARY_MAX_LENGTH", 99) CAPTCHA_IMAGE_SIZE = getattr(settings, "CAPTCHA_IMAGE_SIZE", None) -CAPTCHA_IMAGE_TEMPLATE = getattr( - settings, "CAPTCHA_IMAGE_TEMPLATE", "captcha/image.html" -) -CAPTCHA_HIDDEN_FIELD_TEMPLATE = getattr( - settings, "CAPTCHA_HIDDEN_FIELD_TEMPLATE", "captcha/hidden_field.html" -) -CAPTCHA_TEXT_FIELD_TEMPLATE = getattr( - settings, "CAPTCHA_TEXT_FIELD_TEMPLATE", "captcha/text_field.html" -) - -if getattr(settings, "CAPTCHA_FIELD_TEMPLATE", None): - msg = "CAPTCHA_FIELD_TEMPLATE setting is deprecated in favor of widget's template_name." - warnings.warn(msg, DeprecationWarning) -CAPTCHA_FIELD_TEMPLATE = getattr(settings, "CAPTCHA_FIELD_TEMPLATE", None) -if getattr(settings, "CAPTCHA_OUTPUT_FORMAT", None): - msg = "CAPTCHA_OUTPUT_FORMAT setting is deprecated in favor of widget's template_name." - warnings.warn(msg, DeprecationWarning) -CAPTCHA_OUTPUT_FORMAT = getattr(settings, "CAPTCHA_OUTPUT_FORMAT", None) CAPTCHA_MATH_CHALLENGE_OPERATOR = getattr( settings, "CAPTCHA_MATH_CHALLENGE_OPERATOR", "*" diff --git a/captcha/fields.py b/captcha/fields.py index 6aa4e59d..36c4fdca 100644 --- a/captcha/fields.py +++ b/captcha/fields.py @@ -1,14 +1,9 @@ -import warnings - -import django from django.core.exceptions import ImproperlyConfigured from django.forms import ValidationError from django.forms.fields import CharField, MultiValueField from django.forms.widgets import HiddenInput, MultiWidget, TextInput -from django.template.loader import render_to_string from django.urls import NoReverseMatch, reverse from django.utils import timezone -from django.utils.safestring import mark_safe from django.utils.translation import gettext_lazy from captcha.conf import settings @@ -97,34 +92,19 @@ def refresh_url(self): class CaptchaTextInput(BaseCaptchaTextInput): - template_name = "captcha/widgets/captcha.html" def __init__( self, attrs=None, - field_template=None, id_prefix=None, generator=None, - output_format=None, + # output_format=None, ): self.id_prefix = id_prefix self.generator = generator - if field_template is not None: - msg = "CaptchaTextInput's field_template argument is deprecated in favor of widget's template_name." - warnings.warn(msg, DeprecationWarning) - self.field_template = field_template or settings.CAPTCHA_FIELD_TEMPLATE - if output_format is not None: - msg = "CaptchaTextInput's output_format argument is deprecated in favor of widget's template_name." - warnings.warn(msg, DeprecationWarning) - self.output_format = output_format or settings.CAPTCHA_OUTPUT_FORMAT - # Fallback to custom rendering in Django < 1.11 - if ( - not hasattr(self, "_render") - and self.field_template is None - and self.output_format is None - ): - self.field_template = "captcha/field.html" + + self.output_format = None if self.output_format: for key in ("image", "hidden_field", "text_field"): @@ -173,46 +153,11 @@ def format_output(self, rendered_widgets): } return ret - elif self.field_template: - context = { - "image": mark_safe(self.image_and_audio), - "hidden_field": mark_safe(self.hidden_field), - "text_field": mark_safe(self.text_field), - } - return render_to_string(self.field_template, context) - - def _direct_render(self, name, attrs): - """Render the widget the old way - using field_template or output_format.""" - context = { - "image": self.image_url(), - "name": name, - "key": self._key, - "id": "%s_%s" % (self.id_prefix, attrs.get("id")) - if self.id_prefix - else attrs.get("id"), - "audio": self.audio_url(), - } - self.image_and_audio = render_to_string( - settings.CAPTCHA_IMAGE_TEMPLATE, context - ) - self.hidden_field = render_to_string( - settings.CAPTCHA_HIDDEN_FIELD_TEMPLATE, context - ) - self.text_field = render_to_string( - settings.CAPTCHA_TEXT_FIELD_TEMPLATE, context - ) - return self.format_output(None) - def render(self, name, value, attrs=None, renderer=None): self.fetch_captcha_store(name, value, attrs, self.generator) - if self.field_template or self.output_format: - return self._direct_render(name, attrs) - extra_kwargs = {} - if django.VERSION >= (1, 11): - # https://docs.djangoproject.com/en/1.11/ref/forms/widgets/#django.forms.Widget.render - extra_kwargs["renderer"] = renderer + extra_kwargs["renderer"] = renderer return super(CaptchaTextInput, self).render( name, self._value, attrs=attrs, **extra_kwargs @@ -234,7 +179,6 @@ def __init__(self, *args, **kwargs): kwargs["widget"] = kwargs.pop( "widget", CaptchaTextInput( - output_format=kwargs.pop("output_format", None), id_prefix=kwargs.pop("id_prefix", None), generator=kwargs.pop("generator", None), ), diff --git a/captcha/templates/captcha/field.html b/captcha/templates/captcha/field.html deleted file mode 100644 index 8c5d5d61..00000000 --- a/captcha/templates/captcha/field.html +++ /dev/null @@ -1 +0,0 @@ -{{image}}{{hidden_field}}{{text_field}} diff --git a/captcha/templates/captcha/hidden_field.html b/captcha/templates/captcha/hidden_field.html deleted file mode 100644 index 36d7490a..00000000 --- a/captcha/templates/captcha/hidden_field.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/captcha/templates/captcha/image.html b/captcha/templates/captcha/image.html deleted file mode 100644 index a73e956e..00000000 --- a/captcha/templates/captcha/image.html +++ /dev/null @@ -1,4 +0,0 @@ -{% load i18n %} -{% spaceless %} - {% if audio %}{% endif %}captcha{% if audio %}{% endif %} -{% endspaceless %} diff --git a/captcha/templates/captcha/text_field.html b/captcha/templates/captcha/text_field.html deleted file mode 100644 index fee60f44..00000000 --- a/captcha/templates/captcha/text_field.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/captcha/templates/captcha/widgets/captcha.html b/captcha/templates/captcha/widgets/captcha.html index 49faaf25..e9000d5f 100644 --- a/captcha/templates/captcha/widgets/captcha.html +++ b/captcha/templates/captcha/widgets/captcha.html @@ -6,4 +6,4 @@ captcha {% if audio %}{% endif %} {% endspaceless %} -{% include "django/forms/widgets/multiwidget.html" %} +{% spaceless %}{% for widget in widget.subwidgets %}{% include widget.template_name %}{% endfor %}{% endspaceless %} diff --git a/captcha/tests/tests.py b/captcha/tests/tests.py index 9d0f7158..86061afe 100644 --- a/captcha/tests/tests.py +++ b/captcha/tests/tests.py @@ -2,7 +2,6 @@ import json import os import re -import warnings from io import BytesIO from PIL import Image @@ -17,16 +16,13 @@ from django.utils.translation import gettext_lazy from captcha.conf import settings -from captcha.fields import CaptchaField, CaptchaTextInput from captcha.models import CaptchaStore @override_settings(ROOT_URLCONF="captcha.tests.urls") class CaptchaCase(TestCase): def setUp(self): - self.stores = {} - self.__current_settings_output_format = settings.CAPTCHA_OUTPUT_FORMAT self.__current_settings_dictionary = settings.CAPTCHA_WORDS_DICTIONARY self.__current_settings_punctuation = settings.CAPTCHA_PUNCTUATION @@ -57,7 +53,6 @@ def setUp(self): self.default_store = self.stores["default_store"] def tearDown(self): - settings.CAPTCHA_OUTPUT_FORMAT = self.__current_settings_output_format settings.CAPTCHA_WORDS_DICTIONARY = self.__current_settings_dictionary settings.CAPTCHA_PUNCTUATION = self.__current_settings_punctuation @@ -273,47 +268,11 @@ def test_repeated_challenge_form_submit(self): self.assertTrue(str(r2.content).find("Form validated") > 0) settings.CAPTCHA_CHALLENGE_FUNCT = __current_challange_function - def test_output_format(self): - for urlname in ("captcha-test", "captcha-test-model-form"): - settings.CAPTCHA_OUTPUT_FORMAT = ( - "%(image)s

Hello, captcha world

%(hidden_field)s%(text_field)s" - ) - r = self.client.get(reverse(urlname)) - self.assertEqual(r.status_code, 200) - self.assertTrue("

Hello, captcha world

" in str(r.content)) - - def test_invalid_output_format(self): - for urlname in ("captcha-test", "captcha-test-model-form"): - settings.CAPTCHA_OUTPUT_FORMAT = "%(image)s" - try: - with warnings.catch_warnings(record=True) as w: - self.client.get(reverse(urlname)) - assert len(w) == 1 - self.assertTrue("CAPTCHA_OUTPUT_FORMAT" in str(w[-1].message)) - self.fail() - - except ImproperlyConfigured as e: - self.assertTrue("CAPTCHA_OUTPUT_FORMAT" in str(e)) - - def test_per_form_format(self): - settings.CAPTCHA_OUTPUT_FORMAT = ( - "%(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 test_custom_generator(self): r = self.client.get(reverse("test_custom_generator")) hash_, response = self.__extract_hash_and_response(r) self.assertEqual(response, "111111") - def test_issue31_proper_abel(self): - settings.CAPTCHA_OUTPUT_FORMAT = "%(image)s %(hidden_field)s %(text_field)s" - r = self.client.get(reverse("captcha-test")) - self.assertTrue('