diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 81e1235714..8cdf5ca0dd 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -23,6 +23,7 @@ Changelog * Page model fields without a FieldPanel are no longer displayed in the form * No longer need to specify the base model on InlinePanel definitions * The project template Vagrantfile now listens on port 8000 + * The external link chooser in rich text areas now accepts URLs of the form '/some/local/path', to allow linking to non-Wagtail-controlled URLs within the local site (Eric Drechsel) 0.8.5 (xx.xx.20xx) diff --git a/docs/releases/0.9.rst b/docs/releases/0.9.rst index e265150aea..2482d718db 100644 --- a/docs/releases/0.9.rst +++ b/docs/releases/0.9.rst @@ -23,15 +23,16 @@ Minor features * Dropped Python 2.6 and 3.2 support * Dropped Elasticsearch 0.90.x support * Search view accepts "page" GET parameter in line with pagination - * Removed the dependency on `LOGIN_URL` and `LOGIN_REDIRECT_URL` settings + * Removed the dependency on ``LOGIN_URL`` and ``LOGIN_REDIRECT_URL`` settings * Password reset view names namespaced to wagtailadmin * Removed the need to add permission check on admin views (now automated) - * Reversing `django.contrib.auth.admin.login` will no longer lead to Wagtails login view (making it easier to have front end views) + * Reversing ``django.contrib.auth.admin.login`` will no longer lead to Wagtails login view (making it easier to have front end views) * Added cache-control headers to all admin views. This allows Varnish/Squid/CDN to run on vanilla settings in front of a Wagtail site * Added validation to prevent pages being created with only whitespace characters in their title fields * Page model fields without a FieldPanel are no longer displayed in the form * No longer need to specify the base model on InlinePanel definitions * The project template Vagrantfile now listens on port 8000 + * The external link chooser in rich text areas now accepts URLs of the form '/some/local/path', to allow linking to non-Wagtail-controlled URLs within the local site Bug fixes diff --git a/wagtail/wagtailadmin/forms.py b/wagtail/wagtailadmin/forms.py index f62d7ae319..de07ac01c0 100644 --- a/wagtail/wagtailadmin/forms.py +++ b/wagtail/wagtailadmin/forms.py @@ -1,4 +1,6 @@ from django import forms +from django.core import validators +from django.forms.widgets import TextInput from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm from django.utils.translation import ugettext as _ @@ -6,6 +8,25 @@ from django.utils.translation import ungettext, ugettext_lazy from wagtail.wagtailadmin.widgets import AdminPageChooser from wagtail.wagtailcore.models import Page +class URLOrAbsolutePathValidator(validators.URLValidator): + @staticmethod + def is_absolute_path(value): + return value.startswith('/') + + def __call__(self, value): + if URLOrAbsolutePathValidator.is_absolute_path(value): + return None + else: + return super(URLOrAbsolutePathValidator, self).__call__(value) + +class URLOrAbsolutePathField(forms.URLField): + widget = TextInput + default_validators = [URLOrAbsolutePathValidator()] + + def to_python(self, value): + if not URLOrAbsolutePathValidator.is_absolute_path(value): + value = super(URLOrAbsolutePathField, self).to_python(value) + return value class SearchForm(forms.Form): def __init__(self, *args, **kwargs): @@ -22,11 +43,11 @@ class SearchForm(forms.Form): class ExternalLinkChooserForm(forms.Form): - url = forms.URLField(required=True) + url = URLOrAbsolutePathField(required=True) class ExternalLinkChooserWithLinkTextForm(forms.Form): - url = forms.URLField(required=True) + url = URLOrAbsolutePathField(required=True) link_text = forms.CharField(required=True) diff --git a/wagtail/wagtailadmin/tests/test_page_chooser.py b/wagtail/wagtailadmin/tests/test_page_chooser.py index 475e4f7b19..834acde8cf 100644 --- a/wagtail/wagtailadmin/tests/test_page_chooser.py +++ b/wagtail/wagtailadmin/tests/test_page_chooser.py @@ -101,9 +101,24 @@ class TestChooserExternalLink(TestCase, WagtailTestUtils): self.assertEqual(self.get({'prompt_for_link_text': 'foo'}).status_code, 200) def test_create_link(self): - request = self.post({'url': 'http://www.example.com'}) - self.assertContains(request, "'url': 'http://www.example.com/',") - self.assertContains(request, "'title': 'http://www.example.com/'") + response = self.post({'url': 'http://www.example.com'}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "'onload'") # indicates success / post back to calling page + self.assertContains(response, "'url': 'http://www.example.com/',") + self.assertContains(response, "'title': 'http://www.example.com/'") + + def test_invalid_url(self): + response = self.post({'url': 'ntp://www.example.com'}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "'html'") # indicates failure / show error message + self.assertContains(response, "Enter a valid URL.") + + def test_allow_local_url(self): + response = self.post({'url': '/admin/'}) + self.assertEqual(response.status_code, 200) + self.assertContains(response, "'onload'") # indicates success / post back to calling page + self.assertContains(response, "'url': '/admin/',") + self.assertContains(response, "'title': '/admin/'") class TestChooserEmailLink(TestCase, WagtailTestUtils):