diff --git a/.travis.yml b/.travis.yml index cc1cb74e86..e23794d4f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,7 +13,7 @@ services: install: - python setup.py install - pip install psycopg2 pyelasticsearch elasticutils==0.8.2 wand - - pip install coveralls unittest2 + - pip install coveralls # Pre-test configuration before_script: - psql -c 'create database wagtaildemo;' -U postgres diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0a6024bd16..9a5a5669fe 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,16 @@ Changelog ========= +0.4 (xx.xx.20xx) +~~~~~~~~~~~~~~~~~~ + * When logged in user visits login page, they are now redirected to the dashboard + +0.3.1 (03.06.2014) +~~~~~~~~~~~~~~~~~~ + * Fix: When constructing dummy requests for pages with no routable URL, fall back on a hostname from ALLOWED_HOSTS and finally 'localhost', to avoid 'Invalid HTTP_HOST header' errors on preview when DEBUG=False. + * Fix: Ensure that url_path is populated when previewing a newly created page, to avoid unnecessarily taking the above fallback. + * Fix: Deleting an item from an InlinePanel, then generating a validation error on saving, no longer causes the deleted item to confusingly reappear with an error of its own. + 0.3 (28.05.2014) ~~~~~~~~~~~~~~~~ * Added toolbar to allow logged-in users to add and edit pages from the site front-end diff --git a/docs/building_your_site/frontenddevelopers.rst b/docs/building_your_site/frontenddevelopers.rst index d009a1af87..e5f5833979 100644 --- a/docs/building_your_site/frontenddevelopers.rst +++ b/docs/building_your_site/frontenddevelopers.rst @@ -7,8 +7,6 @@ For Front End developers Overview ======================== -This page is aimed at non-Django-literate Front End developers. - Wagtail uses Django's templating language. For developers new to Django, start with Django's own template documentation: https://docs.djangoproject.com/en/dev/topics/templates/ @@ -75,7 +73,7 @@ Images uploaded to Wagtail by its users (as opposed to a developer's static file Unlike other CMS, adding images to a page does not involve choosing a "version" of the image to use. Wagtail has no predefined image "formats" or "sizes". Instead the template developer defines image manipulation to occur *on the fly* when the image is requested, via a special syntax within the template. -Images from the library **must** be requested using this syntax, but a developer's static images can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly. +Images from the library must be requested using this syntax, but a developer's static images can be added via conventional means e.g ``img`` tags. Only images from the library can be manipulated on the fly. Read more about the image manipulation syntax here :ref:`image_tag`. @@ -84,7 +82,7 @@ Read more about the image manipulation syntax here :ref:`image_tag`. Template tags & filters ======================== -In addition to Django's standard tags and filters, Wagtail provides some of it's own, which can be ``load``-ed `as you would any other `_ +In addition to Django's standard tags and filters, Wagtail provides some of its own, which can be ``load``-ed `as you would any other `_ .. _image_tag: @@ -146,15 +144,15 @@ The available ``method`` s are: Resize and **crop** to fill the **exact** dimensions. - This can be particularly useful for websites requiring square thumbnails of arbitrary images. e.g A landscape image of width 2000, height 1000, treated with ``fill`` dimensions ``200x200`` would have it's height reduced to 200, then it's width (ordinarily 400) cropped to 200. + This can be particularly useful for websites requiring square thumbnails of arbitrary images. For example, a landscape image of width 2000, height 1000, treated with ``fill`` dimensions ``200x200`` would have its height reduced to 200, then its width (ordinarily 400) cropped to 200. **The crop always aligns on the centre of the image.** .. Note:: - Wagtail *does not allow deforming or stretching images*. Image dimension ratios will always be kept. Wagtail also *does not support upscaling*. Small images forced to appear at larger sizes will "max out" at their their native dimensions. + Wagtail does not allow deforming or stretching images. Image dimension ratios will always be kept. Wagtail also *does not support upscaling*. Small images forced to appear at larger sizes will "max out" at their their native dimensions. .. Note:: - Wagtail does not make the "original" version of an image explicitly available. To request it, it's suggested you rely on the lack of upscaling by requesting an image much larger than it's maximum dimensions. e.g to insert an image who's dimensions are uncertain/unknown at it's maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide. + Wagtail does not make the "original" version of an image explicitly available. To request it, you could rely on the lack of upscaling by requesting an image larger than its maximum dimensions. e.g to insert an image whose dimensions are unknown at its maximum size, try: ``{% image self.image width-10000 %}``. This assumes the image is unlikely to be larger than 10000px wide. .. _image_tag_alt: diff --git a/requirements-dev.txt b/requirements-dev.txt index 008d24d2ec..d35d6b087e 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,7 +1,3 @@ -# Requirements essential for developing wagtail (not needed to run it) - -unittest2==0.5.1 - # For coverage and PEP8 linting coverage==3.7.1 flake8==2.1.0 diff --git a/setup.py b/setup.py index 4f5b87c121..7b26817cd8 100644 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ except ImportError: setup( name='wagtail', - version='0.3', + version='0.3.1', description='A Django content management system focused on flexibility and user experience', author='Matthew Westcott', author_email='matthew.westcott@torchbox.com', diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js index cbff2c8af8..4e8e1b02dc 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js @@ -181,6 +181,17 @@ function InlinePanel(opts) { self.updateMoveButtonDisabledStates(); }); } + + /* Hide container on page load if it is marked as deleted. Remove the error + message so that it doesn't count towards the number of errors on the tab at the + top of the page. */ + if ( $('#' + deleteInputId).val() === "1" ) { + $('#' + childId).hide(0, function() { + self.updateMoveButtonDisabledStates(); + self.setHasContent(); + }); + $('#' + childId).find(".error-message").remove(); + } }; self.formsUl = $('#' + opts.formsetPrefix + '-FORMS'); diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html index c0cf872e25..3dc8272b07 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/account/password_reset/complete.html @@ -13,6 +13,6 @@ {% block furniture %}

