diff --git a/gulpfile.js/config.js b/gulpfile.js/config.js index e9c7c2ff42..5c471423ba 100644 --- a/gulpfile.js/config.js +++ b/gulpfile.js/config.js @@ -55,6 +55,7 @@ var apps = [ 'wagtailstyleguide/scss/styleguide.scss' ], }), + new App('wagtail/contrib/settings'), ]; module.exports = { diff --git a/wagtail/contrib/settings/.gitignore b/wagtail/contrib/settings/.gitignore new file mode 100644 index 0000000000..980c85122d --- /dev/null +++ b/wagtail/contrib/settings/.gitignore @@ -0,0 +1 @@ +static/ diff --git a/wagtail/contrib/settings/forms.py b/wagtail/contrib/settings/forms.py new file mode 100644 index 0000000000..d6cb2e29ba --- /dev/null +++ b/wagtail/contrib/settings/forms.py @@ -0,0 +1,27 @@ +from __future__ import absolute_import, unicode_literals + +from django import forms +from django.core.urlresolvers import reverse + +from wagtail.wagtailcore.models import Site + + +class SiteSwitchForm(forms.Form): + site = forms.ChoiceField(choices=[]) + + class Media: + js = [ + 'settings/js/site-switcher.js', + ] + + def __init__(self, current_site, model, **kwargs): + initial_data = {'site': self.get_change_url(current_site, model)} + super(SiteSwitchForm, self).__init__(initial=initial_data, **kwargs) + sites = [(self.get_change_url(site, model), site) + for site in Site.objects.all()] + self.fields['site'].choices = sites + + @classmethod + def get_change_url(cls, site, model): + return reverse('wagtailsettings_edit', args=[ + site.pk, model._meta.app_label, model._meta.model_name]) diff --git a/wagtail/contrib/settings/static_src/settings/js/site-switcher.js b/wagtail/contrib/settings/static_src/settings/js/site-switcher.js new file mode 100644 index 0000000000..61d302c584 --- /dev/null +++ b/wagtail/contrib/settings/static_src/settings/js/site-switcher.js @@ -0,0 +1,12 @@ +$(function() { + var $switcher = $('form#settings-site-switch select'); + if (!$switcher.length) return; + + var initial = $switcher.val(); + $switcher.on('change', function() { + var url = $switcher.val(); + if (url != initial) { + window.location = url; + } + }); +}); diff --git a/wagtail/contrib/settings/templates/wagtailsettings/edit.html b/wagtail/contrib/settings/templates/wagtailsettings/edit.html index 5ce65b3cf7..574b16efa0 100644 --- a/wagtail/contrib/settings/templates/wagtailsettings/edit.html +++ b/wagtail/contrib/settings/templates/wagtailsettings/edit.html @@ -3,10 +3,30 @@ {% block titletag %}{% blocktrans %}Editing {{ setting_type_name}} - {{ instance }}{% endblocktrans %}{% endblock %} {% block bodyclass %}menu-settings{% endblock %} {% block content %} - {% trans "Editing" as editing_str %} - {% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=setting_type_name|capfirst icon="cogs" %} +
+
+
+
+

+ {% trans "Editing" %} + {{ setting_type_name|capfirst }} +

+
+
+
+ {% if site_switcher %} +
+ + {{ site_switcher.site }} +
+ {% endif %} +
+
+
-
+ {% csrf_token %} {{ edit_handler.render_form_content }} @@ -23,7 +43,11 @@ {% block extra_css %} {% include "wagtailadmin/pages/_editor_css.html" %} + {{ form.media.css }} + {{ site_switcher.media.css }} {% endblock %} {% block extra_js %} {% include "wagtailadmin/pages/_editor_js.html" %} + {{ form.media.js }} + {{ site_switcher.media.js }} {% endblock %} diff --git a/wagtail/contrib/settings/tests/test_admin.py b/wagtail/contrib/settings/tests/test_admin.py index 89004417d2..9d9c69489c 100644 --- a/wagtail/contrib/settings/tests/test_admin.py +++ b/wagtail/contrib/settings/tests/test_admin.py @@ -1,3 +1,5 @@ +from __future__ import absolute_import, unicode_literals + from django.contrib.auth import get_user_model from django.contrib.auth.models import Permission from django.core.urlresolvers import reverse @@ -8,7 +10,7 @@ from wagtail.contrib.settings.registry import SettingMenuItem from wagtail.tests.testapp.models import IconSetting, TestSetting from wagtail.tests.utils import WagtailTestUtils from wagtail.wagtailcore import hooks -from wagtail.wagtailcore.models import Site +from wagtail.wagtailcore.models import Page, Site class TestSettingMenu(TestCase, WagtailTestUtils): @@ -42,20 +44,23 @@ class TestSettingMenu(TestCase, WagtailTestUtils): self.assertEqual(classnames, {'icon', 'icon-tag', 'test-class'}) -class TestSettingCreateView(TestCase, WagtailTestUtils): +class BaseTestSettingView(TestCase, WagtailTestUtils): + def get(self, site_pk=1, params={}): + url = self.edit_url('tests', 'testsetting', site_pk=site_pk) + return self.client.get(url, params) + + def post(self, site_pk=1, post_data={}): + url = self.edit_url('tests', 'testsetting', site_pk=site_pk) + return self.client.post(url, post_data) + + def edit_url(self, app, model, site_pk=1): + return reverse('wagtailsettings_edit', args=[site_pk, app, model]) + + +class TestSettingCreateView(BaseTestSettingView): def setUp(self): self.login() - def get(self, params={}): - return self.client.get( - reverse('wagtailsettings_edit', args=('tests', 'testsetting')), - params) - - def post(self, post_data={}): - return self.client.post( - reverse('wagtailsettings_edit', args=('tests', 'testsetting')), - post_data) - def test_status_code(self): self.assertEqual(self.get().status_code, 200) @@ -75,7 +80,7 @@ class TestSettingCreateView(TestCase, WagtailTestUtils): self.assertEqual(setting.email, 'test@example.com') -class TestSettingEditView(TestCase, WagtailTestUtils): +class TestSettingEditView(BaseTestSettingView): def setUp(self): default_site = Site.objects.get(is_default_site=True) @@ -87,22 +92,11 @@ class TestSettingEditView(TestCase, WagtailTestUtils): self.login() - def get(self, params={}): - return self.client.get( - reverse('wagtailsettings_edit', args=('tests', 'testsetting')), - params) - - def post(self, post_data={}): - return self.client.post( - reverse('wagtailsettings_edit', args=('tests', 'testsetting')), - post_data) - def test_status_code(self): self.assertEqual(self.get().status_code, 200) def test_non_existant_model(self): - response = self.client.get( - reverse('wagtailsettings_edit', args=('tests', 'foo'))) + response = self.client.get(self.edit_url('test', 'foo')) self.assertEqual(response.status_code, 404) def test_edit_invalid(self): @@ -121,6 +115,74 @@ class TestSettingEditView(TestCase, WagtailTestUtils): self.assertEqual(setting.email, 'test@example.com') +class TestMultiSite(BaseTestSettingView): + def setUp(self): + self.default_site = Site.objects.get(is_default_site=True) + self.other_site = Site.objects.create(hostname='example.com', root_page=Page.objects.get(pk=2)) + self.login() + + def test_redirect_to_default(self): + """ + Should redirect to the setting for the default site. + """ + start_url = reverse('wagtailsettings_edit', args=[ + 'tests', 'testsetting']) + dest_url = 'http://testserver' + reverse('wagtailsettings_edit', args=[ + self.default_site.pk, 'tests', 'testsetting']) + response = self.client.get(start_url, follow=True) + self.assertEqual([(dest_url, 302)], response.redirect_chain) + + def test_redirect_to_current(self): + """ + Should redirect to the setting for the current site taken from the URL, + by default + """ + start_url = reverse('wagtailsettings_edit', args=[ + 'tests', 'testsetting']) + dest_url = 'http://example.com' + reverse('wagtailsettings_edit', args=[ + self.other_site.pk, 'tests', 'testsetting']) + response = self.client.get(start_url, follow=True, HTTP_HOST=self.other_site.hostname) + self.assertEqual([(dest_url, 302)], response.redirect_chain) + + def test_switcher(self): + """ Check that the switcher form exists in the page """ + response = self.get() + self.assertEqual(response.status_code, 200) + self.assertContains(response, 'id="settings-site-switch"') + + def test_unknown_site(self): + """ Check that unknown sites throw a 404 """ + response = self.get(site_pk=3) + self.assertEqual(response.status_code, 404) + + def test_edit(self): + """ + Check that editing settings in multi-site mode edits the correct + setting, and leaves the other ones alone + """ + TestSetting.objects.create( + title='default', + email='default@example.com', + site=self.default_site) + TestSetting.objects.create( + title='other', + email='other@example.com', + site=self.other_site) + response = self.post(site_pk=self.other_site.pk, post_data={ + 'title': 'other-new', 'email': 'other-other@example.com'}) + self.assertEqual(response.status_code, 302) + + # Check that the correct setting was updated + other_setting = TestSetting.for_site(self.other_site) + self.assertEqual(other_setting.title, 'other-new') + self.assertEqual(other_setting.email, 'other-other@example.com') + + # Check that the other setting was not updated + default_setting = TestSetting.for_site(self.default_site) + self.assertEqual(default_setting.title, 'default') + self.assertEqual(default_setting.email, 'default@example.com') + + class TestAdminPermission(TestCase, WagtailTestUtils): def test_registered_permission(self): permission = Permission.objects.get_by_natural_key( diff --git a/wagtail/contrib/settings/urls.py b/wagtail/contrib/settings/urls.py index 489a922dc0..767c717f07 100644 --- a/wagtail/contrib/settings/urls.py +++ b/wagtail/contrib/settings/urls.py @@ -3,5 +3,6 @@ from django.conf.urls import url from . import views urlpatterns = [ - url(r'^(\w+)/(\w+)/$', views.edit, name='wagtailsettings_edit'), + url(r'^(\w+)/(\w+)/$', views.edit_current_site, name='wagtailsettings_edit'), + url(r'^(\d+)/(\w+)/(\w+)/$', views.edit, name='wagtailsettings_edit'), ] diff --git a/wagtail/contrib/settings/views.py b/wagtail/contrib/settings/views.py index 7268d8fa45..177ba6204b 100644 --- a/wagtail/contrib/settings/views.py +++ b/wagtail/contrib/settings/views.py @@ -1,7 +1,6 @@ -from django.contrib.auth.decorators import permission_required from django.core.exceptions import PermissionDenied from django.http import Http404 -from django.shortcuts import redirect, render +from django.shortcuts import redirect, render, get_object_or_404 from django.utils.lru_cache import lru_cache from django.utils.text import capfirst from django.utils.translation import ugettext as _ @@ -9,7 +8,9 @@ from django.utils.translation import ugettext as _ from wagtail.wagtailadmin import messages from wagtail.wagtailadmin.edit_handlers import ( ObjectList, extract_panel_definitions_from_model_class) +from wagtail.wagtailcore.models import Site +from .forms import SiteSwitchForm from .permissions import user_can_edit_setting_type from .registry import registry @@ -31,15 +32,20 @@ def get_setting_edit_handler(model): return ObjectList(panels).bind_to_model(model) -@permission_required('wagtailadmin.access_admin') # further permissions are enforced within the view -def edit(request, app_name, model_name): +def edit_current_site(request, app_name, model_name): + # Redirect the user to the edit page for the current site + return redirect('wagtailsettings_edit', request.site.pk, app_name, model_name) + + +def edit(request, site_pk, app_name, model_name): model = get_model_from_url_params(app_name, model_name) if not user_can_edit_setting_type(request.user, model): raise PermissionDenied + site = get_object_or_404(Site, pk=site_pk) setting_type_name = model._meta.verbose_name - instance = model.for_site(request.site) + instance = model.for_site(site) edit_handler_class = get_setting_edit_handler(model) form_class = edit_handler_class.get_form_class(model) @@ -56,7 +62,7 @@ def edit(request, app_name, model_name): instance=instance ) ) - return redirect('wagtailsettings_edit', app_name, model_name) + return redirect('wagtailsettings_edit', site.pk, app_name, model_name) else: messages.error(request, _("The setting could not be saved due to errors.")) edit_handler = edit_handler_class(instance=instance, form=form) @@ -64,9 +70,16 @@ def edit(request, app_name, model_name): form = form_class(instance=instance) edit_handler = edit_handler_class(instance=instance, form=form) + # Show a site switcher form if there are multiple sites + site_switcher = None + if Site.objects.count() > 1: + site_switcher = SiteSwitchForm(site, model) + return render(request, 'wagtailsettings/edit.html', { 'opts': model._meta, 'setting_type_name': setting_type_name, 'instance': instance, 'edit_handler': edit_handler, + 'site': site, + 'site_switcher': site_switcher, })