Merge branch 'master' into sass

This commit is contained in:
Dave Cranwell 2014-03-04 17:41:55 +00:00
commit 7cdd021ffd
17 changed files with 727 additions and 230 deletions

View file

@ -82,6 +82,9 @@ if not settings.configured:
'wagtail.wagtailredirects',
'wagtail.tests',
],
PASSWORD_HASHERS=(
'django.contrib.auth.hashers.MD5PasswordHasher', # don't use the intentionally slow default password hasher
),
WAGTAILSEARCH_BACKENDS=WAGTAILSEARCH_BACKENDS,
WAGTAIL_SITE_NAME='Test Site'
)

View file

@ -39,7 +39,7 @@
"model": "wagtailcore.page",
"fields": {
"title": "Events",
"numchild": 1,
"numchild": 2,
"show_in_menus": true,
"live": true,
"depth": 3,
@ -69,7 +69,8 @@
"content_type": ["tests", "eventpage"],
"path": "0001000100010001",
"url_path": "/home/events/christmas/",
"slug": "christmas"
"slug": "christmas",
"owner": 2
}
},
{
@ -84,6 +85,62 @@
}
},
{
"pk": 5,
"model": "wagtailcore.page",
"fields": {
"title": "Tentative Unpublished Event",
"numchild": 1,
"show_in_menus": true,
"live": false,
"depth": 4,
"content_type": ["tests", "eventpage"],
"path": "0001000100010002",
"url_path": "/home/events/tentative-unpublished-event/",
"slug": "tentative-unpublished-event",
"owner": 2
}
},
{
"pk": 5,
"model": "tests.eventpage",
"fields": {
"date_from": "2015-07-04",
"audience": "public",
"location": "The moon",
"body": "<p>I haven't worked out the details yet, but it's going to have cake and ponies</p>",
"cost": "Free"
}
},
{
"pk": 6,
"model": "wagtailcore.page",
"fields": {
"title": "Someone Else's Event",
"numchild": 1,
"show_in_menus": true,
"live": false,
"depth": 4,
"content_type": ["tests", "eventpage"],
"path": "0001000100010003",
"url_path": "/home/events/someone-elses-event/",
"slug": "someone-elses-event",
"owner": 3
}
},
{
"pk": 6,
"model": "tests.eventpage",
"fields": {
"date_from": "2015-07-04",
"audience": "private",
"location": "The moon",
"body": "<p>your name's not down, you're not coming in</p>",
"cost": "Free (but not for you)"
}
},
{
"pk": 1,
"model": "wagtailcore.site",
@ -93,5 +150,140 @@
"port": 80,
"is_default_site": true
}
},
{
"pk": 3,
"model": "auth.group",
"fields": {
"name": "Event editors",
"permissions": [
["access_admin", "wagtailadmin", "admin"],
["add_image", "wagtailimages", "image"],
["change_image", "wagtailimages", "image"],
["delete_image", "wagtailimages", "image"]
]
}
},
{
"pk": 4,
"model": "auth.group",
"fields": {
"name": "Event moderators",
"permissions": [
["access_admin", "wagtailadmin", "admin"],
["add_image", "wagtailimages", "image"],
["change_image", "wagtailimages", "image"],
["delete_image", "wagtailimages", "image"]
]
}
},
{
"pk": 1,
"model": "wagtailcore.grouppagepermission",
"fields": {
"group": ["Event editors"],
"page": 3,
"permission_type": "add"
}
},
{
"pk": 2,
"model": "wagtailcore.grouppagepermission",
"fields": {
"group": ["Event moderators"],
"page": 3,
"permission_type": "add"
}
},
{
"pk": 3,
"model": "wagtailcore.grouppagepermission",
"fields": {
"group": ["Event moderators"],
"page": 3,
"permission_type": "edit"
}
},
{
"pk": 4,
"model": "wagtailcore.grouppagepermission",
"fields": {
"group": ["Event moderators"],
"page": 3,
"permission_type": "publish"
}
},
{
"pk": 1,
"model": "auth.user",
"fields": {
"username": "superuser",
"first_name": "",
"last_name": "",
"is_active": true,
"is_superuser": true,
"is_staff": true,
"groups": [
],
"user_permissions": [],
"password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22",
"email": "superuser@example.com"
}
},
{
"pk": 2,
"model": "auth.user",
"fields": {
"username": "eventeditor",
"first_name": "",
"last_name": "",
"is_active": true,
"is_superuser": false,
"is_staff": false,
"groups": [
["Event editors"]
],
"user_permissions": [],
"password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22",
"email": "eventeditor@example.com"
}
},
{
"pk": 3,
"model": "auth.user",
"fields": {
"username": "eventmoderator",
"first_name": "",
"last_name": "",
"is_active": true,
"is_superuser": false,
"is_staff": false,
"groups": [
["Event moderators"]
],
"user_permissions": [],
"password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22",
"email": "eventmoderator@example.com"
}
},
{
"pk": 4,
"model": "auth.user",
"fields": {
"username": "inactiveuser",
"first_name": "",
"last_name": "",
"is_active": false,
"is_superuser": false,
"is_staff": false,
"groups": [
["Event moderators"]
],
"user_permissions": [],
"password": "md5$seasalt$1e9bf2bf5606aa5c39852cc30f0f6f22",
"email": "inactiveuser@example.com"
}
}
]

