From f6e8e27687e11c66ffaee6b965dd8d02e4111aff Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 20 Sep 2015 01:11:14 +0200 Subject: [PATCH 01/48] Add support for apphook config --- cms_helper.py | 1 + djangocms_blog/admin.py | 58 +++- djangocms_blog/cms_app.py | 6 +- djangocms_blog/cms_appconfig.py | 47 +++ djangocms_blog/cms_plugins.py | 25 +- djangocms_blog/feeds.py | 9 +- djangocms_blog/forms.py | 28 ++ djangocms_blog/managers.py | 9 +- djangocms_blog/menu.py | 70 ++--- .../migrations/0010_auto_20150923_1151.py | 118 ++++++++ djangocms_blog/models.py | 69 +++-- djangocms_blog/settings.py | 1 + ...__add_unique_blogconfigtranslation_lang.py | 286 ++++++++++++++++++ .../south_migrations/0015_create_appconfig.py | 234 ++++++++++++++ .../templates/djangocms_blog/post_detail.html | 2 +- djangocms_blog/views.py | 18 +- setup.py | 1 + tests/__init__.py | 186 +++++++----- tests/test_menu.py | 83 +++-- tests/test_models.py | 164 +++++----- tests/test_plugins.py | 113 ++++--- tests/test_toolbar.py | 10 +- tests/test_views.py | 198 +++++++----- 23 files changed, 1308 insertions(+), 428 deletions(-) create mode 100644 djangocms_blog/cms_appconfig.py create mode 100644 djangocms_blog/migrations/0010_auto_20150923_1151.py create mode 100644 djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py create mode 100644 djangocms_blog/south_migrations/0015_create_appconfig.py diff --git a/cms_helper.py b/cms_helper.py index ef6b154..3b7af0d 100755 --- a/cms_helper.py +++ b/cms_helper.py @@ -18,6 +18,7 @@ HELPER_SETTINGS = dict( 'cmsplugin_filer_image', 'taggit', 'taggit_autosuggest', + 'aldryn_apphooks_config', ], LANGUAGE_CODE='en', LANGUAGES=( diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index 70189fe..db966e1 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -3,6 +3,7 @@ from __future__ import absolute_import, print_function, unicode_literals from copy import deepcopy +from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin from django import forms from django.conf import settings @@ -10,6 +11,8 @@ from django.contrib import admin from django.contrib.auth import get_user_model from parler.admin import TranslatableAdmin +from .cms_appconfig import BlogConfig +from .forms import PostAdminForm from .models import BlogCategory, Post from .settings import get_setting @@ -20,19 +23,22 @@ except ImportError: pass -class BlogCategoryAdmin(EnhancedModelAdminMixin, TranslatableAdmin): +class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin): def get_prepopulated_fields(self, request, obj=None): + app_config_default = self._app_config_select(request, obj) + if app_config_default is None and request.method == 'GET': + return {} return {'slug': ('name',)} 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 PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, - PlaceholderAdminMixin, TranslatableAdmin): +class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, + ModelAppHookConfig, TranslatableAdmin): + form = PostAdminForm list_display = ['title', 'author', 'date_published', 'date_published_end'] date_hierarchy = 'date_published' raw_id_fields = ['author'] @@ -40,7 +46,7 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, enhance_exclude = ('main_image', 'tags') _fieldsets = [ (None, { - 'fields': [('title', 'categories', 'publish')] + 'fields': [('title', 'categories', 'publish', 'app_config')] }), ('Info', { 'fields': (['slug', 'tags'], @@ -57,6 +63,10 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, }), ] + app_config_values = { + 'default_published': 'publish' + } + def formfield_for_dbfield(self, db_field, **kwargs): field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name == 'meta_description': @@ -68,11 +78,25 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, return field def get_fieldsets(self, request, obj=None): + app_config_default = self._app_config_select(request, obj) + if app_config_default is None and request.method == 'GET': + return super(PostAdmin, self).get_fieldsets(request, obj) + if not obj: + config = app_config_default + else: + config = obj.app_config + fsets = deepcopy(self._fieldsets) - if get_setting('USE_ABSTRACT'): - fsets[0][1]['fields'].append('abstract') - if not get_setting('USE_PLACEHOLDER'): - fsets[0][1]['fields'].append('post_text') + if config: + if config.use_abstract: + fsets[0][1]['fields'].append('abstract') + if not config.use_placeholder: + fsets[0][1]['fields'].append('post_text') + else: + if get_setting('USE_ABSTRACT'): + fsets[0][1]['fields'].append('abstract') + if not get_setting('USE_PLACEHOLDER'): + fsets[0][1]['fields'].append('post_text') if get_setting('MULTISITE'): fsets[1][1]['fields'][0].append('sites') if request.user.is_superuser: @@ -83,7 +107,7 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, return {'slug': ('title',)} def save_model(self, request, obj, form, change): - if not obj.author_id and get_setting('AUTHOR_DEFAULT'): + if not obj.author_id and obj.app_config.set_author: if get_setting('AUTHOR_DEFAULT') is True: user = request.user else: @@ -93,10 +117,18 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, 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 BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): + + def get_config_fields(self): + return ( + 'app_title', 'paginate_by', 'set_author', 'config.default_published', + 'config.use_placeholder' + ) + admin.site.register(BlogCategory, BlogCategoryAdmin) admin.site.register(Post, PostAdmin) +admin.site.register(BlogConfig, BlogConfigAdmin) diff --git a/djangocms_blog/cms_app.py b/djangocms_blog/cms_app.py index 508c665..5209555 100644 --- a/djangocms_blog/cms_app.py +++ b/djangocms_blog/cms_app.py @@ -1,17 +1,19 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from cms.app_base import CMSApp +from aldryn_apphooks_config.app_base import CMSConfigApp from cms.apphook_pool import apphook_pool from django.utils.translation import ugettext_lazy as _ +from .cms_appconfig import BlogConfig from .menu import BlogCategoryMenu -class BlogApp(CMSApp): +class BlogApp(CMSConfigApp): name = _('Blog') urls = ['djangocms_blog.urls'] app_name = 'djangocms_blog' + app_config = BlogConfig menus = [BlogCategoryMenu] apphook_pool.register(BlogApp) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py new file mode 100644 index 0000000..34c39f4 --- /dev/null +++ b/djangocms_blog/cms_appconfig.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django import forms +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from aldryn_apphooks_config.utils import setup_config +from aldryn_apphooks_config.models import AppHookConfig +from app_data import AppDataForm +from parler.models import TranslatableModel +from parler.models import TranslatedFields + +from .settings import get_setting + + +class BlogConfig(TranslatableModel, AppHookConfig): + """ + Adds some translatable, per-app-instance fields. + """ + translations = TranslatedFields( + app_title=models.CharField(_('application title'), max_length=234), + ) + + def get_app_title(self): + return getattr(self, 'app_title', _('untitled')) + + +class BlogConfigForm(AppDataForm): + default_published = forms.BooleanField(label=_('Post published by default'), required=False, + initial=get_setting('DEFAULT_PUBLISHED')) + use_placeholder = forms.BooleanField(label=_('Use placeholder and plugins for article body'), + required=False, + initial=get_setting('USE_PLACEHOLDER')) + use_abstract = forms.BooleanField(label=_('Use abstract field'), + required=False, + initial=get_setting('USE_ABSTRACT')) + set_author = forms.BooleanField(label=_('Set author'), + required=False, + help_text=_('Set author by default'), + initial=get_setting('AUTHOR_DEFAULT')) + paginate_by = forms.IntegerField(label=_('Paginate size'), + required=False, + initial=get_setting('PAGINATION'), + help_text=_('When paginating list views, ' + 'how many articles per page?')) +setup_config(BlogConfigForm, BlogConfig) diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index 42ce80a..739e468 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -7,7 +7,7 @@ from cms.plugin_pool import plugin_pool from django.utils.translation import ugettext_lazy as _ from .forms import LatestEntriesForm -from .models import AuthorEntriesPlugin, BlogCategory, LatestPostsPlugin, Post +from .models import AuthorEntriesPlugin, BlogCategory, LatestPostsPlugin, Post, GenericBlogPlugin from .settings import get_setting @@ -70,36 +70,47 @@ class BlogAuthorPostsPlugin(BlogPlugin): class BlogTagsPlugin(BlogPlugin): module = _('Blog') name = _('Tags') - model = CMSPlugin + model = GenericBlogPlugin render_template = 'djangocms_blog/plugins/tags.html' def render(self, context, instance, placeholder): context = super(BlogTagsPlugin, self).render(context, instance, placeholder) - context['tags'] = Post.objects.tag_cloud(queryset=Post.objects.published()) + qs = Post._default_manager + qs_post = qs + if instance.app_config: + qs_post = qs_post.namespace(instance.app_config.namespace) + context['tags'] = qs.tag_cloud(queryset=qs_post.published()) return context class BlogCategoryPlugin(BlogPlugin): module = _('Blog') name = _('Categories') - model = CMSPlugin + model = GenericBlogPlugin render_template = 'djangocms_blog/plugins/categories.html' def render(self, context, instance, placeholder): context = super(BlogCategoryPlugin, self).render(context, instance, placeholder) - context['categories'] = BlogCategory.objects.all() + qs = BlogCategory._default_manager + if instance.app_config: + qs = qs.namespace(instance.app_config.namespace) + context['categories'] = qs return context class BlogArchivePlugin(BlogPlugin): module = _('Blog') name = _('Archive') - model = CMSPlugin + model = GenericBlogPlugin render_template = 'djangocms_blog/plugins/archive.html' def render(self, context, instance, placeholder): context = super(BlogArchivePlugin, self).render(context, instance, placeholder) - context['dates'] = Post.objects.get_months(queryset=Post.objects.published()) + qs = Post._default_manager + qs_post = qs + if instance.app_config: + qs_post = qs.namespace(instance.app_config.namespace) + context['dates'] = qs.get_months(queryset=qs_post.published()) return context diff --git a/djangocms_blog/feeds.py b/djangocms_blog/feeds.py index 0675c0d..481e0eb 100644 --- a/djangocms_blog/feeds.py +++ b/djangocms_blog/feeds.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from aldryn_apphooks_config.utils import get_app_instance from django.contrib.sites.models import Site from django.contrib.syndication.views import Feed @@ -12,14 +13,18 @@ from .settings import get_setting class LatestEntriesFeed(Feed): + def __call__(self, request, *args, **kwargs): + self.namespace, self.config = get_app_instance(request) + return super(LatestEntriesFeed, self).__call__(request, *args, **kwargs) + def link(self): - return reverse('djangocms_blog:posts-latest') + return reverse('%s:posts-latest' % self.namespace, current_app=self.namespace) def title(self): return _('Blog articles on %(site_name)s') % {'site_name': Site.objects.get_current().name} def items(self, obj=None): - return Post.objects.published().order_by('-date_published')[:10] + return Post.objects.namespace(self.namespace).published().order_by('-date_published')[:10] def item_title(self, item): return item.safe_translation_getter('title') diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py index 9ffc323..01de6a2 100644 --- a/djangocms_blog/forms.py +++ b/djangocms_blog/forms.py @@ -3,8 +3,11 @@ from __future__ import absolute_import, print_function, unicode_literals from django import forms from django.conf import settings +from parler.forms import TranslatableModelForm from taggit_autosuggest.widgets import TagAutoSuggest +from .models import Post, BlogCategory + class LatestEntriesForm(forms.ModelForm): def __init__(self, *args, **kwargs): @@ -16,3 +19,28 @@ class LatestEntriesForm(forms.ModelForm): 'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),) } + + +class PostAdminForm(TranslatableModelForm): + + class Meta: + model = Post + fields = '__all__' + + def __init__(self, *args, **kwargs): + super(PostAdminForm, self).__init__(*args, **kwargs) + + qs = BlogCategory.objects + + 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']: + qs = qs.namespace(kwargs['initial']['app_config']) + + if 'categories' in self.fields: + self.fields['categories'].queryset = qs + + 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 diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py index 75d670a..9fce9dd 100644 --- a/djangocms_blog/managers.py +++ b/djangocms_blog/managers.py @@ -2,10 +2,13 @@ from __future__ import absolute_import, print_function, unicode_literals import django + +from aldryn_apphooks_config.managers.parler import ( + AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset +) from django.contrib.sites.models import Site from django.db import models from django.utils.timezone import now -from parler.managers import TranslatableQuerySet, TranslationManager try: from collections import Counter @@ -76,7 +79,7 @@ class TaggedFilterItem(object): return sorted(tags, key=lambda x: -x.count) -class GenericDateQuerySet(TranslatableQuerySet): +class GenericDateQuerySet(AppHookConfigTranslatableQueryset): start_date_field = 'date_published' end_date_field = 'date_published_end' publish_field = 'publish' @@ -120,7 +123,7 @@ class GenericDateQuerySet(TranslatableQuerySet): return self.active_translations(language_code=language).on_site() -class GenericDateTaggedManager(TaggedFilterItem, TranslationManager): +class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager): use_for_related_fields = True queryset_class = GenericDateQuerySet diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index d28099f..c821484 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -3,11 +3,11 @@ from __future__ import absolute_import, print_function, unicode_literals from cms.menu_bases import CMSAttachMenu from django.db.models.signals import post_delete, post_save -from django.utils.translation import get_language, ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _, get_language_from_request from menus.base import Modifier, NavigationNode from menus.menu_pool import menu_pool -from .models import BlogCategory +from .models import BlogCategory, Post class BlogCategoryMenu(CMSAttachMenu): @@ -15,54 +15,42 @@ class BlogCategoryMenu(CMSAttachMenu): def get_nodes(self, request): nodes = [] - qs = BlogCategory.objects.translated(get_language()) - qs = qs.order_by('parent__id', 'translations__name') - for category in qs: + + language = get_language_from_request(request, check_path=True) + + categories = BlogCategory.objects + if hasattr(self, 'instance') and self.instance: + categories = categories.namespace(self.instance.application_namespace) + categories = categories.active_translations(language).distinct() + categories = categories.order_by('parent__id', 'translations__name') + for category in categories: node = NavigationNode( category.name, category.get_absolute_url(), - category.pk, - category.parent_id + '%s-%s' % (category.__class__.__name__, category.pk), + ('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent + else None) ) nodes.append(node) + + posts = Post.objects + if hasattr(self, 'instance') and self.instance: + posts = posts.namespace(self.instance.application_namespace) + posts = posts.active_translations(language).distinct() + for post in posts: + category = post.categories.first() + node = NavigationNode( + post.get_title(), + post.get_absolute_url(language), + '%s-%s' % (post.__class__.__name__, category.pk), + '%s-%s' % (category.__class__.__name__, category.pk) + ) + nodes.append(node) + return nodes menu_pool.register_menu(BlogCategoryMenu) - -class BlogNavModifier(Modifier): - """ - This navigation modifier makes sure that when - a particular blog post is viewed, - a corresponding category is selected in menu - """ - def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb): - if post_cut: - return nodes - if not hasattr(request, 'toolbar'): - return nodes - models = ('djangocms_blog.post', 'djangocms_blog.blogcategory') - model = request.toolbar.get_object_model() - if model not in models: - return nodes - if model == 'djangocms_blog.blogcategory': - cat = request.toolbar.obj - else: - cat = request.toolbar.obj.categories.first() - if not cat: - return nodes - - for node in nodes: - if (node.namespace.startswith(BlogCategoryMenu.__name__) and - cat.pk == node.id): - node.selected = True - # no break here because django-cms maintains two menu structures - # for every apphook (attached to published page and draft page) - return nodes - -menu_pool.register_modifier(BlogNavModifier) - - def clear_menu_cache(**kwargs): menu_pool.clear(all=True) diff --git a/djangocms_blog/migrations/0010_auto_20150923_1151.py b/djangocms_blog/migrations/0010_auto_20150923_1151.py new file mode 100644 index 0000000..25db956 --- /dev/null +++ b/djangocms_blog/migrations/0010_auto_20150923_1151.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import aldryn_apphooks_config.fields +import app_data.fields +import djangocms_text_ckeditor.fields + +from cms.models import Page +from cms.utils.conf import get_languages +from django.db import models, migrations + + +def forwards(apps, schema_editor): + BlogConfig = apps.get_model('djangocms_blog', 'BlogConfig') + Post = apps.get_model('djangocms_blog', 'Post') + BlogCategory = apps.get_model('djangocms_blog', 'BlogCategory') + GenericBlogPlugin = apps.get_model('djangocms_blog', 'GenericBlogPlugin') + LatestPostsPlugin = apps.get_model('djangocms_blog', 'LatestPostsPlugin') + AuthorEntriesPlugin = apps.get_model('djangocms_blog', 'AuthorEntriesPlugin') + config = None + for page in Page.objects.drafts().filter(application_urls='BlogApp'): + config = BlogConfig.objects.create(namespace=page.application_namespace) + for lang in get_languages(): + config.create_translation(lang, app_title='Blog') + if config: + for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): + for item in model.objects.all(): + item.app_config = config + item.save() + + +def backwards(apps, schema_editor): + # No need for backward data migration + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('cms', '__latest__'), + ('djangocms_blog', '0009_latestpostsplugin_tags_new'), + ] + + operations = [ + migrations.CreateModel( + name='BlogConfig', + fields=[ + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('type', models.CharField(verbose_name='type', max_length=100)), + ('namespace', models.CharField(default=None, verbose_name='instance namespace', unique=True, max_length=100)), + ('app_data', app_data.fields.AppDataField(editable=False, default='{}')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='BlogConfigTranslation', + fields=[ + ('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)), + ('language_code', models.CharField(db_index=True, verbose_name='Language', max_length=15)), + ('app_title', models.CharField(verbose_name='application title', max_length=234)), + ('master', models.ForeignKey(editable=False, to='djangocms_blog.BlogConfig', related_name='translations', null=True)), + ], + options={ + 'verbose_name': 'blog config Translation', + 'db_table': 'djangocms_blog_blogconfig_translation', + 'default_permissions': (), + 'db_tablespace': '', + 'managed': True, + }, + ), + migrations.CreateModel( + name='GenericBlogPlugin', + fields=[ + ('cmsplugin_ptr', models.OneToOneField(parent_link=True, serialize=False, primary_key=True, auto_created=True, to='cms.CMSPlugin')), + ('app_config', aldryn_apphooks_config.fields.AppHookConfigField(verbose_name='app. config', blank=True, to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default')), + ], + options={ + 'abstract': False, + }, + bases=('cms.cmsplugin',), + ), + migrations.AlterField( + model_name='posttranslation', + name='abstract', + field=djangocms_text_ckeditor.fields.HTMLField(default='', verbose_name='abstract', blank=True), + ), + migrations.AddField( + model_name='authorentriesplugin', + name='app_config', + field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, blank=True, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'), + preserve_default=False, + ), + migrations.AddField( + model_name='blogcategory', + name='app_config', + field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'), + preserve_default=False, + ), + migrations.AddField( + model_name='latestpostsplugin', + name='app_config', + field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, blank=True, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'), + preserve_default=False, + ), + migrations.AddField( + model_name='post', + name='app_config', + field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'), + preserve_default=False, + ), + migrations.AlterUniqueTogether( + name='blogconfigtranslation', + unique_together=set([('language_code', 'master')]), + ), + migrations.RunPython(forwards, backwards) + ] diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index f5deb6a..8a90879 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager +from aldryn_apphooks_config.fields import AppHookConfigField from cms.models import CMSPlugin, PlaceholderField from django.conf import settings as dj_settings from django.core.urlresolvers import reverse @@ -13,10 +15,10 @@ from django.utils.translation import get_language, ugettext_lazy as _ from djangocms_text_ckeditor.fields import HTMLField from filer.fields.image import FilerImageField from meta_mixin.models import ModelMeta -from parler.managers import TranslationManager from parler.models import TranslatableModel, TranslatedFields from taggit_autosuggest.managers import TaggableManager +from .cms_appconfig import BlogConfig from .managers import GenericDateTaggedManager from .settings import get_setting @@ -28,10 +30,10 @@ class BlogCategory(TranslatableModel): """ Blog category """ - parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, - blank=True) + parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True) date_created = models.DateTimeField(_('created at'), auto_now_add=True) date_modified = models.DateTimeField(_('modified at'), auto_now=True) + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config')) translations = TranslatedFields( name=models.CharField(_('name'), max_length=255), @@ -39,7 +41,7 @@ class BlogCategory(TranslatableModel): meta={'unique_together': (('language_code', 'slug'),)} ) - objects = TranslationManager() + objects = AppHookConfigTranslatableManager() class Meta: verbose_name = _('blog category') @@ -47,16 +49,23 @@ class BlogCategory(TranslatableModel): @property def count(self): - return self.blog_posts.published().count() + return self.blog_posts.namespace(self.app_config.namespace).published().count() - def get_absolute_url(self): - lang = get_language() - if self.has_translation(lang): + def get_absolute_url(self, lang=None): + if not lang: + lang = get_language() + if self.has_translation(lang, ): slug = self.safe_translation_getter('slug', language_code=lang) - return reverse('djangocms_blog:posts-category', kwargs={'category': slug}) + return reverse( + '%s:posts-category' % self.app_config.namespace, + kwargs={'category': slug}, + current_app=self.app_config.namespace + ) # in case category doesn't exist in this language, gracefully fallback # to posts-latest - return reverse('djangocms_blog:posts-latest') + return reverse( + '%s:posts-latest' % self.app_config.namespace, current_app=self.app_config.namespace + ) def __str__(self): return self.safe_translation_getter('name') @@ -108,6 +117,7 @@ class Post(ModelMeta, TranslatableModel): help_text=_('Select sites in which to show the post. ' u'If none is set it will be ' u'visible in all the configured sites.')) + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config')) translations = TranslatedFields( title=models.CharField(_('title'), max_length=255), @@ -165,19 +175,21 @@ class Post(ModelMeta, TranslatableModel): def __str__(self): return self.safe_translation_getter('title') - def get_absolute_url(self): + def get_absolute_url(self, lang=None): + if not lang: + lang = get_language() kwargs = {'year': self.date_published.year, 'month': '%02d' % self.date_published.month, 'day': '%02d' % self.date_published.day, 'slug': self.safe_translation_getter('slug', - language_code=get_language(), + language_code=lang, any_language=True)} - return reverse('djangocms_blog:post-detail', kwargs=kwargs) + return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs) - def save(self, *args, **kwargs): - if not self.slug and self.title: - self.slug = slugify(self.title) - super(Post, self).save(*args, **kwargs) + def save_translation(self, translation, *args, **kwargs): + if not translation.slug and translation.title: + translation.slug = slugify(translation.title) + super(Post, self).save_translation(translation, *args, **kwargs) def get_title(self): title = self.safe_translation_getter('meta_title', any_language=True) @@ -230,7 +242,10 @@ class BasePostPlugin(CMSPlugin): def post_queryset(self, request=None): language = get_language() - posts = Post._default_manager.active_translations(language_code=language) + posts = Post._default_manager + if self.app_config: + posts = posts.namespace(self.app_config.namespace) + posts = posts.active_translations(language_code=language) if not request or not getattr(request, 'toolbar', False) or not request.toolbar.edit_mode: posts = posts.published() return posts @@ -240,7 +255,7 @@ class BasePostPlugin(CMSPlugin): class LatestPostsPlugin(BasePostPlugin): - + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'), help_text=_('The number of latests ' u'articles to be displayed.')) @@ -269,6 +284,7 @@ class LatestPostsPlugin(BasePostPlugin): class AuthorEntriesPlugin(BasePostPlugin): + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) authors = models.ManyToManyField( dj_settings.AUTH_USER_MODEL, verbose_name=_('authors'), limit_choices_to={'djangocms_blog_post_author__publish': True} @@ -292,6 +308,17 @@ class AuthorEntriesPlugin(BasePostPlugin): authors = self.authors.all() for author in authors: author.count = 0 - if author.djangocms_blog_post_author.filter(publish=True).exists(): - author.count = author.djangocms_blog_post_author.filter(publish=True).count() + qs = author.djangocms_blog_post_author + if self.app_config: + qs = qs.namespace(self.app_config.namespace) + count = qs.filter(publish=True).count() + if count: + author.count = count return authors + + +class GenericBlogPlugin(BasePostPlugin): + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) + + def __str__(self): + return _('generic blog plugin') diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index 6676154..40b9dbe 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -52,5 +52,6 @@ def get_setting(name): 'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True), 'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True), 'BLOG_AUTHOR_DEFAULT': getattr(settings, 'BLOG_AUTHOR_DEFAULT', True), + 'BLOG_DEFAULT_PUBLISHED': getattr(settings, 'BLOG_DEFAULT_PUBLISHED', False), } return default['BLOG_%s' % name] diff --git a/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py b/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py new file mode 100644 index 0000000..9cd1487 --- /dev/null +++ b/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py @@ -0,0 +1,286 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'BlogConfigTranslation' + db.create_table('djangocms_blog_blogconfig_translation', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('language_code', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=15)), + ('app_title', self.gf('django.db.models.fields.CharField')(max_length=234)), + ('master', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['djangocms_blog.BlogConfig'], related_name='translations', null=True)), + )) + db.send_create_signal('djangocms_blog', ['BlogConfigTranslation']) + + # Adding unique constraint on 'BlogConfigTranslation', fields ['language_code', 'master'] + db.create_unique('djangocms_blog_blogconfig_translation', ['language_code', 'master_id']) + + # Adding model 'BlogConfig' + db.create_table('djangocms_blog_blogconfig', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('type', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('namespace', self.gf('django.db.models.fields.CharField')(default=None, max_length=100, unique=True)), + ('app_data', self.gf('app_data.fields.AppDataField')(default='{}')), + )) + db.send_create_signal('djangocms_blog', ['BlogConfig']) + + # Adding model 'GenericBlogPlugin' + db.create_table('djangocms_blog_genericblogplugin', ( + ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(primary_key=True, to=orm['cms.CMSPlugin'], unique=True)), + ('app_config', self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(blank=True, null=True, to=orm['djangocms_blog.BlogConfig'])), + )) + db.send_create_signal('djangocms_blog', ['GenericBlogPlugin']) + + # Adding field 'AuthorEntriesPlugin.app_config' + db.add_column('djangocms_blog_authorentriesplugin', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(default=None, blank=True, null=True, to=orm['djangocms_blog.BlogConfig']), + keep_default=False) + + # Adding field 'Post.app_config' + db.add_column('djangocms_blog_post', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, default=None), + keep_default=False) + + # Adding field 'BlogCategory.app_config' + db.add_column('djangocms_blog_blogcategory', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, default=None), + keep_default=False) + + # Adding field 'LatestPostsPlugin.app_config' + db.add_column('djangocms_blog_latestpostsplugin', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(default=None, null=True, blank=True, to=orm['djangocms_blog.BlogConfig']), + keep_default=False) + + + def backwards(self, orm): + # Removing unique constraint on 'BlogConfigTranslation', fields ['language_code', 'master'] + db.delete_unique('djangocms_blog_blogconfig_translation', ['language_code', 'master_id']) + + # Deleting model 'BlogConfigTranslation' + db.delete_table('djangocms_blog_blogconfig_translation') + + # Deleting model 'BlogConfig' + db.delete_table('djangocms_blog_blogconfig') + + # Deleting model 'GenericBlogPlugin' + db.delete_table('djangocms_blog_genericblogplugin') + + # Deleting field 'AuthorEntriesPlugin.app_config' + db.delete_column('djangocms_blog_authorentriesplugin', 'app_config_id') + + # Deleting field 'Post.app_config' + db.delete_column('djangocms_blog_post', 'app_config_id') + + # Deleting field 'BlogCategory.app_config' + db.delete_column('djangocms_blog_blogcategory', 'app_config_id') + + # Deleting field 'LatestPostsPlugin.app_config' + db.delete_column('djangocms_blog_latestpostsplugin', 'app_config_id') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['cms.CMSPlugin']", 'null': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'blank': 'True', 'null': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) + }, + 'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'object_name': 'ThumbnailOption', 'ordering': "('width', 'height')"}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True'}) + }, + 'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "'djangocms_blog_blogcategory_translation'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}) + }, + 'djangocms_blog.blogconfig': { + 'Meta': {'object_name': 'BlogConfig'}, + 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), + 'paginate_by': ('django.db.models.fields.PositiveIntegerField', [], {'default': '10'}), + 'set_author': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.blogconfigtranslation': { + 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "'djangocms_blog_blogconfig_translation'"}, + 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) + }, + 'djangocms_blog.genericblogplugin': { + 'Meta': {'object_name': 'GenericBlogPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}) + }, + 'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.post': { + 'Meta': {'object_name': 'Post', 'ordering': "('-date_published', '-date_created')"}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'to': "orm['auth.User']", 'null': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'to': "orm['filer.Image']", 'null': 'True'}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['sites.Site']", 'null': 'True'}) + }, + 'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'PostTranslation', 'db_table': "'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), + 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'null': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'all_files'", 'to': "orm['filer.Folder']", 'null': 'True'}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_files'", 'to': "orm['auth.User']", 'null': 'True'}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), + 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) + }, + 'filer.folder': { + 'Meta': {'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder', 'ordering': "('name',)"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'filer_owned_folders'", 'to': "orm['auth.User']", 'null': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'to': "orm['filer.Folder']", 'null': 'True'}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['filer.File']", 'unique': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '64', 'default': 'None', 'null': 'True'}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['djangocms_blog'] diff --git a/djangocms_blog/south_migrations/0015_create_appconfig.py b/djangocms_blog/south_migrations/0015_create_appconfig.py new file mode 100644 index 0000000..90c6d73 --- /dev/null +++ b/djangocms_blog/south_migrations/0015_create_appconfig.py @@ -0,0 +1,234 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import DataMigration +from django.db import models +from cms.models import Page +from cms.utils.conf import get_languages + + +class Migration(DataMigration): + + def forwards(self, orm): + BlogConfig = orm['djangocms_blog.BlogConfig'] + Post = orm['djangocms_blog.Post'] + BlogCategory = orm['djangocms_blog.BlogCategory'] + GenericBlogPlugin = orm['djangocms_blog.GenericBlogPlugin'] + LatestPostsPlugin = orm['djangocms_blog.LatestPostsPlugin'] + AuthorEntriesPlugin = orm['djangocms_blog.AuthorEntriesPlugin'] + config = None + for page in Page.objects.drafts().filter(application_urls='BlogApp'): + config = BlogConfig.objects.create(namespace=page.application_namespace) + for lang in get_languages(): + config.create_translation(lang, app_title='Blog') + if config: + for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): + for item in model.objects.all(): + item.app_config = config + item.save() + + def backwards(self, orm): + # No need for backward data migration + pass + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) + }, + 'auth.permission': { + 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), + 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Group']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['cms.CMSPlugin']", 'null': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'blank': 'True', 'null': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) + }, + 'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'object_name': 'ThumbnailOption', 'ordering': "('width', 'height')"}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + 'contenttypes.contenttype': { + 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True'}) + }, + 'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "'djangocms_blog_blogcategory_translation'"}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}) + }, + 'djangocms_blog.blogconfig': { + 'Meta': {'object_name': 'BlogConfig'}, + 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), + 'paginate_by': ('django.db.models.fields.PositiveIntegerField', [], {'default': '10'}), + 'set_author': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.blogconfigtranslation': { + 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "'djangocms_blog_blogconfig_translation'"}, + 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) + }, + 'djangocms_blog.genericblogplugin': { + 'Meta': {'object_name': 'GenericBlogPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}) + }, + 'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']"}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.post': { + 'Meta': {'object_name': 'Post', 'ordering': "('-date_published', '-date_created')"}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'to': "orm['auth.User']", 'null': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'to': "orm['filer.Image']", 'null': 'True'}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['sites.Site']", 'null': 'True'}) + }, + 'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'PostTranslation', 'db_table': "'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), + 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'null': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'all_files'", 'to': "orm['filer.Folder']", 'null': 'True'}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_files'", 'to': "orm['auth.User']", 'null': 'True'}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), + 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) + }, + 'filer.folder': { + 'Meta': {'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder', 'ordering': "('name',)"}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'filer_owned_folders'", 'to': "orm['auth.User']", 'null': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'to': "orm['filer.Folder']", 'null': 'True'}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), + 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['filer.File']", 'unique': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '64', 'default': 'None', 'null': 'True'}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['djangocms_blog'] diff --git a/djangocms_blog/templates/djangocms_blog/post_detail.html b/djangocms_blog/templates/djangocms_blog/post_detail.html index 9313f11..1ca1fe3 100644 --- a/djangocms_blog/templates/djangocms_blog/post_detail.html +++ b/djangocms_blog/templates/djangocms_blog/post_detail.html @@ -21,7 +21,7 @@ {% endif %} {% endspaceless %} - {% if use_placeholder %} + {% if post.app_config.use_placeholder %}
{% render_placeholder post.content %}
{% else %}
{% render_model post "post_text" "post_text" %}
diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py index 31c64f5..87268d4 100644 --- a/djangocms_blog/views.py +++ b/djangocms_blog/views.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from aldryn_apphooks_config.mixins import AppConfigMixin from django.contrib.auth import get_user_model -from django.core.urlresolvers import resolve from django.utils.timezone import now from django.utils.translation import get_language from django.views.generic import DetailView, ListView @@ -14,25 +14,24 @@ from .settings import get_setting User = get_user_model() -class BaseBlogView(ViewUrlMixin): +class BaseBlogView(AppConfigMixin, ViewUrlMixin): def get_queryset(self): language = get_language() - queryset = self.model._default_manager.all().active_translations(language_code=language) + queryset = self.model._default_manager.namespace( + self.namespace + ).active_translations( + language_code=language + ) if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode: queryset = queryset.published() return queryset - def render_to_response(self, context, **response_kwargs): - response_kwargs['current_app'] = resolve(self.request.path).namespace - return super(BaseBlogView, self).render_to_response(context, **response_kwargs) - class PostListView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' template_name = 'djangocms_blog/post_list.html' - paginate_by = get_setting('PAGINATION') view_url_name = 'djangocms_blog:posts-latest' def get_context_data(self, **kwargs): @@ -40,6 +39,9 @@ class PostListView(BaseBlogView, ListView): context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT') return context + def get_paginate_by(self, queryset): + return self.config.paginate_by + class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView): model = Post diff --git a/setup.py b/setup.py index da66159..0623fcb 100755 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ setup( 'cmsplugin-filer', 'django-meta>=0.2', 'django-meta-mixin>=0.1.1', + 'aldryn-apphooks-config', ], license='BSD', zip_safe=False, diff --git a/tests/__init__.py b/tests/__init__.py index 9cf872c..73624fa 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,11 +3,13 @@ Tests for `djangocms_blog` module. """ from __future__ import absolute_import, print_function, unicode_literals +from copy import deepcopy from cmsplugin_filer_image.models import ThumbnailOption from django.contrib.auth import get_user_model from django.contrib.sites.models import Site -from django.utils.translation import activate +from parler.utils.context import smart_override +from djangocms_blog.cms_appconfig import BlogConfig from djangocms_helper.base_test import BaseTestCase from djangocms_blog.models import BlogCategory, Post @@ -31,85 +33,123 @@ class BaseTest(BaseTestCase): {'en': {'title': 'page one', 'template': 'page.html', 'publish': True}, 'fr': {'title': 'page un', 'publish': True}, 'it': {'title': 'pagina uno', 'publish': True}}, - {'en': {'title': 'page two', 'template': 'page.html', 'publish': True}, + {'en': {'title': 'page two', 'template': 'page.html', 'publish': True, + 'apphook': 'BlogApp', 'apphook_namespace': 'sample_app'}, 'fr': {'title': 'page deux', 'publish': True}, 'it': {'title': 'pagina due', 'publish': True}}, + {'en': {'title': 'page three', 'template': 'page.html', 'publish': True, + 'apphook': 'BlogApp', 'apphook_namespace': 'sample_app2'}, + 'fr': {'title': 'page trois', 'publish': True}, + 'it': {'title': 'pagina tre', 'publish': True}}, ) - data = { - 'it': [ - {'title': u'Primo post', 'abstract': u'

prima riga

', - 'description': u'Questa è la descrizione', 'keywords': u'keyword1, keyword2', - 'text': u'Testo del post'}, - {'title': u'Secondo post', 'abstract': u'

prima riga del secondo post

', - 'description': u'Descrizione del secondo post', 'keywords': u'keyword3, keyword4', - 'text': u'Testo del secondo post'}, - {'title': u'Terzo post', 'abstract': u'

prima riga del terzo post

', - 'description': u'Descrizione del terzo post', 'keywords': u'keyword5, keyword6', - 'text': u'Testo del terzo post'}, - ], - 'en': [ - {'title': u'First post', 'abstract': u'

first line

', - 'description': u'This is the description', 'keywords': u'keyword1, keyword2', - 'text': u'Post text'}, - {'title': u'Second post', 'abstract': u'

second post first line

', - 'description': u'Second post description', 'keywords': u'keyword3, keyword4', - 'text': u'Second post text'}, - {'title': u'Third post', 'abstract': u'

third post first line

', - 'description': u'third post description', 'keywords': u'keyword5, keyword6', - 'text': u'Third post text'} - ] - } + _post_data = ( + {'en': {'title': 'First post', 'abstract': '

first line

', + 'description': 'This is the description', 'keywords': 'keyword1, keyword2', + 'text': 'Post text', 'app_config': 'sample_app', 'publish': True}, + 'it': {'title': 'Primo post', 'abstract': '

prima riga

', + 'description': 'Questa è la descrizione', 'keywords': 'keyword1, keyword2', + 'text': 'Testo del post'}, + }, + {'en': {'title': 'Second post', 'abstract': '

second post first line

', + 'description': 'Second post description', 'keywords': 'keyword3, keyword4', + 'text': 'Second post text', 'app_config': 'sample_app', 'publish': False}, + 'it': {'title': 'Secondo post', 'abstract': '

prima riga del secondo post

', + 'description': 'Descrizione del secondo post', 'keywords': 'keyword3, keyword4', + 'text': 'Testo del secondo post', 'app_config': 'sample_app'}, + }, + {'en': {'title': 'Third post', 'abstract': '

third post first line

', + 'description': 'third post description', 'keywords': 'keyword5, keyword6', + 'text': 'Third post text', 'app_config': 'sample_app', 'publish': False}, + 'it': {'title': 'Terzo post', 'abstract': '

prima riga del terzo post

', + 'description': 'Descrizione del terzo post', 'keywords': 'keyword5, keyword6', + 'text': 'Testo del terzo post'}, + }, + {'en': {'title': 'Different appconfig', 'abstract': '

Different appconfig first line

', + 'description': 'Different appconfig description', 'keywords': 'keyword5, keyword6', + 'text': 'Different appconfig text', 'app_config': 'sample_app2', 'publish': True}, + 'it': {'title': 'Altro appconfig', 'abstract': '

prima riga del Altro appconfig

', + 'description': 'Descrizione Altro appconfig', 'keywords': 'keyword5, keyword6', + 'text': 'Testo del Altro appconfig'}, + }, + ) - cat_data = { - 'it': [ - {'name': u'Fortissimo'}, - {'name': u'Pianississimo'}, - {'name': u'Mezzo'}, - {'name': u'Forte', 'parent_id': _get_cat_pk('it', 'Mezzo')}, - ], - 'en': [ - {'name': u'Very loud'}, - {'name': u'Very very silent'}, - {'name': u'Almost'}, - {'name': u'Loud', 'parent_id': _get_cat_pk('en', 'Almost')}, - {'name': u'Silent', 'parent_id': _get_cat_pk('en', 'Almost')}, - ] - } + _categories_data = ( + {'en': {'name': 'Very loud', 'app_config': 'sample_app'}, + 'it': {'name': 'Fortissimo'}, + }, + {'en': {'name': 'Very very silent', 'app_config': 'sample_app'}, + 'it': {'name': 'Pianississimo'}, + }, + {'en': {'name': 'Almost', 'app_config': 'sample_app'}, + 'it': {'name': 'Mezzo'}, + }, + {'en': {'name': 'Loud', 'parent_id': _get_cat_pk('en', 'Almost'), 'app_config': 'sample_app'}, + 'it': {'name': 'Forte', 'parent_id': _get_cat_pk('it', 'Mezzo')}, + }, + {'en': {'name': 'Silent', 'parent_id': _get_cat_pk('en', 'Almost'), 'app_config': 'sample_app'}, + }, + {'en': {'name': 'Drums', 'app_config': 'sample_app2'}, + 'it': {'name': 'Tamburi'}, + }, + {'en': {'name': 'Guitars', 'app_config': 'sample_app2'}, + 'it': {'name': 'Chitarre'}, + }, + ) @classmethod def setUpClass(cls): super(BaseTest, cls).setUpClass() + cls.thumb_1 = ThumbnailOption.objects.create( + name='base', width=100, height=100, crop=True, upscale=False + ) + cls.thumb_2 = ThumbnailOption.objects.create( + name='main', width=200, height=200, crop=False, upscale=False + ) + cls.app_config_1 = BlogConfig.objects.create( + namespace='sample_app', app_title='app1' + ) + cls.app_config_2 = BlogConfig.objects.create( + namespace='sample_app2', app_title='app2' + ) + cls.app_config_1.app_data.config.paginate_by = 1 + cls.app_config_1.save() + cls.app_config_2.app_data.config.paginate_by = 2 + cls.app_config_2.save() + cls.app_configs = { + 'sample_app': cls.app_config_1, + 'sample_app2': cls.app_config_2, + } + cls.category_1 = BlogCategory.objects.create(name='category 1', app_config=cls.app_config_1) + cls.category_1.set_current_language('it', initialize=True) + cls.category_1.name = 'categoria 1' + cls.category_1.save() cls.site_2 = Site.objects.create(domain='http://example2.com', name='example 2') def setUp(self): - activate('en') super(BaseTest, self).setUp() - self.category_1 = BlogCategory.objects.create(name=u'category 1') - self.category_1.set_current_language('it', initialize=True) - self.category_1.name = u'categoria 1' - self.category_1.save() - self.thumb_1 = ThumbnailOption.objects.create( - name='base', width=100, height=100, crop=True, upscale=False - ) - self.thumb_2 = ThumbnailOption.objects.create( - name='main', width=200, height=200, crop=False, upscale=False - ) self.img = self.create_filer_image_object() def tearDown(self): - for post in Post.objects.all(): - post.delete() + Post.objects.all().delete() super(BaseTest, self).tearDown() + @classmethod + def tearDownClass(cls): + BlogConfig.objects.all().delete() + super(BaseTest, cls).tearDownClass() + def _get_category(self, data, category=None, lang='en'): + data = deepcopy(data) for k, v in data.items(): if hasattr(v, '__call__'): data[k] = v() if not category: - category = BlogCategory.objects.create(**data) + with smart_override(lang): + data['app_config'] = self.app_configs[data['app_config']] + category = BlogCategory.objects.create(**data) else: - category.set_current_language(lang) + category.set_current_language(lang, initialize=True) for attr, val in data.items(): setattr(category, attr, val) category.save() @@ -123,15 +163,18 @@ class BaseTest(BaseTestCase): 'abstract': data['abstract'], 'meta_description': data['description'], 'meta_keywords': data['keywords'], + 'app_config': self.app_configs[data['app_config']] } post = Post.objects.create(**post_data) else: - post.set_current_language(lang) - post.title = data['title'] - post.abstract = data['abstract'] - post.meta_description = data['description'] - post.meta_keywords = data['keywords'] - post.save() + post.create_translation( + lang, + title=data['title'], + abstract=data['abstract'], + meta_description=data['description'], + meta_keywords=data['keywords'] + ) + post = self.reload_model(post) post.categories.add(self.category_1) if sites: for site in sites: @@ -139,13 +182,12 @@ class BaseTest(BaseTestCase): return post def get_posts(self, sites=None): - post1 = self._get_post(self.data['en'][0], sites=sites) - post1 = self._get_post(self.data['it'][0], post1, 'it') - post1.publish = True - post1.main_image = self.img - post1.save() - post2 = self._get_post(self.data['en'][1], sites=sites) - post2 = self._get_post(self.data['it'][1], post2, 'it') - post2.main_image = self.img - post2.save() - return post1, post2 + posts = [] + for post in self._post_data: + post1 = self._get_post(post['en'], sites=sites) + post1 = self._get_post(post['it'], post=post1, lang='it') + post1.publish = post['en']['publish'] + post1.main_image = self.img + post1.save() + posts.append(post1) + return posts diff --git a/tests/test_menu.py b/tests/test_menu.py index 84d334c..56673d6 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -2,10 +2,11 @@ from __future__ import absolute_import, print_function, unicode_literals import copy +from aldryn_apphooks_config.utils import get_app_instance from django.utils.translation import activate from menus.menu_pool import menu_pool -from parler.utils.context import switch_language +from parler.utils.context import switch_language, smart_override from djangocms_blog.views import CategoryEntriesView, PostDetailView @@ -13,13 +14,15 @@ from . import BaseTest class MenuTest(BaseTest): + cats = [] + def setUp(self): super(MenuTest, self).setUp() self.cats = [self.category_1] - for i, cat_data in enumerate(self.cat_data['en']): - cat = self._get_category(cat_data) - if i < len(self.cat_data['it']): - cat = self._get_category(self.cat_data['it'][i], cat, 'it') + for i, lang_data in enumerate(self._categories_data): + cat = self._get_category(lang_data['en']) + if 'it' in lang_data: + cat = self._get_category(lang_data['it'], cat, 'it') self.cats.append(cat) activate('en') @@ -32,54 +35,46 @@ class MenuTest(BaseTest): """ Tests if all categories are present in the menu """ + self.get_posts() + self.get_pages() + for lang in ('en', 'it'): - request = self.get_page_request(None, self.user, - r'/%s/blog/' % lang, edit=False) - activate(lang) - nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') - nodes_copy = copy.deepcopy(nodes) - for cat in self.cats: - if not cat.has_translation(lang): - continue - with switch_language(cat, lang): - # find in node list - found = None - for node in nodes_copy: - if node.url == cat.get_absolute_url(): - found = node - break - self.assertIsNotNone(found) - nodes_copy.remove(found) - self.assertEqual(node.id, cat.id) - self.assertEqual(node.title, cat.name) - # check that all categories were found in menu - self.assertEqual(len(nodes_copy), 0) + request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang) + with smart_override(lang): + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + nodes_url = set([node.url for node in nodes]) + cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)]) + self.assertTrue(cats_url.issubset(nodes_url)) def test_modifier(self): """ Tests if correct category is selected in the menu according to context (view object) """ - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() + tests = ( # view class, view kwarg, view object, category - (PostDetailView, 'slug', post1, post1.categories.first()), + (PostDetailView, 'slug', posts[0], posts[0].categories.first()), (CategoryEntriesView, 'category', self.cats[2], self.cats[2]) ) for view_cls, kwarg, obj, cat in tests: - request = self.get_page_request(None, self.user, r'/en/blog/', edit=False) - activate('en') - with switch_language(obj, 'en'): - view_obj = view_cls() - view_obj.request = request - view_obj.kwargs = {kwarg: obj.slug} - view_obj.get(request) - # check if selected menu node points to cat - nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') - found = False - for node in nodes: - if node.selected: - self.assertEqual(node.url, cat.get_absolute_url()) - found = True - break - self.assertTrue(found) + request = self.get_page_request(pages[1], self.user, path=obj.get_absolute_url()) + with smart_override('en'): + with switch_language(obj, 'en'): + view_obj = view_cls() + view_obj.request = request + view_obj.namespace, view_obj.config = get_app_instance(request) + view_obj.app_config = self.app_config_1 + view_obj.kwargs = {kwarg: obj.slug} + view_obj.get(request) + # check if selected menu node points to cat + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + found = False + for node in nodes: + if node.selected: + self.assertEqual(node.url, obj.get_absolute_url()) + found = True + break + self.assertTrue(found) diff --git a/tests/test_models.py b/tests/test_models.py index 771ac94..9d40595 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -25,24 +25,41 @@ from . import BaseTest class AdminTest(BaseTest): + def setUp(self): + super(AdminTest, self).setUp() + admin.autodiscover() + def test_admin_fieldsets(self): - request = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False) post_admin = admin.site._registry[Post] + request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False) - with self.settings(BLOG_USE_PLACEHOLDER=True): - fsets = post_admin.get_fieldsets(request) - self.assertFalse('post_text' in fsets[0][1]['fields']) + # Use placeholder + self.app_config_1.app_data.config.use_placeholder = True + self.app_config_1.save() + fsets = post_admin.get_fieldsets(request) + self.assertFalse('post_text' in fsets[0][1]['fields']) - with self.settings(BLOG_USE_PLACEHOLDER=False): - fsets = post_admin.get_fieldsets(request) - self.assertTrue('post_text' in fsets[0][1]['fields']) + self.app_config_1.app_data.config.use_placeholder = False + self.app_config_1.save() + fsets = post_admin.get_fieldsets(request) + self.assertTrue('post_text' in fsets[0][1]['fields']) - with self.settings(BLOG_USE_ABSTRACT=True): - fsets = post_admin.get_fieldsets(request) - self.assertTrue('abstract' in fsets[0][1]['fields']) - with self.settings(BLOG_USE_ABSTRACT=False): - fsets = post_admin.get_fieldsets(request) - self.assertFalse('abstract' in fsets[0][1]['fields']) + self.app_config_1.app_data.config.use_placeholder = True + self.app_config_1.save() + + # Use abstract + self.app_config_1.app_data.config.use_abstract = True + self.app_config_1.save() + fsets = post_admin.get_fieldsets(request) + self.assertTrue('abstract' in fsets[0][1]['fields']) + + self.app_config_1.app_data.config.use_abstract = False + self.app_config_1.save() + fsets = post_admin.get_fieldsets(request) + self.assertFalse('abstract' in fsets[0][1]['fields']) + + self.app_config_1.app_data.config.use_abstract = True + self.app_config_1.save() with self.settings(BLOG_MULTISITE=True): fsets = post_admin.get_fieldsets(request) @@ -51,49 +68,55 @@ class AdminTest(BaseTest): fsets = post_admin.get_fieldsets(request) self.assertFalse('sites' in fsets[1][1]['fields'][0]) - request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) + request = self.get_page_request('/', self.user, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False) fsets = post_admin.get_fieldsets(request) self.assertTrue('author' in fsets[1][1]['fields'][0]) def test_admin_auto_author(self): - page1, page2 = self.get_pages() - data = deepcopy(self.data['en'][0]) + pages = self.get_pages() + data = deepcopy(self._post_data[0]['en']) with self.login_user_context(self.user): - with self.settings(BLOG_AUTHOR_DEFAULT=True): - data['date_published_0'] = now().strftime('%Y-%m-%d') - data['date_published_1'] = now().strftime('%H:%M:%S') - data['categories'] = self.category_1.pk - request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') - msg_mid = MessageMiddleware() - msg_mid.process_request(request) - post_admin = admin.site._registry[Post] - response = post_admin.add_view(request) - self.assertEqual(response.status_code, 302) - self.assertEqual(Post.objects.count(), 1) - self.assertEqual(Post.objects.get(translations__slug='first-post').author_id, - request.user.pk) + self.app_config_1.app_data.config.set_author = True + self.app_config_1.save() + data['date_published_0'] = now().strftime('%Y-%m-%d') + data['date_published_1'] = now().strftime('%H:%M:%S') + data['categories'] = self.category_1.pk + data['app_config'] = self.app_config_1.pk + request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk) + msg_mid = MessageMiddleware() + msg_mid.process_request(request) + post_admin = admin.site._registry[Post] + response = post_admin.add_view(request) + self.assertEqual(response.status_code, 302) + self.assertEqual(Post.objects.count(), 1) + self.assertEqual(Post.objects.get(translations__slug='first-post').author_id, request.user.pk) - with self.settings(BLOG_AUTHOR_DEFAULT=False): - data = deepcopy(self.data['en'][1]) - data['date_published_0'] = now().strftime('%Y-%m-%d') - data['date_published_1'] = now().strftime('%H:%M:%S') - data['categories'] = self.category_1.pk - request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') - msg_mid = MessageMiddleware() - msg_mid.process_request(request) - post_admin = admin.site._registry[Post] - response = post_admin.add_view(request) - self.assertEqual(response.status_code, 302) - self.assertEqual(Post.objects.count(), 2) - self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None) + self.app_config_1.app_data.config.set_author = False + self.app_config_1.save() + data = deepcopy(self._post_data[1]['en']) + data['date_published_0'] = now().strftime('%Y-%m-%d') + data['date_published_1'] = now().strftime('%H:%M:%S') + data['categories'] = self.category_1.pk + data['app_config'] = self.app_config_1.pk + request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk) + msg_mid = MessageMiddleware() + msg_mid.process_request(request) + post_admin = admin.site._registry[Post] + response = post_admin.add_view(request) + self.assertEqual(response.status_code, 302) + self.assertEqual(Post.objects.count(), 2) + self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None) with self.settings(BLOG_AUTHOR_DEFAULT='staff'): - data = deepcopy(self.data['en'][2]) + self.app_config_1.app_data.config.set_author = True + self.app_config_1.save() + data = deepcopy(self._post_data[2]['en']) data['date_published_0'] = now().strftime('%Y-%m-%d') data['date_published_1'] = now().strftime('%H:%M:%S') data['categories'] = self.category_1.pk - request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') + data['app_config'] = self.app_config_1.pk + request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk) msg_mid = MessageMiddleware() msg_mid.process_request(request) post_admin = admin.site._registry[Post] @@ -103,17 +126,18 @@ class AdminTest(BaseTest): self.assertEqual(Post.objects.get(translations__slug='third-post').author.username, 'staff') def test_admin_post_text(self): - page1, page2 = self.get_pages() - post = self._get_post(self.data['en'][0]) + pages = self.get_pages() + post = self._get_post(self._post_data[0]['en']) with self.login_user_context(self.user): with self.settings(BLOG_USE_PLACEHOLDER=False): - data = {'post_text': 'ehi text'} - request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') + data = {'post_text': 'ehi text', 'title': 'some title'} + request = self.post_request(pages[0], 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') msg_mid = MessageMiddleware() msg_mid.process_request(request) post_admin = admin.site._registry[Post] response = post_admin.edit_field(request, post.pk, 'en') + #print(response.content.decode('utf-8')) self.assertEqual(response.status_code, 200) modified_post = Post.objects.language('en').get(pk=post.pk) self.assertEqual(modified_post.safe_translation_getter('post_text'), data['post_text']) @@ -122,8 +146,8 @@ class AdminTest(BaseTest): class ModelsTest(BaseTest): def test_model_attributes(self): - post = self._get_post(self.data['en'][0]) - post = self._get_post(self.data['it'][0], post, 'it') + post = self._get_post(self._post_data[0]['en']) + post = self._get_post(self._post_data[0]['it'], post, 'it') post.main_image = self.img post.save() post.set_current_language('en') @@ -186,8 +210,8 @@ class ModelsTest(BaseTest): self.assertEqual(post.get_title(), 'meta title') def test_manager(self): - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) + post1 = self._get_post(self._post_data[0]['en']) + post2 = self._get_post(self._post_data[1]['en']) # default queryset, published and unpublished posts months = Post.objects.get_months() @@ -222,7 +246,7 @@ class ModelsTest(BaseTest): self.assertEqual(len(Post.objects.archived()), 1) # counting with language fallback enabled - self._get_post(self.data['it'][0], post1, 'it') + self._get_post(self._post_data[0]['it'], post1, 'it') self.assertEqual(len(Post.objects.filter_by_language('it')), 2) # No fallback @@ -235,8 +259,8 @@ class ModelsTest(BaseTest): parler.appsettings.PARLER_LANGUAGES[Site.objects.get_current().pk][index]['hide_untranslated'] = False def test_tag_cloud(self): - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) + post1 = self._get_post(self._post_data[0]['en']) + post2 = self._get_post(self._post_data[1]['en']) post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') post1.save() post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') @@ -270,14 +294,14 @@ class ModelsTest(BaseTest): self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags)) def test_plugin_latest(self): - post1 = self._get_post(self.data['en'][0]) - self._get_post(self.data['en'][1]) + post1 = self._get_post(self._post_data[0]['en']) + self._get_post(self._post_data[1]['en']) post1.tags.add('tag 1') post1.save() request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False) request_auth = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False) request_edit = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=True) - plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en') + plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) tag = Tag.objects.get(slug='tag-1') plugin.tags.add(tag) # unauthenticated users get no post @@ -292,11 +316,11 @@ class ModelsTest(BaseTest): self.assertEqual(len(plugin.get_posts(request)), 1) def test_copy_plugin_latest(self): - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) + post1 = self._get_post(self._post_data[0]['en']) + post2 = self._get_post(self._post_data[1]['en']) tag1 = Tag.objects.create(name='tag 1') tag2 = Tag.objects.create(name='tag 2') - plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en') + plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) plugin.tags.add(tag1) plugin.tags.add(tag2) if CMS_30: @@ -309,10 +333,10 @@ class ModelsTest(BaseTest): self.assertEqual(set(new[0].tags.all()), set(plugin.tags.all())) def test_plugin_author(self): - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) + post1 = self._get_post(self._post_data[0]['en']) + post2 = self._get_post(self._post_data[1]['en']) request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False) - plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en') + plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1) plugin.authors.add(self.user) self.assertEqual(len(plugin.get_posts(request)), 0) self.assertEqual(plugin.get_authors()[0].count, 0) @@ -328,9 +352,9 @@ class ModelsTest(BaseTest): self.assertEqual(plugin.get_authors()[0].count, 2) def test_copy_plugin_author(self): - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) - plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en') + post1 = self._get_post(self._post_data[0]['en']) + post2 = self._get_post(self._post_data[1]['en']) + plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1) plugin.authors.add(self.user) if CMS_30: plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position')) @@ -342,9 +366,9 @@ class ModelsTest(BaseTest): def test_multisite(self): with override('en'): - post1 = self._get_post(self.data['en'][0], sites=(self.site_1,)) - post2 = self._get_post(self.data['en'][1], sites=(self.site_2,)) - post3 = self._get_post(self.data['en'][2], sites=(self.site_2, self.site_1)) + post1 = self._get_post(self._post_data[0]['en'], sites=(self.site_1,)) + post2 = self._get_post(self._post_data[1]['en'], sites=(self.site_2,)) + post3 = self._get_post(self._post_data[2]['en'], sites=(self.site_2, self.site_1)) self.assertEqual(len(Post.objects.all()), 3) with self.settings(**{'SITE_ID': self.site_1.pk}): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 348a6ca..9981d81 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -16,73 +16,70 @@ from . import BaseTest class PluginTest(BaseTest): def test_plugin_latest(self): - page1, page2 = self.get_pages() - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) - post1.tags.add('tag 1') - post1.publish = True - post1.save() - ph = page1.placeholders.get(slot='content') + pages = self.get_pages() + posts = self.get_posts() + posts[0].tags.add('tag 1') + posts[0].publish = True + posts[0].save() + ph = pages[0].placeholders.get(slot='content') - plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en') + plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) tag = Tag.objects.get(slug='tag-1') plugin.tags.add(tag) - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1) self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1) self.assertTrue(rendered.find('

first line

') > -1) self.assertTrue(rendered.find('
-1) - self.assertTrue(rendered.find(post1.get_absolute_url()) > -1) + self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1) - category_2 = BlogCategory.objects.create(name=u'category 2') + category_2 = BlogCategory.objects.create(name='category 2', app_config=self.app_config_1) category_2.set_current_language('it', initialize=True) - category_2.name = u'categoria 2' + category_2.name = 'categoria 2' category_2.save() category_2.set_current_language('en') - post2.categories.add(category_2) - plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en') + posts[1].categories.add(category_2) + plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) plugin.categories.add(category_2) - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1) self.assertTrue(rendered.find(reverse('djangocms_blog:posts-category', kwargs={'category': category_2.slug})) > -1) self.assertTrue(rendered.find('

second post first line

') > -1) self.assertTrue(rendered.find('
-1) - self.assertTrue(rendered.find(post2.get_absolute_url()) > -1) + self.assertTrue(rendered.find(posts[1].get_absolute_url()) > -1) def test_plugin_authors(self): - page1, page2 = self.get_pages() - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) - post1.publish = True - post1.save() - post2.publish = True - post2.save() - ph = page1.placeholders.get(slot='content') - plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en') + pages = self.get_pages() + posts = self.get_posts() + posts[0].publish = True + posts[0].save() + posts[1].publish = True + posts[1].save() + ph = pages[0].placeholders.get(slot='content') + plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1) plugin.authors.add(self.user) - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) self.assertTrue(rendered.find(reverse('djangocms_blog:posts-author', kwargs={'username': self.user.get_username()})) > -1) self.assertTrue(rendered.find('2 articles') > -1) def test_plugin_tags(self): - page1, page2 = self.get_pages() - post1 = self._get_post(self.data['en'][0]) - post2 = self._get_post(self.data['en'][1]) - post1.tags.add('tag 1', 'tag 2', 'test tag') - post1.publish = True - post1.save() - post2.tags.add('test tag', 'another tag') - post2.publish = True - post2.save() - ph = page1.placeholders.get(slot='content') - plugin = add_plugin(ph, 'BlogTagsPlugin', language='en') - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + pages = self.get_pages() + posts = self.get_posts() + posts[0].tags.add('tag 1', 'tag 2', 'test tag') + posts[0].publish = True + posts[0].save() + posts[1].tags.add('test tag', 'another tag') + posts[1].publish = True + posts[1].save() + ph = pages[0].placeholders.get(slot='content') + plugin = add_plugin(ph, 'BlogTagsPlugin', language='en', app_config=self.app_config_1) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) for tag in Tag.objects.all(): self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1) @@ -94,38 +91,38 @@ class PluginTest(BaseTest): self.assertEqual(len(rx.findall(rendered)), 1) def test_blog_category_plugin(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() - post1.publish = True - post1.save() - post2.publish = True - post2.save() - ph = page1.placeholders.get(slot='content') - plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en') + pages = self.get_pages() + posts = self.get_posts() + posts[0].publish = True + posts[0].save() + posts[1].publish = True + posts[1].save() + ph = pages[0].placeholders.get(slot='content') + plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en', app_config=self.app_config_1) plugin_class = plugin.get_plugin_class_instance() - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) context = plugin_class.render(context, plugin, ph) self.assertTrue(context['categories']) self.assertEqual(list(context['categories']), [self.category_1]) def test_blog_archive_plugin(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() - post1.publish = True - post1.save() - post2.publish = True - post2.save() - ph = page1.placeholders.get(slot='content') - plugin = add_plugin(ph, 'BlogArchivePlugin', language='en') + pages = self.get_pages() + posts = self.get_posts() + posts[0].publish = True + posts[0].save() + posts[1].publish = True + posts[1].save() + ph = pages[0].placeholders.get(slot='content') + plugin = add_plugin(ph, 'BlogArchivePlugin', language='en', app_config=self.app_config_1) plugin_class = plugin.get_plugin_class_instance() - context = self.get_plugin_context(page1, 'en', plugin, edit=True) + context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) context = plugin_class.render(context, plugin, ph) self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date()) self.assertEqual(context['dates'][0]['count'], 2) - post2.publish = False - post2.save() + posts[1].publish = False + posts[1].save() context = plugin_class.render(context, plugin, ph) self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date()) self.assertEqual(context['dates'][0]['count'], 1) diff --git a/tests/test_toolbar.py b/tests/test_toolbar.py index be0ffb9..180191d 100644 --- a/tests/test_toolbar.py +++ b/tests/test_toolbar.py @@ -16,13 +16,13 @@ class ToolbarTest(BaseTest): Test that Blog toolbar is present and contains all items """ from cms.toolbar.toolbar import CMSToolbar - post = self._get_post(self.data['en'][0]) - page1, page2 = self.get_pages() - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) - setattr(request, BLOG_CURRENT_POST_IDENTIFIER, post) + posts = self.get_posts() + pages = self.get_pages() + request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True) + setattr(request, BLOG_CURRENT_POST_IDENTIFIER, posts[0]) toolbar = CMSToolbar(request) toolbar.get_left_items() blog_menu = toolbar.menus['djangocms_blog'] self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_changelist'))), 1) self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_add'))), 1) - self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(post.pk,)))), 1) + self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(posts[0].pk,)))), 1) diff --git a/tests/test_views.py b/tests/test_views.py index 27c35e8..ebaf6dd 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -21,51 +21,53 @@ from . import BaseTest class ViewTest(BaseTest): def test_post_list_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() - request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False) + request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) with smart_override('en'): view_obj = PostListView() view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 - self.assertEqual(list(view_obj.get_queryset()), [post1]) + self.assertEqual(list(view_obj.get_queryset()), [posts[0]]) - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=False) + request = self.get_page_request(pages[1], self.user, edit=False) view_obj.request = request view_obj.kwargs = {} qs = view_obj.get_queryset() self.assertEqual(qs.count(), 1) - self.assertEqual(set(qs), set([post1])) + self.assertEqual(set(qs), set([posts[0]])) - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) + request = self.get_page_request(pages[1], self.user, edit=True) view_obj.request = request - self.assertEqual(set(view_obj.get_queryset()), set([post1, post2])) + self.assertEqual(set(view_obj.get_queryset()), set([posts[0], posts[1], posts[2]])) view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() view_obj.paginate_by = 1 context = view_obj.get_context_data(object_list=view_obj.object_list) self.assertTrue(context['is_paginated']) - self.assertEqual(list(context['post_list']), [post2]) - self.assertEqual(context['paginator'].count, 2) - self.assertEqual(context['post_list'][0].title, 'Second post') + self.assertEqual(list(context['post_list']), [posts[2]]) + self.assertEqual(context['paginator'].count, 3) + self.assertEqual(context['post_list'][0].title, 'Third post') response = view_obj.render_to_response(context) self.assertContains(response, context['post_list'][0].get_absolute_url()) with smart_override('it'): - request = self.get_page_request(page1, self.user, r'/it/blog/', lang='it', edit=True) + request = self.get_page_request(pages[1], self.user, lang='it', edit=True) view_obj.request = request view_obj.object_list = view_obj.get_queryset() context = view_obj.get_context_data(object_list=view_obj.object_list) - self.assertEqual(context['post_list'][0].title, 'Secondo post') + self.assertEqual(context['post_list'][0].title, 'Terzo post') response = view_obj.render_to_response(context) self.assertContains(response, context['post_list'][0].get_absolute_url()) def test_post_list_view_fallback(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() PARLER_FALLBACK = { 1: ( @@ -81,89 +83,103 @@ class ViewTest(BaseTest): with smart_override('fr'): view_obj = PostListView() - request = self.get_page_request(page1, self.user, r'/fr/blog/', lang='fr', edit=True) + request = self.get_page_request(pages[0], self.user, r'/fr/blog/', lang='fr', edit=True) view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() view_obj.get_context_data(object_list=view_obj.object_list) - self.assertEqual(view_obj.get_queryset().count(), 2) + self.assertEqual(view_obj.get_queryset().count(), 3) PARLER_FALLBACK = add_default_language_settings(PARLER_FALLBACK) with override_parler_settings(PARLER_LANGUAGES=PARLER_FALLBACK): view_obj = PostListView() - request = self.get_page_request(page1, self.user, r'/fr/blog/', lang='fr', edit=True) + request = self.get_page_request(pages[0], self.user, r'/fr/blog/', lang='fr', edit=True) view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() view_obj.get_context_data(object_list=view_obj.object_list) self.assertEqual(view_obj.get_queryset().count(), 0) def test_post_detail_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() with smart_override('en'): - with switch_language(post1, 'en'): - request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False) + with switch_language(posts[0], 'en'): + request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) view_obj = PostDetailView() view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 with self.assertRaises(Http404): view_obj.kwargs = {'slug': 'not-existing'} post_obj = view_obj.get_object() - view_obj.kwargs = {'slug': post1.slug} + view_obj.kwargs = {'slug': posts[0].slug} post_obj = view_obj.get_object() - self.assertEqual(post_obj, post1) + self.assertEqual(post_obj, posts[0]) self.assertEqual(post_obj.language_code, 'en') with smart_override('it'): - with switch_language(post1, 'it'): - request = self.get_page_request(page1, AnonymousUser(), r'/it/blog/', lang='it', edit=False) + with switch_language(posts[0], 'it'): + request = self.get_page_request(pages[0], AnonymousUser(), r'/it/blog/', lang='it', edit=False) + view_obj = PostDetailView() view_obj.request = request - view_obj.kwargs = {'slug': post1.slug} + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 + + view_obj.kwargs = {'slug': posts[0].slug} post_obj = view_obj.get_object() - self.assertEqual(post_obj, post1) + self.assertEqual(post_obj, posts[0]) self.assertEqual(post_obj.language_code, 'it') view_obj.object = post_obj context = view_obj.get_context_data() - self.assertEqual(context['post'], post1) + self.assertEqual(context['post'], posts[0]) self.assertEqual(context['post'].language_code, 'it') self.assertTrue(context['meta']) def test_post_archive_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False) + request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) view_obj = PostArchiveView() view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.kwargs = {'year': now().year, 'month': now().month} # One post only, anonymous request qs = view_obj.get_queryset() self.assertEqual(qs.count(), 1) - self.assertEqual(list(qs), [post1]) + self.assertEqual(list(qs), [posts[0]]) view_obj.object_list = qs context = view_obj.get_context_data(object_list=view_obj.object_list) self.assertEqual(context['archive_date'].date(), now().replace(year=now().year, month=now().month, day=1).date()) def test_category_entries_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) + request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True) view_obj = CategoryEntriesView() view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.kwargs = {'category': 'category-1'} qs = view_obj.get_queryset() - self.assertEqual(qs.count(), 2) - self.assertEqual(set(qs), set([post1, post2])) + self.assertEqual(qs.count(), 3) + self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]])) view_obj.paginate_by = 1 view_obj.object_list = qs @@ -171,22 +187,29 @@ class ViewTest(BaseTest): self.assertTrue(context['category']) self.assertEqual(context['category'], self.category_1) self.assertTrue(context['is_paginated']) - self.assertEqual(list(context['post_list']), [post2]) - self.assertEqual(context['paginator'].count, 2) - self.assertEqual(context['post_list'][0].title, 'Second post') + self.assertEqual(list(context['post_list']), [posts[2]]) + self.assertEqual(context['paginator'].count, 3) + self.assertEqual(context['post_list'][0].title, 'Third post') + + request = self.get_page_request(pages[1], self.user, edit=False) + view_obj.request = request + qs = view_obj.get_queryset() + self.assertEqual(qs.count(), 1) def test_author_entries_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() + posts = self.get_posts() + pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) + request = self.get_page_request(pages[1], self.user, edit=True) view_obj = AuthorEntriesView() + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.request = request view_obj.kwargs = {'username': self.user.get_username()} qs = view_obj.get_queryset() - self.assertEqual(qs.count(), 2) - self.assertEqual(set(qs), set([post1, post2])) + self.assertEqual(qs.count(), 3) + self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]])) view_obj.paginate_by = 1 view_obj.object_list = qs @@ -194,74 +217,87 @@ class ViewTest(BaseTest): self.assertTrue(context['author']) self.assertEqual(context['author'], self.user) self.assertTrue(context['is_paginated']) - self.assertEqual(list(context['post_list']), [post2]) - self.assertEqual(context['paginator'].count, 2) - self.assertEqual(context['post_list'][0].title, 'Second post') + self.assertEqual(list(context['post_list']), [posts[2]]) + self.assertEqual(context['paginator'].count, 3) + self.assertEqual(context['post_list'][0].title, 'Third post') + + request = self.get_page_request(pages[1], self.user, edit=False) + view_obj.request = request + qs = view_obj.get_queryset() + self.assertEqual(qs.count(), 1) def test_taggedlist_view(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() - post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') - post1.save() - post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') - post2.save() + pages = self.get_pages() + posts = self.get_posts() + posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') + posts[0].save() + posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') + posts[1].save() with smart_override('en'): - request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) + request = self.get_page_request(pages[1], self.user, edit=True) view_obj = TaggedListView() view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 view_obj.kwargs = {'tag': 'tag-2'} qs = view_obj.get_queryset() self.assertEqual(qs.count(), 2) - self.assertEqual(set(qs), set([post1, post2])) + self.assertEqual(set(qs), set([posts[0], posts[1]])) view_obj.paginate_by = 1 view_obj.object_list = qs context = view_obj.get_context_data(object_list=view_obj.object_list) self.assertTrue(context['tagged_entries'], 'tag-2') self.assertTrue(context['is_paginated']) - self.assertEqual(list(context['post_list']), [post2]) + self.assertEqual(list(context['post_list']), [posts[1]]) self.assertEqual(context['paginator'].count, 2) self.assertEqual(context['post_list'][0].title, 'Second post') def test_feed(self): - page1, page2 = self.get_pages() - post1, post2 = self.get_posts() - post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') - post1.save() - post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') - post2.save() - post1.set_current_language('en') + pages = self.get_pages() + posts = self.get_posts() + posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') + posts[0].save() + posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') + posts[1].save() + posts[0].set_current_language('en') feed = LatestEntriesFeed() - self.assertEqual(list(feed.items()), [post1]) - request = self.get_page_request(page1, self.user, r'/en/blog/', lang='en', edit=False) + feed.namespace = self.app_config_1.namespace + feed.config = self.app_config_1 + self.assertEqual(list(feed.items()), [posts[0]]) + request = self.get_page_request(pages[1], self.user, lang='en', edit=False) xml = feed(request) - self.assertContains(xml, post1.get_absolute_url()) + self.assertContains(xml, posts[0].get_absolute_url()) self.assertContains(xml, 'Blog articles on example.com') with smart_override('it'): - with switch_language(post1, 'it'): + with switch_language(posts[0], 'it'): feed = LatestEntriesFeed() - self.assertEqual(list(feed.items()), [post1]) - request = self.get_page_request(page1, self.user, r'/it/blog/', lang='it', edit=False) + feed.namespace = self.app_config_1.namespace + feed.config = self.app_config_1 + self.assertEqual(list(feed.items()), [posts[0]]) + request = self.get_page_request(pages[1], self.user, lang='en', edit=False) xml = feed(request) - self.assertContains(xml, post1.get_absolute_url()) + self.assertContains(xml, posts[0].get_absolute_url()) self.assertContains(xml, 'Articoli del blog su example.com') feed = TagFeed() - self.assertEqual(list(feed.items('tag-2')), [post1]) + feed.namespace = self.app_config_1.namespace + feed.config = self.app_config_1 + self.assertEqual(list(feed.items('tag-2')), [posts[0]]) def test_sitemap(self): - post1, post2 = self.get_posts() - post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') - post1.save() - post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') - post2.publish = True - post2.save() - post1.set_current_language('en') + posts = self.get_posts() + posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') + posts[0].save() + posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') + posts[1].publish = True + posts[1].save() + posts[0].set_current_language('en') sitemap = BlogSitemap() - self.assertEqual(sitemap.items().count(), 2) + self.assertEqual(sitemap.items().count(), 3) for item in sitemap.items(): self.assertTrue(sitemap.lastmod(item).date(), now().today()) From c60e32f21490c3e4f03c2c18be297cdbe8376e3a Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 2 Sep 2015 15:30:45 +0200 Subject: [PATCH 02/48] Pin parler version in tests --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index 1b3859d..e003e84 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,7 @@ deps = cms32: https://github.com/divio/django-cms/archive/develop.zip https://github.com/nephila/djangocms-helper/archive/develop.zip py26: unittest2 + django-parler<1.5 -r{toxinidir}/requirements-test.txt [testenv:isort] From 70cab57ef8fcc90e903f683709ecd06c60054219 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 3 Sep 2015 10:23:05 +0200 Subject: [PATCH 03/48] Use apphooks-config master --- tox.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/tox.ini b/tox.ini index e003e84..00a4b67 100644 --- a/tox.ini +++ b/tox.ini @@ -14,6 +14,7 @@ deps = https://github.com/nephila/djangocms-helper/archive/develop.zip py26: unittest2 django-parler<1.5 + https://github.com/aldryn/aldryn-apphooks-config/archive/master.zip -r{toxinidir}/requirements-test.txt [testenv:isort] From 9d168f6f45e57c348b0a902116806bd701e33874 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 6 Sep 2015 19:34:20 +0200 Subject: [PATCH 04/48] Fix tests on django CMS 3.0 --- tests/test_menu.py | 1 - tests/test_models.py | 13 ++++++++++--- tests/test_views.py | 25 +++++++++++++++---------- tox.ini | 1 - 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/tests/test_menu.py b/tests/test_menu.py index 56673d6..26119d5 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -import copy from aldryn_apphooks_config.utils import get_app_instance from django.utils.translation import activate diff --git a/tests/test_models.py b/tests/test_models.py index 9d40595..db01203 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -137,7 +137,6 @@ class AdminTest(BaseTest): msg_mid.process_request(request) post_admin = admin.site._registry[Post] response = post_admin.edit_field(request, post.pk, 'en') - #print(response.content.decode('utf-8')) self.assertEqual(response.status_code, 200) modified_post = Post.objects.language('en').get(pk=post.pk) self.assertEqual(modified_post.safe_translation_getter('post_text'), data['post_text']) @@ -171,7 +170,11 @@ class ModelsTest(BaseTest): 'month': '%02d' % post.date_published.month, 'day': '%02d' % post.date_published.day, 'slug': post.safe_translation_getter('slug', any_language=get_language())} - url_en = reverse('djangocms_blog:post-detail', kwargs=kwargs) + url_en = reverse( + '%s:post-detail' % self.app_config_1.namespace, + kwargs=kwargs, + current_app=self.app_config_1 + ) self.assertEqual(url_en, post.get_absolute_url()) with override('it'): @@ -180,7 +183,11 @@ class ModelsTest(BaseTest): 'month': '%02d' % post.date_published.month, 'day': '%02d' % post.date_published.day, 'slug': post.safe_translation_getter('slug', any_language=get_language())} - url_it = reverse('djangocms_blog:post-detail', kwargs=kwargs) + url_it = reverse( + '%s:post-detail' % self.app_config_1.namespace, + kwargs=kwargs, + current_app=self.app_config_1 + ) self.assertEqual(url_it, post.get_absolute_url()) self.assertNotEqual(url_it, url_en) diff --git a/tests/test_views.py b/tests/test_views.py index ebaf6dd..c257472 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -255,22 +255,27 @@ class ViewTest(BaseTest): self.assertEqual(context['post_list'][0].title, 'Second post') def test_feed(self): - pages = self.get_pages() posts = self.get_posts() + pages = self.get_pages() posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') posts[0].save() posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') posts[1].save() posts[0].set_current_language('en') - feed = LatestEntriesFeed() - feed.namespace = self.app_config_1.namespace - feed.config = self.app_config_1 - self.assertEqual(list(feed.items()), [posts[0]]) - request = self.get_page_request(pages[1], self.user, lang='en', edit=False) - xml = feed(request) - self.assertContains(xml, posts[0].get_absolute_url()) - self.assertContains(xml, 'Blog articles on example.com') + with smart_override('en'): + with switch_language(posts[0], 'en'): + + request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url()) + + feed = LatestEntriesFeed() + feed.namespace = self.app_config_1.namespace + feed.config = self.app_config_1 + self.assertEqual(list(feed.items()), [posts[0]]) + self.reload_urlconf() + xml = feed(request) + self.assertContains(xml, posts[0].get_absolute_url()) + self.assertContains(xml, 'Blog articles on example.com') with smart_override('it'): with switch_language(posts[0], 'it'): @@ -278,7 +283,7 @@ class ViewTest(BaseTest): feed.namespace = self.app_config_1.namespace feed.config = self.app_config_1 self.assertEqual(list(feed.items()), [posts[0]]) - request = self.get_page_request(pages[1], self.user, lang='en', edit=False) + request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url()) xml = feed(request) self.assertContains(xml, posts[0].get_absolute_url()) self.assertContains(xml, 'Articoli del blog su example.com') diff --git a/tox.ini b/tox.ini index 00a4b67..fc47ec9 100644 --- a/tox.ini +++ b/tox.ini @@ -7,7 +7,6 @@ deps = django16: Django>=1.6,<1.7 django17: Django>=1.7,<1.8 django18: Django>=1.7,<1.9 - django18: https://github.com/stefanfoulis/django-filer/archive/develop.zip cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip cms32: https://github.com/divio/django-cms/archive/develop.zip From a651144ec2ccad3bc8d4ffe12c9e052ea57e236a Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Mon, 7 Sep 2015 09:12:17 +0200 Subject: [PATCH 05/48] Fix code style --- djangocms_blog/cms_plugins.py | 1 - djangocms_blog/menu.py | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index 739e468..8445731 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from cms.models.pluginmodel import CMSPlugin from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool from django.utils.translation import ugettext_lazy as _ diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index c821484..10031b2 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -4,7 +4,7 @@ from __future__ import absolute_import, print_function, unicode_literals from cms.menu_bases import CMSAttachMenu from django.db.models.signals import post_delete, post_save from django.utils.translation import ugettext_lazy as _, get_language_from_request -from menus.base import Modifier, NavigationNode +from menus.base import NavigationNode from menus.menu_pool import menu_pool from .models import BlogCategory, Post @@ -51,6 +51,7 @@ class BlogCategoryMenu(CMSAttachMenu): menu_pool.register_menu(BlogCategoryMenu) + def clear_menu_cache(**kwargs): menu_pool.clear(all=True) From d6049a8e7875413a80241db0698370f2703e120c Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Mon, 7 Sep 2015 09:18:17 +0200 Subject: [PATCH 06/48] Fix code style II --- djangocms_blog/__init__.py | 1 - djangocms_blog/cms_appconfig.py | 12 +++++------- djangocms_blog/cms_plugins.py | 2 +- djangocms_blog/feeds.py | 2 +- djangocms_blog/forms.py | 2 +- djangocms_blog/managers.py | 3 +-- djangocms_blog/menu.py | 2 +- djangocms_blog/models.py | 2 +- tests/test_menu.py | 3 +-- 9 files changed, 12 insertions(+), 17 deletions(-) diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py index 335edb1..7472c18 100644 --- a/djangocms_blog/__init__.py +++ b/djangocms_blog/__init__.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- __author__ = 'Iacopo Spalletti' __email__ = 'i.spalletti@nephila.it' diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index 34c39f4..5c4cdc8 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -1,15 +1,13 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals +from __future__ import absolute_import, print_function, unicode_literals +from aldryn_apphooks_config.models import AppHookConfig +from aldryn_apphooks_config.utils import setup_config +from app_data import AppDataForm from django import forms from django.db import models from django.utils.translation import ugettext_lazy as _ - -from aldryn_apphooks_config.utils import setup_config -from aldryn_apphooks_config.models import AppHookConfig -from app_data import AppDataForm -from parler.models import TranslatableModel -from parler.models import TranslatedFields +from parler.models import TranslatableModel, TranslatedFields from .settings import get_setting diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index 8445731..ea8b522 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -6,7 +6,7 @@ from cms.plugin_pool import plugin_pool from django.utils.translation import ugettext_lazy as _ from .forms import LatestEntriesForm -from .models import AuthorEntriesPlugin, BlogCategory, LatestPostsPlugin, Post, GenericBlogPlugin +from .models import AuthorEntriesPlugin, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, Post from .settings import get_setting diff --git a/djangocms_blog/feeds.py b/djangocms_blog/feeds.py index 481e0eb..7583018 100644 --- a/djangocms_blog/feeds.py +++ b/djangocms_blog/feeds.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from aldryn_apphooks_config.utils import get_app_instance +from aldryn_apphooks_config.utils import get_app_instance from django.contrib.sites.models import Site from django.contrib.syndication.views import Feed from django.core.urlresolvers import reverse diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py index 01de6a2..f2d4543 100644 --- a/djangocms_blog/forms.py +++ b/djangocms_blog/forms.py @@ -6,7 +6,7 @@ from django.conf import settings from parler.forms import TranslatableModelForm from taggit_autosuggest.widgets import TagAutoSuggest -from .models import Post, BlogCategory +from .models import BlogCategory, Post class LatestEntriesForm(forms.ModelForm): diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py index 9fce9dd..f70a59d 100644 --- a/djangocms_blog/managers.py +++ b/djangocms_blog/managers.py @@ -2,9 +2,8 @@ from __future__ import absolute_import, print_function, unicode_literals import django - from aldryn_apphooks_config.managers.parler import ( - AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset + AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset, ) from django.contrib.sites.models import Site from django.db import models diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index 10031b2..319aa7a 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -3,7 +3,7 @@ from __future__ import absolute_import, print_function, unicode_literals from cms.menu_bases import CMSAttachMenu from django.db.models.signals import post_delete, post_save -from django.utils.translation import ugettext_lazy as _, get_language_from_request +from django.utils.translation import get_language_from_request, ugettext_lazy as _ from menus.base import NavigationNode from menus.menu_pool import menu_pool diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 8a90879..af0ee36 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -1,8 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager from aldryn_apphooks_config.fields import AppHookConfigField +from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager from cms.models import CMSPlugin, PlaceholderField from django.conf import settings as dj_settings from django.core.urlresolvers import reverse diff --git a/tests/test_menu.py b/tests/test_menu.py index 26119d5..8830468 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -2,10 +2,9 @@ from __future__ import absolute_import, print_function, unicode_literals from aldryn_apphooks_config.utils import get_app_instance - from django.utils.translation import activate from menus.menu_pool import menu_pool -from parler.utils.context import switch_language, smart_override +from parler.utils.context import smart_override, switch_language from djangocms_blog.views import CategoryEntriesView, PostDetailView From d6dc6eac23a9e9af02ff8f6522ce99728711d2f3 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Mon, 7 Sep 2015 09:23:23 +0200 Subject: [PATCH 07/48] Fix code style III --- tests/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index 73624fa..229cea7 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,15 +3,16 @@ Tests for `djangocms_blog` module. """ from __future__ import absolute_import, print_function, unicode_literals + from copy import deepcopy from cmsplugin_filer_image.models import ThumbnailOption from django.contrib.auth import get_user_model from django.contrib.sites.models import Site -from parler.utils.context import smart_override -from djangocms_blog.cms_appconfig import BlogConfig from djangocms_helper.base_test import BaseTestCase +from parler.utils.context import smart_override +from djangocms_blog.cms_appconfig import BlogConfig from djangocms_blog.models import BlogCategory, Post User = get_user_model() From 34fedfae072f39453d3a0bbcf7e3552d4a4ce1aa Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Tue, 15 Sep 2015 08:17:39 +0200 Subject: [PATCH 08/48] Support for django CMS 3.2 --- tests/test_plugins.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 9981d81..6e8b640 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -29,7 +29,10 @@ class PluginTest(BaseTest): context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) - self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1) + try: + self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1) + except AssertionError: + self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-1') > -1) self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1) self.assertTrue(rendered.find('