{% trans "Password change successful" %}

-

{% trans "Login" %}

+

{% trans "Login" %}

{% endblock %} \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/login.html b/wagtail/wagtailadmin/templates/wagtailadmin/login.html index 012dd32f89..22682500f7 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/login.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/login.html @@ -20,7 +20,7 @@ {% endif %} -
+ {% csrf_token %}

{% trans "Sign in to Wagtail" %}

diff --git a/wagtail/wagtailadmin/tests/test_account_management.py b/wagtail/wagtailadmin/tests/test_account_management.py index 95d54d7d93..63c70779b2 100644 --- a/wagtail/wagtailadmin/tests/test_account_management.py +++ b/wagtail/wagtailadmin/tests/test_account_management.py @@ -49,7 +49,6 @@ class TestAuthentication(TestCase): self.assertTrue('_auth_user_id' in self.client.session) self.assertEqual(self.client.session['_auth_user_id'], User.objects.get(username='test').id) - @unittest.expectedFailure # See: https://github.com/torchbox/wagtail/issues/25 def test_already_logged_in_redirect(self): """ This tests that a user who is already logged in is automatically diff --git a/wagtail/wagtailadmin/urls.py b/wagtail/wagtailadmin/urls.py index 8fbf2f6e77..806240c5c5 100644 --- a/wagtail/wagtailadmin/urls.py +++ b/wagtail/wagtailadmin/urls.py @@ -5,15 +5,8 @@ from wagtail.wagtailadmin.forms import LoginForm, PasswordResetForm from wagtail.wagtailadmin.views import account, chooser, home, pages, tags, userbar from wagtail.wagtailadmin import hooks -urlpatterns = [ - url( - r'^login/$', 'django.contrib.auth.views.login', { - 'template_name': 'wagtailadmin/login.html', - 'authentication_form': LoginForm, - 'extra_context': {'show_password_reset': getattr(settings, 'WAGTAIL_PASSWORD_MANAGEMENT_ENABLED', True)}, - }, name='wagtailadmin_login' - ), +urlpatterns = [ # Password reset url( r'^password_reset/$', 'django.contrib.auth.views.password_reset', { @@ -81,6 +74,7 @@ urlpatterns += [ url(r'^tag-autocomplete/$', tags.autocomplete, name='wagtailadmin_tag_autocomplete'), + url(r'^login/$', account.login, name='wagtailadmin_login'), url(r'^account/$', account.account, name='wagtailadmin_account'), url(r'^account/change_password/$', account.change_password, name='wagtailadmin_account_change_password'), url(r'^logout/$', account.logout, name='wagtailadmin_logout'), diff --git a/wagtail/wagtailadmin/views/account.py b/wagtail/wagtailadmin/views/account.py index 8479ea6b0e..c5e461f55c 100644 --- a/wagtail/wagtailadmin/views/account.py +++ b/wagtail/wagtailadmin/views/account.py @@ -3,8 +3,13 @@ from django.shortcuts import render, redirect from django.contrib import messages from django.contrib.auth.forms import SetPasswordForm from django.contrib.auth.decorators import permission_required -from django.contrib.auth.views import logout as auth_logout +from django.contrib.auth.views import logout as auth_logout, login as auth_login from django.utils.translation import ugettext as _ +from django.views.decorators.debug import sensitive_post_parameters +from django.views.decorators.cache import never_cache + +from wagtail.wagtailadmin import forms + @permission_required('wagtailadmin.access_admin') def account(request): @@ -37,6 +42,21 @@ def change_password(request): }) +@sensitive_post_parameters() +@never_cache +def login(request): + if request.user.is_authenticated(): + return redirect('wagtailadmin_home') + else: + return auth_login(request, + template_name='wagtailadmin/login.html', + authentication_form=forms.LoginForm, + extra_context={ + 'show_password_reset': getattr(settings, 'WAGTAIL_PASSWORD_MANAGEMENT_ENABLED', True), + }, + ) + + def logout(request): response = auth_logout(request, next_page = 'wagtailadmin_login') diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 308f6fdd41..9bf6dbf444 100644 --- a/wagtail/wagtailadmin/views/pages.py +++ b/wagtail/wagtailadmin/views/pages.py @@ -5,7 +5,7 @@ from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.contrib.auth.decorators import permission_required from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _ from django.views.decorators.vary import vary_on_headers from wagtail.wagtailadmin.edit_handlers import TabbedInterface, ObjectList @@ -349,6 +349,10 @@ def preview_on_create(request, content_type_app_name, content_type_model_name, p if form.is_valid(): form.save(commit=False) + # ensure that our unsaved page instance has a suitable url set + parent_page = get_object_or_404(Page, id=parent_page_id).specific + page.set_url_path(parent_page) + # This view will generally be invoked as an AJAX request; as such, in the case of # an error Django will return a plaintext response. This isn't what we want, since # we will be writing the response back to an HTML page regardless of success or diff --git a/wagtail/wagtailcore/models.py b/wagtail/wagtailcore/models.py index f72e6f6f9e..30ffc8a77f 100644 --- a/wagtail/wagtailcore/models.py +++ b/wagtail/wagtailcore/models.py @@ -581,7 +581,12 @@ class Page(MP_Node, ClusterableModel, Indexed): path = url_info.path port = url_info.port or 80 else: - hostname = 'example.com' + # Cannot determine a URL to this page - cobble one together based on + # whatever we find in ALLOWED_HOSTS + try: + hostname = settings.ALLOWED_HOSTS[0] + except IndexError: + hostname = 'localhost' path = '/' port = 80 diff --git a/wagtail/wagtailsnippets/tests.py b/wagtail/wagtailsnippets/tests.py index fd283f7f55..727e5e52df 100644 --- a/wagtail/wagtailsnippets/tests.py +++ b/wagtail/wagtailsnippets/tests.py @@ -5,6 +5,8 @@ from django.contrib.auth.models import User from wagtail.tests.utils import login, unittest from wagtail.tests.models import Advert +from wagtail.wagtailsnippets.views.snippets import get_content_type_from_url_params, get_snippet_edit_handler +from wagtail.wagtailsnippets.edit_handlers import SnippetChooserPanel class TestSnippetIndexView(TestCase): def setUp(self): @@ -137,3 +139,32 @@ class TestSnippetDelete(TestCase): # Check that the page is gone self.assertEqual(Advert.objects.filter(text='test_advert').count(), 0) + + +class TestSnippetChooserPanel(TestCase): + def setUp(self): + content_type = get_content_type_from_url_params('tests', + 'advert') + + test_snippet = Advert() + test_snippet.text = 'test_advert' + test_snippet.url = 'http://www.example.com/' + test_snippet.save() + + edit_handler_class = get_snippet_edit_handler(Advert) + form_class = edit_handler_class.get_form_class(Advert) + form = form_class(instance=test_snippet) + + self.snippet_chooser_panel_class = SnippetChooserPanel('text', content_type) + self.snippet_chooser_panel = self.snippet_chooser_panel_class(instance=test_snippet, + form=form) + + def test_create_snippet_chooser_panel_class(self): + self.assertEqual(self.snippet_chooser_panel_class.__name__, '_SnippetChooserPanel') + + def test_render_as_field(self): + self.assertTrue('test_advert' in self.snippet_chooser_panel.render_as_field()) + + def test_render_js(self): + self.assertTrue("createSnippetChooser(fixPrefix('id_text'), 'contenttypes/contenttype');" + in self.snippet_chooser_panel.render_js())