View file

@ -0,0 +1,10 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Event: {{ self.title }}</title>
</head>
<body>
<h1>{{ self.title }}</h1>
<h2>Event</h2>
</body>
</html>

View file

@ -1,28 +0,0 @@
# Hallo - a rich text editing jQuery UI widget
# (c) 2011 Henri Bergius, IKS Consortium
# Hallo may be freely distributed under the MIT license
((jQuery) ->
jQuery.widget "IKS.hallohr",
options:
editable: null
toolbar: null
uuid: ''
buttonCssClass: null
populateToolbar: (toolbar) ->
buttonset = jQuery "<span class=\"#{@widgetName}\"></span>"
buttonElement = jQuery '<span></span>'
buttonElement.hallobutton
uuid: @options.uuid
editable: @options.editable
label: "Horizontal rule"
command: "insertHorizontalRule"
icon: "icon-horizontalrule"
cssClass: @options.buttonCssClass
buttonset.append buttonElement
buttonset.hallobuttonset()
toolbar.append buttonset
)(jQuery)

View file

@ -0,0 +1,31 @@
// Generated by CoffeeScript 1.6.2
(function() {
(function(jQuery) {
return jQuery.widget("IKS.hallohr", {
options: {
editable: null,
toolbar: null,
uuid: '',
buttonCssClass: null
},
populateToolbar: function(toolbar) {
var buttonElement, buttonset;
buttonset = jQuery("<span class=\"" + this.widgetName + "\"></span>");
buttonElement = jQuery('<span></span>');
buttonElement.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: "Horizontal rule",
command: "insertHorizontalRule",
icon: "icon-horizontalrule",
cssClass: this.options.buttonCssClass
});
buttonset.append(buttonElement);
buttonset.hallobuttonset();
return toolbar.append(buttonset);
}
});
})(jQuery);
}).call(this);

View file