first line

') > -1) self.assertTrue(rendered.find('
-1) @@ -46,7 +49,10 @@ class PluginTest(BaseTest): context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) - self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1) + try: + self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1) + except AssertionError: + self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-2') > -1) self.assertTrue(rendered.find(reverse('djangocms_blog:posts-category', kwargs={'category': category_2.slug})) > -1) self.assertTrue(rendered.find('

second post first line

') > -1) self.assertTrue(rendered.find('
-1) From b79102b17a3b67ee818b2962cc3b4336fcd2cf6b Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Fri, 25 Sep 2015 00:24:38 +0200 Subject: [PATCH 09/48] Add experimental support Django 1.9 --- tox.ini | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index fc47ec9..4132b3d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{26}-django16-cms{30,31},py{27,33,34}-django{16,17}-cms{30,31,32},py{27,33,34}-django{18}-cms{31,32},pep8,isort +envlist = py{26}-django16-cms{30,31},py{27,33,34}-django{16,17}-cms{30,31,32},py{27,33,34}-django{18,19}-cms{31,32},pep8,isort [testenv] commands = {env:COMMAND:python} setup.py test @@ -7,6 +7,9 @@ deps = django16: Django>=1.6,<1.7 django17: Django>=1.7,<1.8 django18: Django>=1.7,<1.9 + django18: https://github.com/stefanfoulis/django-filer/archive/develop.zip + django19: Django==1.9a1 + django19: https://github.com/stefanfoulis/django-filer/archive/develop.zip cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip cms32: https://github.com/divio/django-cms/archive/develop.zip From 35a0f621b84fb03fb9f5caa60fad36cbc4a170a9 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Fri, 25 Sep 2015 00:57:58 +0200 Subject: [PATCH 10/48] Use cmsplugin filer test version --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index 4132b3d..34b82dd 100644 --- a/tox.ini +++ b/tox.ini @@ -8,8 +8,10 @@ deps = django17: Django>=1.7,<1.8 django18: Django>=1.7,<1.9 django18: https://github.com/stefanfoulis/django-filer/archive/develop.zip + django18: https://github.com/nephila/cmsplugin-filer/archive/fix/static_filer.zip django19: Django==1.9a1 django19: https://github.com/stefanfoulis/django-filer/archive/develop.zip + django19: https://github.com/nephila/cmsplugin-filer/archive/fix/static_filer.zip cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip cms32: https://github.com/divio/django-cms/archive/develop.zip From a1497360b10b8dc46730b3985bafdbb3b4d46c4c Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 26 Sep 2015 10:38:57 +0200 Subject: [PATCH 11/48] Code cleanup --- djangocms_blog/admin.py | 4 +- djangocms_blog/cms_plugins.py | 1 - .../migrations/0010_auto_20150923_1151.py | 6 +- djangocms_blog/models.py | 5 +- tests/__init__.py | 15 +---- tests/test_models.py | 2 +- tests/test_views.py | 59 ++++++++----------- tox.ini | 2 +- 8 files changed, 38 insertions(+), 56 deletions(-) diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index db966e1..1d50b13 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -125,8 +125,8 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): def get_config_fields(self): return ( - 'app_title', 'paginate_by', 'set_author', 'config.default_published', - 'config.use_placeholder' + 'app_title', 'config.paginate_by', 'config.set_author', 'config.default_published', + 'config.use_placeholder', 'config.use_abstract', ) admin.site.register(BlogCategory, BlogCategoryAdmin) diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index ea8b522..d115b78 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -56,7 +56,6 @@ class BlogAuthorPostsPlugin(BlogPlugin): module = _('Blog') name = _('Author Blog Articles') model = AuthorEntriesPlugin - form = LatestEntriesForm render_template = 'djangocms_blog/plugins/authors.html' filter_horizontal = ['authors'] diff --git a/djangocms_blog/migrations/0010_auto_20150923_1151.py b/djangocms_blog/migrations/0010_auto_20150923_1151.py index 25db956..96d3e67 100644 --- a/djangocms_blog/migrations/0010_auto_20150923_1151.py +++ b/djangocms_blog/migrations/0010_auto_20150923_1151.py @@ -4,7 +4,6 @@ from __future__ import unicode_literals import aldryn_apphooks_config.fields import app_data.fields import djangocms_text_ckeditor.fields - from cms.models import Page from cms.utils.conf import get_languages from django.db import models, migrations @@ -114,5 +113,10 @@ class Migration(migrations.Migration): name='blogconfigtranslation', unique_together=set([('language_code', 'master')]), ), + migrations.AlterField( + model_name='post', + name='sites', + field=models.ManyToManyField(to='sites.Site', help_text='Select sites in which to show the post. If none is set it will be visible in all the configured sites.', blank=True, verbose_name='Site(s)'), + ), migrations.RunPython(forwards, backwards) ] diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index af0ee36..54aa6cf 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -113,10 +113,9 @@ class Post(ModelMeta, TranslatableModel): enable_comments = models.BooleanField(verbose_name=_('enable comments on post'), default=get_setting('ENABLE_COMMENTS')) sites = models.ManyToManyField('sites.Site', verbose_name=_('Site(s)'), blank=True, - null=True, help_text=_('Select sites in which to show the post. ' - u'If none is set it will be ' - u'visible in all the configured sites.')) + 'If none is set it will be ' + 'visible in all the configured sites.')) app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config')) translations = TranslatedFields( diff --git a/tests/__init__.py b/tests/__init__.py index 229cea7..fe9cbfe 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -127,19 +127,6 @@ class BaseTest(BaseTestCase): cls.category_1.save() cls.site_2 = Site.objects.create(domain='http://example2.com', name='example 2') - def setUp(self): - super(BaseTest, self).setUp() - self.img = self.create_filer_image_object() - - def tearDown(self): - Post.objects.all().delete() - super(BaseTest, self).tearDown() - - @classmethod - def tearDownClass(cls): - BlogConfig.objects.all().delete() - super(BaseTest, cls).tearDownClass() - def _get_category(self, data, category=None, lang='en'): data = deepcopy(data) for k, v in data.items(): @@ -188,7 +175,7 @@ class BaseTest(BaseTestCase): post1 = self._get_post(post['en'], sites=sites) post1 = self._get_post(post['it'], post=post1, lang='it') post1.publish = post['en']['publish'] - post1.main_image = self.img + post1.main_image = self.create_filer_image_object() post1.save() posts.append(post1) return posts diff --git a/tests/test_models.py b/tests/test_models.py index db01203..3004bbb 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -147,7 +147,7 @@ class ModelsTest(BaseTest): def test_model_attributes(self): post = self._get_post(self._post_data[0]['en']) post = self._get_post(self._post_data[0]['it'], post, 'it') - post.main_image = self.img + post.main_image = self.create_filer_image_object() post.save() post.set_current_language('en') meta_en = post.as_meta() diff --git a/tests/test_views.py b/tests/test_views.py index c257472..adc716b 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +from aldryn_apphooks_config.utils import get_app_instance from django.contrib.auth.models import AnonymousUser from django.http import Http404 @@ -24,24 +25,25 @@ class ViewTest(BaseTest): posts = self.get_posts() pages = self.get_pages() - request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) + request = self.get_request(pages[1], 'en', AnonymousUser()) with smart_override('en'): view_obj = PostListView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) self.assertEqual(list(view_obj.get_queryset()), [posts[0]]) - request = self.get_page_request(pages[1], self.user, edit=False) + request = self.get_page_request(pages[1], self.user, lang='en', edit=False) + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.request = request view_obj.kwargs = {} qs = view_obj.get_queryset() self.assertEqual(qs.count(), 1) self.assertEqual(set(qs), set([posts[0]])) - request = self.get_page_request(pages[1], self.user, edit=True) + request = self.get_page_request(pages[1], self.user, lang='en', edit=True) + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.request = request self.assertEqual(set(view_obj.get_queryset()), set([posts[0], posts[1], posts[2]])) @@ -58,6 +60,7 @@ class ViewTest(BaseTest): with smart_override('it'): request = self.get_page_request(pages[1], self.user, lang='it', edit=True) + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.request = request view_obj.object_list = view_obj.get_queryset() context = view_obj.get_context_data(object_list=view_obj.object_list) @@ -83,10 +86,9 @@ class ViewTest(BaseTest): with smart_override('fr'): view_obj = PostListView() - request = self.get_page_request(pages[0], self.user, r'/fr/blog/', lang='fr', edit=True) + request = self.get_page_request(pages[1], self.user, lang='fr', edit=True) view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() view_obj.get_context_data(object_list=view_obj.object_list) @@ -96,10 +98,9 @@ class ViewTest(BaseTest): with override_parler_settings(PARLER_LANGUAGES=PARLER_FALLBACK): view_obj = PostListView() - request = self.get_page_request(pages[0], self.user, r'/fr/blog/', lang='fr', edit=True) + request = self.get_page_request(pages[1], self.user, lang='fr', edit=True) view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() view_obj.get_context_data(object_list=view_obj.object_list) @@ -111,11 +112,10 @@ class ViewTest(BaseTest): with smart_override('en'): with switch_language(posts[0], 'en'): - request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) + request = self.get_page_request(pages[1], AnonymousUser(), lang='en', edit=False) view_obj = PostDetailView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) with self.assertRaises(Http404): view_obj.kwargs = {'slug': 'not-existing'} @@ -128,11 +128,10 @@ class ViewTest(BaseTest): with smart_override('it'): with switch_language(posts[0], 'it'): - request = self.get_page_request(pages[0], AnonymousUser(), r'/it/blog/', lang='it', edit=False) + request = self.get_page_request(pages[1], AnonymousUser(), lang='it', edit=False) view_obj = PostDetailView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {'slug': posts[0].slug} post_obj = view_obj.get_object() @@ -150,11 +149,10 @@ class ViewTest(BaseTest): pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(pages[0], AnonymousUser(), r'/en/blog/', edit=False) + request = self.get_page_request(pages[1], AnonymousUser(), lang='en', edit=False) view_obj = PostArchiveView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {'year': now().year, 'month': now().month} # One post only, anonymous request @@ -171,11 +169,10 @@ class ViewTest(BaseTest): pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True) + request = self.get_page_request(pages[1], self.user, lang='en', edit=True) view_obj = CategoryEntriesView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {'category': 'category-1'} qs = view_obj.get_queryset() self.assertEqual(qs.count(), 3) @@ -201,10 +198,9 @@ class ViewTest(BaseTest): pages = self.get_pages() with smart_override('en'): - request = self.get_page_request(pages[1], self.user, edit=True) + request = self.get_page_request(pages[1], self.user, lang='en', edit=True) view_obj = AuthorEntriesView() - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.request = request view_obj.kwargs = {'username': self.user.get_username()} qs = view_obj.get_queryset() @@ -235,11 +231,10 @@ class ViewTest(BaseTest): posts[1].save() with smart_override('en'): - request = self.get_page_request(pages[1], self.user, edit=True) + request = self.get_page_request(pages[1], self.user, lang='en', edit=True) view_obj = TaggedListView() view_obj.request = request - view_obj.namespace = self.app_config_1.namespace - view_obj.config = self.app_config_1 + view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.kwargs = {'tag': 'tag-2'} qs = view_obj.get_queryset() self.assertEqual(qs.count(), 2) @@ -269,8 +264,7 @@ class ViewTest(BaseTest): request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url()) feed = LatestEntriesFeed() - feed.namespace = self.app_config_1.namespace - feed.config = self.app_config_1 + feed.namespace, feed.config = get_app_instance(request) self.assertEqual(list(feed.items()), [posts[0]]) self.reload_urlconf() xml = feed(request) @@ -280,8 +274,7 @@ class ViewTest(BaseTest): with smart_override('it'): with switch_language(posts[0], 'it'): feed = LatestEntriesFeed() - feed.namespace = self.app_config_1.namespace - feed.config = self.app_config_1 + feed.namespace, feed.config = get_app_instance(request) self.assertEqual(list(feed.items()), [posts[0]]) request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url()) xml = feed(request) diff --git a/tox.ini b/tox.ini index 34b82dd..2dbc0d7 100644 --- a/tox.ini +++ b/tox.ini @@ -2,7 +2,7 @@ envlist = py{26}-django16-cms{30,31},py{27,33,34}-django{16,17}-cms{30,31,32},py{27,33,34}-django{18,19}-cms{31,32},pep8,isort [testenv] -commands = {env:COMMAND:python} setup.py test +commands = {env:COMMAND:python} cms_helper.py test djangocms_blog --no-migrate deps = django16: Django>=1.6,<1.7 django17: Django>=1.7,<1.8 From 8d69fb107ed5b5a1e283cbb2d7767cccbf3f47e6 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 6 Sep 2015 13:40:20 +0200 Subject: [PATCH 12/48] Add customisable templates --- djangocms_blog/cms_appconfig.py | 4 ++++ djangocms_blog/cms_plugins.py | 20 ++++++++++++++------ djangocms_blog/views.py | 20 ++++++++++++++------ tests/test_plugins.py | 19 +++++++++++++++++++ tests/test_views.py | 20 ++++++++++++++++++++ 5 files changed, 71 insertions(+), 12 deletions(-) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index 5c4cdc8..dc55dab 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -42,4 +42,8 @@ class BlogConfigForm(AppDataForm): initial=get_setting('PAGINATION'), help_text=_('When paginating list views, ' 'how many articles per page?')) + template_prefix = forms.CharField(label=_('Template prefix'), required=False, initial='', + help_text=_('Alternative directory to load the blog ' + 'templates from') + ) setup_config(BlogConfigForm, BlogConfig) diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py index d115b78..aab204a 100644 --- a/djangocms_blog/cms_plugins.py +++ b/djangocms_blog/cms_plugins.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import os.path + from cms.plugin_base import CMSPluginBase from cms.plugin_pool import plugin_pool from django.utils.translation import ugettext_lazy as _ @@ -13,19 +15,25 @@ from .settings import get_setting class BlogPlugin(CMSPluginBase): module = 'Blog' + def get_render_template(self, context, instance, placeholder): + if instance.app_config.template_prefix: + return os.path.join(instance.app_config.template_prefix, self.base_render_template) + else: + return os.path.join('djangocms_blog', self.base_render_template) + class BlogLatestEntriesPlugin(BlogPlugin): """ Non cached plugin which returns the latest posts taking into account the user / toolbar state """ - render_template = 'djangocms_blog/plugins/latest_entries.html' name = _('Latest Blog Articles') model = LatestPostsPlugin form = LatestEntriesForm filter_horizontal = ('categories',) fields = ('latest_posts', 'tags', 'categories') cache = False + base_render_template = 'plugins/latest_entries.html' def render(self, context, instance, placeholder): context = super(BlogLatestEntriesPlugin, self).render(context, instance, placeholder) @@ -38,12 +46,12 @@ class BlogLatestEntriesPluginCached(BlogPlugin): """ Cached plugin which returns the latest published posts """ - render_template = 'djangocms_blog/plugins/latest_entries.html' name = _('Latest Blog Articles') model = LatestPostsPlugin form = LatestEntriesForm filter_horizontal = ('categories',) fields = ('latest_posts', 'tags', 'categories') + base_render_template = 'plugins/latest_entries.html' def render(self, context, instance, placeholder): context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder) @@ -56,7 +64,7 @@ class BlogAuthorPostsPlugin(BlogPlugin): module = _('Blog') name = _('Author Blog Articles') model = AuthorEntriesPlugin - render_template = 'djangocms_blog/plugins/authors.html' + base_render_template = 'plugins/authors.html' filter_horizontal = ['authors'] def render(self, context, instance, placeholder): @@ -69,7 +77,7 @@ class BlogTagsPlugin(BlogPlugin): module = _('Blog') name = _('Tags') model = GenericBlogPlugin - render_template = 'djangocms_blog/plugins/tags.html' + base_render_template = 'plugins/tags.html' def render(self, context, instance, placeholder): context = super(BlogTagsPlugin, self).render(context, instance, placeholder) @@ -85,7 +93,7 @@ class BlogCategoryPlugin(BlogPlugin): module = _('Blog') name = _('Categories') model = GenericBlogPlugin - render_template = 'djangocms_blog/plugins/categories.html' + base_render_template = 'plugins/categories.html' def render(self, context, instance, placeholder): context = super(BlogCategoryPlugin, self).render(context, instance, placeholder) @@ -100,7 +108,7 @@ class BlogArchivePlugin(BlogPlugin): module = _('Blog') name = _('Archive') model = GenericBlogPlugin - render_template = 'djangocms_blog/plugins/archive.html' + base_render_template = 'plugins/archive.html' def render(self, context, instance, placeholder): context = super(BlogArchivePlugin, self).render(context, instance, placeholder) diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py index 87268d4..d0ec7b5 100644 --- a/djangocms_blog/views.py +++ b/djangocms_blog/views.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import os.path + from aldryn_apphooks_config.mixins import AppConfigMixin from django.contrib.auth import get_user_model from django.utils.timezone import now @@ -27,11 +29,17 @@ class BaseBlogView(AppConfigMixin, ViewUrlMixin): queryset = queryset.published() return queryset + def get_template_names(self): + if self.config.template_prefix: + return os.path.join(self.config.template_prefix, self.base_template_name) + else: + return os.path.join('djangocms_blog', self.base_template_name) + class PostListView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' - template_name = 'djangocms_blog/post_list.html' + base_template_name = 'post_list.html' view_url_name = 'djangocms_blog:posts-latest' def get_context_data(self, **kwargs): @@ -46,7 +54,7 @@ class PostListView(BaseBlogView, ListView): class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView): model = Post context_object_name = 'post' - template_name = 'djangocms_blog/post_detail.html' + base_template_name = 'post_detail.html' slug_field = 'slug' view_url_name = 'djangocms_blog:post-detail' @@ -73,7 +81,7 @@ class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView): class PostArchiveView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' - template_name = 'djangocms_blog/post_list.html' + base_template_name = 'post_list.html' date_field = 'date_published' allow_empty = True allow_future = True @@ -101,7 +109,7 @@ class PostArchiveView(BaseBlogView, ListView): class TaggedListView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' - template_name = 'djangocms_blog/post_list.html' + base_template_name = 'post_list.html' paginate_by = get_setting('PAGINATION') view_url_name = 'djangocms_blog:posts-tagged' @@ -120,7 +128,7 @@ class TaggedListView(BaseBlogView, ListView): class AuthorEntriesView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' - template_name = 'djangocms_blog/post_list.html' + base_template_name = 'post_list.html' paginate_by = get_setting('PAGINATION') view_url_name = 'djangocms_blog:posts-authors' @@ -140,7 +148,7 @@ class AuthorEntriesView(BaseBlogView, ListView): class CategoryEntriesView(BaseBlogView, ListView): model = Post context_object_name = 'post_list' - template_name = 'djangocms_blog/post_list.html' + base_template_name = 'post_list.html' _category = None paginate_by = get_setting('PAGINATION') view_url_name = 'djangocms_blog:posts-category' diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 6e8b640..4b00943 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import os.path import re from cms.api import add_plugin @@ -132,3 +133,21 @@ class PluginTest(BaseTest): context = plugin_class.render(context, plugin, ph) self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date()) self.assertEqual(context['dates'][0]['count'], 1) + + def test_templates(self): + posts = self.get_posts() + pages = self.get_pages() + + ph = pages[0].placeholders.get(slot='content') + plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) + + context = self.get_plugin_context(pages[0], 'en', plugin) + plugin_class = plugin.get_plugin_class_instance() + self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('djangocms_blog', plugin_class.base_render_template)) + + self.app_config_1.app_data.config.template_prefix = 'whatever' + self.app_config_1.save() + self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('whatever', plugin_class.base_render_template)) + self.app_config_1.app_data.config.template_prefix = '' + self.app_config_1.save() + diff --git a/tests/test_views.py b/tests/test_views.py index adc716b..ae921c0 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -2,6 +2,8 @@ from __future__ import absolute_import, print_function, unicode_literals from aldryn_apphooks_config.utils import get_app_instance +import os.path + from django.contrib.auth.models import AnonymousUser from django.http import Http404 from django.utils.timezone import now @@ -299,3 +301,21 @@ class ViewTest(BaseTest): self.assertEqual(sitemap.items().count(), 3) for item in sitemap.items(): self.assertTrue(sitemap.lastmod(item).date(), now().today()) + + def test_templates(self): + posts = self.get_posts() + pages = self.get_pages() + + with smart_override('en'): + request = self.get_page_request(pages[1], self.user, edit=True) + view_obj = PostListView() + view_obj.request = request + view_obj.namespace = self.app_config_1.namespace + view_obj.config = self.app_config_1 + self.assertEqual(view_obj.get_template_names(), os.path.join('djangocms_blog', 'post_list.html')) + + self.app_config_1.app_data.config.template_prefix = 'whatever' + self.app_config_1.save() + self.assertEqual(view_obj.get_template_names(), os.path.join('whatever', 'post_list.html')) + self.app_config_1.app_data.config.template_prefix = '' + self.app_config_1.save() From 63adbb1131528d6e64b635f7329766c606bf4653 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 24 Sep 2015 21:51:54 +0200 Subject: [PATCH 13/48] Add meta attributes --- djangocms_blog/cms_appconfig.py | 82 ++++++++++++++++++++++++--------- djangocms_blog/models.py | 30 +++++++----- djangocms_blog/settings.py | 42 ++++++++--------- 3 files changed, 100 insertions(+), 54 deletions(-) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index dc55dab..f10ba0e 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -25,25 +25,65 @@ class BlogConfig(TranslatableModel, AppHookConfig): class BlogConfigForm(AppDataForm): - default_published = forms.BooleanField(label=_('Post published by default'), required=False, - initial=get_setting('DEFAULT_PUBLISHED')) - use_placeholder = forms.BooleanField(label=_('Use placeholder and plugins for article body'), - required=False, - initial=get_setting('USE_PLACEHOLDER')) - use_abstract = forms.BooleanField(label=_('Use abstract field'), - required=False, - initial=get_setting('USE_ABSTRACT')) - set_author = forms.BooleanField(label=_('Set author'), - required=False, - help_text=_('Set author by default'), - initial=get_setting('AUTHOR_DEFAULT')) - paginate_by = forms.IntegerField(label=_('Paginate size'), - required=False, - initial=get_setting('PAGINATION'), - help_text=_('When paginating list views, ' - 'how many articles per page?')) - template_prefix = forms.CharField(label=_('Template prefix'), required=False, initial='', - help_text=_('Alternative directory to load the blog ' - 'templates from') - ) + default_published = forms.BooleanField( + label=_('Post published by default'), required=False, + initial=get_setting('DEFAULT_PUBLISHED') + ) + use_placeholder = forms.BooleanField( + label=_('Use placeholder and plugins for article body'), required=False, + initial=get_setting('USE_PLACEHOLDER') + ) + use_abstract = forms.BooleanField( + label=_('Use abstract field'), required=False, + initial=get_setting('USE_ABSTRACT') + ) + set_author = forms.BooleanField( + label=_('Set author'), required=False, help_text=_('Set author by default'), + initial=get_setting('AUTHOR_DEFAULT') + ) + paginate_by = forms.IntegerField( + label=_('Paginate size'), required=False, initial=get_setting('PAGINATION'), + help_text=_('When paginating list views, how many articles per page?') + ) + template_prefix = forms.CharField( + label=_('Template prefix'), required=False, initial='', + help_text=_('Alternative directory to load the blog templates from') + ) + object_type = forms.ChoiceField( + label=_('Object type'), required=False, + choices=get_setting('TYPES_GENERIC'), initial=get_setting('TYPE') + ) + og_type = forms.ChoiceField( + label=_('Facebook type'), required=False, + choices=get_setting('TYPES_FACEBOOK'), initial=get_setting('TYPES_FACEBOOK')[0][0] + ) + og_app_id = forms.CharField( + max_length=200, label=_('Facebook application ID'), required=False, + ) + og_profile_id = forms.CharField( + max_length=200, label=_('Facebook profile ID'), required=False, + ) + og_publisher = forms.CharField( + max_length=200, label=_('Facebook page URL'), required=False + ) + og_author_url = forms.CharField( + max_length=200, label=_('Facebook author URL'), required=False + ) + twitter_type = forms.ChoiceField( + label=_('Twitter type'), required=False, + choices=get_setting('TYPES_TWITTER'), initial=get_setting('TYPES_TWITTER')[0][0] + ) + twitter_site = forms.CharField( + max_length=200, label=_('Twitter site handle'), required=False + ) + twitter_author = forms.CharField( + max_length=200, label=_('Twitter author handle'), required=False + ) + gplus_type = forms.CharField( + max_length=200, label=_('Google+ type'), required=False, + choices=get_setting('TYPES_GPLUS'), initial=get_setting('TYPES_GPLUS')[0][0] + ) + gplus_author = forms.CharField( + max_length=200, label=_('Google+ author name'), required=False + ) setup_config(BlogConfigForm, BlogConfig) diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 54aa6cf..95be636 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -144,20 +144,19 @@ class Post(ModelMeta, TranslatableModel): 'og_description': 'get_description', 'twitter_description': 'get_description', 'gplus_description': 'get_description', - 'keywords': 'get_keywords', 'locale': None, 'image': 'get_image_full_url', - 'object_type': get_setting('TYPE'), - 'og_type': get_setting('FB_TYPE'), - 'og_app_id': get_setting('FB_APPID'), - 'og_profile_id': get_setting('FB_PROFILE_ID'), - 'og_publisher': get_setting('FB_PUBLISHER'), - 'og_author_url': get_setting('FB_AUTHOR_URL'), - 'twitter_type': get_setting('TWITTER_TYPE'), - 'twitter_site': get_setting('TWITTER_SITE'), - 'twitter_author': get_setting('TWITTER_AUTHOR'), - 'gplus_type': get_setting('GPLUS_TYPE'), - 'gplus_author': get_setting('GPLUS_AUTHOR'), + 'object_type': 'get_meta_attribute', + 'og_type': 'get_meta_attribute', + 'og_app_id': 'get_meta_attribute', + 'og_profile_id': 'get_meta_attribute', + 'og_publisher': 'get_meta_attribute', + 'og_author_url': 'get_meta_attribute', + 'twitter_type': 'get_meta_attribute', + 'twitter_site': 'get_meta_attribute', + 'twitter_author': 'get_meta_attribute', + 'gplus_type': 'get_meta_attribute', + 'gplus_author': 'get_meta_attribute', 'published_time': 'date_published', 'modified_time': 'date_modified', 'expiration_time': 'date_published_end', @@ -185,6 +184,13 @@ class Post(ModelMeta, TranslatableModel): any_language=True)} return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs) + def get_meta_attribute(self, param): + """ + Retrieves django-meta attributes from apphook config instance + :param param: django-meta attribute passed as key + """ + return getattr(self.app_config, param) + def save_translation(self, translation, *args, **kwargs): if not translation.slug and translation.title: translation.slug = slugify(translation.title) diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index 40b9dbe..a7564ac 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -6,6 +6,11 @@ def get_setting(name): from django.conf import settings from meta_mixin import settings as meta_settings + OBJECT_TYPES = { + 'Article': _('Article'), + 'Website': _('Website'), + } + default = { 'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', { 'size': '120x120', @@ -23,30 +28,25 @@ def get_setting(name): 'BLOG_TAGCLOUD_MAX': getattr(settings, 'BLOG_TAGCLOUD_MAX', 10), 'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10), 'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5), - 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(settings, - 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', - 100), + 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr( + settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100 + ), 'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'), + 'BLOG_TYPES': getattr(settings, 'BLOG_TYPES', ), 'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'), - 'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', - meta_settings.FB_APPID), - 'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', - meta_settings.FB_PROFILE_ID), - 'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', - meta_settings.FB_PUBLISHER), - 'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', - 'get_author_url'), - 'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', - 'get_author_name'), + 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', OBJECT_TYPES.items()), + 'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID), + 'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID), + 'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER), + 'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'), + 'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'), 'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'), - 'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', - meta_settings.TWITTER_SITE), - 'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', - 'get_author_twitter'), - 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', - 'Blog'), - 'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', - 'get_author_gplus'), + 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', OBJECT_TYPES.items()), + 'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE), + 'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'), + 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'), + 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', OBJECT_TYPES.items()), + 'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'), 'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True), 'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True), 'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True), From 29df04208c77d06635c02b85e1376845f293c411 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 26 Sep 2015 18:01:22 +0200 Subject: [PATCH 14/48] Scattered fixes --- djangocms_blog/cms_appconfig.py | 12 ++++++------ djangocms_blog/models.py | 1 + djangocms_blog/settings.py | 18 ++++++++++-------- tests/__init__.py | 7 +++++++ tests/test_plugins.py | 6 ++---- tests/test_views.py | 2 +- tox.ini | 1 + 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index f10ba0e..2407968 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -51,11 +51,11 @@ class BlogConfigForm(AppDataForm): ) object_type = forms.ChoiceField( label=_('Object type'), required=False, - choices=get_setting('TYPES_GENERIC'), initial=get_setting('TYPE') + choices=get_setting('TYPES'), initial=get_setting('TYPE') ) og_type = forms.ChoiceField( label=_('Facebook type'), required=False, - choices=get_setting('TYPES_FACEBOOK'), initial=get_setting('TYPES_FACEBOOK')[0][0] + choices=get_setting('FB_TYPES'), initial=get_setting('FB_TYPES')[0][0] ) og_app_id = forms.CharField( max_length=200, label=_('Facebook application ID'), required=False, @@ -71,7 +71,7 @@ class BlogConfigForm(AppDataForm): ) twitter_type = forms.ChoiceField( label=_('Twitter type'), required=False, - choices=get_setting('TYPES_TWITTER'), initial=get_setting('TYPES_TWITTER')[0][0] + choices=get_setting('TWITTER_TYPES'), initial=get_setting('TWITTER_TYPES')[0][0] ) twitter_site = forms.CharField( max_length=200, label=_('Twitter site handle'), required=False @@ -79,9 +79,9 @@ class BlogConfigForm(AppDataForm): twitter_author = forms.CharField( max_length=200, label=_('Twitter author handle'), required=False ) - gplus_type = forms.CharField( - max_length=200, label=_('Google+ type'), required=False, - choices=get_setting('TYPES_GPLUS'), initial=get_setting('TYPES_GPLUS')[0][0] + gplus_type = forms.ChoiceField( + label=_('Google+ type'), required=False, + choices=get_setting('GPLUS_TYPES'), initial=get_setting('GPLUS_TYPES')[0][0] ) gplus_author = forms.CharField( max_length=200, label=_('Google+ author name'), required=False diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 95be636..d3dcd8d 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -141,6 +141,7 @@ class Post(ModelMeta, TranslatableModel): _metadata = { 'title': 'get_title', 'description': 'get_description', + 'keywords': 'get_keywords', 'og_description': 'get_description', 'twitter_description': 'get_description', 'gplus_description': 'get_description', diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index a7564ac..3b25659 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -4,12 +4,14 @@ from __future__ import absolute_import, print_function, unicode_literals def get_setting(name): from django.conf import settings + from django.utils.translation import ugettext_lazy as _ from meta_mixin import settings as meta_settings - OBJECT_TYPES = { - 'Article': _('Article'), - 'Website': _('Website'), - } + OBJECT_TYPES = ( + ('Article', _('Article')), + ('Website', _('Website')), + ) + BLOG_TYPES = getattr(settings, 'BLOG_TYPES', OBJECT_TYPES) default = { 'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', { @@ -32,20 +34,20 @@ def get_setting(name): settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100 ), 'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'), - 'BLOG_TYPES': getattr(settings, 'BLOG_TYPES', ), + 'BLOG_TYPES': BLOG_TYPES, 'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'), - 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', OBJECT_TYPES.items()), + 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', BLOG_TYPES), 'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID), 'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID), 'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER), 'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'), 'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'), 'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'), - 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', OBJECT_TYPES.items()), + 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', BLOG_TYPES), 'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE), 'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'), 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'), - 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', OBJECT_TYPES.items()), + 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', BLOG_TYPES), 'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'), 'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True), 'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True), diff --git a/tests/__init__.py b/tests/__init__.py index fe9cbfe..6fad4a6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -127,6 +127,13 @@ class BaseTest(BaseTestCase): cls.category_1.save() cls.site_2 = Site.objects.create(domain='http://example2.com', name='example 2') + @classmethod + def tearDownClass(cls): + super(BaseTest, cls).tearDownClass() + BlogConfig.objects.all().delete() + BlogCategory.objects.all().delete() + ThumbnailOption.objects.all().delete() + def _get_category(self, data, category=None, lang='en'): data = deepcopy(data) for k, v in data.items(): diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 4b00943..73b0e2a 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -7,6 +7,7 @@ import re from cms.api import add_plugin from django.core.urlresolvers import reverse from django.utils.timezone import now +from djangocms_helper.utils import get_user_model from taggit.models import Tag from djangocms_blog.models import BlogCategory @@ -68,12 +69,10 @@ class PluginTest(BaseTest): posts[1].save() ph = pages[0].placeholders.get(slot='content') plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1) - plugin.authors.add(self.user) context = self.get_plugin_context(pages[0], 'en', plugin, edit=True) rendered = plugin.render_plugin(context, ph) - self.assertTrue(rendered.find(reverse('djangocms_blog:posts-author', kwargs={'username': self.user.get_username()})) > -1) - self.assertTrue(rendered.find('2 articles') > -1) + self.assertTrue(rendered.find('No article found') > -1) def test_plugin_tags(self): pages = self.get_pages() @@ -150,4 +149,3 @@ class PluginTest(BaseTest): self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('whatever', plugin_class.base_render_template)) self.app_config_1.app_data.config.template_prefix = '' self.app_config_1.save() - diff --git a/tests/test_views.py b/tests/test_views.py index ae921c0..818484a 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from aldryn_apphooks_config.utils import get_app_instance import os.path +from aldryn_apphooks_config.utils import get_app_instance from django.contrib.auth.models import AnonymousUser from django.http import Http404 from django.utils.timezone import now diff --git a/tox.ini b/tox.ini index 2dbc0d7..2887060 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ deps = cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip cms32: https://github.com/divio/django-cms/archive/develop.zip + https://github.com/nephila/django-meta-mixin/archive/master.zip https://github.com/nephila/djangocms-helper/archive/develop.zip py26: unittest2 django-parler<1.5 From cae4595a99aca19a89c99a30bee7bcdd45107c26 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 26 Sep 2015 23:55:07 +0200 Subject: [PATCH 15/48] Minor code refactoring Improve coverage --- djangocms_blog/managers.py | 16 ++++++++-------- djangocms_blog/models.py | 10 ++++------ djangocms_blog/views.py | 15 +++++++++++++++ tests/test_models.py | 36 ++++++++++++++++++++++++++++++++++++ tests/test_views.py | 26 ++++++++++++++++++++++++++ 5 files changed, 89 insertions(+), 14 deletions(-) diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py index f70a59d..644fbc6 100644 --- a/djangocms_blog/managers.py +++ b/djangocms_blog/managers.py @@ -23,7 +23,7 @@ class TaggedFilterItem(object): o con gli stessi tag di un model o un queryset """ tags = self._taglist(other_model, queryset) - return self.get_queryset().filter(taglist__in=tags) + return self.get_queryset().filter(tags__in=tags).distinct() def _taglist(self, other_model=None, queryset=None): """ @@ -31,21 +31,21 @@ class TaggedFilterItem(object): o queryset passati come argomento """ from taggit.models import TaggedItem - filtro = None + filter = None if queryset is not None: - filtro = set() + filter = set() for item in queryset.all(): - filtro.update(item.tags.all()) - filtro = set([tag.id for tag in filtro]) + filter.update(item.tags.all()) + filter = set([tag.id for tag in filter]) elif other_model is not None: - filtro = set(TaggedItem.objects.filter( + filter = set(TaggedItem.objects.filter( content_type__model=other_model.__name__.lower() ).values_list('tag_id', flat=True)) tags = set(TaggedItem.objects.filter( content_type__model=self.model.__name__.lower() ).values_list('tag_id', flat=True)) - if filtro is not None: - tags = tags.intersection(filtro) + if filter is not None: + tags = tags.intersection(filter) return list(tags) def tag_list(self, other_model=None, queryset=None): diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index d3dcd8d..0460447 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -242,6 +242,7 @@ class Post(ModelMeta, TranslatableModel): @python_2_unicode_compatible class BasePostPlugin(CMSPlugin): + app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) class Meta: abstract = True @@ -257,11 +258,10 @@ class BasePostPlugin(CMSPlugin): return posts def __str__(self): - return force_text(self.latest_posts) + return _('generic blog plugin') class LatestPostsPlugin(BasePostPlugin): - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'), help_text=_('The number of latests ' u'articles to be displayed.')) @@ -290,7 +290,6 @@ class LatestPostsPlugin(BasePostPlugin): class AuthorEntriesPlugin(BasePostPlugin): - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) authors = models.ManyToManyField( dj_settings.AUTH_USER_MODEL, verbose_name=_('authors'), limit_choices_to={'djangocms_blog_post_author__publish': True} @@ -324,7 +323,6 @@ class AuthorEntriesPlugin(BasePostPlugin): class GenericBlogPlugin(BasePostPlugin): - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) - def __str__(self): - return _('generic blog plugin') + class Meta: + abstract = False diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py index d0ec7b5..70e138a 100644 --- a/djangocms_blog/views.py +++ b/djangocms_blog/views.py @@ -5,6 +5,8 @@ import os.path from aldryn_apphooks_config.mixins import AppConfigMixin from django.contrib.auth import get_user_model +from django.core.exceptions import ImproperlyConfigured +from django.core.urlresolvers import reverse from django.utils.timezone import now from django.utils.translation import get_language from django.views.generic import DetailView, ListView @@ -18,6 +20,19 @@ User = get_user_model() class BaseBlogView(AppConfigMixin, ViewUrlMixin): + def get_view_url(self): + if not self.view_url_name: + raise ImproperlyConfigured( + 'Missing `view_url_name` attribute on {0}'.format(self.__class__.__name__) + ) + + return reverse( + self.view_url_name, + args=self.args, + kwargs=self.kwargs, + current_app=self.namespace + ) + def get_queryset(self): language = get_language() queryset = self.model._default_manager.namespace( diff --git a/tests/test_models.py b/tests/test_models.py index 3004bbb..b9c59c9 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -12,6 +12,8 @@ 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.utils.encoding import force_text +from django.utils.html import strip_tags from django.utils.timezone import now from django.utils.translation import get_language, override from djangocms_helper.utils import CMS_30 @@ -145,6 +147,8 @@ class AdminTest(BaseTest): class ModelsTest(BaseTest): def test_model_attributes(self): + self.get_pages() + post = self._get_post(self._post_data[0]['en']) post = self._get_post(self._post_data[0]['it'], post, 'it') post.main_image = self.create_filer_image_object() @@ -242,6 +246,7 @@ class ModelsTest(BaseTest): post2.save() self.assertEqual(len(Post.objects.available()), 2) self.assertEqual(len(Post.objects.published()), 1) + self.assertEqual(len(Post.objects.published_future()), 2) self.assertEqual(len(Post.objects.archived()), 0) # If post is published but end publishing date is in the past @@ -300,6 +305,15 @@ class ModelsTest(BaseTest): self.assertEqual(set(Post.objects.tag_cloud()), set(tags_1)) self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags)) + tags1 = set(Post.objects.tag_list(Post)) + tags2 = set(Tag.objects.all()) + self.assertEqual(tags1, tags2) + + self.assertEqual( + list(Post.objects.tagged(queryset=Post.objects.filter(pk=post1.pk)).order_by('pk').values_list('pk')), + list(Post.objects.filter(pk__in=(post1.pk, post2.pk)).order_by('pk').values_list('pk')) + ) + def test_plugin_latest(self): post1 = self._get_post(self._post_data[0]['en']) self._get_post(self._post_data[1]['en']) @@ -384,3 +398,25 @@ class ModelsTest(BaseTest): with self.settings(**{'SITE_ID': self.site_2.pk}): self.assertEqual(len(Post.objects.all().on_site()), 2) self.assertEqual(set(list(Post.objects.all().on_site())), set([post2, post3])) + + def test_str_repr(self): + post1 = self._get_post(self._post_data[0]['en']) + post1.meta_description = '' + post1.main_image = None + post1.save() + + self.assertEqual(force_text(post1), post1.title) + self.assertEqual(post1.get_description(), strip_tags(post1.abstract)) + self.assertEqual(post1.get_image_full_url(), '') + self.assertEqual(post1.get_author(), self.user) + + self.assertEqual(force_text(post1.categories.first()), 'category 1') + + plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1) + self.assertEqual(force_text(plugin.__str__()), '5 latest articles by author') + + plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1) + self.assertEqual(force_text(plugin.__str__()), '5 latest articles by tag') + + plugin = add_plugin(post1.content, 'BlogArchivePlugin', language='en', app_config=self.app_config_1) + self.assertEqual(force_text(plugin.__str__()), 'generic blog plugin') diff --git a/tests/test_views.py b/tests/test_views.py index 818484a..5c45a32 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -5,6 +5,7 @@ import os.path from aldryn_apphooks_config.utils import get_app_instance from django.contrib.auth.models import AnonymousUser +from django.core.exceptions import ImproperlyConfigured from django.http import Http404 from django.utils.timezone import now from parler.tests.utils import override_parler_settings @@ -70,6 +71,31 @@ class ViewTest(BaseTest): response = view_obj.render_to_response(context) self.assertContains(response, context['post_list'][0].get_absolute_url()) + def test_get_view_url(self): + posts = self.get_posts() + pages = self.get_pages() + + # Test the custom version of get_view_url against the different namespaces + request = self.get_request(pages[1], 'en', AnonymousUser()) + view_obj_1 = PostListView() + view_obj_1.request = request + view_obj_1.args = () + view_obj_1.kwargs = {} + view_obj_1.namespace, view_obj_1.config = get_app_instance(request) + self.assertEqual(view_obj_1.get_view_url(), pages[1].get_absolute_url()) + + request = self.get_request(pages[2], 'en', AnonymousUser()) + view_obj_2 = PostListView() + view_obj_2.request = request + view_obj_2.args = () + view_obj_2.kwargs = {} + view_obj_2.namespace, view_obj_2.config = get_app_instance(request) + self.assertEqual(view_obj_2.get_view_url(), pages[2].get_absolute_url()) + + view_obj_2.view_url_name = None + with self.assertRaises(ImproperlyConfigured): + view_obj_2.get_view_url() + def test_post_list_view_fallback(self): posts = self.get_posts() pages = self.get_pages() From 8f7c76cecc97a37ebc4c23689ff0d2213d5b9b05 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 27 Sep 2015 01:14:41 +0200 Subject: [PATCH 16/48] Use configurable permalinks. Add all fields to config admin. --- djangocms_blog/admin.py | 45 +++++++++++++++++++++++++++++---- djangocms_blog/cms_appconfig.py | 5 ++++ djangocms_blog/models.py | 19 +++++++++----- djangocms_blog/settings.py | 15 +++++++++++ djangocms_blog/urls.py | 18 ++++++++++--- tests/test_models.py | 39 ++++++++++++++++++++++++++++ 6 files changed, 127 insertions(+), 14 deletions(-) diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index 1d50b13..95a3641 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -123,11 +123,46 @@ class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): - def get_config_fields(self): - return ( - 'app_title', 'config.paginate_by', 'config.set_author', 'config.default_published', - 'config.use_placeholder', 'config.use_abstract', - ) + @property + def declared_fieldsets(self): + return [ + (None, { + 'fields': ('type', 'namespace',) + }), + ('Generic', { + 'fields': ( + 'config.default_published', 'config.use_placeholder', 'config.use_abstract', + 'config.set_author', + ) + }), + ('Layout', { + 'fields': ( + 'config.paginate_by', 'config.url_patterns', 'config.template_prefix', + ), + 'classes': ('collapse',) + }), + ('Meta', { + 'fields': ( + 'config.object_type', + ) + }), + ('Open Graph', { + 'fields': ( + 'config.og_type', 'config.og_app_id', 'config.og_profile_id', + 'config.og_publisher', 'config.og_author_url', + ) + }), + ('Twitter', { + 'fields': ( + 'config.twitter_type', 'config.twitter_site', 'config.twitter_author', + ) + }), + ('Google+', { + 'fields': ( + 'config.gplus_type', 'config.gplus_author', + ) + }), + ] admin.site.register(BlogCategory, BlogCategoryAdmin) admin.site.register(Post, PostAdmin) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index 2407968..c23c4d3 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -29,6 +29,11 @@ class BlogConfigForm(AppDataForm): label=_('Post published by default'), required=False, initial=get_setting('DEFAULT_PUBLISHED') ) + url_patterns = forms.ChoiceField( + label=_('Permalink structure'), required=False, + initial=get_setting('AVAILABLE_PERMALINK_STYLES')[0][0], + choices=get_setting('AVAILABLE_PERMALINK_STYLES') + ) use_placeholder = forms.BooleanField( label=_('Use placeholder and plugins for article body'), required=False, initial=get_setting('USE_PLACEHOLDER') diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 0460447..aa2f70d 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -177,12 +177,19 @@ class Post(ModelMeta, TranslatableModel): def get_absolute_url(self, lang=None): if not lang: lang = get_language() - kwargs = {'year': self.date_published.year, - 'month': '%02d' % self.date_published.month, - 'day': '%02d' % self.date_published.day, - 'slug': self.safe_translation_getter('slug', - language_code=lang, - any_language=True)} + category = self.categories.first() + kwargs = {} + urlconf = get_setting('PERMALINK_URLS')[self.app_config.url_patterns] + if '' in urlconf: + kwargs['year'] = self.date_published.year + if '' in urlconf: + kwargs['month'] = '%02d' % self.date_published.month + if '' in urlconf: + kwargs['day'] = '%02d' % self.date_published.day + if '' in urlconf: + 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 return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs) def get_meta_attribute(self, param): diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index 3b25659..d483b83 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -13,6 +13,19 @@ def get_setting(name): ) BLOG_TYPES = getattr(settings, 'BLOG_TYPES', OBJECT_TYPES) + PERMALINKS = ( + ('full_date', _('Full date')), + ('short_date', _('Year / Month')), + ('category', _('Category')), + ('slug', _('Just slug')), + ) + PERMALINKS_URLS = { + 'full_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', + 'short_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\w[-\w]*)/$', + 'category': r'^(?P\w[-\w]*)/(?P\w[-\w]*)/$', + 'slug': r'^(?P\w[-\w]*)/$', + } + default = { 'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', { 'size': '120x120', @@ -55,5 +68,7 @@ def get_setting(name): 'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True), 'BLOG_AUTHOR_DEFAULT': getattr(settings, 'BLOG_AUTHOR_DEFAULT', True), 'BLOG_DEFAULT_PUBLISHED': getattr(settings, 'BLOG_DEFAULT_PUBLISHED', False), + 'BLOG_AVAILABLE_PERMALINK_STYLES': getattr(settings, 'BLOG_AVAILABLE_PERMALINK_STYLES', PERMALINKS), # NOQA + 'BLOG_PERMALINK_URLS': getattr(settings, 'BLOG_PERMALINK_URLS', PERMALINKS_URLS), } return default['BLOG_%s' % name] diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py index b4896cb..2dfec6f 100644 --- a/djangocms_blog/urls.py +++ b/djangocms_blog/urls.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, print_function, unicode_literals from django.conf.urls import patterns, url +from djangocms_blog.settings import get_setting from .feeds import LatestEntriesFeed, TagFeed from .views import ( @@ -9,6 +10,18 @@ from .views import ( TaggedListView, ) + +def get_urls(): + urls = get_setting('PERMALINK_URLS') + details = [] + for urlconf in urls.values(): + details.append( + url(urlconf, PostDetailView.as_view(), name='post-detail'), + ) + return details + +detail_urls = get_urls() + urlpatterns = patterns( '', url(r'^$', @@ -19,8 +32,7 @@ urlpatterns = patterns( PostArchiveView.as_view(), name='posts-archive'), url(r'^(?P\d{4})/(?P\d{1,2})/$', PostArchiveView.as_view(), name='posts-archive'), - url(r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', - PostDetailView.as_view(), name='post-detail'), +) + detail_urls + [ url(r'^author/(?P[\w\.@+-]+)/$', AuthorEntriesView.as_view(), name='posts-author'), url(r'^category/(?P[\w\.@+-]+)/$', @@ -29,4 +41,4 @@ urlpatterns = patterns( TaggedListView.as_view(), name='posts-tagged'), url(r'^tag/(?P[-\w]+)/feed/$', TagFeed(), name='posts-tagged-feed'), -) +] diff --git a/tests/test_models.py b/tests/test_models.py index b9c59c9..192d742 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import re from copy import deepcopy import parler @@ -220,6 +221,44 @@ class ModelsTest(BaseTest): post.meta_title = 'meta title' self.assertEqual(post.get_title(), 'meta title') + def test_urls(self): + self.get_pages() + post = self._get_post(self._post_data[0]['en']) + post = self._get_post(self._post_data[0]['it'], post, 'it') + + # default + self.assertTrue(re.match(r'.*\d{4}/\d{2}/\d{2}/%s/$' % post.slug, post.get_absolute_url())) + + # full date + self.app_config_1.app_data.config.url_patterns = 'full_date' + self.app_config_1.save() + post.app_config = self.app_config_1 + self.assertTrue(re.match(r'.*\d{4}/\d{2}/\d{2}/%s/$' % post.slug, post.get_absolute_url())) + + # short date + self.app_config_1.app_data.config.url_patterns = 'short_date' + self.app_config_1.save() + post.app_config = self.app_config_1 + self.assertTrue(re.match(r'.*\d{4}/\d{2}/%s/$' % post.slug, post.get_absolute_url())) + + # category + self.app_config_1.app_data.config.url_patterns = 'category' + self.app_config_1.save() + post.app_config = self.app_config_1 + self.assertTrue(re.match(r'.*/\w[-\w]*/%s/$' % post.slug, post.get_absolute_url())) + self.assertTrue( + re.match( + r'.*%s/%s/$' % (post.categories.first().slug, post.slug), + post.get_absolute_url() + ) + ) + + # slug only + self.app_config_1.app_data.config.url_patterns = 'category' + self.app_config_1.save() + post.app_config = self.app_config_1 + self.assertTrue(re.match(r'.*/%s/$' % post.slug, post.get_absolute_url())) + def test_manager(self): post1 = self._get_post(self._post_data[0]['en']) post2 = self._get_post(self._post_data[1]['en']) From d9edf74c8d2eea3f39e17256d572e34bb7e1113f Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 27 Sep 2015 01:29:00 +0200 Subject: [PATCH 17/48] Add admin tests --- tests/test_models.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_models.py b/tests/test_models.py index 192d742..1c5a5a1 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -20,6 +20,7 @@ from django.utils.translation import get_language, override from djangocms_helper.utils import CMS_30 from taggit.models import Tag +from djangocms_blog.cms_appconfig import BlogConfig from djangocms_blog.models import Post from djangocms_blog.settings import get_setting @@ -32,6 +33,39 @@ class AdminTest(BaseTest): super(AdminTest, self).setUp() admin.autodiscover() + def test_admin_post_views(self): + post_admin = admin.site._registry[Post] + request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) + + post = self._get_post(self._post_data[0]['en']) + post = self._get_post(self._post_data[0]['it'], post, 'it') + + # Add view only contains the apphook selection widget + response = post_admin.add_view(request) + self.assertNotContains(response, 'Blog / sample_app') + + # Changeview is 'normal' + response = post_admin.change_view(request, str(post.pk)) + self.assertContains(response, '') + self.assertContains(response, '') + + def test_admin_blogconfig_views(self): + post_admin = admin.site._registry[BlogConfig] + request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) + + # Add view only has an empty form - no type + response = post_admin.add_view(request) + self.assertNotContains(response, 'djangocms_blog.cms_appconfig.BlogConfig') + self.assertContains(response, '') + + # Changeview is 'normal', with a few preselected items + response = post_admin.change_view(request, str(self.app_config_1.pk)) + self.assertContains(response, 'djangocms_blog.cms_appconfig.BlogConfig') + self.assertContains(response, '') + self.assertContains(response, '') + self.assertContains(response, '') + def test_admin_fieldsets(self): post_admin = admin.site._registry[Post] request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False) From f5b7d4ab721dbe7ff0f28f630342febcdf4ca908 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 27 Sep 2015 01:34:23 +0200 Subject: [PATCH 18/48] Better admin test --- tests/test_models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index 1c5a5a1..75e758d 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -20,7 +20,7 @@ from django.utils.translation import get_language, override from djangocms_helper.utils import CMS_30 from taggit.models import Tag -from djangocms_blog.cms_appconfig import BlogConfig +from djangocms_blog.cms_appconfig import BlogConfig, BlogConfigForm from djangocms_blog.models import Post from djangocms_blog.settings import get_setting @@ -63,6 +63,10 @@ class AdminTest(BaseTest): response = post_admin.change_view(request, str(self.app_config_1.pk)) self.assertContains(response, 'djangocms_blog.cms_appconfig.BlogConfig') self.assertContains(response, '') + # check that all the form fields are visible in the admin + response.render() + for fieldname in BlogConfigForm.base_fields: + self.assertContains(response, 'id="id_config-%s"' % fieldname) self.assertContains(response, '') self.assertContains(response, '') From 97e2f8a37367e62b6435a3da7a06241bd0039422 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 27 Sep 2015 01:38:59 +0200 Subject: [PATCH 19/48] Fixes --- djangocms_blog/urls.py | 2 +- tests/test_models.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py index 2dfec6f..561a56c 100644 --- a/djangocms_blog/urls.py +++ b/djangocms_blog/urls.py @@ -2,9 +2,9 @@ from __future__ import absolute_import, print_function, unicode_literals from django.conf.urls import patterns, url -from djangocms_blog.settings import get_setting from .feeds import LatestEntriesFeed, TagFeed +from .settings import get_setting from .views import ( AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView, TaggedListView, diff --git a/tests/test_models.py b/tests/test_models.py index 75e758d..7a59c9a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -43,12 +43,12 @@ class AdminTest(BaseTest): # Add view only contains the apphook selection widget response = post_admin.add_view(request) self.assertNotContains(response, 'Blog / sample_app') + self.assertContains(response, '' % self.app_config_1.pk) # Changeview is 'normal' response = post_admin.change_view(request, str(post.pk)) self.assertContains(response, '') - self.assertContains(response, '') + self.assertContains(response, '' % self.app_config_1.pk) def test_admin_blogconfig_views(self): post_admin = admin.site._registry[BlogConfig] @@ -64,7 +64,6 @@ class AdminTest(BaseTest): self.assertContains(response, 'djangocms_blog.cms_appconfig.BlogConfig') self.assertContains(response, '') # check that all the form fields are visible in the admin - response.render() for fieldname in BlogConfigForm.base_fields: self.assertContains(response, 'id="id_config-%s"' % fieldname) self.assertContains(response, '') From e0c93decd1de9e58352a88a03571a79192b55d04 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 27 Sep 2015 01:44:54 +0200 Subject: [PATCH 20/48] More tests --- tests/test_models.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index 7a59c9a..9613bf1 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -21,7 +21,7 @@ from djangocms_helper.utils import CMS_30 from taggit.models import Tag from djangocms_blog.cms_appconfig import BlogConfig, BlogConfigForm -from djangocms_blog.models import Post +from djangocms_blog.models import BlogCategory, Post from djangocms_blog.settings import get_setting from . import BaseTest @@ -69,6 +69,22 @@ class AdminTest(BaseTest): self.assertContains(response, '') self.assertContains(response, '') + def test_admin_category_views(self): + post_admin = admin.site._registry[BlogCategory] + request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) + + # Add view only has an empty form - no type + response = post_admin.add_view(request) + self.assertNotContains(response, '') + self.assertContains(response, '' % self.app_config_1.pk) + + # Changeview is 'normal', with a few preselected items + response = post_admin.change_view(request, str(self.category_1.pk)) + # response.render() + # print(response.content.decode('utf-8')) + self.assertContains(response, '') + self.assertContains(response, '' % self.app_config_1.pk) + def test_admin_fieldsets(self): post_admin = admin.site._registry[Post] request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False) From 98ce9d232ca673c00e27159cc0d948d9540bcc37 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Tue, 29 Sep 2015 08:26:02 +0200 Subject: [PATCH 21/48] Add forgotten migration --- ...nate_by__del_field_blogconfig_set_autho.py | 228 ++++++++++++++++++ ..._appconfig.py => 0016_create_appconfig.py} | 0 2 files changed, 228 insertions(+) create mode 100644 djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py rename djangocms_blog/south_migrations/{0015_create_appconfig.py => 0016_create_appconfig.py} (100%) diff --git a/djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py b/djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py new file mode 100644 index 0000000..3eee8dc --- /dev/null +++ b/djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py @@ -0,0 +1,228 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Deleting field 'BlogConfig.paginate_by' + db.delete_column('djangocms_blog_blogconfig', 'paginate_by') + + # Deleting field 'BlogConfig.set_author' + db.delete_column('djangocms_blog_blogconfig', 'set_author') + + + def backwards(self, orm): + # Adding field 'BlogConfig.paginate_by' + db.add_column('djangocms_blog_blogconfig', 'paginate_by', + self.gf('django.db.models.fields.PositiveIntegerField')(default=10), + keep_default=False) + + # Adding field 'BlogConfig.set_author' + db.add_column('djangocms_blog_blogconfig', 'set_author', + self.gf('django.db.models.fields.BooleanField')(default=True), + keep_default=False) + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'related_name': "'user_set'", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'user_set'", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) + }, + 'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "('width', 'height')", 'object_name': 'ThumbnailOption'}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + }, + 'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'db_table': "'djangocms_blog_blogcategory_translation'", 'object_name': 'BlogCategoryTranslation'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + 'djangocms_blog.blogconfig': { + 'Meta': {'object_name': 'BlogConfig'}, + 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'djangocms_blog.blogconfigtranslation': { + 'Meta': {'unique_together': "[('language_code', 'master')]", 'db_table': "'djangocms_blog_blogconfig_translation'", 'object_name': 'BlogConfigTranslation'}, + 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) + }, + 'djangocms_blog.genericblogplugin': { + 'Meta': {'object_name': 'GenericBlogPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}) + }, + 'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), + 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + 'djangocms_blog.post': { + 'Meta': {'ordering': "('-date_published', '-date_created')", 'object_name': 'Post'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'djangocms_blog_post_author'", 'null': 'True', 'blank': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'", 'symmetrical': 'False'}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Image']", 'related_name': "'djangocms_blog_post_image'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'related_name': "'djangocms_blog_post_full'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'related_name': "'djangocms_blog_post_thumbnail'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'db_table': "'djangocms_blog_post_translation'", 'object_name': 'PostTranslation'}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), + 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), + 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + 'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Folder']", 'related_name': "'all_files'", 'null': 'True', 'blank': 'True'}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), + 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'owned_files'", 'null': 'True', 'blank': 'True'}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), + 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.folder': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'filer_owned_folders'", 'null': 'True', 'blank': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Folder']", 'related_name': "'children'", 'null': 'True', 'blank': 'True'}), + 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['filer.File']", 'primary_key': 'True', 'unique': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'null': 'True', 'blank': 'True', 'max_length': '64'}) + }, + 'sites.site': { + 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + } + } + + complete_apps = ['djangocms_blog'] \ No newline at end of file diff --git a/djangocms_blog/south_migrations/0015_create_appconfig.py b/djangocms_blog/south_migrations/0016_create_appconfig.py similarity index 100% rename from djangocms_blog/south_migrations/0015_create_appconfig.py rename to djangocms_blog/south_migrations/0016_create_appconfig.py From 05e95087178fcf41d0f2d653bce0ce655f9df412 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 30 Sep 2015 09:09:14 +0200 Subject: [PATCH 22/48] Fix migrations --- .../migrations/0010_auto_20150923_1151.py | 8 +- djangocms_blog/models.py | 6 +- ...__add_unique_blogconfigtranslation_lang.py | 286 ------------------ ...d_blogconfig__add_blogconfigtranslation.py | 285 +++++++++++++++++ ..._set_autho.py => 0015_create_appconfig.py} | 246 +++++++-------- .../south_migrations/0016_create_appconfig.py | 234 -------------- 6 files changed, 420 insertions(+), 645 deletions(-) delete mode 100644 djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py create mode 100644 djangocms_blog/south_migrations/0014_auto__add_genericblogplugin__add_blogconfig__add_blogconfigtranslation.py rename djangocms_blog/south_migrations/{0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py => 0015_create_appconfig.py} (50%) delete mode 100644 djangocms_blog/south_migrations/0016_create_appconfig.py diff --git a/djangocms_blog/migrations/0010_auto_20150923_1151.py b/djangocms_blog/migrations/0010_auto_20150923_1151.py index 96d3e67..6458e8f 100644 --- a/djangocms_blog/migrations/0010_auto_20150923_1151.py +++ b/djangocms_blog/migrations/0010_auto_20150923_1151.py @@ -1,4 +1,4 @@ -# -*- coding: utf-8 -*- + #-*- coding: utf-8 -*- from __future__ import unicode_literals import aldryn_apphooks_config.fields @@ -11,6 +11,7 @@ from django.db import models, migrations def forwards(apps, schema_editor): BlogConfig = apps.get_model('djangocms_blog', 'BlogConfig') + BlogConfigTranslation = apps.get_model('djangocms_blog', 'BlogConfigTranslation') Post = apps.get_model('djangocms_blog', 'Post') BlogCategory = apps.get_model('djangocms_blog', 'BlogCategory') GenericBlogPlugin = apps.get_model('djangocms_blog', 'GenericBlogPlugin') @@ -19,8 +20,9 @@ def forwards(apps, schema_editor): config = None for page in Page.objects.drafts().filter(application_urls='BlogApp'): config = BlogConfig.objects.create(namespace=page.application_namespace) - for lang in get_languages(): - config.create_translation(lang, app_title='Blog') + for lang in get_language_list(): + title = page.get_title(lang) + translation = BlogConfigTranslation.objects.create(language_code=lang, master_id=config.pk, app_title=title) if config: for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): for item in model.objects.all(): diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index aa2f70d..8d03f24 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -33,7 +33,7 @@ class BlogCategory(TranslatableModel): parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True) date_created = models.DateTimeField(_('created at'), auto_now_add=True) date_modified = models.DateTimeField(_('modified at'), auto_now=True) - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config')) + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config')) translations = TranslatedFields( name=models.CharField(_('name'), max_length=255), @@ -116,7 +116,7 @@ class Post(ModelMeta, TranslatableModel): help_text=_('Select sites in which to show the post. ' 'If none is set it will be ' 'visible in all the configured sites.')) - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config')) + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config')) translations = TranslatedFields( title=models.CharField(_('title'), max_length=255), @@ -249,7 +249,7 @@ class Post(ModelMeta, TranslatableModel): @python_2_unicode_compatible class BasePostPlugin(CMSPlugin): - app_config = AppHookConfigField(BlogConfig, verbose_name=_('app. config'), blank=True) + app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config'), blank=True) class Meta: abstract = True diff --git a/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py b/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py deleted file mode 100644 index 9cd1487..0000000 --- a/djangocms_blog/south_migrations/0014_auto__add_blogconfigtranslation__add_unique_blogconfigtranslation_lang.py +++ /dev/null @@ -1,286 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import SchemaMigration -from django.db import models - - -class Migration(SchemaMigration): - - def forwards(self, orm): - # Adding model 'BlogConfigTranslation' - db.create_table('djangocms_blog_blogconfig_translation', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('language_code', self.gf('django.db.models.fields.CharField')(db_index=True, max_length=15)), - ('app_title', self.gf('django.db.models.fields.CharField')(max_length=234)), - ('master', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['djangocms_blog.BlogConfig'], related_name='translations', null=True)), - )) - db.send_create_signal('djangocms_blog', ['BlogConfigTranslation']) - - # Adding unique constraint on 'BlogConfigTranslation', fields ['language_code', 'master'] - db.create_unique('djangocms_blog_blogconfig_translation', ['language_code', 'master_id']) - - # Adding model 'BlogConfig' - db.create_table('djangocms_blog_blogconfig', ( - ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), - ('type', self.gf('django.db.models.fields.CharField')(max_length=100)), - ('namespace', self.gf('django.db.models.fields.CharField')(default=None, max_length=100, unique=True)), - ('app_data', self.gf('app_data.fields.AppDataField')(default='{}')), - )) - db.send_create_signal('djangocms_blog', ['BlogConfig']) - - # Adding model 'GenericBlogPlugin' - db.create_table('djangocms_blog_genericblogplugin', ( - ('cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(primary_key=True, to=orm['cms.CMSPlugin'], unique=True)), - ('app_config', self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(blank=True, null=True, to=orm['djangocms_blog.BlogConfig'])), - )) - db.send_create_signal('djangocms_blog', ['GenericBlogPlugin']) - - # Adding field 'AuthorEntriesPlugin.app_config' - db.add_column('djangocms_blog_authorentriesplugin', 'app_config', - self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(default=None, blank=True, null=True, to=orm['djangocms_blog.BlogConfig']), - keep_default=False) - - # Adding field 'Post.app_config' - db.add_column('djangocms_blog_post', 'app_config', - self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, default=None), - keep_default=False) - - # Adding field 'BlogCategory.app_config' - db.add_column('djangocms_blog_blogcategory', 'app_config', - self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, default=None), - keep_default=False) - - # Adding field 'LatestPostsPlugin.app_config' - db.add_column('djangocms_blog_latestpostsplugin', 'app_config', - self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(default=None, null=True, blank=True, to=orm['djangocms_blog.BlogConfig']), - keep_default=False) - - - def backwards(self, orm): - # Removing unique constraint on 'BlogConfigTranslation', fields ['language_code', 'master'] - db.delete_unique('djangocms_blog_blogconfig_translation', ['language_code', 'master_id']) - - # Deleting model 'BlogConfigTranslation' - db.delete_table('djangocms_blog_blogconfig_translation') - - # Deleting model 'BlogConfig' - db.delete_table('djangocms_blog_blogconfig') - - # Deleting model 'GenericBlogPlugin' - db.delete_table('djangocms_blog_genericblogplugin') - - # Deleting field 'AuthorEntriesPlugin.app_config' - db.delete_column('djangocms_blog_authorentriesplugin', 'app_config_id') - - # Deleting field 'Post.app_config' - db.delete_column('djangocms_blog_post', 'app_config_id') - - # Deleting field 'BlogCategory.app_config' - db.delete_column('djangocms_blog_blogcategory', 'app_config_id') - - # Deleting field 'LatestPostsPlugin.app_config' - db.delete_column('djangocms_blog_latestpostsplugin', 'app_config_id') - - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) - }, - 'auth.permission': { - 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), - 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Group']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) - }, - 'cms.cmsplugin': { - 'Meta': {'object_name': 'CMSPlugin'}, - 'changed_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['cms.CMSPlugin']", 'null': 'True'}), - 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), - 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), - 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), - 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'blank': 'True', 'null': 'True'}) - }, - 'cms.placeholder': { - 'Meta': {'object_name': 'Placeholder'}, - 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) - }, - 'cmsplugin_filer_image.thumbnailoption': { - 'Meta': {'object_name': 'ThumbnailOption', 'ordering': "('width', 'height')"}, - 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'height': ('django.db.models.fields.IntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'width': ('django.db.models.fields.IntegerField', [], {}) - }, - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'djangocms_blog.authorentriesplugin': { - 'Meta': {'object_name': 'AuthorEntriesPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), - 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) - }, - 'djangocms_blog.blogcategory': { - 'Meta': {'object_name': 'BlogCategory'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), - 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True'}) - }, - 'djangocms_blog.blogcategorytranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "'djangocms_blog_blogcategory_translation'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}) - }, - 'djangocms_blog.blogconfig': { - 'Meta': {'object_name': 'BlogConfig'}, - 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), - 'paginate_by': ('django.db.models.fields.PositiveIntegerField', [], {'default': '10'}), - 'set_author': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'djangocms_blog.blogconfigtranslation': { - 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "'djangocms_blog_blogconfig_translation'"}, - 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) - }, - 'djangocms_blog.genericblogplugin': { - 'Meta': {'object_name': 'GenericBlogPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}) - }, - 'djangocms_blog.latestpostsplugin': { - 'Meta': {'object_name': 'LatestPostsPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), - 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) - }, - 'djangocms_blog.post': { - 'Meta': {'object_name': 'Post', 'ordering': "('-date_published', '-date_created')"}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), - 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'to': "orm['auth.User']", 'null': 'True'}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'"}), - 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), - 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), - 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'to': "orm['filer.Image']", 'null': 'True'}), - 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), - 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), - 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['sites.Site']", 'null': 'True'}) - }, - 'djangocms_blog.posttranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'PostTranslation', 'db_table': "'djangocms_blog_post_translation'"}, - 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), - 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), - 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'filer.file': { - 'Meta': {'object_name': 'File'}, - '_file_size': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'null': 'True'}), - 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'all_files'", 'to': "orm['filer.Folder']", 'null': 'True'}), - 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_files'", 'to': "orm['auth.User']", 'null': 'True'}), - 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), - 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) - }, - 'filer.folder': { - 'Meta': {'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder', 'ordering': "('name',)"}, - 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'filer_owned_folders'", 'to': "orm['auth.User']", 'null': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'to': "orm['filer.Folder']", 'null': 'True'}), - 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) - }, - 'filer.image': { - 'Meta': {'object_name': 'Image'}, - '_height': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - '_width': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'date_taken': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), - 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['filer.File']", 'unique': 'True'}), - 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'subject_location': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '64', 'default': 'None', 'null': 'True'}) - }, - 'sites.site': { - 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, - 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['djangocms_blog'] diff --git a/djangocms_blog/south_migrations/0014_auto__add_genericblogplugin__add_blogconfig__add_blogconfigtranslation.py b/djangocms_blog/south_migrations/0014_auto__add_genericblogplugin__add_blogconfig__add_blogconfigtranslation.py new file mode 100644 index 0000000..ecd1300 --- /dev/null +++ b/djangocms_blog/south_migrations/0014_auto__add_genericblogplugin__add_blogconfig__add_blogconfigtranslation.py @@ -0,0 +1,285 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'GenericBlogPlugin' + db.create_table(u'djangocms_blog_genericblogplugin', ( + (u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)), + ('app_config', self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True)), + )) + db.send_create_signal(u'djangocms_blog', ['GenericBlogPlugin']) + + # Adding model 'BlogConfig' + db.create_table(u'djangocms_blog_blogconfig', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('type', self.gf('django.db.models.fields.CharField')(max_length=100)), + ('namespace', self.gf('django.db.models.fields.CharField')(default=None, unique=True, max_length=100)), + ('app_data', self.gf('app_data.fields.AppDataField')(default='{}')), + )) + db.send_create_signal(u'djangocms_blog', ['BlogConfig']) + + # Adding model 'BlogConfigTranslation' + db.create_table(u'djangocms_blog_blogconfig_translation', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('language_code', self.gf('django.db.models.fields.CharField')(max_length=15, db_index=True)), + ('app_title', self.gf('django.db.models.fields.CharField')(max_length=234)), + (u'master', self.gf('django.db.models.fields.related.ForeignKey')(related_name='translations', null=True, to=orm['djangocms_blog.BlogConfig'])), + )) + db.send_create_signal(u'djangocms_blog', ['BlogConfigTranslation']) + + # Adding unique constraint on 'BlogConfigTranslation', fields ['language_code', u'master'] + db.create_unique(u'djangocms_blog_blogconfig_translation', ['language_code', u'master_id']) + + # Adding field 'BlogCategory.app_config' + db.add_column(u'djangocms_blog_blogcategory', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True), + keep_default=False) + + # Adding field 'Post.app_config' + db.add_column(u'djangocms_blog_post', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True), + keep_default=False) + + # Adding field 'LatestPostsPlugin.app_config' + db.add_column(u'djangocms_blog_latestpostsplugin', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True), + keep_default=False) + + # Adding field 'AuthorEntriesPlugin.app_config' + db.add_column(u'djangocms_blog_authorentriesplugin', 'app_config', + self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + # Removing unique constraint on 'BlogConfigTranslation', fields ['language_code', u'master'] + db.delete_unique(u'djangocms_blog_blogconfig_translation', ['language_code', u'master_id']) + + # Deleting model 'GenericBlogPlugin' + db.delete_table(u'djangocms_blog_genericblogplugin') + + # Deleting model 'BlogConfig' + db.delete_table(u'djangocms_blog_blogconfig') + + # Deleting model 'BlogConfigTranslation' + db.delete_table(u'djangocms_blog_blogconfig_translation') + + # Deleting field 'BlogCategory.app_config' + db.delete_column(u'djangocms_blog_blogcategory', 'app_config_id') + + # Deleting field 'Post.app_config' + db.delete_column(u'djangocms_blog_post', 'app_config_id') + + # Deleting field 'LatestPostsPlugin.app_config' + db.delete_column(u'djangocms_blog_latestpostsplugin', 'app_config_id') + + # Deleting field 'AuthorEntriesPlugin.app_config' + db.delete_column(u'djangocms_blog_authorentriesplugin', 'app_config_id') + + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + 'cms.cmsplugin': { + 'Meta': {'object_name': 'CMSPlugin'}, + 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), + 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) + }, + 'cms.placeholder': { + 'Meta': {'object_name': 'Placeholder'}, + 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) + }, + u'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'}, + 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'height': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'width': ('django.db.models.fields.IntegerField', [], {}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'djangocms_blog.authorentriesplugin': { + 'Meta': {'object_name': 'AuthorEntriesPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.blogcategory': { + 'Meta': {'object_name': 'BlogCategory'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + }, + u'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) + }, + u'djangocms_blog.blogconfig': { + 'Meta': {'object_name': 'BlogConfig'}, + 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'unique': 'True', 'max_length': '100'}), + 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'djangocms_blog.blogconfigtranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "u'djangocms_blog_blogconfig_translation'"}, + 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogConfig']"}) + }, + u'djangocms_blog.genericblogplugin': { + 'Meta': {'object_name': 'GenericBlogPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}) + }, + u'djangocms_blog.latestpostsplugin': { + 'Meta': {'object_name': 'LatestPostsPlugin'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), + 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) + }, + u'djangocms_blog.post': { + 'Meta': {'ordering': "(u'-date_published', u'-date_created')", 'object_name': 'Post'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}), + 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}), + 'meta_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), + 'meta_title': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + }, + u'filer.file': { + 'Meta': {'object_name': 'File'}, + '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}), + 'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + u'filer.folder': { + 'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'}, + 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) + }, + 'filer.image': { + 'Meta': {'object_name': 'Image'}, + '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}), + 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'related_url': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'}) + }, + u'sites.site': { + 'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) + } + } + + complete_apps = ['djangocms_blog'] \ No newline at end of file diff --git a/djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py b/djangocms_blog/south_migrations/0015_create_appconfig.py similarity index 50% rename from djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py rename to djangocms_blog/south_migrations/0015_create_appconfig.py index 3eee8dc..7f8e270 100644 --- a/djangocms_blog/south_migrations/0015_auto__del_field_blogconfig_paginate_by__del_field_blogconfig_set_autho.py +++ b/djangocms_blog/south_migrations/0015_create_appconfig.py @@ -1,227 +1,235 @@ # -*- coding: utf-8 -*- from south.utils import datetime_utils as datetime from south.db import db -from south.v2 import SchemaMigration +from south.v2 import DataMigration from django.db import models +from cms.models import Page +from cms.utils.i18n import get_language_list -class Migration(SchemaMigration): +class Migration(DataMigration): def forwards(self, orm): - # Deleting field 'BlogConfig.paginate_by' - db.delete_column('djangocms_blog_blogconfig', 'paginate_by') - - # Deleting field 'BlogConfig.set_author' - db.delete_column('djangocms_blog_blogconfig', 'set_author') - + BlogConfig = orm['djangocms_blog.BlogConfig'] + BlogConfigTranslation = orm['djangocms_blog.BlogConfigTranslation'] + Post = orm['djangocms_blog.Post'] + BlogCategory = orm['djangocms_blog.BlogCategory'] + GenericBlogPlugin = orm['djangocms_blog.GenericBlogPlugin'] + LatestPostsPlugin = orm['djangocms_blog.LatestPostsPlugin'] + AuthorEntriesPlugin = orm['djangocms_blog.AuthorEntriesPlugin'] + config = None + for page in Page.objects.drafts().filter(application_urls='BlogApp'): + config = BlogConfig.objects.create(namespace=page.application_namespace) + for lang in get_language_list(): + title = page.get_title(lang) + translation = BlogConfigTranslation.objects.create(language_code=lang, master_id=config.pk, app_title=title) + if config: + for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): + for item in model.objects.all(): + item.app_config = config + item.save() def backwards(self, orm): - # Adding field 'BlogConfig.paginate_by' - db.add_column('djangocms_blog_blogconfig', 'paginate_by', - self.gf('django.db.models.fields.PositiveIntegerField')(default=10), - keep_default=False) - - # Adding field 'BlogConfig.set_author' - db.add_column('djangocms_blog_blogconfig', 'set_author', - self.gf('django.db.models.fields.BooleanField')(default=True), - keep_default=False) + # No need for backward data migration + pass models = { - 'auth.group': { + u'auth.group': { 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) }, - 'auth.permission': { - 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) }, - 'auth.user': { + u'auth.user': { 'Meta': {'object_name': 'User'}, 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'related_name': "'user_set'", 'symmetrical': 'False', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'related_name': "'user_set'", 'symmetrical': 'False', 'blank': 'True'}), - 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) }, 'cms.cmsplugin': { 'Meta': {'object_name': 'CMSPlugin'}, 'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}), - 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), + 'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), - 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), + 'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}), 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'}) }, 'cms.placeholder': { 'Meta': {'object_name': 'Placeholder'}, 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}) }, - 'cmsplugin_filer_image.thumbnailoption': { - 'Meta': {'ordering': "('width', 'height')", 'object_name': 'ThumbnailOption'}, + u'cmsplugin_filer_image.thumbnailoption': { + 'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'}, 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 'height': ('django.db.models.fields.IntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 'width': ('django.db.models.fields.IntegerField', [], {}) }, - 'contenttypes.contenttype': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, - 'djangocms_blog.authorentriesplugin': { + u'djangocms_blog.authorentriesplugin': { 'Meta': {'object_name': 'AuthorEntriesPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), - 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.User']", 'symmetrical': 'False'}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}), + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) }, - 'djangocms_blog.blogcategory': { + u'djangocms_blog.blogcategory': { 'Meta': {'object_name': 'BlogCategory'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}), 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'}) }, - 'djangocms_blog.blogcategorytranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'db_table': "'djangocms_blog_blogcategory_translation'", 'object_name': 'BlogCategoryTranslation'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), + u'djangocms_blog.blogcategorytranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}) }, - 'djangocms_blog.blogconfig': { + u'djangocms_blog.blogconfig': { 'Meta': {'object_name': 'BlogConfig'}, 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'unique': 'True', 'max_length': '100'}), 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, - 'djangocms_blog.blogconfigtranslation': { - 'Meta': {'unique_together': "[('language_code', 'master')]", 'db_table': "'djangocms_blog_blogconfig_translation'", 'object_name': 'BlogConfigTranslation'}, + u'djangocms_blog.blogconfigtranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "u'djangocms_blog_blogconfig_translation'"}, 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogConfig']"}) }, - 'djangocms_blog.genericblogplugin': { + u'djangocms_blog.genericblogplugin': { 'Meta': {'object_name': 'GenericBlogPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}) + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}) }, - 'djangocms_blog.latestpostsplugin': { + u'djangocms_blog.latestpostsplugin': { 'Meta': {'object_name': 'LatestPostsPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']", 'blank': 'True'}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'primary_key': 'True', 'unique': 'True'}), + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}), + u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}), 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) }, - 'djangocms_blog.post': { - 'Meta': {'ordering': "('-date_published', '-date_created')", 'object_name': 'Post'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), - 'author': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'djangocms_blog_post_author'", 'null': 'True', 'blank': 'True'}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'", 'symmetrical': 'False'}), - 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), + u'djangocms_blog.post': { + 'Meta': {'ordering': "(u'-date_published', u'-date_created')", 'object_name': 'Post'}, + 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}), + 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}), + 'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}), 'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), 'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Image']", 'related_name': "'djangocms_blog_post_image'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), - 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'related_name': "'djangocms_blog_post_full'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), - 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'related_name': "'djangocms_blog_post_thumbnail'", 'blank': 'True', 'null': 'True', 'on_delete': 'models.SET_NULL'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}), + 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), + 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}), 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'}) + 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'}) }, - 'djangocms_blog.posttranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'db_table': "'djangocms_blog_post_translation'", 'object_name': 'PostTranslation'}, - 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), - 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), + u'djangocms_blog.posttranslation': { + 'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"}, + 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}), + u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}), + 'meta_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), + 'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}), + 'meta_title': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}), 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}), 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) }, - 'filer.file': { + u'filer.file': { 'Meta': {'object_name': 'File'}, '_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), - 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), - 'folder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Folder']", 'related_name': "'all_files'", 'null': 'True', 'blank': 'True'}), + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}), 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'owned_files'", 'null': 'True', 'blank': 'True'}), - 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), - 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), + 'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}), + 'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}), + 'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}), 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) }, - 'filer.folder': { - 'Meta': {'ordering': "('name',)", 'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder'}, + u'filer.folder': { + 'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'}, 'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), 'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'related_name': "'filer_owned_folders'", 'null': 'True', 'blank': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['filer.Folder']", 'related_name': "'children'", 'null': 'True', 'blank': 'True'}), - 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}), + 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}), + u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), + u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}) }, 'filer.image': { 'Meta': {'object_name': 'Image'}, '_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), '_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), - 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), + 'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), 'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), - 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), - 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'null': 'True', 'max_length': '255'}), - 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['filer.File']", 'primary_key': 'True', 'unique': 'True'}), + 'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + 'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}), + u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}), 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'null': 'True', 'blank': 'True', 'max_length': '64'}) + 'related_url': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}), + 'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'}) }, - 'sites.site': { - 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, - 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + u'sites.site': { + 'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"}, + 'domain': ('django.db.models.fields.CharField', [], {'max_length': '255'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}) } } diff --git a/djangocms_blog/south_migrations/0016_create_appconfig.py b/djangocms_blog/south_migrations/0016_create_appconfig.py deleted file mode 100644 index 90c6d73..0000000 --- a/djangocms_blog/south_migrations/0016_create_appconfig.py +++ /dev/null @@ -1,234 +0,0 @@ -# -*- coding: utf-8 -*- -from south.utils import datetime_utils as datetime -from south.db import db -from south.v2 import DataMigration -from django.db import models -from cms.models import Page -from cms.utils.conf import get_languages - - -class Migration(DataMigration): - - def forwards(self, orm): - BlogConfig = orm['djangocms_blog.BlogConfig'] - Post = orm['djangocms_blog.Post'] - BlogCategory = orm['djangocms_blog.BlogCategory'] - GenericBlogPlugin = orm['djangocms_blog.GenericBlogPlugin'] - LatestPostsPlugin = orm['djangocms_blog.LatestPostsPlugin'] - AuthorEntriesPlugin = orm['djangocms_blog.AuthorEntriesPlugin'] - config = None - for page in Page.objects.drafts().filter(application_urls='BlogApp'): - config = BlogConfig.objects.create(namespace=page.application_namespace) - for lang in get_languages(): - config.create_translation(lang, app_title='Blog') - if config: - for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin): - for item in model.objects.all(): - item.app_config = config - item.save() - - def backwards(self, orm): - # No need for backward data migration - pass - - models = { - 'auth.group': { - 'Meta': {'object_name': 'Group'}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), - 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['auth.Permission']"}) - }, - 'auth.permission': { - 'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission', 'ordering': "('content_type__app_label', 'content_type__model', 'codename')"}, - 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - }, - 'auth.user': { - 'Meta': {'object_name': 'User'}, - 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'email': ('django.db.models.fields.EmailField', [], {'blank': 'True', 'max_length': '75'}), - 'first_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), - 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Group']"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'last_name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '30'}), - 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), - 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'related_name': "'user_set'", 'to': "orm['auth.Permission']"}), - 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) - }, - 'cms.cmsplugin': { - 'Meta': {'object_name': 'CMSPlugin'}, - 'changed_date': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'depth': ('django.db.models.fields.PositiveIntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['cms.CMSPlugin']", 'null': 'True'}), - 'path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'unique': 'True'}), - 'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}), - 'plugin_type': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '50'}), - 'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'blank': 'True', 'null': 'True'}) - }, - 'cms.placeholder': { - 'Meta': {'object_name': 'Placeholder'}, - 'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'slot': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '255'}) - }, - 'cmsplugin_filer_image.thumbnailoption': { - 'Meta': {'object_name': 'ThumbnailOption', 'ordering': "('width', 'height')"}, - 'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'height': ('django.db.models.fields.IntegerField', [], {}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'width': ('django.db.models.fields.IntegerField', [], {}) - }, - 'contenttypes.contenttype': { - 'Meta': {'unique_together': "(('app_label', 'model'),)", 'ordering': "('name',)", 'db_table': "'django_content_type'", 'object_name': 'ContentType'}, - 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'djangocms_blog.authorentriesplugin': { - 'Meta': {'object_name': 'AuthorEntriesPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'authors': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['auth.User']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), - 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) - }, - 'djangocms_blog.blogcategory': { - 'Meta': {'object_name': 'BlogCategory'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), - 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']", 'null': 'True'}) - }, - 'djangocms_blog.blogcategorytranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "'djangocms_blog_blogcategory_translation'"}, - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'translations'", 'null': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}) - }, - 'djangocms_blog.blogconfig': { - 'Meta': {'object_name': 'BlogConfig'}, - 'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '100', 'unique': 'True'}), - 'paginate_by': ('django.db.models.fields.PositiveIntegerField', [], {'default': '10'}), - 'set_author': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'type': ('django.db.models.fields.CharField', [], {'max_length': '100'}) - }, - 'djangocms_blog.blogconfigtranslation': { - 'Meta': {'unique_together': "[('language_code', 'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "'djangocms_blog_blogconfig_translation'"}, - 'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.BlogConfig']", 'related_name': "'translations'", 'null': 'True'}) - }, - 'djangocms_blog.genericblogplugin': { - 'Meta': {'object_name': 'GenericBlogPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}) - }, - 'djangocms_blog.latestpostsplugin': { - 'Meta': {'object_name': 'LatestPostsPlugin'}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'blank': 'True', 'to': "orm['djangocms_blog.BlogConfig']"}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['djangocms_blog.BlogCategory']"}), - 'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['cms.CMSPlugin']", 'unique': 'True'}), - 'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'}) - }, - 'djangocms_blog.post': { - 'Meta': {'object_name': 'Post', 'ordering': "('-date_published', '-date_created')"}, - 'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': "orm['djangocms_blog.BlogConfig']"}), - 'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'djangocms_blog_post_author'", 'to': "orm['auth.User']", 'null': 'True'}), - 'categories': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': "orm['djangocms_blog.BlogCategory']", 'related_name': "'blog_posts'"}), - 'content': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'related_name': "'post_content'", 'null': 'True'}), - 'date_created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'date_modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), - 'date_published_end': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), - 'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'main_image': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_image'", 'to': "orm['filer.Image']", 'null': 'True'}), - 'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_full'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), - 'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'on_delete': 'models.SET_NULL', 'blank': 'True', 'related_name': "'djangocms_blog_post_thumbnail'", 'to': "orm['cmsplugin_filer_image.ThumbnailOption']", 'null': 'True'}), - 'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'sites': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'blank': 'True', 'to': "orm['sites.Site']", 'null': 'True'}) - }, - 'djangocms_blog.posttranslation': { - 'Meta': {'unique_together': "[('language_code', 'slug'), ('language_code', 'master')]", 'object_name': 'PostTranslation', 'db_table': "'djangocms_blog_post_translation'"}, - 'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'language_code': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '15'}), - 'master': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['djangocms_blog.Post']", 'related_name': "'translations'", 'null': 'True'}), - 'meta_description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_keywords': ('django.db.models.fields.TextField', [], {'blank': 'True', 'default': "''"}), - 'meta_title': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'blank': 'True', 'default': "''"}), - 'slug': ('django.db.models.fields.SlugField', [], {'blank': 'True', 'max_length': '50'}), - 'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}) - }, - 'filer.file': { - 'Meta': {'object_name': 'File'}, - '_file_size': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - 'description': ('django.db.models.fields.TextField', [], {'blank': 'True', 'null': 'True'}), - 'file': ('django.db.models.fields.files.FileField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'all_files'", 'to': "orm['filer.Folder']", 'null': 'True'}), - 'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'default': "''"}), - 'original_filename': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_files'", 'to': "orm['auth.User']", 'null': 'True'}), - 'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']", 'related_name': "'polymorphic_filer.file_set+'", 'null': 'True'}), - 'sha1': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '40', 'default': "''"}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) - }, - 'filer.folder': { - 'Meta': {'unique_together': "(('parent', 'name'),)", 'object_name': 'Folder', 'ordering': "('name',)"}, - 'created_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'modified_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}), - 'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'filer_owned_folders'", 'to': "orm['auth.User']", 'null': 'True'}), - 'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'children'", 'to': "orm['filer.Folder']", 'null': 'True'}), - 'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}), - 'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'auto_now_add': 'True'}) - }, - 'filer.image': { - 'Meta': {'object_name': 'Image'}, - '_height': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - '_width': ('django.db.models.fields.IntegerField', [], {'blank': 'True', 'null': 'True'}), - 'author': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'date_taken': ('django.db.models.fields.DateTimeField', [], {'blank': 'True', 'null': 'True'}), - 'default_alt_text': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'default_caption': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '255', 'null': 'True'}), - 'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'primary_key': 'True', 'to': "orm['filer.File']", 'unique': 'True'}), - 'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), - 'subject_location': ('django.db.models.fields.CharField', [], {'blank': 'True', 'max_length': '64', 'default': 'None', 'null': 'True'}) - }, - 'sites.site': { - 'Meta': {'ordering': "('domain',)", 'db_table': "'django_site'", 'object_name': 'Site'}, - 'domain': ('django.db.models.fields.CharField', [], {'max_length': '100'}), - 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), - 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) - } - } - - complete_apps = ['djangocms_blog'] From 2fd5bca07cbc3ae0993d4b73522b481dff2e3cfa Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 30 Sep 2015 09:09:28 +0200 Subject: [PATCH 23/48] Enhance the admin --- djangocms_blog/admin.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index 95a3641..b21f256 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -39,7 +39,10 @@ class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, Translatabl class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, ModelAppHookConfig, TranslatableAdmin): form = PostAdminForm - list_display = ['title', 'author', 'date_published', 'date_published_end'] + list_display = [ + 'title', 'author', 'date_published', 'app_config', 'languages', 'date_published_end' + ] + list_filter = ('app_config',) date_hierarchy = 'date_published' raw_id_fields = ['author'] frontend_editable_fields = ('title', 'abstract', 'post_text') @@ -67,6 +70,9 @@ class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin, 'default_published': 'publish' } + def languages(self, obj): + return ','.join(obj.get_available_languages()) + def formfield_for_dbfield(self, db_field, **kwargs): field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs) if db_field.name == 'meta_description': From a703b188fa8b654afab127c149db4d63b754b709 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 30 Sep 2015 09:34:11 +0200 Subject: [PATCH 24/48] Add edit configuration link to toolbar --- djangocms_blog/cms_toolbar.py | 27 ++++++++++++++++----------- djangocms_blog/models.py | 1 + djangocms_blog/views.py | 3 ++- tests/test_views.py | 23 +++++++++++++++++++++++ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/djangocms_blog/cms_toolbar.py b/djangocms_blog/cms_toolbar.py index e8b7a17..505f0e0 100644 --- a/djangocms_blog/cms_toolbar.py +++ b/djangocms_blog/cms_toolbar.py @@ -4,9 +4,9 @@ from __future__ import absolute_import, print_function, unicode_literals from cms.toolbar_base import CMSToolbar from cms.toolbar_pool import toolbar_pool from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _, override -from .models import BLOG_CURRENT_POST_IDENTIFIER +from .models import BLOG_CURRENT_NAMESPACE, BLOG_CURRENT_POST_IDENTIFIER @toolbar_pool.register @@ -17,16 +17,21 @@ class BlogToolbar(CMSToolbar): if not self.request.user.has_perm('djangocms_blog.add_post'): return # pragma: no cover admin_menu = self.toolbar.get_or_create_menu('djangocms_blog', _('Blog')) - url = reverse('admin:djangocms_blog_post_changelist') - admin_menu.add_modal_item(_('Post list'), url=url) - url = reverse('admin:djangocms_blog_post_add') - admin_menu.add_modal_item(_('Add post'), url=url) + with override(self.current_lang): + url = reverse('admin:djangocms_blog_post_changelist') + admin_menu.add_modal_item(_('Post list'), url=url) + url = reverse('admin:djangocms_blog_post_add') + admin_menu.add_modal_item(_('Add post'), url=url) + current_config = getattr(self.request, BLOG_CURRENT_NAMESPACE, None) + if current_config: + url = reverse('admin:djangocms_blog_blogconfig_change', args=(current_config.pk,)) + admin_menu.add_modal_item(_('Edit configuration'), url=url) - current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None) - if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA - admin_menu.add_modal_item(_('Edit Post'), reverse( - 'admin:djangocms_blog_post_change', args=(current_post.pk,)), - active=True) + current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None) + if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA + admin_menu.add_modal_item(_('Edit Post'), reverse( + 'admin:djangocms_blog_post_change', args=(current_post.pk,)), + active=True) def post_template_populate(self): current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None) diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 8d03f24..df81d1b 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -23,6 +23,7 @@ from .managers import GenericDateTaggedManager from .settings import get_setting BLOG_CURRENT_POST_IDENTIFIER = 'djangocms_post_current' +BLOG_CURRENT_NAMESPACE = 'djangocms_post_current_config' @python_2_unicode_compatible diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py index 70e138a..b982e02 100644 --- a/djangocms_blog/views.py +++ b/djangocms_blog/views.py @@ -12,7 +12,7 @@ from django.utils.translation import get_language from django.views.generic import DetailView, ListView from parler.views import TranslatableSlugMixin, ViewUrlMixin -from .models import BLOG_CURRENT_POST_IDENTIFIER, BlogCategory, Post +from .models import BLOG_CURRENT_NAMESPACE, BLOG_CURRENT_POST_IDENTIFIER, BlogCategory, Post from .settings import get_setting User = get_user_model() @@ -42,6 +42,7 @@ class BaseBlogView(AppConfigMixin, ViewUrlMixin): ) if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode: queryset = queryset.published() + setattr(self.request, BLOG_CURRENT_NAMESPACE, self.config) return queryset def get_template_names(self): diff --git a/tests/test_views.py b/tests/test_views.py index 5c45a32..3bcb5fc 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -4,15 +4,19 @@ from __future__ import absolute_import, print_function, unicode_literals import os.path from aldryn_apphooks_config.utils import get_app_instance +from cms.toolbar.items import ModalItem from django.contrib.auth.models import AnonymousUser from django.core.exceptions import ImproperlyConfigured +from django.core.urlresolvers import reverse from django.http import Http404 from django.utils.timezone import now +from django.utils.translation import ugettext_lazy as _ from parler.tests.utils import override_parler_settings from parler.utils.conf import add_default_language_settings from parler.utils.context import smart_override, switch_language from djangocms_blog.feeds import LatestEntriesFeed, TagFeed +from djangocms_blog.models import BLOG_CURRENT_NAMESPACE from djangocms_blog.sitemaps import BlogSitemap from djangocms_blog.views import ( AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView, @@ -34,8 +38,10 @@ class ViewTest(BaseTest): view_obj = PostListView() view_obj.request = request view_obj.namespace, view_obj.config = get_app_instance(request) + self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE, None), None) self.assertEqual(list(view_obj.get_queryset()), [posts[0]]) + self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE), self.app_config_1) request = self.get_page_request(pages[1], self.user, lang='en', edit=False) view_obj.namespace, view_obj.config = get_app_instance(request) @@ -60,16 +66,33 @@ class ViewTest(BaseTest): self.assertEqual(context['post_list'][0].title, 'Third post') response = view_obj.render_to_response(context) self.assertContains(response, context['post_list'][0].get_absolute_url()) + self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE), self.app_config_1) with smart_override('it'): request = self.get_page_request(pages[1], self.user, lang='it', edit=True) + view_obj = PostListView() view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.request = request + view_obj.kwargs = {} view_obj.object_list = view_obj.get_queryset() context = view_obj.get_context_data(object_list=view_obj.object_list) self.assertEqual(context['post_list'][0].title, 'Terzo post') response = view_obj.render_to_response(context) self.assertContains(response, context['post_list'][0].get_absolute_url()) + blog_menu = request.toolbar.get_or_create_menu('djangocms_blog', _('Blog')) + + self.assertEqual(len(blog_menu.items), 3) + self.assertEqual(len(blog_menu.find_items( + ModalItem, url=reverse('admin:djangocms_blog_post_changelist') + )), 1) + self.assertEqual(len(blog_menu.find_items( + ModalItem, url=reverse('admin:djangocms_blog_post_add') + )), 1) + self.assertEqual(len(blog_menu.find_items( + ModalItem, url=reverse( + 'admin:djangocms_blog_blogconfig_change', args=(self.app_config_1.pk,) + ) + )), 1) def test_get_view_url(self): posts = self.get_posts() From a316667209395308247944df25a0bd879419a2a8 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 30 Sep 2015 09:57:40 +0200 Subject: [PATCH 25/48] Update translatiosn --- .../locale/en/LC_MESSAGES/django.po | 359 +++++++++++++----- .../migrations/0010_auto_20150923_1151.py | 2 +- djangocms_blog/models.py | 4 +- 3 files changed, 274 insertions(+), 91 deletions(-) diff --git a/djangocms_blog/locale/en/LC_MESSAGES/django.po b/djangocms_blog/locale/en/LC_MESSAGES/django.po index 8f44053..9752fd1 100644 --- a/djangocms_blog/locale/en/LC_MESSAGES/django.po +++ b/djangocms_blog/locale/en/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2014-11-30 12:50+0100\n" +"POT-Creation-Date: 2015-09-30 09:56+0200\n" "PO-Revision-Date: 2014-03-05 18:09+0100\n" "Last-Translator: Iacopo Spalletti\n" "Language-Team: Italian \n" @@ -17,189 +17,361 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 1.5\n" -#: cms_app.py:8 cms_plugins.py:32 cms_plugins.py:45 cms_plugins.py:56 -#: cms_plugins.py:67 cms_toolbar.py:18 +#: cms_app.py:13 cms_plugins.py:64 cms_plugins.py:77 cms_plugins.py:93 +#: cms_plugins.py:108 cms_toolbar.py:19 msgid "Blog" msgstr "Blog" -#: cms_plugins.py:20 +#: cms_appconfig.py:20 +msgid "application title" +msgstr "" + +#: cms_appconfig.py:24 +#, fuzzy +msgid "untitled" +msgstr "Title" + +#: cms_appconfig.py:29 +msgid "Post published by default" +msgstr "" + +#: cms_appconfig.py:33 +msgid "Permalink structure" +msgstr "" + +#: cms_appconfig.py:38 +msgid "Use placeholder and plugins for article body" +msgstr "" + +#: cms_appconfig.py:42 +msgid "Use abstract field" +msgstr "" + +#: cms_appconfig.py:46 +#, fuzzy +msgid "Set author" +msgstr "Author" + +#: cms_appconfig.py:46 +msgid "Set author by default" +msgstr "" + +#: cms_appconfig.py:50 +msgid "Paginate size" +msgstr "" + +#: cms_appconfig.py:51 +msgid "When paginating list views, how many articles per page?" +msgstr "" + +#: cms_appconfig.py:54 +msgid "Template prefix" +msgstr "" + +#: cms_appconfig.py:55 +msgid "Alternative directory to load the blog templates from" +msgstr "" + +#: cms_appconfig.py:58 +msgid "Object type" +msgstr "" + +#: cms_appconfig.py:62 +msgid "Facebook type" +msgstr "" + +#: cms_appconfig.py:66 +msgid "Facebook application ID" +msgstr "" + +#: cms_appconfig.py:69 +msgid "Facebook profile ID" +msgstr "" + +#: cms_appconfig.py:72 +msgid "Facebook page URL" +msgstr "" + +#: cms_appconfig.py:75 +msgid "Facebook author URL" +msgstr "" + +#: cms_appconfig.py:78 +msgid "Twitter type" +msgstr "" + +#: cms_appconfig.py:82 +msgid "Twitter site handle" +msgstr "" + +#: cms_appconfig.py:85 +msgid "Twitter author handle" +msgstr "" + +#: cms_appconfig.py:88 +msgid "Google+ type" +msgstr "" + +#: cms_appconfig.py:92 +msgid "Google+ author name" +msgstr "" + +#: cms_plugins.py:30 cms_plugins.py:49 msgid "Latest Blog Articles" msgstr "Latest Blog Articles" -#: cms_plugins.py:33 +#: cms_plugins.py:65 msgid "Author Blog Articles" msgstr "Author Blog Articles" -#: cms_plugins.py:46 templates/djangocms_blog/plugins/tags.html:4 +#: cms_plugins.py:78 templates/djangocms_blog/plugins/tags.html:3 msgid "Tags" msgstr "Tags" -#: cms_plugins.py:57 templates/djangocms_blog/plugins/categories.html:4 +#: cms_plugins.py:94 templates/djangocms_blog/plugins/categories.html:3 msgid "Categories" msgstr "Categories" -#: cms_plugins.py:68 templates/djangocms_blog/post_list.html:12 -#: templates/djangocms_blog/plugins/archive.html:4 +#: cms_plugins.py:109 templates/djangocms_blog/post_list.html:12 +#: templates/djangocms_blog/plugins/archive.html:3 msgid "Archive" msgstr "Archive" -#: cms_toolbar.py:20 +#: cms_toolbar.py:22 msgid "Post list" msgstr "Post list" -#: cms_toolbar.py:22 +#: cms_toolbar.py:24 msgid "Add post" msgstr "Add post" -#: cms_toolbar.py:26 +#: cms_toolbar.py:28 +msgid "Edit configuration" +msgstr "" + +#: cms_toolbar.py:32 msgid "Edit Post" msgstr "" -#: feeds.py:16 +#: feeds.py:24 #, python-format msgid "Blog articles on %(site_name)s" msgstr "Blog articles on %(site_name)s" -#: models.py:31 +#: menu.py:14 +#, fuzzy +msgid "Blog Category menu" +msgstr "blog category" + +#: models.py:34 msgid "parent" msgstr "parent" -#: models.py:33 +#: models.py:35 msgid "created at" msgstr "created at" -#: models.py:34 +#: models.py:36 msgid "modified at" msgstr "modified at" -#: models.py:37 +#: models.py:37 models.py:120 models.py:253 +msgid "app. config" +msgstr "" + +#: models.py:40 msgid "name" msgstr "name" -#: models.py:38 models.py:105 +#: models.py:41 models.py:124 msgid "slug" msgstr "slug" -#: models.py:45 +#: models.py:48 msgid "blog category" msgstr "blog category" -#: models.py:46 +#: models.py:49 msgid "blog categories" msgstr "blog categories" -#: models.py:70 -msgid "Author" +#: models.py:89 +#, fuzzy +msgid "author" msgstr "Author" -#: models.py:75 -msgid "Published Since" +#: models.py:92 +#, fuzzy +msgid "created" +msgstr "created at" + +#: models.py:93 +#, fuzzy +msgid "last modified" +msgstr "modified at" + +#: models.py:94 +#, fuzzy +msgid "published since" msgstr "Published Since" -#: models.py:77 -msgid "Published Until" +#: models.py:96 +#, fuzzy +msgid "published until" msgstr "Published Until" -#: models.py:79 -msgid "Publish" +#: models.py:98 +#, fuzzy +msgid "publish" msgstr "Publish" -#: models.py:80 +#: models.py:99 msgid "category" msgstr "category" -#: models.py:82 -msgid "Main image" +#: models.py:101 +#, fuzzy +msgid "main image" msgstr "Main image" -#: models.py:85 -msgid "Main image thumbnail" +#: models.py:105 +#, fuzzy +msgid "main image thumbnail" msgstr "Main image thumbnail" -#: models.py:89 -msgid "Main image full" +#: models.py:110 +#, fuzzy +msgid "main image full" msgstr "Main image full" -#: models.py:93 -msgid "Enable comments on post" +#: models.py:114 +msgid "enable comments on post" msgstr "" -#: models.py:96 +#: models.py:116 msgid "Site(s)" msgstr "" -#: models.py:98 +#: models.py:117 msgid "" "Select sites in which to show the post. If none is set it will be visible in " "all the configured sites." msgstr "" -#: models.py:104 -msgid "Title" +#: models.py:123 +#, fuzzy +msgid "title" msgstr "Title" -#: models.py:106 -msgid "Abstract" +#: models.py:125 +msgid "abstract" msgstr "" -#: models.py:107 -msgid "Post meta description" -msgstr "Post meta description" - -#: models.py:109 +#: models.py:126 #, fuzzy -msgid "Post meta keywords" +msgid "post meta description" msgstr "Post meta description" -#: models.py:111 +#: models.py:128 #, fuzzy -msgid "Post meta title" +msgid "post meta keywords" msgstr "Post meta description" -#: models.py:112 +#: models.py:130 +#, fuzzy +msgid "post meta title" +msgstr "Post meta description" + +#: models.py:131 msgid "used in title tag and social sharing" msgstr "" -#: models.py:115 -msgid "Text" -msgstr "Text" +#: models.py:134 +msgid "text" +msgstr "" -#: models.py:178 +#: models.py:170 msgid "blog article" msgstr "blog article" -#: models.py:179 +#: models.py:171 msgid "blog articles" msgstr "blog articles" -#: models.py:222 models.py:250 -msgid "Articles" -msgstr "Articles" +#: models.py:269 +msgid "generic blog plugin" +msgstr "" -#: models.py:223 +#: models.py:273 models.py:306 +#, fuzzy +msgid "articles" +msgstr "0 articles" + +#: models.py:274 msgid "The number of latests articles to be displayed." msgstr "The number of latests articles to be displayed." -#: models.py:225 +#: models.py:276 +msgid "filter by tag" +msgstr "" + +#: models.py:277 msgid "Show only the blog articles tagged with chosen tags." msgstr "Show only the blog articles tagged with chosen tags." -#: models.py:227 +#: models.py:280 +#, fuzzy +msgid "filter by category" +msgstr "blog category" + +#: models.py:281 msgid "Show only the blog articles tagged with chosen categories." msgstr "Show only the blog articles tagged with chosen categories." -#: models.py:246 templates/djangocms_blog/plugins/authors.html:3 -msgid "Authors" +#: models.py:285 +#, python-format +msgid "%s latest articles by tag" +msgstr "" + +#: models.py:302 +#, fuzzy +msgid "authors" msgstr "Authors" -#: models.py:251 +#: models.py:307 msgid "The number of author articles to be displayed." msgstr "The number of author articles to be displayed." -#: templates/djangocms_blog/post_detail.html:18 -#: templates/djangocms_blog/includes/blog_item.html:11 -msgid "by" -msgstr "by" +#: models.py:311 +#, python-format +msgid "%s latest articles by author" +msgstr "" + +#: settings.py:11 +#, fuzzy +msgid "Article" +msgstr "Articles" + +#: settings.py:12 +msgid "Website" +msgstr "" + +#: settings.py:17 +msgid "Full date" +msgstr "" + +#: settings.py:18 +msgid "Year / Month" +msgstr "" + +#: settings.py:19 templates/djangocms_blog/post_list.html:14 +msgid "Category" +msgstr "Category" + +#: settings.py:20 +#, fuzzy +msgid "Just slug" +msgstr "slug" #: templates/djangocms_blog/post_list.html:11 msgid "Articles by" @@ -209,16 +381,11 @@ msgstr "Articles by" msgid "Tag" msgstr "Tag" -#: templates/djangocms_blog/post_list.html:14 -msgid "Category" -msgstr "Category" - #: templates/djangocms_blog/post_list.html:21 -#: templates/djangocms_blog/plugins/archive.html:27 +#: templates/djangocms_blog/plugins/archive.html:26 #: templates/djangocms_blog/plugins/authors.html:15 -#: templates/djangocms_blog/plugins/categories.html:16 #: templates/djangocms_blog/plugins/latest_entries.html:7 -#: templates/djangocms_blog/plugins/tags.html:16 +#: templates/djangocms_blog/plugins/tags.html:15 msgid "No article found." msgstr "No article found." @@ -226,43 +393,59 @@ msgstr "No article found." msgid "Back" msgstr "Back" -#: templates/djangocms_blog/post_list.html:30 +#: templates/djangocms_blog/post_list.html:29 msgid "previous" msgstr "previous" -#: templates/djangocms_blog/post_list.html:33 +#: templates/djangocms_blog/post_list.html:32 msgid "Page" msgstr "Page" -#: templates/djangocms_blog/post_list.html:33 +#: templates/djangocms_blog/post_list.html:32 msgid "of" msgstr "of" -#: templates/djangocms_blog/post_list.html:36 +#: templates/djangocms_blog/post_list.html:35 msgid "next" msgstr "next" -#: templates/djangocms_blog/includes/blog_item.html:46 +#: templates/djangocms_blog/includes/blog_item.html:24 msgid "read more" msgstr "read more" -#: templates/djangocms_blog/plugins/archive.html:18 +#: templates/djangocms_blog/includes/blog_meta.html:6 +msgid "by" +msgstr "by" + +#: templates/djangocms_blog/plugins/archive.html:17 #: templates/djangocms_blog/plugins/authors.html:10 -#: templates/djangocms_blog/plugins/categories.html:11 -#: templates/djangocms_blog/plugins/tags.html:11 +#: templates/djangocms_blog/plugins/categories.html:10 +#: templates/djangocms_blog/plugins/tags.html:10 #, python-format msgid "1 article" msgid_plural "%(articles)s articles" msgstr[0] "1 article" msgstr[1] "%(articles)s articles" -#: templates/djangocms_blog/plugins/archive.html:19 +#: templates/djangocms_blog/plugins/archive.html:18 #: templates/djangocms_blog/plugins/authors.html:11 -#: templates/djangocms_blog/plugins/categories.html:12 -#: templates/djangocms_blog/plugins/tags.html:12 +#: templates/djangocms_blog/plugins/categories.html:11 +#: templates/djangocms_blog/plugins/tags.html:11 msgid "0 articles" msgstr "0 articles" +#: templates/djangocms_blog/plugins/authors.html:3 +msgid "Authors" +msgstr "Authors" + +#: templates/djangocms_blog/plugins/categories.html:15 +#, fuzzy +msgid "No categories found." +msgstr "No article found." + +#~ msgid "Text" +#~ msgstr "Text" + #~ msgid "blog post" #~ msgstr "blog post" diff --git a/djangocms_blog/migrations/0010_auto_20150923_1151.py b/djangocms_blog/migrations/0010_auto_20150923_1151.py index 6458e8f..37a831e 100644 --- a/djangocms_blog/migrations/0010_auto_20150923_1151.py +++ b/djangocms_blog/migrations/0010_auto_20150923_1151.py @@ -5,7 +5,7 @@ import aldryn_apphooks_config.fields import app_data.fields import djangocms_text_ckeditor.fields from cms.models import Page -from cms.utils.conf import get_languages +from cms.utils.i18n import get_language_list from django.db import models, migrations diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index df81d1b..ae98aea 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -91,9 +91,9 @@ class Post(ModelMeta, TranslatableModel): date_created = models.DateTimeField(_('created'), auto_now_add=True) date_modified = models.DateTimeField(_('last modified'), auto_now=True) - date_published = models.DateTimeField(_('published Since'), + date_published = models.DateTimeField(_('published since'), default=timezone.now) - date_published_end = models.DateTimeField(_('published Until'), null=True, + date_published_end = models.DateTimeField(_('published until'), null=True, blank=True) publish = models.BooleanField(_('publish'), default=False) categories = models.ManyToManyField('djangocms_blog.BlogCategory', verbose_name=_('category'), From 12e7af2a0be2e5e5f8c8373254c777088a5f155d Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Wed, 30 Sep 2015 10:03:02 +0200 Subject: [PATCH 26/48] Code style --- djangocms_blog/cms_toolbar.py | 2 +- djangocms_blog/models.py | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/djangocms_blog/cms_toolbar.py b/djangocms_blog/cms_toolbar.py index 505f0e0..0cbddc6 100644 --- a/djangocms_blog/cms_toolbar.py +++ b/djangocms_blog/cms_toolbar.py @@ -4,7 +4,7 @@ from __future__ import absolute_import, print_function, unicode_literals from cms.toolbar_base import CMSToolbar from cms.toolbar_pool import toolbar_pool from django.core.urlresolvers import reverse -from django.utils.translation import ugettext_lazy as _, override +from django.utils.translation import override, ugettext_lazy as _ from .models import BLOG_CURRENT_NAMESPACE, BLOG_CURRENT_POST_IDENTIFIER diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index ae98aea..2a4a486 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -34,7 +34,9 @@ class BlogCategory(TranslatableModel): parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True) date_created = models.DateTimeField(_('created at'), auto_now_add=True) date_modified = models.DateTimeField(_('modified at'), auto_now=True) - app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config')) + app_config = AppHookConfigField( + BlogConfig, null=True, verbose_name=_('app. config') + ) translations = TranslatedFields( name=models.CharField(_('name'), max_length=255), @@ -117,7 +119,9 @@ class Post(ModelMeta, TranslatableModel): help_text=_('Select sites in which to show the post. ' 'If none is set it will be ' 'visible in all the configured sites.')) - app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config')) + app_config = AppHookConfigField( + BlogConfig, null=True, verbose_name=_('app. config') + ) translations = TranslatedFields( title=models.CharField(_('title'), max_length=255), @@ -250,7 +254,9 @@ class Post(ModelMeta, TranslatableModel): @python_2_unicode_compatible class BasePostPlugin(CMSPlugin): - app_config = AppHookConfigField(BlogConfig, null=True, verbose_name=_('app. config'), blank=True) + app_config = AppHookConfigField( + BlogConfig, null=True, verbose_name=_('app. config'), blank=True + ) class Meta: abstract = True From 0e8aba7bce1c1174546ce97d157be688035cab98 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 07:23:05 +0200 Subject: [PATCH 27/48] Rename menu label --- djangocms_blog/menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index 319aa7a..a36730f 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -11,7 +11,7 @@ from .models import BlogCategory, Post class BlogCategoryMenu(CMSAttachMenu): - name = _('Blog Category menu') + name = _('Blog menu') def get_nodes(self, request): nodes = [] From d50c4348334f63313d8883f3c16f2eae622e59db Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 08:15:37 +0200 Subject: [PATCH 28/48] Update readme [ci skip] --- README.rst | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 42db8ee..98154f5 100644 --- a/README.rst +++ b/README.rst @@ -94,7 +94,7 @@ Quick hint The following are minimal defaults to get the blog running; they may not be suited for your deployment. -* Add the following settings to your project:: +* Add the following settings to your project:: SOUTH_MIGRATION_MODULES = { 'easy_thumbnails': 'easy_thumbnails.south_migrations', @@ -108,17 +108,17 @@ suited for your deployment. ) META_SITE_PROTOCOL = 'http' META_USE_SITES = True - + * If you are using Django 1.7+, be aware than ``filer`` < 0.9.10, ``cmsplugin_filer`` and ``django-cms`` < 3.1 currently requires you to setup ``MIGRATION_MODULES`` in settings:: - + MIGRATION_MODULES = { 'cms': 'cms.migrations_django', # only for django CMS 3.0 'menus': 'menus.migrations_django', # only for django CMS 3.0 'filer': 'filer.migrations_django', # only for django filer 0.9.9 and below 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django', } - + Please check `django CMS installation `_, `cmsplugin-filer README `_ @@ -138,9 +138,9 @@ suited for your deployment. url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), -* To start your blog you need to use `AppHooks from django CMS `_ +* To start your blog you need to use `AppHooks from django CMS `_ to add the blog to a django CMS page: - + * Create a new django CMS page * Go to Advanced settings and select Blog from the Application selector; * Eventually customise the Application instance name; @@ -150,12 +150,35 @@ suited for your deployment. * Add and edit blog by creating them in the admin or using the toolbar, and the use the `django CMS frontend editor `_ to edit the blog content: - + * Create a new blog entry in django admin backend or from the toolbar * Click on "view on site" button to view the post detail page * Edit the post via djangocms frontend by adding / editing plugins * Publish the blog post by flagging the "Publish" switch in the blog post admin +Configurable permalinks ++++++++++++++++++++++++ + +Blog comes with four different styles of permalinks styles: + +* Full date: ``YYYY/MM/DD/SLUG`` +* Year / Month: ``YYYY/MM/SLUG`` +* Category: ``CATEGORY/SLUG`` +* Just slug: ``SLUG`` + +As all the styles are loaded in the urlconf, the latter two does not allow to have CMS pages +beneath the page the blog is attached to. If you want to do this, you have to override the default +urlconfs by setting somethik like the following in the project settings:: + + BLOG_PERMALINKS_URLS = { + 'full_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', + 'short_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\w[-\w]*)/$', + 'category': r'^post/(?P\w[-\w]*)/(?P\w[-\w]*)/$', + 'slug': r'^post/(?P\w[-\w]*)/$', + } + +And change ``post/`` with the desired prefix. + Templates +++++++++ From e50ba0a4ecb038f3698a19826612f8c150b3f297 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 08:56:15 +0200 Subject: [PATCH 29/48] Fix typo [ci skip] --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 98154f5..900427b 100644 --- a/README.rst +++ b/README.rst @@ -170,7 +170,7 @@ As all the styles are loaded in the urlconf, the latter two does not allow to ha beneath the page the blog is attached to. If you want to do this, you have to override the default urlconfs by setting somethik like the following in the project settings:: - BLOG_PERMALINKS_URLS = { + BLOG_PERMALINK_URLS = { 'full_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', 'short_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\w[-\w]*)/$', 'category': r'^post/(?P\w[-\w]*)/(?P\w[-\w]*)/$', From 9a3761a73ae7ff33e64fec079fcc7448dbd62533 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 23:16:10 +0200 Subject: [PATCH 30/48] Fix retrieval of categories when adding post --- djangocms_blog/forms.py | 5 +++-- tests/test_models.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/djangocms_blog/forms.py b/djangocms_blog/forms.py index f2d4543..c1be621 100644 --- a/djangocms_blog/forms.py +++ b/djangocms_blog/forms.py @@ -6,7 +6,7 @@ from django.conf import settings from parler.forms import TranslatableModelForm from taggit_autosuggest.widgets import TagAutoSuggest -from .models import BlogCategory, Post +from .models import BlogCategory, BlogConfig, Post class LatestEntriesForm(forms.ModelForm): @@ -35,7 +35,8 @@ class PostAdminForm(TranslatableModelForm): 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']: - qs = qs.namespace(kwargs['initial']['app_config']) + config = BlogConfig.objects.get(pk=kwargs['initial']['app_config']) + qs = qs.namespace(config.namespace) if 'categories' in self.fields: self.fields['categories'].queryset = qs diff --git a/tests/test_models.py b/tests/test_models.py index 9613bf1..5534e1a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -128,6 +128,14 @@ class AdminTest(BaseTest): fsets = post_admin.get_fieldsets(request) self.assertTrue('author' in fsets[1][1]['fields'][0]) + with self.login_user_context(self.user): + request = self.get_request('/', 'en', user=self.user, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk) + msg_mid = MessageMiddleware() + msg_mid.process_request(request) + post_admin = admin.site._registry[Post] + response = post_admin.add_view(request) + self.assertContains(response, '') + def test_admin_auto_author(self): pages = self.get_pages() data = deepcopy(self._post_data[0]['en']) From 5bf1aaf05304ae1b445c2264c4e475be364633ae Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 23:59:23 +0200 Subject: [PATCH 31/48] Fix tests --- tests/test_models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_models.py b/tests/test_models.py index 5534e1a..f95b619 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -134,7 +134,9 @@ class AdminTest(BaseTest): msg_mid.process_request(request) post_admin = admin.site._registry[Post] response = post_admin.add_view(request) - self.assertContains(response, '') + self.assertContains(response, '' % ( + self.category_1.pk, self.category_1.safe_translation_getter('name', language_code='en') + )) def test_admin_auto_author(self): pages = self.get_pages() From 4197532cd3035dc5df292a1493c5536a2ebd16b7 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Thu, 1 Oct 2015 23:59:46 +0200 Subject: [PATCH 32/48] Add codecov --- .travis.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c4c516f..011e49d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,7 +36,11 @@ install: # command to run tests, e.g. python setup.py test script: COMMAND='coverage run' tox -e$TOXENV -after_success: coveralls +before_install: + - pip install codecov +after_success: + - codecov + - coveralls matrix: exclude: From 8b97c9256e4626bd495f14f0d4a080ef12dc4cae Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 11:04:17 +0200 Subject: [PATCH 33/48] Bump development version --- djangocms_blog/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py index 7472c18..11030d9 100644 --- a/djangocms_blog/__init__.py +++ b/djangocms_blog/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- __author__ = 'Iacopo Spalletti' __email__ = 'i.spalletti@nephila.it' -__version__ = '0.5.1.dev1' +__version__ = '0.5.1.dev2' From c5e0227565b9fad2e1997817ad4520d8c1366a11 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 15:37:22 +0200 Subject: [PATCH 34/48] Add support for configurable menu structure --- djangocms_blog/cms_appconfig.py | 34 +++++++++++----- djangocms_blog/menu.py | 72 +++++++++++++++++++++------------ tests/test_menu.py | 69 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 35 deletions(-) diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py index c23c4d3..5c0927b 100644 --- a/djangocms_blog/cms_appconfig.py +++ b/djangocms_blog/cms_appconfig.py @@ -9,7 +9,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from parler.models import TranslatableModel, TranslatedFields -from .settings import get_setting +from .settings import MENU_TYPE_COMPLETE, get_setting class BlogConfig(TranslatableModel, AppHookConfig): @@ -54,41 +54,57 @@ class BlogConfigForm(AppDataForm): label=_('Template prefix'), required=False, initial='', help_text=_('Alternative directory to load the blog templates from') ) + menu_structure = forms.ChoiceField( + label=_('Menu structure'), required=True, + choices=get_setting('MENU_TYPES'), initial=MENU_TYPE_COMPLETE, + help_text=_('Structure of the django CMS menu') + ) object_type = forms.ChoiceField( label=_('Object type'), required=False, choices=get_setting('TYPES'), initial=get_setting('TYPE') ) og_type = forms.ChoiceField( label=_('Facebook type'), required=False, - choices=get_setting('FB_TYPES'), initial=get_setting('FB_TYPES')[0][0] + choices=get_setting('FB_TYPES'), initial=get_setting('FB_TYPE') ) og_app_id = forms.CharField( max_length=200, label=_('Facebook application ID'), required=False, + initial=get_setting('FB_PROFILE_ID') ) og_profile_id = forms.CharField( max_length=200, label=_('Facebook profile ID'), required=False, + initial=get_setting('FB_PROFILE_ID') ) og_publisher = forms.CharField( - max_length=200, label=_('Facebook page URL'), required=False + max_length=200, label=_('Facebook page URL'), required=False, + initial=get_setting('FB_PUBLISHER') ) og_author_url = forms.CharField( - max_length=200, label=_('Facebook author URL'), required=False + max_length=200, label=_('Facebook author URL'), required=False, + initial=get_setting('FB_AUTHOR_URL') + ) + og_author = forms.CharField( + max_length=200, label=_('Facebook author'), required=False, + initial=get_setting('FB_AUTHOR') ) twitter_type = forms.ChoiceField( label=_('Twitter type'), required=False, - choices=get_setting('TWITTER_TYPES'), initial=get_setting('TWITTER_TYPES')[0][0] + choices=get_setting('TWITTER_TYPES'), initial=get_setting('TWITTER_TYPE') ) twitter_site = forms.CharField( - max_length=200, label=_('Twitter site handle'), required=False + max_length=200, label=_('Twitter site handle'), required=False, + initial=get_setting('TWITTER_SITE') ) twitter_author = forms.CharField( - max_length=200, label=_('Twitter author handle'), required=False + max_length=200, label=_('Twitter author handle'), required=False, + initial=get_setting('TWITTER_AUTHOR') ) gplus_type = forms.ChoiceField( label=_('Google+ type'), required=False, - choices=get_setting('GPLUS_TYPES'), initial=get_setting('GPLUS_TYPES')[0][0] + choices=get_setting('GPLUS_TYPES'), initial=get_setting('GPLUS_TYPE') ) gplus_author = forms.CharField( - max_length=200, label=_('Google+ author name'), required=False + max_length=200, label=_('Google+ author name'), required=False, + initial=get_setting('GPLUS_AUTHOR') ) setup_config(BlogConfigForm, BlogConfig) diff --git a/djangocms_blog/menu.py b/djangocms_blog/menu.py index a36730f..6b4e883 100644 --- a/djangocms_blog/menu.py +++ b/djangocms_blog/menu.py @@ -7,7 +7,9 @@ from django.utils.translation import get_language_from_request, ugettext_lazy as from menus.base import NavigationNode from menus.menu_pool import menu_pool +from .cms_appconfig import BlogConfig from .models import BlogCategory, Post +from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS class BlogCategoryMenu(CMSAttachMenu): @@ -18,34 +20,52 @@ class BlogCategoryMenu(CMSAttachMenu): language = get_language_from_request(request, check_path=True) - categories = BlogCategory.objects + categories_menu = False + posts_menu = False + config = False if hasattr(self, 'instance') and self.instance: - categories = categories.namespace(self.instance.application_namespace) - categories = categories.active_translations(language).distinct() - categories = categories.order_by('parent__id', 'translations__name') - for category in categories: - node = NavigationNode( - category.name, - category.get_absolute_url(), - '%s-%s' % (category.__class__.__name__, category.pk), - ('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent - else None) - ) - nodes.append(node) + config = BlogConfig.objects.get(namespace=self.instance.application_namespace) + if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES): + categories_menu = True + if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS): + posts_menu = True - posts = Post.objects - if hasattr(self, 'instance') and self.instance: - posts = posts.namespace(self.instance.application_namespace) - posts = posts.active_translations(language).distinct() - for post in posts: - category = post.categories.first() - node = NavigationNode( - post.get_title(), - post.get_absolute_url(language), - '%s-%s' % (post.__class__.__name__, category.pk), - '%s-%s' % (category.__class__.__name__, category.pk) - ) - nodes.append(node) + if categories_menu: + categories = BlogCategory.objects + if config: + categories = categories.namespace(self.instance.application_namespace) + categories = categories.active_translations(language).distinct() + categories = categories.order_by('parent__id', 'translations__name') + for category in categories: + node = NavigationNode( + category.name, + category.get_absolute_url(), + '%s-%s' % (category.__class__.__name__, category.pk), + ('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent + else None) + ) + nodes.append(node) + + if posts_menu: + posts = Post.objects + if hasattr(self, 'instance') and self.instance: + posts = posts.namespace(self.instance.application_namespace) + posts = posts.active_translations(language).distinct() + for post in posts: + if categories_menu: + category = post.categories.first() + parent = '%s-%s' % (category.__class__.__name__, category.pk) + post_id = '%s-%s' % (post.__class__.__name__, post.pk), + else: + parent = None + post_id = '%s-%s' % (post.__class__.__name__, post.pk), + node = NavigationNode( + post.get_title(), + post.get_absolute_url(language), + post_id, + parent + ) + nodes.append(node) return nodes diff --git a/tests/test_menu.py b/tests/test_menu.py index 8830468..62275b2 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -2,11 +2,15 @@ from __future__ import absolute_import, print_function, unicode_literals from aldryn_apphooks_config.utils import get_app_instance +from django.core.cache import cache from django.utils.translation import activate from menus.menu_pool import menu_pool from parler.utils.context import smart_override, switch_language from djangocms_blog.views import CategoryEntriesView, PostDetailView +from djangocms_blog.settings import ( + MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_NONE, MENU_TYPE_POSTS, +) from . import BaseTest @@ -44,6 +48,71 @@ class MenuTest(BaseTest): cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)]) self.assertTrue(cats_url.issubset(nodes_url)) + def test_menu_options(self): + """ + Tests menu structure based on menu_structure configuration + """ + posts = self.get_posts() + self.get_pages() + + cats_url = {} + posts_url = {} + + languages = ('en', 'it') + + for lang in languages: + with smart_override(lang): + cats_url[lang] = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)]) + posts_url[lang] = set([post.get_absolute_url() for post in posts if post.has_translation(lang) and post.app_config == self.app_config_1]) + + # No item in the menu + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_NONE + self.app_config_1.save() + cache.clear() + for lang in languages: + request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang) + with smart_override(lang): + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + nodes_url = set([node.url for node in nodes]) + self.assertFalse(cats_url[lang].issubset(nodes_url)) + self.assertFalse(posts_url[lang].issubset(nodes_url)) + + # Only posts in the menu + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_POSTS + self.app_config_1.save() + cache.clear() + for lang in languages: + request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang) + with smart_override(lang): + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + nodes_url = set([node.url for node in nodes]) + self.assertFalse(cats_url[lang].issubset(nodes_url)) + self.assertTrue(posts_url[lang].issubset(nodes_url)) + + # Only categories in the menu + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES + self.app_config_1.save() + cache.clear() + for lang in languages: + request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang) + with smart_override(lang): + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + nodes_url = set([node.url for node in nodes]) + self.assertTrue(cats_url[lang].issubset(nodes_url)) + self.assertFalse(posts_url[lang].issubset(nodes_url)) + + # Both types in the menu + self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE + self.app_config_1.save() + cache.clear() + for lang in languages: + request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang) + with smart_override(lang): + nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu') + nodes_url = set([node.url for node in nodes]) + self.assertTrue(cats_url[lang].issubset(nodes_url)) + self.assertTrue(posts_url[lang].issubset(nodes_url)) + def test_modifier(self): """ Tests if correct category is selected in the menu From ccd27bdb99bcce2fb86949907fcf52afbb35c995 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 22 Aug 2015 22:35:30 +0200 Subject: [PATCH 35/48] Use types from django-meta-mixin --- README.rst | 63 ++++++++++++++++++++++++++++---------- djangocms_blog/admin.py | 3 +- djangocms_blog/models.py | 1 + djangocms_blog/settings.py | 34 +++++++++++--------- setup.py | 4 +-- 5 files changed, 70 insertions(+), 35 deletions(-) diff --git a/README.rst b/README.rst index 900427b..57b08d4 100644 --- a/README.rst +++ b/README.rst @@ -197,6 +197,7 @@ Features * Support for Twitter cards, Open Graph and Google+ snippets meta tags * Optional support for simpler TextField-based content editing * Multisite support (posts can be visible in one or more Django sites on the same project) +* Per-apphook configuration Import from Wordpress +++++++++++++++++++++ @@ -206,15 +207,8 @@ https://pypi.python.org/pypi/the-real-django-wordpress and this gist https://gist.github.com/yakky/11336204 as a base. -Settings --------- -* BLOG_ENABLE_COMMENTS: Whether to enable comments by default on posts; - while ``djangocms_blog`` does not ship any comment system, this flag can be used - to control the chosen comments framework; (default: True) -* BLOG_USE_PLACEHOLDER: Post content is managed via placeholder; if ``False`` a - simple HTMLField is used; (default: True) -* BLOG_USE_ABSTRACT: Use an abstract field for the post; if ``False`` no abstract field - is available for every post; (default: True) +Global Settings +--------------- * BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post lists; it's a dictionary with ``size``, ``crop`` and ``upscale`` keys; (default: ``{'size': '120x120', 'crop': True,'upscale': False}``) @@ -224,27 +218,62 @@ Settings * BLOG_PAGINATION: Number of post per page; (default: 10) * BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin; (default: 5) * BLOG_POSTS_LIST_TRUNCWORDS_COUNT: Default number of words shown for abstract in the post list; (default: 100) -* BLOG_MULTISITE: Add support for multisite setup -* BLOG_AUTHOR_DEFAULT: Use a default if not specified; if set to ``True`` the - current user is set as the default author, if set to ``False`` no default - author is set, if set to a string the user with the provided username is - used; (default: True) - -Social media tags settings -++++++++++++++++++++++++++ * BLOG_TYPE: Generic type for the post object; (default: Article) +* BLOG_TYPES: Choices of available blog types; (default: Article, Website) * BLOG_FB_TYPE: Open Graph type for the post object; (default: Article) +* BLOG_FB_TYPES: Choices of available blog types; (default: Article, Website) * BLOG_FB_APPID: Facebook Application ID * BLOG_FB_PROFILE_ID: Facebook profile ID of the post author * BLOG_FB_PUBLISHER: Facebook URL of the blog publisher * BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author * BLOG_FB_AUTHOR: Facebook profile URL of the post author * BLOG_TWITTER_TYPE: Twitter Card type for the post object; (default: Summary) +* BLOG_TWITTER_TYPES: Choices of available blog types for twitter; (default: Article, Website) * BLOG_TWITTER_SITE: Twitter account of the site * BLOG_TWITTER_AUTHOR: Twitter account of the post author * BLOG_GPLUS_TYPE: Google+ Snippet type for the post object; (default: Blog) +* BLOG_GPLUS_TYPES: Choices of available blog types for twitter; (default: Article, Website) * BLOG_GPLUS_AUTHOR: Google+ account of the post author +* BLOG_ENABLE_COMMENTS: Whether to enable comments by default on posts; + while ``djangocms_blog`` does not ship any comment system, this flag can be used + to control the chosen comments framework; (default: True) +* BLOG_USE_ABSTRACT: Use an abstract field for the post; if ``False`` no abstract field + is available for every post; (default: True) +* BLOG_USE_PLACEHOLDER: Post content is managed via placeholder; if ``False`` a + simple HTMLField is used; (default: True) +* BLOG_MULTISITE: Add support for multisite setup +* BLOG_MENU_TYPE: Structure of the Blog menu; (default: Posts and Categories) +* BLOG_AUTHOR_DEFAULT: Use a default if not specified; if set to ``True`` the + current user is set as the default author, if set to ``False`` no default + author is set, if set to a string the user with the provided username is + used; (default: True) +* BLOG_DEFAULT_PUBLISHED: If posts are marked as published by default; (default: False) +* BLOG_AVAILABLE_PERMALINK_STYLES: Choices of permalinks styles; +* BLOG_PERMALINK_URLS: URLConf corresponding to BLOG_AVAILABLE_PERMALINK_STYLES; +Per-Apphook settings +-------------------- + +* default_published: Per-apphook setting for BLOG_DEFAULT_PUBLISHED; +* Permalink structure: Per-apphook setting for BLOG_AVAILABLE_PERMALINK_STYLES; +* Use placeholder and plugins for article body: Per-apphook setting for BLOG_USE_PLACEHOLDER; +* Use abstract field: Per-apphook setting for BLOG_USE_ABSTRACT; +* Set author: Per-apphook setting for BLOG_AUTHOR_DEFAULT; +* Paginate sizePer-apphook setting for BLOG_PAGINATION; +* Template prefix: Alternative directory to load the blog templates from; +* Menu structure: Per-apphook setting for BLOG_MENU_TYPE +* Object type:Per-apphook setting for BLOG_TYPE +* Facebook type: Per-apphook setting for BLOG_FB_TYPE +* Facebook application ID: Per-apphook setting for BLOG_FB_APP_ID +* Facebook profile ID: Per-apphook setting for BLOG_FB_PROFILE_ID +* Facebook page URL: Per-apphook setting for BLOG_FB_PUBLISHER +* Facebook author URL: Per-apphook setting for BLOG_AUTHOR_URL +* Facebook author: Per-apphook setting for BLOG_AUTHOR +* Twitter type: Per-apphook setting for BLOG_TWITTER_TYPE +* Twitter site handle: Per-apphook setting for BLOG_TWITTER_SITE +* Twitter author handle: Per-apphook setting for BLOG_TWITTER_AUTHOR +* Google+ type: Per-apphook setting for BLOG_GPLUS_TYPE +* Google+ author name: Per-apphook setting for BLOG_GPLUS_AUTHOR Known djangocms-blog websites +++++++++++++++++++++++++++++ diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index b21f256..d47c8f6 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -144,6 +144,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): ('Layout', { 'fields': ( 'config.paginate_by', 'config.url_patterns', 'config.template_prefix', + 'config.menu_structure', ), 'classes': ('collapse',) }), @@ -155,7 +156,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): ('Open Graph', { 'fields': ( 'config.og_type', 'config.og_app_id', 'config.og_profile_id', - 'config.og_publisher', 'config.og_author_url', + 'config.og_publisher', 'config.og_author_url', 'config.og_author', ) }), ('Twitter', { diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 2a4a486..418e39b 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -158,6 +158,7 @@ class Post(ModelMeta, TranslatableModel): 'og_profile_id': 'get_meta_attribute', 'og_publisher': 'get_meta_attribute', 'og_author_url': 'get_meta_attribute', + 'og_author': 'get_meta_attribute', 'twitter_type': 'get_meta_attribute', 'twitter_site': 'get_meta_attribute', 'twitter_author': 'get_meta_attribute', diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index d483b83..0bb3e37 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -1,18 +1,17 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +MENU_TYPE_COMPLETE = 'complete' +MENU_TYPE_CATEGORIES = 'categories' +MENU_TYPE_POSTS = 'posts' +MENU_TYPE_NONE = 'none' + def get_setting(name): from django.conf import settings from django.utils.translation import ugettext_lazy as _ from meta_mixin import settings as meta_settings - OBJECT_TYPES = ( - ('Article', _('Article')), - ('Website', _('Website')), - ) - BLOG_TYPES = getattr(settings, 'BLOG_TYPES', OBJECT_TYPES) - PERMALINKS = ( ('full_date', _('Full date')), ('short_date', _('Year / Month')), @@ -25,7 +24,12 @@ def get_setting(name): 'category': r'^(?P\w[-\w]*)/(?P\w[-\w]*)/$', 'slug': r'^(?P\w[-\w]*)/$', } - + MENU_TYPES = ( + (MENU_TYPE_COMPLETE, _('Categories and posts')), + (MENU_TYPE_CATEGORIES, _('Categories only')), + (MENU_TYPE_POSTS, _('Posts only')), + (MENU_TYPE_NONE, _('None')), + ) default = { 'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', { 'size': '120x120', @@ -39,28 +43,28 @@ def get_setting(name): 'upscale': False }), - 'BLOG_TAGCLOUD_MIN': getattr(settings, 'BLOG_TAGCLOUD_MIN', 1), - 'BLOG_TAGCLOUD_MAX': getattr(settings, 'BLOG_TAGCLOUD_MAX', 10), 'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10), 'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5), 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr( settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100 ), + 'BLOG_MENU_TYPE': MENU_TYPES, + 'BLOG_MENU_TYPES': MENU_TYPES, 'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'), - 'BLOG_TYPES': BLOG_TYPES, + 'BLOG_TYPES': meta_settings.OBJECT_TYPES, 'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'), - 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', BLOG_TYPES), + 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', meta_settings.FB_TYPES), 'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID), 'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID), 'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER), 'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'), 'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'), - 'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'), - 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', BLOG_TYPES), + 'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'summary'), + 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', meta_settings.TWITTER_TYPES), 'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE), 'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'), - 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'), - 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', BLOG_TYPES), + 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_TYPE', 'Blog'), + 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', meta_settings.GPLUS_TYPES), 'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'), 'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True), 'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True), diff --git a/setup.py b/setup.py index 0623fcb..5c3a112 100755 --- a/setup.py +++ b/setup.py @@ -47,8 +47,8 @@ setup( 'djangocms-text-ckeditor', 'cmsplugin-filer', 'django-meta>=0.2', - 'django-meta-mixin>=0.1.1', - 'aldryn-apphooks-config', + 'django-meta-mixin>=0.2.1', + 'aldryn-apphooks-config>=0.2.6', ], license='BSD', zip_safe=False, From df987d85e3af4a5a1f97128d90852ef558a8cd35 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 15:55:54 +0200 Subject: [PATCH 36/48] Update badges --- README.rst | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/README.rst b/README.rst index 57b08d4..a0c7348 100644 --- a/README.rst +++ b/README.rst @@ -2,28 +2,34 @@ djangocms-blog ============== +.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg?style=flat-square + :target: https://pypi.python.org/pypi/djangocms-blog + :alt: Latest PyPI version -.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg - :target: https://pypi.python.org/pypi/djangocms-blog - :alt: Latest PyPI version +.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg?style=flat-square + :target: https://pypi.python.org/pypi/djangocms-blog + :alt: Monthly downloads -.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg - :target: https://travis-ci.org/nephila/djangocms-blog - :alt: Latest Travis CI build status +.. image:: https://img.shields.io/pypi/pyversions/djangocms-blog.svg?style=flat-square + :target: https://pypi.python.org/pypi/djangocms-blog + :alt: Python versions -.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg - :target: https://pypi.python.org/pypi/djangocms-blog - :alt: Monthly downloads +.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg?style=flat-square + :target: https://travis-ci.org/nephila/djangocms-blog + :alt: Latest Travis CI build status -.. image:: https://coveralls.io/repos/nephila/djangocms-blog/badge.png - :target: https://coveralls.io/r/nephila/djangocms-blog - :alt: Test coverage +.. image:: https://img.shields.io/coveralls/nephila/djangocms-blog/master.svg?style=flat-square + :target: https://coveralls.io/r/nephila/djangocms-blog?branch=master + :alt: Test coverage -.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg +.. image:: https://img.shields.io/codecov/c/github/nephila/djangocms-blog/master.svg?style=flat-square + :target: https://codecov.io/github/nephila/djangocms-blog + :alt: Test coverage + +.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg?style=flat-square :target: https://codeclimate.com/github/nephila/djangocms-blog :alt: Code Climate - A djangoCMS 3 blog application. Supported Django versions: From b4db62f0aa16bcaf6121c0905da76ed82a003909 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 16:23:15 +0200 Subject: [PATCH 37/48] Fix isort --- tests/test_menu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_menu.py b/tests/test_menu.py index 62275b2..b98c02e 100644 --- a/tests/test_menu.py +++ b/tests/test_menu.py @@ -7,10 +7,10 @@ from django.utils.translation import activate from menus.menu_pool import menu_pool from parler.utils.context import smart_override, switch_language -from djangocms_blog.views import CategoryEntriesView, PostDetailView from djangocms_blog.settings import ( MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_NONE, MENU_TYPE_POSTS, ) +from djangocms_blog.views import CategoryEntriesView, PostDetailView from . import BaseTest From 49a76302c2fd09df75d7f9b4e44ecf4bb488ef39 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 17:05:19 +0200 Subject: [PATCH 38/48] Bump development version --- djangocms_blog/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py index 11030d9..341f071 100644 --- a/djangocms_blog/__init__.py +++ b/djangocms_blog/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- __author__ = 'Iacopo Spalletti' __email__ = 'i.spalletti@nephila.it' -__version__ = '0.5.1.dev2' +__version__ = '0.6.0.dev1' From aa84f8b63a030df4e451ad9d58143a68d2239536 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sat, 17 Oct 2015 18:11:57 +0200 Subject: [PATCH 39/48] Add Python 3.5 --- .travis.yml | 36 ++++++++++++++++++++++++++++-------- setup.py | 1 + tox.ini | 2 +- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 011e49d..44158b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ language: python sudo: false python: + - 3.5 - 3.4 - 3.3 - 2.7 @@ -12,16 +13,16 @@ python: env: matrix: - - DJANGO='django16' CMS='cms30' - - DJANGO='django16' CMS='cms31' - - DJANGO='django16' CMS='cms32' - - DJANGO='django17' CMS='cms30' - - DJANGO='django17' CMS='cms31' - - DJANGO='django17' CMS='cms32' - - DJANGO='django18' CMS='cms31' - - DJANGO='django18' CMS='cms32' - TOXENV='pep8' - TOXENV='isort' + - DJANGO='django18' CMS='cms32' + - DJANGO='django18' CMS='cms31' + - DJANGO='django17' CMS='cms32' + - DJANGO='django17' CMS='cms31' + - DJANGO='django17' CMS='cms30' + - DJANGO='django16' CMS='cms32' + - DJANGO='django16' CMS='cms31' + - DJANGO='django16' CMS='cms30' # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors @@ -31,6 +32,7 @@ install: - "if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then export PYVER=py27; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then export PYVER=py33; fi" - "if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then export PYVER=py34; fi" + - "if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export PYVER=py35; fi" - "if [[ ${DJANGO}z != 'z' ]]; then export TOXENV=$PYVER-$DJANGO-$CMS; fi" # command to run tests, e.g. python setup.py test @@ -56,6 +58,10 @@ matrix: env: TOXENV='pep8' - python: 3.3 env: TOXENV='isort' + - python: 3.4 + env: TOXENV='pep8' + - python: 3.4 + env: TOXENV='isort' - python: 2.6 env: DJANGO='django17' CMS='cms30' - python: 2.6 @@ -66,6 +72,18 @@ matrix: env: DJANGO='django18' CMS='cms31' - python: 2.6 env: DJANGO='django18' CMS='cms32' + - python: 3.5 + env: DJANGO='django16' CMS='cms30' + - python: 3.5 + env: DJANGO='django16' CMS='cms31' + - python: 3.5 + env: DJANGO='django16' CMS='cms32' + - python: 3.5 + env: DJANGO='django17' CMS='cms30' + - python: 3.5 + env: DJANGO='django17' CMS='cms31' + - python: 3.5 + env: DJANGO='django17' CMS='cms32' allow_failures: - python: 2.6 @@ -88,3 +106,5 @@ matrix: env: DJANGO='django17' CMS='cms32' - python: 3.4 env: DJANGO='django18' CMS='cms32' + - python: 3.5 + env: DJANGO='django18' CMS='cms32' diff --git a/setup.py b/setup.py index 5c3a112..7ee4f3b 100755 --- a/setup.py +++ b/setup.py @@ -74,5 +74,6 @@ setup( 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', ], ) diff --git a/tox.ini b/tox.ini index 2887060..ba86678 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py{26}-django16-cms{30,31},py{27,33,34}-django{16,17}-cms{30,31,32},py{27,33,34}-django{18,19}-cms{31,32},pep8,isort +envlist = pep8,isort,py{35,34,33,27}-django{19,18}-cms{32,31},py{34,33,27}-django{17,16}-cms{32,31,30},py{26}-django16-cms{31,30} [testenv] commands = {env:COMMAND:python} cms_helper.py test djangocms_blog --no-migrate From 75eca9aad3688c78db408dcbf2126575539cefbb Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 09:59:19 +0200 Subject: [PATCH 40/48] Do not load blog urlconf in server mode --- djangocms_blog/apps.py | 10 ++++++++++ tests/test_utils/urls.py | 10 +++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 djangocms_blog/apps.py diff --git a/djangocms_blog/apps.py b/djangocms_blog/apps.py new file mode 100644 index 0000000..aa257da --- /dev/null +++ b/djangocms_blog/apps.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +from django.apps import AppConfig +from django.utils.translation import ugettext_lazy as _ + + +class PageMetaConfig(AppConfig): + name = 'djangocms_page_meta' + verbose_name = _('django CMS Page Meta') diff --git a/tests/test_utils/urls.py b/tests/test_utils/urls.py index ec11f56..b15d02a 100644 --- a/tests/test_utils/urls.py +++ b/tests/test_utils/urls.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals +import sys + from cms.utils.conf import get_cms_setting from django.conf import settings from django.conf.urls import include, patterns, url @@ -22,9 +24,15 @@ urlpatterns = patterns( urlpatterns += staticfiles_urlpatterns() +if 'server' not in sys.argv: + urlpatterns += i18n_patterns( + '', + url(r'^blog/', include( + 'djangocms_blog.urls', namespace='djangocms_blog', app_name='djangocms_blog' + )), + ) urlpatterns += i18n_patterns( '', url(r'^admin/', include(admin.site.urls)), - url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')), url(r'^', include('cms.urls')), ) From d6516c13c31c592ff83b11adbadd7e009ee1bc06 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 09:59:41 +0200 Subject: [PATCH 41/48] Add app_title to admin --- djangocms_blog/admin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index d47c8f6..1670c41 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -133,7 +133,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): def declared_fieldsets(self): return [ (None, { - 'fields': ('type', 'namespace',) + 'fields': ('type', 'namespace', 'app_title') }), ('Generic', { 'fields': ( From a811ef9a0e351b0bb48800f6a82129ce7017733e Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 10:00:12 +0200 Subject: [PATCH 42/48] Fallback to global setting pagination in view --- djangocms_blog/views.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py index b982e02..5bb995f 100644 --- a/djangocms_blog/views.py +++ b/djangocms_blog/views.py @@ -64,7 +64,10 @@ class PostListView(BaseBlogView, ListView): return context def get_paginate_by(self, queryset): - return self.config.paginate_by + if self.config.paginate_by: + return self.config.paginate_by + else: + return get_setting('PAGINATION') class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView): From 1133c5e48b69f3f8afb3329b931e2b77e93ba73a Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 10:49:45 +0200 Subject: [PATCH 43/48] Improve documentation --- README.rst | 248 +++++++++++++++++++++++++++++++++-------------------- 1 file changed, 156 insertions(+), 92 deletions(-) diff --git a/README.rst b/README.rst index a0c7348..564bfe1 100644 --- a/README.rst +++ b/README.rst @@ -52,6 +52,19 @@ Supported django CMS versions: on South anymore; please install it separately if using this application on Django 1.6. +Features +-------- + +* Placeholder content editing +* Frontend editing using django CMS 3.0 frontend editor +* Multilingual support using django-parler +* Support for Twitter cards, Open Graph and Google+ snippets meta tags +* Optional support for simpler TextField-based content editing +* Multisite support (posts can be visible in one or more Django sites on the + same project) +* Per-Apphook configuration +* Per-Apphook templates set +* Auto Apphook setup Quickstart ---------- @@ -91,6 +104,7 @@ Please, refer to each application documentation on details. * django-filer: http://django-filer.readthedocs.org * django-meta: https://github.com/nephila/django-meta#installation +* django-meta-mixin: https://github.com/nephila/django-meta-mixin#installation * django-parler: http://django-parler.readthedocs.org/en/latest/quickstart.html#configuration * django-taggit-autosuggest: https://bitbucket.org/fabian/django-taggit-autosuggest @@ -115,13 +129,14 @@ suited for your deployment. META_SITE_PROTOCOL = 'http' META_USE_SITES = True -* If you are using Django 1.7+, be aware than ``filer`` < 0.9.10, ``cmsplugin_filer`` - and ``django-cms`` < 3.1 currently requires you to setup ``MIGRATION_MODULES`` in settings:: +* If you are using Django 1.7+, be aware than ``filer`` < 0.9.10, + ``cmsplugin_filer`` and ``django-cms`` < 3.1 currently requires you to + setup ``MIGRATION_MODULES`` in settings:: MIGRATION_MODULES = { 'cms': 'cms.migrations_django', # only for django CMS 3.0 'menus': 'menus.migrations_django', # only for django CMS 3.0 - 'filer': 'filer.migrations_django', # only for django filer 0.9.9 and below + 'filer': 'filer.migrations_django', # only for django filer up to 0.9.9 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django', } @@ -145,10 +160,12 @@ suited for your deployment. url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), * To start your blog you need to use `AppHooks from django CMS `_ - to add the blog to a django CMS page: + to add the blog to a django CMS page; this step is not required when using + **Auto setup** (see below): * Create a new django CMS page - * Go to Advanced settings and select Blog from the Application selector; + * Go to Advanced settings and select Blog from the Application selector and + create an application configuration; * Eventually customise the Application instance name; * Publish the page * Restart the project instance to properly load blog urls. @@ -160,7 +177,8 @@ suited for your deployment. * Create a new blog entry in django admin backend or from the toolbar * Click on "view on site" button to view the post detail page * Edit the post via djangocms frontend by adding / editing plugins - * Publish the blog post by flagging the "Publish" switch in the blog post admin + * Publish the blog post by flagging the "Publish" switch in the blog post + admin Configurable permalinks +++++++++++++++++++++++ @@ -172,9 +190,10 @@ Blog comes with four different styles of permalinks styles: * Category: ``CATEGORY/SLUG`` * Just slug: ``SLUG`` -As all the styles are loaded in the urlconf, the latter two does not allow to have CMS pages -beneath the page the blog is attached to. If you want to do this, you have to override the default -urlconfs by setting somethik like the following in the project settings:: +As all the styles are loaded in the urlconf, the latter two does not allow +to have CMS pages beneath the page the blog is attached to. If you want to +do this, you have to override the default urlconfs by setting something +like the following in the project settings:: BLOG_PERMALINK_URLS = { 'full_date': r'^(?P\d{4})/(?P\d{1,2})/(?P\d{1,2})/(?P\w[-\w]*)/$', @@ -188,22 +207,133 @@ And change ``post/`` with the desired prefix. Templates +++++++++ -To ease the template customisations a ``djangocms_blog/base.html`` template is used by all the blog templates; -the templates itself extends a ``base.html`` template; content is pulled in the ``content`` block. -If you need to define a different base template, or if your base template does not defines a ``content`` block, -copy in your template directory ``djangocms_blog/base.html`` and customise it according to your -needs; the other application templates will use the newly created template and will ignore the bundled one. +To ease the template customisations a ``djangocms_blog/base.html`` template is +used by all the blog templates; the templates itself extends a ``base.html`` +template; content is pulled in the ``content`` block. +If you need to define a different base template, or if your base template does +not defines a ``content`` block, copy in your template directory +``djangocms_blog/base.html`` and customise it according to your needs; the +other application templates will use the newly created base template and +will ignore the bundled one. -Features --------- +Templates set ++++++++++++++ + +By using **Apphook configuration** you can define a different templates set. +To use this feature provide a directory name in **Template prefix** field in +the **Apphook configuration** admin (in *Layout* section): it will be the +root of your custom templates set. + +Auto setup +++++++++++ + +``djangocms_blog`` can install and configue itself if it does not find any +attached instance of itself. +This feature is enable by default and will create: + +* a ``BlogConfig`` with default values +* a ``Blog`` CMS page and will attach ``djangocms_blog`` instance to it +* a **home page** is no, home is found. + +All the items will be created in every language configured for the website +and the pages will be published. If not using **aldryn-apphook-reload** or +**django CMS 3.2** auto-reload middleware you are required to reload the +project instance after this. + + +The auto setup is execute once for each server start but it will skip any +action if a ``BlogConfig`` instance is found. + + +Global Settings +--------------- +* BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post + lists; it's a dictionary with ``size``, ``crop`` and ``upscale`` keys; + (default: ``{'size': '120x120', 'crop': True,'upscale': False}``) +* BLOG_IMAGE_FULL_SIZE: Size of the main image when shown on the post + detail; it's a dictionary with ``size``, ``crop`` and ``upscale`` keys; + (default: ``{'size': '640x120', 'crop': True,'upscale': False}``) +* BLOG_PAGINATION: Number of post per page; (default: ``10``) +* BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin; + (default: ``5'') +* BLOG_POSTS_LIST_TRUNCWORDS_COUNT: Default number of words shown for + abstract in the post list; (default: ``100``) +* BLOG_TYPE: Generic type for the post object; (default: ``Article``) +* BLOG_TYPES: Choices of available blog types; + (default: to ``META_OBJECT_TYPES`` defined in `django-meta-mixin settings`_) +* BLOG_FB_TYPE: Open Graph type for the post object; (default: ``Article``) +* BLOG_FB_TYPES: Choices of available blog types; + (default: to ``META_FB_TYPES`` defined in `django-meta-mixin settings`_) +* BLOG_FB_APPID: Facebook Application ID +* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author +* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher +* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author +* BLOG_FB_AUTHOR: Facebook profile URL of the post author +* BLOG_TWITTER_TYPE: Twitter Card type for the post object; + (default: ``Summary``) +* BLOG_TWITTER_TYPES: Choices of available blog types for twitter; + (default: to ``META_TWITTER_TYPES`` defined in `django-meta-mixin settings`_) +* BLOG_TWITTER_SITE: Twitter account of the site +* BLOG_TWITTER_AUTHOR: Twitter account of the post author +* BLOG_GPLUS_TYPE: Google+ Snippet type for the post object; + (default: ``Blog``) +* BLOG_GPLUS_TYPES: Choices of available blog types for twitter; + (default: to ``META_GPLUS_TYPES`` defined in `django-meta-mixin settings`_) +* BLOG_GPLUS_AUTHOR: Google+ account of the post author +* BLOG_ENABLE_COMMENTS: Whether to enable comments by default on posts; + while ``djangocms_blog`` does not ship any comment system, this flag + can be used to control the chosen comments framework; (default: ``True``) +* BLOG_USE_ABSTRACT: Use an abstract field for the post; if ``False`` + no abstract field is available for every post; (default: ``True``) +* BLOG_USE_PLACEHOLDER: Post content is managed via placeholder; + if ``False`` a simple HTMLField is used; (default: ``True``) +* BLOG_MULTISITE: Add support for multisite setup; (default: ``True``) +* BLOG_MENU_TYPE: Structure of the Blog menu; +(default: ``Posts and Categories``) +* BLOG_AUTHOR_DEFAULT: Use a default if not specified; if set to ``True`` the + current user is set as the default author, if set to ``False`` no default + author is set, if set to a string the user with the provided username is + used; (default: ``True``) +* BLOG_DEFAULT_PUBLISHED: If posts are marked as published by default; + (default: ``False``) +* BLOG_AVAILABLE_PERMALINK_STYLES: Choices of permalinks styles; +* BLOG_PERMALINK_URLS: URLConf corresponding to + BLOG_AVAILABLE_PERMALINK_STYLES; +* BLOG_AUTO_SETUP: Enable the blog **Auto setup** feature; (default: ``True``) +* BLOG_AUTO_HOME_TITLE: Title of the home page created by **Auto setup**; + (default: ``Home``) +* BLOG_AUTO_BLOG_TITLE: Title of the blog page created by **Auto setup**; + (default: ``Blog``) +* BLOG_AUTO_APP_TITLE: Title of the ``BlogConfig`` instance created by + **Auto setup**; (default: ``Blog``) + +Per-Apphook settings +-------------------- + +* application title: Free text title that can be used as title in templates; +* Post published by default: Per-Apphook setting for BLOG_DEFAULT_PUBLISHED; +* Permalink structure: Per-Apphook setting for + BLOG_AVAILABLE_PERMALINK_STYLES; +* Use placeholder and plugins for article body: Per-Apphook setting for + BLOG_USE_PLACEHOLDER; +* Use abstract field: Per-Apphook setting for BLOG_USE_ABSTRACT; +* Set author: Per-Apphook setting for BLOG_AUTHOR_DEFAULT; +* Paginate sizePer-Apphook setting for BLOG_PAGINATION; +* Template prefix: Alternative directory to load the blog templates from; +* Menu structure: Per-Apphook setting for BLOG_MENU_TYPE +* Object type: Per-Apphook setting for BLOG_TYPE +* Facebook type: Per-Apphook setting for BLOG_FB_TYPE +* Facebook application ID: Per-Apphook setting for BLOG_FB_APP_ID +* Facebook profile ID: Per-Apphook setting for BLOG_FB_PROFILE_ID +* Facebook page URL: Per-Apphook setting for BLOG_FB_PUBLISHER +* Facebook author URL: Per-Apphook setting for BLOG_AUTHOR_URL +* Facebook author: Per-Apphook setting for BLOG_AUTHOR +* Twitter type: Per-Apphook setting for BLOG_TWITTER_TYPE +* Twitter site handle: Per-Apphook setting for BLOG_TWITTER_SITE +* Twitter author handle: Per-Apphook setting for BLOG_TWITTER_AUTHOR +* Google+ type: Per-Apphook setting for BLOG_GPLUS_TYPE +* Google+ author name: Per-Apphook setting for BLOG_GPLUS_AUTHOR -* Placeholder content editing -* Frontend editing using django CMS 3.0 frontend editor -* Multilingual support using django-parler -* Support for Twitter cards, Open Graph and Google+ snippets meta tags -* Optional support for simpler TextField-based content editing -* Multisite support (posts can be visible in one or more Django sites on the same project) -* Per-apphook configuration Import from Wordpress +++++++++++++++++++++ @@ -212,77 +342,11 @@ If you want to import content from existing wordpress blog, check https://pypi.python.org/pypi/the-real-django-wordpress and this gist https://gist.github.com/yakky/11336204 as a base. - -Global Settings ---------------- -* BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post lists; - it's a dictionary with ``size``, ``crop`` and ``upscale`` keys; - (default: ``{'size': '120x120', 'crop': True,'upscale': False}``) -* BLOG_IMAGE_FULL_SIZE: Size of the main image when shown on the post detail; - it's a dictionary with ``size``, ``crop`` and ``upscale`` keys; - (default: ``{'size': '640x120', 'crop': True,'upscale': False}``) -* BLOG_PAGINATION: Number of post per page; (default: 10) -* BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin; (default: 5) -* BLOG_POSTS_LIST_TRUNCWORDS_COUNT: Default number of words shown for abstract in the post list; (default: 100) -* BLOG_TYPE: Generic type for the post object; (default: Article) -* BLOG_TYPES: Choices of available blog types; (default: Article, Website) -* BLOG_FB_TYPE: Open Graph type for the post object; (default: Article) -* BLOG_FB_TYPES: Choices of available blog types; (default: Article, Website) -* BLOG_FB_APPID: Facebook Application ID -* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author -* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher -* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author -* BLOG_FB_AUTHOR: Facebook profile URL of the post author -* BLOG_TWITTER_TYPE: Twitter Card type for the post object; (default: Summary) -* BLOG_TWITTER_TYPES: Choices of available blog types for twitter; (default: Article, Website) -* BLOG_TWITTER_SITE: Twitter account of the site -* BLOG_TWITTER_AUTHOR: Twitter account of the post author -* BLOG_GPLUS_TYPE: Google+ Snippet type for the post object; (default: Blog) -* BLOG_GPLUS_TYPES: Choices of available blog types for twitter; (default: Article, Website) -* BLOG_GPLUS_AUTHOR: Google+ account of the post author -* BLOG_ENABLE_COMMENTS: Whether to enable comments by default on posts; - while ``djangocms_blog`` does not ship any comment system, this flag can be used - to control the chosen comments framework; (default: True) -* BLOG_USE_ABSTRACT: Use an abstract field for the post; if ``False`` no abstract field - is available for every post; (default: True) -* BLOG_USE_PLACEHOLDER: Post content is managed via placeholder; if ``False`` a - simple HTMLField is used; (default: True) -* BLOG_MULTISITE: Add support for multisite setup -* BLOG_MENU_TYPE: Structure of the Blog menu; (default: Posts and Categories) -* BLOG_AUTHOR_DEFAULT: Use a default if not specified; if set to ``True`` the - current user is set as the default author, if set to ``False`` no default - author is set, if set to a string the user with the provided username is - used; (default: True) -* BLOG_DEFAULT_PUBLISHED: If posts are marked as published by default; (default: False) -* BLOG_AVAILABLE_PERMALINK_STYLES: Choices of permalinks styles; -* BLOG_PERMALINK_URLS: URLConf corresponding to BLOG_AVAILABLE_PERMALINK_STYLES; - -Per-Apphook settings --------------------- - -* default_published: Per-apphook setting for BLOG_DEFAULT_PUBLISHED; -* Permalink structure: Per-apphook setting for BLOG_AVAILABLE_PERMALINK_STYLES; -* Use placeholder and plugins for article body: Per-apphook setting for BLOG_USE_PLACEHOLDER; -* Use abstract field: Per-apphook setting for BLOG_USE_ABSTRACT; -* Set author: Per-apphook setting for BLOG_AUTHOR_DEFAULT; -* Paginate sizePer-apphook setting for BLOG_PAGINATION; -* Template prefix: Alternative directory to load the blog templates from; -* Menu structure: Per-apphook setting for BLOG_MENU_TYPE -* Object type:Per-apphook setting for BLOG_TYPE -* Facebook type: Per-apphook setting for BLOG_FB_TYPE -* Facebook application ID: Per-apphook setting for BLOG_FB_APP_ID -* Facebook profile ID: Per-apphook setting for BLOG_FB_PROFILE_ID -* Facebook page URL: Per-apphook setting for BLOG_FB_PUBLISHER -* Facebook author URL: Per-apphook setting for BLOG_AUTHOR_URL -* Facebook author: Per-apphook setting for BLOG_AUTHOR -* Twitter type: Per-apphook setting for BLOG_TWITTER_TYPE -* Twitter site handle: Per-apphook setting for BLOG_TWITTER_SITE -* Twitter author handle: Per-apphook setting for BLOG_TWITTER_AUTHOR -* Google+ type: Per-apphook setting for BLOG_GPLUS_TYPE -* Google+ author name: Per-apphook setting for BLOG_GPLUS_AUTHOR - Known djangocms-blog websites +++++++++++++++++++++++++++++ * http://nephila.co.uk/blog * https://blog.ungleich.ch/ + + +.. _django-meta-mixin settings: https://github.com/nephila/django-meta-mixin#settings From cd4f6553ddfd2d7b623e4426db01836296834276 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 11:03:47 +0200 Subject: [PATCH 44/48] Autosetup implementation --- README.rst | 2 ++ djangocms_blog/__init__.py | 3 ++ djangocms_blog/apps.py | 62 +++++++++++++++++++++++++++++++++++--- djangocms_blog/settings.py | 5 +++ djangocms_blog/urls.py | 4 ++- 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 564bfe1..2fe56f6 100644 --- a/README.rst +++ b/README.rst @@ -239,6 +239,8 @@ All the items will be created in every language configured for the website and the pages will be published. If not using **aldryn-apphook-reload** or **django CMS 3.2** auto-reload middleware you are required to reload the project instance after this. +This will only work for the current website as detected by +``Site.objects.get_current()``. The auto setup is execute once for each server start but it will skip any diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py index 341f071..e34aa06 100644 --- a/djangocms_blog/__init__.py +++ b/djangocms_blog/__init__.py @@ -2,3 +2,6 @@ __author__ = 'Iacopo Spalletti' __email__ = 'i.spalletti@nephila.it' __version__ = '0.6.0.dev1' + +default_app_config = 'djangocms_blog.apps.BlogAppConfig' + diff --git a/djangocms_blog/apps.py b/djangocms_blog/apps.py index aa257da..d232d49 100644 --- a/djangocms_blog/apps.py +++ b/djangocms_blog/apps.py @@ -1,10 +1,64 @@ # -*- coding: utf-8 -*- from __future__ import absolute_import, print_function, unicode_literals -from django.apps import AppConfig from django.utils.translation import ugettext_lazy as _ +try: + from django.apps import AppConfig +except ImportError: + class AppConfig(object): + pass -class PageMetaConfig(AppConfig): - name = 'djangocms_page_meta' - verbose_name = _('django CMS Page Meta') +class BlogAppConfig(AppConfig): + name = 'djangocms_blog' + verbose_name = _('django CMS Blog') + + @staticmethod + def setup(): + from cms.api import create_page, create_title + from cms.exceptions import NoHomeFound + from cms.models import Page + from cms.utils import get_language_list + from cms.utils.conf import get_templates + from django.utils.translation import override + + from .cms_appconfig import BlogConfig + from .settings import get_setting + + if get_setting('AUTO_SETUP'): + configs = BlogConfig.objects.all() + if not configs.exists(): + config = BlogConfig.objects.create(namespace='Blog') + langs = get_language_list() + blog = None + for lang in langs: + with override(lang): + config.set_current_language(lang) + config.app_title = get_setting('AUTO_APP_TITLE') + config.save() + default_template = get_templates()[0][0] + try: + home = Page.objects.get_home() + except NoHomeFound: + home = None + if not home: + home = create_page( + get_setting('AUTO_HOME_TITLE'), language=lang, + template=default_template, in_navigation=True, published=True + ) + else: + create_title( + language=lang, title=get_setting('AUTO_HOME_TITLE'), page=home + ) + home.publish(lang) + if not blog: + blog = create_page( + get_setting('AUTO_BLOG_TITLE'), language=lang, apphook='BlogApp', + apphook_namespace=config.namespace, parent=home, + template=default_template, in_navigation=True, published=True + ) + else: + create_title( + language=lang, title=get_setting('AUTO_BLOG_TITLE'), page=blog + ) + blog.publish(lang) diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py index 0bb3e37..f43cd97 100644 --- a/djangocms_blog/settings.py +++ b/djangocms_blog/settings.py @@ -74,5 +74,10 @@ def get_setting(name): 'BLOG_DEFAULT_PUBLISHED': getattr(settings, 'BLOG_DEFAULT_PUBLISHED', False), 'BLOG_AVAILABLE_PERMALINK_STYLES': getattr(settings, 'BLOG_AVAILABLE_PERMALINK_STYLES', PERMALINKS), # NOQA 'BLOG_PERMALINK_URLS': getattr(settings, 'BLOG_PERMALINK_URLS', PERMALINKS_URLS), + + 'BLOG_AUTO_SETUP': getattr(settings, 'BLOG_AUTO_SETUP', True), + 'BLOG_AUTO_HOME_TITLE': getattr(settings, 'BLOG_AUTO_HOME_TITLE', 'Home'), + 'BLOG_AUTO_BLOG_TITLE': getattr(settings, 'BLOG_AUTO_BLOG_TITLE', 'Blog'), + 'BLOG_AUTO_APP_TITLE': getattr(settings, 'BLOG_AUTO_APP_TITLE', 'Blog'), } return default['BLOG_%s' % name] diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py index 561a56c..2a5e0b7 100644 --- a/djangocms_blog/urls.py +++ b/djangocms_blog/urls.py @@ -2,7 +2,7 @@ from __future__ import absolute_import, print_function, unicode_literals from django.conf.urls import patterns, url - +from .apps import BlogAppConfig from .feeds import LatestEntriesFeed, TagFeed from .settings import get_setting from .views import ( @@ -42,3 +42,5 @@ urlpatterns = patterns( url(r'^tag/(?P[-\w]+)/feed/$', TagFeed(), name='posts-tagged-feed'), ] + +BlogAppConfig.setup() From ac42d6cb6ec448cd322343e93f07053504e0fd6d Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 11:45:00 +0200 Subject: [PATCH 45/48] Add tests for auto setup --- djangocms_blog/__init__.py | 1 - djangocms_blog/apps.py | 3 +- djangocms_blog/urls.py | 1 + tests/test_setup.py | 74 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 2 deletions(-) create mode 100644 tests/test_setup.py diff --git a/djangocms_blog/__init__.py b/djangocms_blog/__init__.py index e34aa06..74b54bb 100644 --- a/djangocms_blog/__init__.py +++ b/djangocms_blog/__init__.py @@ -4,4 +4,3 @@ __email__ = 'i.spalletti@nephila.it' __version__ = '0.6.0.dev1' default_app_config = 'djangocms_blog.apps.BlogAppConfig' - diff --git a/djangocms_blog/apps.py b/djangocms_blog/apps.py index d232d49..3da17b3 100644 --- a/djangocms_blog/apps.py +++ b/djangocms_blog/apps.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, print_function, unicode_literals from django.utils.translation import ugettext_lazy as _ + try: from django.apps import AppConfig except ImportError: @@ -46,7 +47,7 @@ class BlogAppConfig(AppConfig): get_setting('AUTO_HOME_TITLE'), language=lang, template=default_template, in_navigation=True, published=True ) - else: + elif lang not in home.get_languages(): create_title( language=lang, title=get_setting('AUTO_HOME_TITLE'), page=home ) diff --git a/djangocms_blog/urls.py b/djangocms_blog/urls.py index 2a5e0b7..0a76e27 100644 --- a/djangocms_blog/urls.py +++ b/djangocms_blog/urls.py @@ -2,6 +2,7 @@ from __future__ import absolute_import, print_function, unicode_literals from django.conf.urls import patterns, url + from .apps import BlogAppConfig from .feeds import LatestEntriesFeed, TagFeed from .settings import get_setting diff --git a/tests/test_setup.py b/tests/test_setup.py new file mode 100644 index 0000000..fa8f1e7 --- /dev/null +++ b/tests/test_setup.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +from __future__ import absolute_import, print_function, unicode_literals + +import sys + +from cms.api import create_page, create_title +from cms.models import Page +from cms.utils import get_language_list +from django.utils.translation import override + +from djangocms_blog.cms_appconfig import BlogConfig + +from . import BaseTest + + +class SetupTest(BaseTest): + + @classmethod + def setUpClass(cls): + super(BaseTest, cls).setUpClass() + + def test_setup_from_url(self): + + # Tests starts with no page and no config + self.assertFalse(Page.objects.exists()) + self.assertFalse(BlogConfig.objects.exists()) + + # importing urls triggers the auto setup + from djangocms_blog import urls # NOQA + + # Home and blog, published and draft + self.assertEqual(Page.objects.count(), 4) + self.assertEqual(BlogConfig.objects.count(), 1) + + def setUp(self): + self.reload_urlconf() + delete = [ + 'djangocms_blog', + 'djangocms_blog.urls', + ] + for module in delete: + del sys.modules[module] + + def test_setup_filled(self): + + # Tests starts with no page and no config + self.assertFalse(Page.objects.exists()) + self.assertFalse(BlogConfig.objects.exists()) + + langs = get_language_list() + home = None + for lang in langs: + with override(lang): + if not home: + home = create_page( + 'a new home', language=lang, + template='page.html', in_navigation=True, published=True + ) + else: + create_title( + language=lang, title='a new home', page=home + ) + home.publish(lang) + + # importing urls triggers the auto setup + from djangocms_blog import urls # NOQA + + # Home and blog, published and draft + self.assertEqual(Page.objects.count(), 4) + self.assertEqual(BlogConfig.objects.count(), 1) + + home = Page.objects.get_home() + for lang in langs: + self.assertEqual(home.get_title(lang), 'a new home') From 2490447f440a8a5200ad4f38b6b1c8fea5d22efe Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 12:46:50 +0200 Subject: [PATCH 46/48] Use custom template to render meta tags properly --- cms_helper.py | 4 ++++ djangocms_blog/models.py | 16 ++++++++++++- tests/__init__.py | 6 ++--- tests/test_setup.py | 2 +- tests/test_utils/models.py | 1 + tests/test_utils/templates/blog.html | 36 ++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 tests/test_utils/models.py create mode 100644 tests/test_utils/templates/blog.html diff --git a/cms_helper.py b/cms_helper.py index 3b7af0d..667833d 100755 --- a/cms_helper.py +++ b/cms_helper.py @@ -19,6 +19,7 @@ HELPER_SETTINGS = dict( 'taggit', 'taggit_autosuggest', 'aldryn_apphooks_config', + 'tests.test_utils', ], LANGUAGE_CODE='en', LANGUAGES=( @@ -72,6 +73,9 @@ HELPER_SETTINGS = dict( MIGRATION_MODULES={ 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django', }, + CMS_TEMPLATES=( + ('blog.html', 'Blog template'), + ), META_SITE_PROTOCOL='http', META_SITE_DOMAIN='example.com', META_USE_OG_PROPERTIES=True, diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index 418e39b..e41a0c4 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -203,7 +203,21 @@ class Post(ModelMeta, TranslatableModel): Retrieves django-meta attributes from apphook config instance :param param: django-meta attribute passed as key """ - return getattr(self.app_config, param) + attr = None + value = getattr(self.app_config, param) + if value: + attr = getattr(self, value, None) + if attr is not None: + if callable(attr): + try: + data = attr(param) + except TypeError: + data = attr() + else: + data = attr + else: + data = value + return data def save_translation(self, translation, *args, **kwargs): if not translation.slug and translation.title: diff --git a/tests/__init__.py b/tests/__init__.py index 6fad4a6..dcfbd5f 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -31,14 +31,14 @@ class BaseTest(BaseTestCase): thumb_2 = None _pages_data = ( - {'en': {'title': 'page one', 'template': 'page.html', 'publish': True}, + {'en': {'title': 'page one', 'template': 'blog.html', 'publish': True}, 'fr': {'title': 'page un', 'publish': True}, 'it': {'title': 'pagina uno', 'publish': True}}, - {'en': {'title': 'page two', 'template': 'page.html', 'publish': True, + {'en': {'title': 'page two', 'template': 'blog.html', 'publish': True, 'apphook': 'BlogApp', 'apphook_namespace': 'sample_app'}, 'fr': {'title': 'page deux', 'publish': True}, 'it': {'title': 'pagina due', 'publish': True}}, - {'en': {'title': 'page three', 'template': 'page.html', 'publish': True, + {'en': {'title': 'page three', 'template': 'blog.html', 'publish': True, 'apphook': 'BlogApp', 'apphook_namespace': 'sample_app2'}, 'fr': {'title': 'page trois', 'publish': True}, 'it': {'title': 'pagina tre', 'publish': True}}, diff --git a/tests/test_setup.py b/tests/test_setup.py index fa8f1e7..7741307 100644 --- a/tests/test_setup.py +++ b/tests/test_setup.py @@ -54,7 +54,7 @@ class SetupTest(BaseTest): if not home: home = create_page( 'a new home', language=lang, - template='page.html', in_navigation=True, published=True + template='blog.html', in_navigation=True, published=True ) else: create_title( diff --git a/tests/test_utils/models.py b/tests/test_utils/models.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/tests/test_utils/models.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/tests/test_utils/templates/blog.html b/tests/test_utils/templates/blog.html new file mode 100644 index 0000000..c1703d6 --- /dev/null +++ b/tests/test_utils/templates/blog.html @@ -0,0 +1,36 @@ +{% load cms_tags static menu_tags sekizai_tags %} + + + + {% block title %}{% page_attribute 'title' %}{% endblock title %} + {% render_block "css" %} + {% include "meta_mixin/meta.html" %} + + + +Post +{% cms_toolbar %} +
+ + {% block content %} + {% placeholder "content" %} + {% endblock content %} +
+{% render_block "js" %} +{% with_data "js-script" as jsset %} + {% for js in jsset %}{% endfor %} +{% end_with_data %} +{% render_block "js_end" %} + + From 0e9a80cb422ceaec3fee327986d1a2582d0c0080 Mon Sep 17 00:00:00 2001 From: Iacopo Spalletti Date: Sun, 18 Oct 2015 13:01:00 +0200 Subject: [PATCH 47/48] Clarify meta tags fields in BlogConfig --- djangocms_blog/admin.py | 10 ++++++++++ djangocms_blog/models.py | 5 ++++- tests/test_models.py | 11 +++++++++++ tests/test_utils/templates/blog.html | 1 - 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py index 1670c41..05380a6 100755 --- a/djangocms_blog/admin.py +++ b/djangocms_blog/admin.py @@ -9,6 +9,7 @@ from django import forms from django.conf import settings from django.contrib import admin from django.contrib.auth import get_user_model +from django.utils.translation import ugettext_lazy as _ from parler.admin import TranslatableAdmin from .cms_appconfig import BlogConfig @@ -157,16 +158,25 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin): 'fields': ( 'config.og_type', 'config.og_app_id', 'config.og_profile_id', 'config.og_publisher', 'config.og_author_url', 'config.og_author', + ), + 'description': _( + 'You can provide plain strings, Post model attribute or method names' ) }), ('Twitter', { 'fields': ( 'config.twitter_type', 'config.twitter_site', 'config.twitter_author', + ), + 'description': _( + 'You can provide plain strings, Post model attribute or method names' ) }), ('Google+', { 'fields': ( 'config.gplus_type', 'config.gplus_author', + ), + 'description': _( + 'You can provide plain strings, Post model attribute or method names' ) }), ] diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py index e41a0c4..9ea7fc0 100644 --- a/djangocms_blog/models.py +++ b/djangocms_blog/models.py @@ -150,7 +150,7 @@ class Post(ModelMeta, TranslatableModel): 'og_description': 'get_description', 'twitter_description': 'get_description', 'gplus_description': 'get_description', - 'locale': None, + 'locale': 'get_locale', 'image': 'get_image_full_url', 'object_type': 'get_meta_attribute', 'og_type': 'get_meta_attribute', @@ -233,6 +233,9 @@ class Post(ModelMeta, TranslatableModel): def get_keywords(self): return self.safe_translation_getter('meta_keywords').strip().split(',') + def get_locale(self): + return self.get_current_language() + def get_description(self): description = self.safe_translation_getter('meta_description', any_language=True) if not description: diff --git a/tests/test_models.py b/tests/test_models.py index f95b619..743dd5a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -213,6 +213,9 @@ class ModelsTest(BaseTest): def test_model_attributes(self): self.get_pages() + self.app_config_1.app_data.config.gplus_author = 'RandomJoe' + self.app_config_1.save() + post = self._get_post(self._post_data[0]['en']) post = self._get_post(self._post_data[0]['it'], post, 'it') post.main_image = self.create_filer_image_object() @@ -225,6 +228,14 @@ class ModelsTest(BaseTest): self.assertEqual(meta_en.description, post.meta_description) self.assertEqual(meta_en.keywords, post.meta_keywords.split(',')) self.assertEqual(meta_en.published_time, post.date_published) + self.assertEqual(meta_en.locale, 'en') + self.assertEqual(meta_en.twitter_site, '') + self.assertEqual(meta_en.twitter_author, '') + self.assertEqual(meta_en.twitter_type, 'summary') + self.assertEqual(meta_en.gplus_author, 'RandomJoe') + self.assertEqual(meta_en.gplus_type, 'Blog') + self.assertEqual(meta_en.og_type, 'Article') + self.assertEqual(meta_en.facebook_app_id, None) post.set_current_language('it') meta_it = post.as_meta() self.assertEqual(meta_it.title, post.title) diff --git a/tests/test_utils/templates/blog.html b/tests/test_utils/templates/blog.html index c1703d6..f46def0 100644 --- a/tests/test_utils/templates/blog.html +++ b/tests/test_utils/templates/blog.html @@ -17,7 +17,6 @@ -Post {% cms_toolbar %}