diff --git a/djangocms_blog/cms_wizards.py b/djangocms_blog/cms_wizards.py index 2f797e4..ae89870 100644 --- a/djangocms_blog/cms_wizards.py +++ b/djangocms_blog/cms_wizards.py @@ -10,13 +10,13 @@ from django import forms from django.conf import settings from django.utils.text import slugify from django.utils.translation import ugettext_lazy as _ -from parler.forms import TranslatableModelForm from .cms_appconfig import BlogConfig +from .forms import PostAdminFormBase from .models import Post -class PostWizardForm(TranslatableModelForm): +class PostWizardForm(PostAdminFormBase): default_appconfig = None def __init__(self, *args, **kwargs): @@ -31,6 +31,8 @@ class PostWizardForm(TranslatableModelForm): choices=self.fields['app_config'].widget.choices, ) self.fields['app_config'].widget.attrs['disabled'] = True + if 'categories' in self.fields: + self.fields['categories'].queryset = self.available_categories class Meta: model = Post diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py index 936a507..1ba16fd 100644 --- a/djangocms_blog/forms.py +++ b/djangocms_blog/forms.py @@ -4,6 +4,7 @@ from __future__ import absolute_import, print_function, unicode_literals from django import forms from django.conf import settings from django.core.validators import MaxLengthValidator +from django.utils.functional import cached_property from parler.forms import TranslatableModelForm from taggit_autosuggest.widgets import TagAutoSuggest @@ -12,7 +13,25 @@ from djangocms_blog.settings import get_setting from .models import BlogCategory, BlogConfig, Post -class CategoryAdminForm(TranslatableModelForm): +class ConfigFormBase(object): + """ + This provide the app_config property which returns the currently + selected app_config, whether it's an instance attribute or + passed in the request + """ + + @cached_property + def app_config(self): + if getattr(self.instance, 'app_config_id', None): + return self.instance.app_config + elif 'app_config' in self.initial: + return BlogConfig.objects.get(pk=self.initial['app_config']) + elif self.data.get('app_config', None): + return BlogConfig.objects.get(pk=self.data['app_config']) + return None + + +class CategoryAdminForm(ConfigFormBase, TranslatableModelForm): def __init__(self, *args, **kwargs): self.base_fields['meta_description'].validators = [ @@ -33,11 +52,14 @@ class CategoryAdminForm(TranslatableModelForm): qs = qs.exclude( pk__in=[self.instance.pk] + [child.pk for child in self.instance.descendants()] ) - + config = None if getattr(self.instance, 'app_config_id', None): qs = qs.namespace(self.instance.app_config.namespace) - elif 'initial' in kwargs and 'app_config' in kwargs['initial']: - config = BlogConfig.objects.get(pk=kwargs['initial']['app_config']) + elif 'app_config' in self.initial: + config = BlogConfig.objects.get(pk=self.initial['app_config']) + elif self.data.get('app_config', None): + config = BlogConfig.objects.get(pk=self.data['app_config']) + if config: qs = qs.namespace(config.namespace) self.fields['parent'].queryset = qs @@ -53,17 +75,31 @@ class LatestEntriesForm(forms.ModelForm): class Media: css = { - 'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, - 'djangocms_blog_admin.css'),) + 'all': ('%sdjangocms_blog/css/%s' % ( + settings.STATIC_URL, 'djangocms_blog_admin.css' + ),) } -class PostAdminForm(TranslatableModelForm): +class PostAdminFormBase(ConfigFormBase, TranslatableModelForm): + """ + This provide common methods between the admin and wizard form + """ class Meta: model = Post fields = '__all__' + @cached_property + def available_categories(self): + qs = BlogCategory.objects + if self.app_config: + return qs.namespace(self.app_config.namespace) + return qs + + +class PostAdminForm(PostAdminFormBase): + def __init__(self, *args, **kwargs): self.base_fields['meta_description'].validators = [ MaxLengthValidator(get_setting('META_DESCRIPTION_LENGTH')) @@ -79,26 +115,16 @@ class PostAdminForm(TranslatableModelForm): MaxLengthValidator(get_setting('META_TITLE_LENGTH')) ] super(PostAdminForm, self).__init__(*args, **kwargs) - - qs = BlogCategory.objects - - config = None - if getattr(self.instance, 'app_config_id', None): - qs = qs.namespace(self.instance.app_config.namespace) - elif 'initial' in kwargs and 'app_config' in kwargs['initial']: - config = BlogConfig.objects.get(pk=kwargs['initial']['app_config']) - qs = qs.namespace(config.namespace) - if 'categories' in self.fields: - self.fields['categories'].queryset = qs + self.fields['categories'].queryset = self.available_categories if 'app_config' in self.fields: # Don't allow app_configs to be added here. The correct way to add an # apphook-config is to create an apphook on a cms Page. self.fields['app_config'].widget.can_add_related = False - if config: + if self.app_config: self.initial['main_image_full'] = \ - config.app_data['config'].get('default_image_full') + self.app_config.app_data['config'].get('default_image_full') self.initial['main_image_thumbnail'] = \ - config.app_data['config'].get('default_image_thumbnail') + self.app_config.app_data['config'].get('default_image_thumbnail') diff --git a/tests/test_models.py b/tests/test_models.py index bc62016..dbf90da 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -15,6 +15,7 @@ from django.contrib.auth.models import AnonymousUser from django.contrib.messages.middleware import MessageMiddleware from django.contrib.sites.models import Site from django.core.urlresolvers import reverse +from django.http import QueryDict from django.test import override_settings from django.utils.encoding import force_text from django.utils.html import strip_tags @@ -181,13 +182,32 @@ class AdminTest(BaseTest): def test_admin_category_views(self): category_admin = admin.site._registry[BlogCategory] request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) + BlogCategory.objects.create(name='category 1 - blog 2', app_config=self.app_config_2) # Add view only has an empty form - no type response = category_admin.add_view(request) self.assertNotContains(response, 'id="id_name" maxlength="767" name="name" type="text" value="category 1"') self.assertContains(response, '' % self.app_config_1.pk) + # Add view select categories on the given appconfig, even when reloading the form + request.POST = QueryDict('app_config=1') + request.method = 'POST' + response = category_admin.add_view(request) + self.assertTrue( + response.context_data['adminform'].form.fields['parent'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) + + request.GET = QueryDict('app_config=1') + request.method = 'GET' + response = category_admin.add_view(request) + self.assertTrue( + response.context_data['adminform'].form.fields['parent'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) + # Changeview is 'normal', with a few preselected items + request.GET = QueryDict() response = category_admin.change_view(request, str(self.category_1.pk)) self.assertContains(response, 'id="id_name" maxlength="767" name="name" type="text" value="category 1"') self.assertContains(response, 'id="id_meta_description" maxlength="320"') @@ -240,31 +260,25 @@ class AdminTest(BaseTest): # Add view shows all the exising categories response = post_admin.add_view(request) - self.assertContains(response, '') - self.assertContains(response, '') - self.assertContains(response, '') - self.assertContains(response, '') - self.assertContains(response, '') - self.assertNotContains(response, 'category different branch') + self.assertTrue( + response.context_data['adminform'].form.fields['parent'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) # Changeview hides the children of the current category response = post_admin.change_view(request, str(category2.pk)) - self.assertContains(response, '') - self.assertContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, 'category different branch') + self.assertTrue( + response.context_data['adminform'].form.fields['parent'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1, parent__isnull=True) + ) # Test second apphook categories request = self.get_page_request('/', self.user, r'/en/blog/?app_config=%s' % self.app_config_2.pk, edit=False) response = post_admin.add_view(request) - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertNotContains(response, '') - self.assertContains(response, 'category different branch') + self.assertTrue( + response.context_data['adminform'].form.fields['parent'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_2) + ) def test_admin_fieldsets(self): post_admin = admin.site._registry[Post] @@ -339,13 +353,31 @@ class AdminTest(BaseTest): msg_mid.process_request(request) post_admin = admin.site._registry[Post] response = post_admin.add_view(request) - self.assertContains(response, '' % ( - self.category_1.pk, self.category_1.safe_translation_getter('name', language_code='en') - )) + self.assertTrue( + response.context_data['adminform'].form.fields['categories'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) self.assertContains(response, 'id="id_sites" name="sites"') self.assertContains(response, 'selected="selected">Blog image') self.assertContains(response, 'selected="selected">Blog thumbnail') + # Add view select categories on the given appconfig, even when reloading the form + request.POST = QueryDict('app_config=1') + request.method = 'POST' + response = post_admin.add_view(request) + self.assertTrue( + response.context_data['adminform'].form.fields['categories'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) + + request.GET = QueryDict('app_config=1') + request.method = 'GET' + response = post_admin.add_view(request) + self.assertTrue( + response.context_data['adminform'].form.fields['categories'].queryset, + BlogCategory.objects.filter(app_config=self.app_config_1) + ) + self.user.sites.add(self.site_1) with self.login_user_context(self.user): request = self.get_request('/', 'en', user=self.user, diff --git a/tests/test_wizards.py b/tests/test_wizards.py index 692c1a8..fda2011 100644 --- a/tests/test_wizards.py +++ b/tests/test_wizards.py @@ -3,6 +3,8 @@ from __future__ import absolute_import, print_function, unicode_literals import sys +from djangocms_blog.models import BlogCategory + from .base import BaseTest @@ -41,6 +43,12 @@ class WizardTest(BaseTest): from djangocms_blog.models import Post self.get_pages() + cat_1 = BlogCategory.objects.create(name='category 1 - blog 1', app_config=self.app_config_1) + cat_2 = BlogCategory.objects.create(name='category 1 - blog 2', app_config=self.app_config_2) + cats = { + self.app_config_1.pk: cat_1, + self.app_config_2.pk: cat_2, + } with current_user(self.user_staff): wizs = [entry for entry in wizard_pool.get_entries() if entry.model == Post] for index, wiz in enumerate(wizs): @@ -52,7 +60,7 @@ class WizardTest(BaseTest): form = wiz.form(data={ '1-title': 'title{0}'.format(index), '1-abstract': 'abstract{0}'.format(index), - '1-categories': [self.category_1.pk], + '1-categories': [cats[app_config].pk], }, prefix=1) self.assertEqual(form.default_appconfig, app_config) self.assertTrue(form.is_valid()) @@ -66,7 +74,7 @@ class WizardTest(BaseTest): form = wiz.form(data={ '1-title': 'title-2{0}'.format(index), '1-abstract': 'abstract-2{0}'.format(index), - '1-categories': [self.category_1.pk], + '1-categories': [cats[app_config].pk], }, prefix=1) self.assertEqual(form.default_appconfig, app_config) self.assertTrue(form.is_valid()) @@ -74,6 +82,26 @@ class WizardTest(BaseTest): instance = form.save() self.assertEqual(instance.author, self.user_normal) + def test_wizard_init_categories_check(self): + from cms.utils.permissions import current_user + from cms.wizards.wizard_pool import wizard_pool + from djangocms_blog.models import Post + self.get_pages() + + with current_user(self.user_staff): + wiz = None + for wiz in wizard_pool.get_entries(): + if wiz.model == Post and wiz.title == 'New Article': + break + form = wiz.form(data={ + '1-title': 'title article', + '1-abstract': 'abstract article', + '1-categories': [self.category_1.pk], + }, prefix=1) + self.assertEqual(form.default_appconfig, self.app_config_2.pk) + self.assertFalse(form.is_valid()) + self.assertTrue('categories' in form.errors.keys()) + def test_wizard_import(self): # The following import should not fail in any django CMS version pass