@ -1,68 +0,0 @@
# plugin for hallo.js to allow inserting links using Wagtail's page chooser
(($) ->
$.widget "IKS.hallowagtaillink",
options:
uuid: ''
editable: null
populateToolbar: (toolbar) ->
widget = this
getEnclosingLink = () ->
# if cursor is currently within a link element, return it, otherwise return null
node = widget.options.editable.getSelection().commonAncestorContainer
return $(node).parents('a').get(0)
# Create an element for holding the button
button = $('<span></span>')
button.hallobutton
uuid: @options.uuid
editable: @options.editable
label: 'Links'
icon: 'icon-link'
command: null
queryState: (event) ->
button.hallobutton('checked', !!getEnclosingLink())
# Append the button to toolbar
toolbar.append button
button.on "click", (event) ->
enclosingLink = getEnclosingLink()
if enclosingLink
# remove existing link
$(enclosingLink).replaceWith(enclosingLink.innerHTML)
button.hallobutton('checked', false)
widget.options.editable.element.trigger('change')
else
# commence workflow to add a link
lastSelection = widget.options.editable.getSelection()
if lastSelection.collapsed
# TODO: don't hard-code this, as it may be changed in urls.py
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true&prompt_for_link_text=true'
else
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true'
ModalWorkflow
url: url
responses:
pageChosen: (pageData) ->
a = document.createElement('a')
a.setAttribute('href', pageData.url)
if pageData.id
a.setAttribute('data-id', pageData.id)
a.setAttribute('data-linktype', 'page')
if (not lastSelection.collapsed) and lastSelection.canSurroundContents()
# use the selected content as the link text
lastSelection.surroundContents(a)
else
# no text is selected, so use the page title as link text
a.appendChild(document.createTextNode pageData.title)
lastSelection.insertNode(a)
widget.options.editable.element.trigger('change')
)(jQuery)

View file

@ -0,0 +1,74 @@
// Generated by CoffeeScript 1.6.2
(function() {
(function($) {
return $.widget("IKS.hallowagtaillink", {
options: {
uuid: '',
editable: null
},
populateToolbar: function(toolbar) {
var button, getEnclosingLink, widget;
widget = this;
getEnclosingLink = function() {
var node;
node = widget.options.editable.getSelection().commonAncestorContainer;
return $(node).parents('a').get(0);
};
button = $('<span></span>');
button.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: 'Links',
icon: 'icon-link',
command: null,
queryState: function(event) {
return button.hallobutton('checked', !!getEnclosingLink());
}
});
toolbar.append(button);
return button.on("click", function(event) {
var enclosingLink, lastSelection, url;
enclosingLink = getEnclosingLink();
if (enclosingLink) {
$(enclosingLink).replaceWith(enclosingLink.innerHTML);
button.hallobutton('checked', false);
return widget.options.editable.element.trigger('change');
} else {
lastSelection = widget.options.editable.getSelection();
if (lastSelection.collapsed) {
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true&prompt_for_link_text=true';
} else {
url = window.chooserUrls.pageChooser + '?allow_external_link=true&allow_email_link=true';
}
return ModalWorkflow({
url: url,
responses: {
pageChosen: function(pageData) {
var a;
a = document.createElement('a');
a.setAttribute('href', pageData.url);
if (pageData.id) {
a.setAttribute('data-id', pageData.id);
a.setAttribute('data-linktype', 'page');
}
if ((!lastSelection.collapsed) && lastSelection.canSurroundContents()) {
lastSelection.surroundContents(a);
} else {
a.appendChild(document.createTextNode(pageData.title));
lastSelection.insertNode(a);
}
return widget.options.editable.element.trigger('change');
}
}
});
}
});
}
});
})(jQuery);
}).call(this);

View file

@ -13,16 +13,16 @@
<script src="{{ STATIC_URL }}wagtailadmin/js/expanding_formset.js"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/modal-workflow.js"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/hallo-plugins/hallo-wagtail-toolbar.js"></script>
<script type="text/coffeescript" src="{{ STATIC_URL }}wagtailadmin/js/hallo-plugins/hallo-wagtaillink.coffee"></script>
<script type="text/coffeescript" src="{{ STATIC_URL }}wagtailadmin/js/hallo-plugins/hallo-hr.coffee"></script>
<script type="text/coffeescript" src="{{ STATIC_URL }}wagtailimages/js/hallo-plugins/hallo-wagtailimage.coffee"></script>
<script type="text/coffeescript" src="{{ STATIC_URL }}wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.coffee"></script>
<script type="text/coffeescript" src="{{ STATIC_URL }}wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.coffee"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/hallo-plugins/hallo-hr.js"></script>
<script src="{{ STATIC_URL }}wagtailimages/js/hallo-plugins/hallo-wagtailimage.js"></script>
<script src="{{ STATIC_URL }}wagtailembeds/js/hallo-plugins/hallo-wagtailembeds.js"></script>
<script src="{{ STATIC_URL }}wagtaildocs/js/hallo-plugins/hallo-wagtaildoclink.js"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/page-editor.js"></script>
<script src="{{ STATIC_URL }}wagtailadmin/js/page-chooser.js"></script>
{% comment %}
TODO: have a mechanism to specify image-chooser.js (and hallo-wagtailimage.coffee)
TODO: have a mechanism to specify image-chooser.js (and hallo-wagtailimage.js)
within the wagtailimages app -
ideally wagtailadmin shouldn't have to know anything at all about wagtailimages
TODO: a method of injecting these sorts of things on demand when the modal is spawned.

