diff --git a/djangocms_blog/cms_wizards.py b/djangocms_blog/cms_wizards.py index ae89870..0c8a7ec 100644 --- a/djangocms_blog/cms_wizards.py +++ b/djangocms_blog/cms_wizards.py @@ -8,10 +8,10 @@ from cms.wizards.wizard_base import Wizard from cms.wizards.wizard_pool import AlreadyRegisteredException, wizard_pool from django import forms from django.conf import settings -from django.utils.text import slugify from django.utils.translation import ugettext_lazy as _ from .cms_appconfig import BlogConfig +from .fields import slugify from .forms import PostAdminFormBase from .models import Post @@ -19,6 +19,11 @@ from .models import Post class PostWizardForm(PostAdminFormBase): default_appconfig = None + slug = forms.SlugField( + label=_('Slug'), max_length=767, required=False, + help_text=_('Leave empty for automatic slug, or override as required.'), + ) + def __init__(self, *args, **kwargs): if 'initial' not in kwargs or not kwargs.get('initial', False): kwargs['initial'] = {} @@ -36,7 +41,7 @@ class PostWizardForm(PostAdminFormBase): class Meta: model = Post - fields = ['app_config', 'title', 'abstract', 'categories'] + fields = ['app_config', 'title', 'slug', 'abstract', 'categories'] class Media: js = ('admin/js/jquery.js', 'admin/js/jquery.init.js',) @@ -45,6 +50,23 @@ class PostWizardForm(PostAdminFormBase): self.instance._set_default_author(get_current_user()) return super(PostWizardForm, self).save(commit) + def clean_slug(self): + """ + Generate a valid slug, in case the given one is taken + """ + source = self.cleaned_data.get('slug', '') + lang_choice = self.language_code + if not source: + source = slugify(self.cleaned_data.get('title', '')) + qs = Post._default_manager.active_translations(lang_choice).language(lang_choice) + used = list(qs.values_list('translations__slug', flat=True)) + slug = source + i = 1 + while slug in used: + slug = '%s-%s' % (source, i) + i += 1 + return slug + class PostWizard(Wizard): pass diff --git a/djangocms_blog/fields.py b/djangocms_blog/fields.py index d2f81f0..e6dfd0f 100644 --- a/djangocms_blog/fields.py +++ b/djangocms_blog/fields.py @@ -1,4 +1,9 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +import django from django.db.models import SlugField +from django.utils.text import slugify as django_slugify __all__ = ['AutoSlugField'] @@ -7,3 +12,10 @@ class AutoSlugField(SlugField): def __init__(self, *args, **kwargs): self.allow_unicode = kwargs.pop('allow_unicode', False) super(SlugField, self).__init__(*args, **kwargs) + + +def slugify(base): + if django.VERSION >= (1, 9): + return django_slugify(base, allow_unicode=True) + else: + return django_slugify(base) diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 47de024..9a3ac39 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -3,7 +3,6 @@ from __future__ import absolute_import, print_function, unicode_literals import hashlib -import django from aldryn_apphooks_config.fields import AppHookConfigField from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager from cms.models import CMSPlugin, PlaceholderField @@ -19,7 +18,6 @@ from django.utils import timezone from django.utils.encoding import force_bytes, force_text, python_2_unicode_compatible from django.utils.functional import cached_property from django.utils.html import escape, strip_tags -from django.utils.text import slugify from django.utils.translation import get_language, ugettext_lazy as _ from djangocms_text_ckeditor.fields import HTMLField from filer.fields.image import FilerImageField @@ -30,7 +28,7 @@ from sortedm2m.fields import SortedManyToManyField from taggit_autosuggest.managers import TaggableManager from .cms_appconfig import BlogConfig -from .fields import AutoSlugField +from .fields import AutoSlugField, slugify from .managers import GenericDateTaggedManager from .settings import get_setting @@ -47,7 +45,6 @@ thumbnail_model = '%s.%s' % ( ThumbnailOption._meta.app_label, ThumbnailOption.__name__ ) - try: from knocker.mixins import KnockerModel except ImportError: @@ -315,10 +312,7 @@ class Post(KnockerModel, BlogMetaMixin, TranslatableModel): if self.publish and self.date_published is None: self.date_published = timezone.now() if not self.slug and self.title: - if django.VERSION >= (1, 9): - self.slug = slugify(self.title, allow_unicode=True) - else: - self.slug = slugify(self.title) + self.slug = slugify(self.title) super(Post, self).save(*args, **kwargs) def save_translation(self, translation, *args, **kwargs): @@ -349,9 +343,12 @@ class Post(KnockerModel, BlogMetaMixin, TranslatableModel): if '' in urlconf: kwargs['day'] = '%02d' % current_date.day if '' in urlconf: - kwargs['slug'] = self.safe_translation_getter('slug', language_code=lang, any_language=True) # NOQA + kwargs['slug'] = self.safe_translation_getter( + 'slug', language_code=lang, any_language=True + ) # NOQA if '' in urlconf: - kwargs['category'] = category.safe_translation_getter('slug', language_code=lang, any_language=True) # NOQA + kwargs['category'] = category.safe_translation_getter( + 'slug', language_code=lang, any_language=True) # NOQA return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs) def get_title(self): @@ -555,7 +552,6 @@ class AuthorEntriesPlugin(BasePostPlugin): @python_2_unicode_compatible class GenericBlogPlugin(BasePostPlugin): - class Meta: abstract = False diff --git a/tests/test_wizards.py b/tests/test_wizards.py index fda2011..4bfcaed 100644 --- a/tests/test_wizards.py +++ b/tests/test_wizards.py @@ -82,6 +82,51 @@ class WizardTest(BaseTest): instance = form.save() self.assertEqual(instance.author, self.user_normal) + def test_wizard_duplicate_slug(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() + cat_2 = BlogCategory.objects.create(name='category 1 - blog 2', app_config=self.app_config_2) + + with current_user(self.user_staff): + wiz = None + for wiz in wizard_pool.get_entries(): + if wiz.model == Post and wiz.title == 'New Blog': + 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_1.pk) + self.assertTrue(form.is_valid()) + instance1 = form.save() + self.assertEqual(instance1.slug, 'title-article') + + 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_1.pk) + self.assertTrue(form.is_valid()) + instance2 = form.save() + self.assertEqual(instance2.slug, 'title-article-1') + + 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': [cat_2.pk], + }, prefix=1) + self.assertEqual(form.default_appconfig, self.app_config_2.pk) + self.assertTrue(form.is_valid()) + instance3 = form.save() + self.assertEqual(instance3.slug, 'title-article-2') + def test_wizard_init_categories_check(self): from cms.utils.permissions import current_user from cms.wizards.wizard_pool import wizard_pool