diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 0a6024bd1..b708cfd21 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,6 +1,15 @@ Changelog ========= +0.4 (xx.xx.20xx) +~~~~~~~~~~~~~~~~~~ + +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 d009a1af8..e5f583397 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/setup.py b/setup.py index 4f5b87c12..7b26817cd 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/core.js b/wagtail/wagtailadmin/static/wagtailadmin/js/core.js index 380c3d179..85c4ca31e 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/core.js +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/core.js @@ -93,17 +93,6 @@ $(function(){ }); }); - $(".nav-main .more > a").bind('click keydown', function(){ - var currentAlt = $(this).data('altstate'); - var newAlt = $(this).html(); - - $(this).html(currentAlt); - $(this).data('altstate', newAlt); - $(this).toggleClass('icon-arrow-up icon-arrow-down'); - $(this).parent().find('ul').toggle('fast'); - return false; - }); - $('#menu-search input').bind('focus', function(){ $('#menu-search').addClass('focussed'); }).bind('blur', function(){ diff --git a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js index af624572e..7f27c4a37 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js +++ b/wagtail/wagtailadmin/static/wagtailadmin/js/page-editor.js @@ -151,6 +151,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/static/wagtailadmin/scss/core.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss index e622680bd..60200de20 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/core.scss @@ -226,19 +226,6 @@ img{ } } - .more{ - border:0; - - > a{ - &:before{ - margin-right:0.4em; - } - font-size:0.8em; - padding:0.2em 1.2em; - background-color:$color-grey-1-1; - } - } - .avatar{ display:none; } @@ -312,10 +299,6 @@ img{ } } - .js .nav-main .more ul{ - display:none; - } - .explorer{ position:absolute; margin-top:70px; @@ -626,7 +609,7 @@ footer, .logo{ padding-right:$desktop-nice-padding; } - body{ + .wrapper{ margin-left:$menu-width; } @@ -645,7 +628,7 @@ footer, .logo{ left:0; height:100%; width:$menu-width; - margin-left: -$menu-width; + margin-left: 0; .inner{ height:100%; diff --git a/wagtail/wagtailadmin/static/wagtailadmin/scss/panels/rich-text.scss b/wagtail/wagtailadmin/static/wagtailadmin/scss/panels/rich-text.scss index 8b26f36ab..c951a0bc5 100644 --- a/wagtail/wagtailadmin/static/wagtailadmin/scss/panels/rich-text.scss +++ b/wagtail/wagtailadmin/static/wagtailadmin/scss/panels/rich-text.scss @@ -3,14 +3,13 @@ .hallotoolbar{ position:absolute; - left:50px; + left:$mobile-nice-padding; z-index:5; margin-top:4em; margin-left:0em; } .hallotoolbar.affixed{ position:fixed; - margin-left:140px; margin-top:0; } .hallotoolbar button{ @@ -148,18 +147,8 @@ } } } - -@media screen and (min-width: $breakpoint-desktop-larger){ - /* .hallotoolbar{ - margin:0 auto; - position:absolute; - left:-$menu-width; - right:0; - z-index:5; - margin-top:3em; +@media screen and (min-width: $breakpoint-mobile){ + .hallotoolbar{ + left:$menu-width + $desktop-nice-padding; } - .hallotoolbar.affixed{ - position:fixed; - margin:0 auto; - }*/ -} + } \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html index 7078834ae..7258c5429 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/pages/list.html @@ -229,4 +229,24 @@

{% trans "No pages have been created." %}{% if parent_page and parent_page_perms.can_add_subpage %} {% blocktrans %}Why not add one?{% endblocktrans %}{% endif %} {% endif %} - \ No newline at end of file + + +{% if parent_page and pages and pages.paginator %} +

+{% endif %} \ No newline at end of file diff --git a/wagtail/wagtailadmin/templates/wagtailadmin/shared/main_nav.html b/wagtail/wagtailadmin/templates/wagtailadmin/shared/main_nav.html index f7eb6528a..ce06d895c 100644 --- a/wagtail/wagtailadmin/templates/wagtailadmin/shared/main_nav.html +++ b/wagtail/wagtailadmin/templates/wagtailadmin/shared/main_nav.html @@ -10,19 +10,5 @@ {% trans "Log out" %} - {% if request.user.is_superuser %} {# for now, 'More' links will be superuser-only #} -
  • - {% trans 'More' %} - -
  • - {% endif %} - diff --git a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py index 311163ca3..2d85c355e 100644 --- a/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py +++ b/wagtail/wagtailadmin/templatetags/wagtailadmin_tags.py @@ -26,17 +26,6 @@ def explorer_subnav(nodes): } -@register.assignment_tag -def get_wagtailadmin_tab_urls(): - resolver = urlresolvers.get_resolver(None) - return [ - (key, value[2].get("title", key)) - for key, value - in resolver.reverse_dict.items() - if isinstance(key, basestring) and key.startswith('wagtailadmin_tab_') - ] - - @register.inclusion_tag('wagtailadmin/shared/main_nav.html', takes_context=True) def main_nav(context): menu_items = [ diff --git a/wagtail/wagtailadmin/tests/test_pages_views.py b/wagtail/wagtailadmin/tests/test_pages_views.py index ccaa67dae..14e57a35e 100644 --- a/wagtail/wagtailadmin/tests/test_pages_views.py +++ b/wagtail/wagtailadmin/tests/test_pages_views.py @@ -25,7 +25,7 @@ class TestPageExplorer(TestCase): response = self.client.get(reverse('wagtailadmin_explore', args=(self.root_page.id, ))) self.assertEqual(response.status_code, 200) self.assertEqual(self.root_page, response.context['parent_page']) - self.assertTrue(response.context['pages'].filter(id=self.child_page.id).exists()) + self.assertTrue(response.context['pages'].paginator.object_list.filter(id=self.child_page.id).exists()) class TestPageCreation(TestCase): diff --git a/wagtail/wagtailadmin/views/pages.py b/wagtail/wagtailadmin/views/pages.py index 308f6fdd4..69e6b7f4f 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 @@ -33,6 +33,17 @@ def index(request, parent_page_id=None): else: ordering = 'title' + # Pagination + if ordering != 'ord': + p = request.GET.get('p', 1) + paginator = Paginator(pages, 50) + try: + pages = paginator.page(p) + except PageNotAnInteger: + pages = paginator.page(1) + except EmptyPage: + pages = paginator.page(paginator.num_pages) + return render(request, 'wagtailadmin/pages/index.html', { 'parent_page': parent_page, 'ordering': ordering, @@ -349,6 +360,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 f72e6f6f9..30ffc8a77 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/wagtailembeds/format.py b/wagtail/wagtailembeds/format.py index 453c73641..1654be989 100644 --- a/wagtail/wagtailembeds/format.py +++ b/wagtail/wagtailembeds/format.py @@ -17,7 +17,7 @@ def embed_to_frontend_html(url): ratio = "0" # Render template - render_to_string('wagtailembeds/embed_frontend.html', { + return render_to_string('wagtailembeds/embed_frontend.html', { 'embed': embed, 'ratio': ratio, }) diff --git a/wagtail/wagtailembeds/templates/wagtailembeds/embed_frontend.html b/wagtail/wagtailembeds/templates/wagtailembeds/embed_frontend.html index feb209311..b97dceb83 100644 --- a/wagtail/wagtailembeds/templates/wagtailembeds/embed_frontend.html +++ b/wagtail/wagtailembeds/templates/wagtailembeds/embed_frontend.html @@ -1,3 +1,3 @@
    - {{ embed.html }} + {{ embed.html|safe }}
    diff --git a/wagtail/wagtailredirects/wagtail_hooks.py b/wagtail/wagtailredirects/wagtail_hooks.py index 22ae97320..4dbe1a028 100644 --- a/wagtail/wagtailredirects/wagtail_hooks.py +++ b/wagtail/wagtailredirects/wagtail_hooks.py @@ -1,11 +1,24 @@ +from django.core import urlresolvers from django.conf.urls import include, url +from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailadmin import hooks from wagtail.wagtailredirects import urls +from wagtail.wagtailadmin.menu import MenuItem + def register_admin_urls(): return [ url(r'^redirects/', include(urls)), ] hooks.register('register_admin_urls', register_admin_urls) + + +def construct_main_menu(request, menu_items): + # TEMPORARY: Only show if the user is a superuser + if request.user.is_superuser: + menu_items.append( + MenuItem(_('Redirects'), urlresolvers.reverse('wagtailredirects_index'), classnames='icon icon-redirect', order=800) + ) +hooks.register('construct_main_menu', construct_main_menu) diff --git a/wagtail/wagtailsearch/wagtail_hooks.py b/wagtail/wagtailsearch/wagtail_hooks.py index b8bbd2c06..1a656c0ef 100644 --- a/wagtail/wagtailsearch/wagtail_hooks.py +++ b/wagtail/wagtailsearch/wagtail_hooks.py @@ -1,11 +1,24 @@ +from django.core import urlresolvers from django.conf.urls import include, url +from django.utils.translation import ugettext_lazy as _ from wagtail.wagtailadmin import hooks from wagtail.wagtailsearch.urls import admin as admin_urls +from wagtail.wagtailadmin.menu import MenuItem + def register_admin_urls(): return [ url(r'^search/', include(admin_urls)), ] hooks.register('register_admin_urls', register_admin_urls) + + +def construct_main_menu(request, menu_items): + # TEMPORARY: Only show if the user is a superuser + if request.user.is_superuser: + menu_items.append( + MenuItem(_('Editors picks'), urlresolvers.reverse('wagtailsearch_editorspicks_index'), classnames='icon icon-pick', order=900) + ) +hooks.register('construct_main_menu', construct_main_menu) diff --git a/wagtail/wagtailsnippets/tests.py b/wagtail/wagtailsnippets/tests.py index fd283f7f5..727e5e52d 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())