View file

@ -75,7 +75,7 @@
</td>
<td class="type">{{ parent_page.content_type.model_class.get_verbose_name }}</td>
<td class="status">
{% if not choosing and parent_page.live and not parent_page.is_root and 'view_live' not in hide_actions|default:'' %}
{% if not choosing and not moving and parent_page.live and not parent_page.is_root and 'view_live' not in hide_actions|default:'' %}
<a href="{{ parent_page.url }}" target="_blank" class="status-tag {% if parent_page.status_string != "draft" %}primary{% endif %}">{{ parent_page.status_string|capfirst }}</a>
{% else %}
<span class="status-tag {% if parent_page.status_string != "draft" %}primary{% endif %}">{{ parent_page.status_string|capfirst }}</span>
@ -208,7 +208,7 @@
{% endif %}
<td class="type">{{ page.content_type.model_class.get_verbose_name }}</td>
<td class="status">
{% if not choosing and page.live and 'view_live' not in hide_actions|default:'' %}
{% if not choosing and not moving and page.live and 'view_live' not in hide_actions|default:'' %}
<a href="{{ page.url }}" target="_blank" class="status-tag {% if page.status_string != "draft" %}primary{% endif %}">{{ page.status_string }}</a>
{% else %}
<span class="status-tag {% if page.status_string != "draft" %}primary{% endif %}">{{ page.status_string }}</span>

View file

@ -6,11 +6,11 @@ from modelcluster.models import ClusterableModel
from django.db import models, connection, transaction
from django.db.models import get_model, Q
from django.http import Http404
from django.shortcuts import render
from django.core.cache import cache
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Group
from django.conf import settings
from django.template.response import TemplateResponse
from django.utils.translation import ugettext_lazy as _
from wagtail.wagtailcore.util import camelcase_to_underscore
@ -326,7 +326,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
return revision.as_page_object()
def serve(self, request):
return render(request, self.template, {
return TemplateResponse(request, self.template, {
'self': self
})
@ -742,7 +742,7 @@ class PagePermissionTester(object):
def can_move_to(self, destination):
# reject the logically impossible cases first
if self.page == destination or destination.is_child_of(self.page):
if self.page == destination or destination.is_descendant_of(self.page):
return False
# and shortcut the trivial 'everything' / 'nothing' permissions

View file

@ -1,10 +1,37 @@
from django.test import TestCase
from django.test import TestCase, Client
from django.http import HttpRequest, Http404
from django.contrib.auth.models import User
from wagtail.wagtailcore.models import Page, Site
from wagtail.tests.models import EventPage
class TestRouting(TestCase):
fixtures = ['test.json']
def test_find_site_for_request(self):
default_site = Site.objects.get(is_default_site=True)
events_page = Page.objects.get(url_path='/home/events/')
events_site = Site.objects.create(hostname='events.example.com', root_page=events_page)
# requests without a Host: header should be directed to the default site
request = HttpRequest()
request.path = '/'
self.assertEqual(Site.find_for_request(request), default_site)
# requests with a known Host: header should be directed to the specific site
request = HttpRequest()
request.path = '/'
request.META['HTTP_HOST'] = 'events.example.com'
self.assertEqual(Site.find_for_request(request), events_site)
# requests with an unrecognised Host: header should be directed to the default site
request = HttpRequest()
request.path = '/'
request.META['HTTP_HOST'] = 'unknown.example.com'
self.assertEqual(Site.find_for_request(request), default_site)
def test_urls(self):
default_site = Site.objects.get(is_default_site=True)
homepage = Page.objects.get(url_path='/home/')
@ -38,3 +65,234 @@ class TestRouting(TestCase):
self.assertEqual(christmas_page.url, 'http://events.example.com/christmas/')
self.assertEqual(christmas_page.relative_url(default_site), 'http://events.example.com/christmas/')
self.assertEqual(christmas_page.relative_url(events_site), '/christmas/')
def test_request_routing(self):
homepage = Page.objects.get(url_path='/home/')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
request = HttpRequest()
request.path = '/events/christmas/'
response = homepage.route(request, ['events', 'christmas'])
self.assertEqual(response.status_code, 200)
self.assertEqual(response.context_data['self'], christmas_page)
used_template = response.resolve_template(response.template_name)
self.assertEqual(used_template.name, 'tests/event_page.html')
def test_route_to_unknown_page_returns_404(self):
homepage = Page.objects.get(url_path='/home/')
request = HttpRequest()
request.path = '/events/quinquagesima/'
with self.assertRaises(Http404):
homepage.route(request, ['events', 'quinquagesima'])
def test_route_to_unpublished_page_returns_404(self):
homepage = Page.objects.get(url_path='/home/')
request = HttpRequest()
request.path = '/events/tentative-unpublished-event/'
with self.assertRaises(Http404):
homepage.route(request, ['events', 'tentative-unpublished-event'])
class TestServeView(TestCase):
fixtures = ['test.json']
def test_serve(self):
response = self.client.get('/events/christmas/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.templates[0].name, 'tests/event_page.html')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
self.assertEqual(response.context['self'], christmas_page)
self.assertContains(response, '<h1>Christmas</h1>')
self.assertContains(response, '<h2>Event</h2>')
def test_serve_unknown_page_returns_404(self):
response = self.client.get('/events/quinquagesima/')
self.assertEqual(response.status_code, 404)
def test_serve_unpublished_page_returns_404(self):
response = self.client.get('/events/tentative-unpublished-event/')
self.assertEqual(response.status_code, 404)
def test_serve_with_multiple_sites(self):
events_page = Page.objects.get(url_path='/home/events/')
Site.objects.create(hostname='events.example.com', root_page=events_page)
response = self.client.get('/christmas/', HTTP_HOST='events.example.com')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.templates[0].name, 'tests/event_page.html')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
self.assertEqual(response.context['self'], christmas_page)
self.assertContains(response, '<h1>Christmas</h1>')
self.assertContains(response, '<h2>Event</h2>')
# same request to the default host should return a 404
c = Client()
response = c.get('/christmas/', HTTP_HOST='localhost')
self.assertEqual(response.status_code, 404)
class TestPagePermission(TestCase):
fixtures = ['test.json']
def test_nonpublisher_page_permissions(self):
event_editor = User.objects.get(username='eventeditor')
homepage = Page.objects.get(url_path='/home/')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/')
someone_elses_event_page = EventPage.objects.get(url_path='/home/events/someone-elses-event/')
homepage_perms = homepage.permissions_for_user(event_editor)
christmas_page_perms = christmas_page.permissions_for_user(event_editor)
unpub_perms = unpublished_event_page.permissions_for_user(event_editor)
someone_elses_event_perms = someone_elses_event_page.permissions_for_user(event_editor)
self.assertFalse(homepage_perms.can_add_subpage())
self.assertTrue(christmas_page_perms.can_add_subpage())
self.assertTrue(unpub_perms.can_add_subpage())
self.assertTrue(someone_elses_event_perms.can_add_subpage())
self.assertFalse(homepage_perms.can_edit())
self.assertTrue(christmas_page_perms.can_edit())
self.assertTrue(unpub_perms.can_edit())
self.assertFalse(someone_elses_event_perms.can_edit()) # basic 'add' permission doesn't allow editing pages owned by someone else
self.assertFalse(homepage_perms.can_delete())
self.assertFalse(christmas_page_perms.can_delete()) # cannot delete because it is published
self.assertTrue(unpub_perms.can_delete())
self.assertFalse(someone_elses_event_perms.can_delete())
self.assertFalse(homepage_perms.can_publish())
self.assertFalse(christmas_page_perms.can_publish())
self.assertFalse(unpub_perms.can_publish())
self.assertFalse(homepage_perms.can_unpublish())
self.assertFalse(christmas_page_perms.can_unpublish())
self.assertFalse(unpub_perms.can_unpublish())
self.assertFalse(homepage_perms.can_publish_subpage())
self.assertFalse(christmas_page_perms.can_publish_subpage())
self.assertFalse(unpub_perms.can_publish_subpage())
self.assertFalse(homepage_perms.can_reorder_children())
self.assertFalse(christmas_page_perms.can_reorder_children())
self.assertFalse(unpub_perms.can_reorder_children())
self.assertFalse(homepage_perms.can_move())
self.assertFalse(christmas_page_perms.can_move()) # cannot move because this would involve unpublishing from its current location
self.assertTrue(unpub_perms.can_move())
self.assertFalse(someone_elses_event_perms.can_move())
self.assertFalse(christmas_page_perms.can_move_to(unpublished_event_page)) # cannot move because this would involve unpublishing from its current location
self.assertTrue(unpub_perms.can_move_to(christmas_page))
self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination
self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself
def test_publisher_page_permissions(self):
event_moderator = User.objects.get(username='eventmoderator')
homepage = Page.objects.get(url_path='/home/')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/')
homepage_perms = homepage.permissions_for_user(event_moderator)
christmas_page_perms = christmas_page.permissions_for_user(event_moderator)
unpub_perms = unpublished_event_page.permissions_for_user(event_moderator)
self.assertFalse(homepage_perms.can_add_subpage())
self.assertTrue(christmas_page_perms.can_add_subpage())
self.assertTrue(unpub_perms.can_add_subpage())
self.assertFalse(homepage_perms.can_edit())
self.assertTrue(christmas_page_perms.can_edit())
self.assertTrue(unpub_perms.can_edit())
self.assertFalse(homepage_perms.can_delete())
self.assertTrue(christmas_page_perms.can_delete()) # cannot delete because it is published
self.assertTrue(unpub_perms.can_delete())
self.assertFalse(homepage_perms.can_publish())
self.assertTrue(christmas_page_perms.can_publish())
self.assertTrue(unpub_perms.can_publish())
self.assertFalse(homepage_perms.can_unpublish())
self.assertTrue(christmas_page_perms.can_unpublish())
self.assertFalse(unpub_perms.can_unpublish()) # cannot unpublish a page that isn't published
self.assertFalse(homepage_perms.can_publish_subpage())
self.assertTrue(christmas_page_perms.can_publish_subpage())
self.assertTrue(unpub_perms.can_publish_subpage())
self.assertFalse(homepage_perms.can_reorder_children())
self.assertTrue(christmas_page_perms.can_reorder_children())
self.assertTrue(unpub_perms.can_reorder_children())
self.assertFalse(homepage_perms.can_move())
self.assertTrue(christmas_page_perms.can_move())
self.assertTrue(unpub_perms.can_move())
self.assertTrue(christmas_page_perms.can_move_to(unpublished_event_page))
self.assertTrue(unpub_perms.can_move_to(christmas_page))
self.assertFalse(unpub_perms.can_move_to(homepage)) # no permission to create pages at destination
self.assertFalse(unpub_perms.can_move_to(unpublished_event_page)) # cannot make page a child of itself
def test_inactive_user_has_no_permissions(self):
user = User.objects.get(username='inactiveuser')
christmas_page = EventPage.objects.get(url_path='/home/events/christmas/')
unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/')
christmas_page_perms = christmas_page.permissions_for_user(user)
unpub_perms = unpublished_event_page.permissions_for_user(user)
self.assertFalse(unpub_perms.can_add_subpage())
self.assertFalse(unpub_perms.can_edit())
self.assertFalse(unpub_perms.can_delete())
self.assertFalse(unpub_perms.can_publish())
self.assertFalse(christmas_page_perms.can_unpublish())
self.assertFalse(unpub_perms.can_publish_subpage())
self.assertFalse(unpub_perms.can_reorder_children())
self.assertFalse(unpub_perms.can_move())
self.assertFalse(unpub_perms.can_move_to(christmas_page))
def test_superuser_has_full_permissions(self):
user = User.objects.get(username='superuser')
homepage = Page.objects.get(url_path='/home/')
root = Page.objects.get(url_path='/')
unpublished_event_page = EventPage.objects.get(url_path='/home/events/tentative-unpublished-event/')
homepage_perms = homepage.permissions_for_user(user)
root_perms = root.permissions_for_user(user)
unpub_perms = unpublished_event_page.permissions_for_user(user)
self.assertTrue(homepage_perms.can_add_subpage())
self.assertTrue(root_perms.can_add_subpage())
self.assertTrue(homepage_perms.can_edit())
self.assertFalse(root_perms.can_edit()) # root is not a real editable page, even to superusers
self.assertTrue(homepage_perms.can_delete())
self.assertFalse(root_perms.can_delete())
self.assertTrue(homepage_perms.can_publish())
self.assertFalse(root_perms.can_publish())
self.assertTrue(homepage_perms.can_unpublish())
self.assertFalse(root_perms.can_unpublish())
self.assertFalse(unpub_perms.can_unpublish())
self.assertTrue(homepage_perms.can_publish_subpage())
self.assertTrue(root_perms.can_publish_subpage())
self.assertTrue(homepage_perms.can_reorder_children())
self.assertTrue(root_perms.can_reorder_children())
self.assertTrue(homepage_perms.can_move())
self.assertFalse(root_perms.can_move())
self.assertTrue(homepage_perms.can_move_to(root))
self.assertFalse(homepage_perms.can_move_to(unpublished_event_page))

View file

@ -1,45 +0,0 @@
# plugin for hallo.js to allow inserting links using Wagtail's page chooser
(($) ->
$.widget "IKS.hallowagtaildoclink",
options:
uuid: ''
editable: null
populateToolbar: (toolbar) ->
widget = this
# Create an element for holding the button
button = $('<span></span>')
button.hallobutton
uuid: @options.uuid
editable: @options.editable
label: 'Documents'
icon: 'icon-file-text-alt'
command: null
# Append the button to toolbar
toolbar.append button
button.on "click", (event) ->
lastSelection = widget.options.editable.getSelection()
ModalWorkflow
url: window.chooserUrls.documentChooser
responses:
documentChosen: (docData) ->
a = document.createElement('a')
a.setAttribute('href', docData.url)
a.setAttribute('data-id', docData.id)
a.setAttribute('data-linktype', 'document')
if (not lastSelection.collapsed) and lastSelection.canSurroundContents()
# use the selected content as the link text
lastSelection.surroundContents(a)
else
# no text is selected, so use the doc title as link text
a.appendChild(document.createTextNode docData.title)
lastSelection.insertNode(a)
widget.options.editable.element.trigger('change')
)(jQuery)

View file

@ -0,0 +1,51 @@
// Generated by CoffeeScript 1.6.2
(function() {
(function($) {
return $.widget("IKS.hallowagtaildoclink", {
options: {
uuid: '',
editable: null
},
populateToolbar: function(toolbar) {
var button, widget;
widget = this;
button = $('<span></span>');
button.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: 'Documents',
icon: 'icon-file-text-alt',
command: null
});
toolbar.append(button);
return button.on("click", function(event) {
var lastSelection;
lastSelection = widget.options.editable.getSelection();
return ModalWorkflow({
url: window.chooserUrls.documentChooser,
responses: {
documentChosen: function(docData) {
var a;
a = document.createElement('a');
a.setAttribute('href', docData.url);
a.setAttribute('data-id', docData.id);
a.setAttribute('data-linktype', 'document');
if ((!lastSelection.collapsed) && lastSelection.canSurroundContents()) {
lastSelection.surroundContents(a);
} else {
a.appendChild(document.createTextNode(docData.title));
lastSelection.insertNode(a);
}
return widget.options.editable.element.trigger('change');
}
}
});
});
}
});
})(jQuery);
}).call(this);

View file

@ -1,36 +0,0 @@
# plugin for hallo.js to allow inserting embeds
(($) ->
$.widget "IKS.hallowagtailembeds",
options:
uuid: ''
editable: null
populateToolbar: (toolbar) ->
widget = this
# Create an element for holding the button
button = $('<span></span>')
button.hallobutton
uuid: @options.uuid
editable: @options.editable
label: 'Embed'
icon: 'icon-media'
command: null
# Append the button to toolbar
toolbar.append button
button.on "click", (event) ->
lastSelection = widget.options.editable.getSelection()
insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last()
ModalWorkflow
url: window.chooserUrls.embedsChooser
responses:
embedChosen: (embedData) ->
elem = $(embedData).get(0)
lastSelection.insertNode(elem)
if elem.getAttribute('contenteditable') == 'false'
insertRichTextDeleteControl(elem)
widget.options.editable.element.trigger('change')
)(jQuery)

View file

@ -0,0 +1,47 @@
// Generated by CoffeeScript 1.6.2
(function() {
(function($) {
return $.widget("IKS.hallowagtailembeds", {
options: {
uuid: '',
editable: null
},
populateToolbar: function(toolbar) {
var button, widget;
widget = this;
button = $('<span></span>');
button.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: 'Embed',
icon: 'icon-media',
command: null
});
toolbar.append(button);
return button.on("click", function(event) {
var insertionPoint, lastSelection;
lastSelection = widget.options.editable.getSelection();
insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last();
return ModalWorkflow({
url: window.chooserUrls.embedsChooser,
responses: {
embedChosen: function(embedData) {
var elem;
elem = $(embedData).get(0);
lastSelection.insertNode(elem);
if (elem.getAttribute('contenteditable') === 'false') {
insertRichTextDeleteControl(elem);
}
return widget.options.editable.element.trigger('change');
}
}
});
});
}
});
})(jQuery);
}).call(this);

View file

@ -1,39 +0,0 @@
# plugin for hallo.js to allow inserting images from the Wagtail image library
(($) ->
$.widget "IKS.hallowagtailimage",
options:
uuid: ''
editable: null
populateToolbar: (toolbar) ->
widget = this
# Create an element for holding the button
button = $('<span></span>')
button.hallobutton
uuid: @options.uuid
editable: @options.editable
label: 'Images'
icon: 'icon-picture'
command: null
# Append the button to toolbar
toolbar.append button
button.on "click", (event) ->
lastSelection = widget.options.editable.getSelection()
insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last()
ModalWorkflow
url: window.chooserUrls.imageChooser + '?select_format=true'
responses:
imageChosen: (imageData) ->
elem = $(imageData.html).get(0)
lastSelection.insertNode(elem)
if elem.getAttribute('contenteditable') == 'false'
insertRichTextDeleteControl(elem)
widget.options.editable.element.trigger('change')
)(jQuery)

View file

@ -0,0 +1,47 @@
// Generated by CoffeeScript 1.6.2
(function() {
(function($) {
return $.widget("IKS.hallowagtailimage", {
options: {
uuid: '',
editable: null
},
populateToolbar: function(toolbar) {
var button, widget;
widget = this;
button = $('<span></span>');
button.hallobutton({
uuid: this.options.uuid,
editable: this.options.editable,
label: 'Images',
icon: 'icon-picture',
command: null
});
toolbar.append(button);
return button.on("click", function(event) {
var insertionPoint, lastSelection;
lastSelection = widget.options.editable.getSelection();
insertionPoint = $(lastSelection.endContainer).parentsUntil('.richtext').last();
return ModalWorkflow({
url: window.chooserUrls.imageChooser + '?select_format=true',
responses: {
imageChosen: function(imageData) {
var elem;
elem = $(imageData.html).get(0);
lastSelection.insertNode(elem);
if (elem.getAttribute('contenteditable') === 'false') {
insertRichTextDeleteControl(elem);
}
return widget.options.editable.element.trigger('change');
}
}
});
});
}
});
})(jQuery);
}).call(this);