Merge branch 'develop' of github.com:nephila/djangocms-blog into develop

This commit is contained in:
Iacopo Spalletti 2016-02-16 08:01:09 +01:00
commit 35f2b47905
15 changed files with 311 additions and 64 deletions

View file

@ -1,3 +1,4 @@
tests/* tests/*
docs/* docs/*
djangocms_blog/south_migrations/* djangocms_blog/south_migrations/*
djangocms_blog/migrations/*

View file

@ -3,11 +3,15 @@
History History
------- -------
0.6.4 (unreleased) 0.7.0 (unreleased)
++++++++++++++++++ ++++++++++++++++++
* Make categories non required * Make categories non required
* Fix tests with parler>=1.6 * Fix tests with parler>=1.6
* Use all_languages_column to admin
* Add publish button
* Fix issues in migrations. Thanks @skirsdeda
* Fix selecting current menu item according to menu layout
0.6.3 (2015-12-22) 0.6.3 (2015-12-22)
++++++++++++++++++ ++++++++++++++++++

View file

@ -190,12 +190,17 @@ suited for your deployment.
`Auto setup <auto_setup>`_: `Auto setup <auto_setup>`_:
* Create a new django CMS page * Create a new django CMS page
* Go to Advanced settings and select Blog from the Application selector and * Go to **Advanced settings** and select Blog from the **Application** selector and
create an application configuration; create an **Application configuration**;
* Eventually customise the Application instance name; * Eventually customise the Application instance name;
* Publish the page * Publish the page
* Restart the project instance to properly load blog urls. * Restart the project instance to properly load blog urls.
.. warning:: After adding the apphook to the page you **cannot** change the **Instance Namspace**
field for the defined **AppHokConfig**; if you want to change it, create a new one
with the correct namespace, go in the CMS page **Advanced settings** and switch to the
new **Application configuration**
* Add and edit blog by creating them in the admin or using the toolbar, * Add and edit blog by creating them in the admin or using the toolbar,
and the use the `django CMS frontend editor <http://django-cms.readthedocs.org/en/support-3.0.x/user/reference/page_admin.html#the-interface>`_ and the use the `django CMS frontend editor <http://django-cms.readthedocs.org/en/support-3.0.x/user/reference/page_admin.html#the-interface>`_
to edit the blog content: to edit the blog content:

View file

@ -7,9 +7,12 @@ from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig
from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.conf.urls import url
from django.contrib import admin from django.contrib import admin
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.utils.six import callable from django.utils.six import callable
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import get_language_from_request, ugettext_lazy as _
from parler.admin import TranslatableAdmin from parler.admin import TranslatableAdmin
from .cms_appconfig import BlogConfig from .cms_appconfig import BlogConfig
@ -25,6 +28,7 @@ except ImportError:
class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin): class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin):
def get_prepopulated_fields(self, request, obj=None): def get_prepopulated_fields(self, request, obj=None):
app_config_default = self._app_config_select(request, obj) app_config_default = self._app_config_select(request, obj)
if app_config_default is None and request.method == 'GET': if app_config_default is None and request.method == 'GET':
@ -41,7 +45,8 @@ class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
ModelAppHookConfig, TranslatableAdmin): ModelAppHookConfig, TranslatableAdmin):
form = PostAdminForm form = PostAdminForm
list_display = [ list_display = [
'title', 'author', 'date_published', 'app_config', 'languages', 'date_published_end' 'title', 'author', 'date_published', 'app_config', 'all_languages_column',
'date_published_end'
] ]
list_filter = ('app_config',) list_filter = ('app_config',)
date_hierarchy = 'date_published' date_hierarchy = 'date_published'
@ -71,8 +76,35 @@ class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
'default_published': 'publish' 'default_published': 'publish'
} }
def languages(self, obj): def get_urls(self):
return ','.join(obj.get_available_languages()) """
Customize the modeladmin urls
"""
urls = [
url(r'^publish/([0-9]+)/$', self.admin_site.admin_view(self.publish_post),
name='djangocms_blog_publish_article'),
]
urls.extend(super(PostAdmin, self).get_urls())
return urls
def publish_post(self, request, pk):
"""
Admin view to publish a single post
:param request: request
:param pk: primary key of the post to publish
:return: Redirect to the post itself (if found) or fallback urls
"""
language = get_language_from_request(request, check_path=True)
try:
post = Post.objects.get(pk=int(pk))
post.publish = True
post.save()
return HttpResponseRedirect(post.get_absolute_url(language))
except Exception:
try:
return HttpResponseRedirect(request.META['HTTP_REFERER'])
except KeyError:
return HttpResponseRedirect(reverse('djangocms_blog:posts-latest'))
def formfield_for_dbfield(self, db_field, **kwargs): def formfield_for_dbfield(self, db_field, **kwargs):
field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs) field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs)
@ -85,6 +117,12 @@ class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
return field return field
def get_fieldsets(self, request, obj=None): def get_fieldsets(self, request, obj=None):
"""
Customize the fieldsets according to the app settings
:param request: request
:param obj: post
:return: fieldsets configuration
"""
app_config_default = self._app_config_select(request, obj) app_config_default = self._app_config_select(request, obj)
if app_config_default is None and request.method == 'GET': if app_config_default is None and request.method == 'GET':
return super(PostAdmin, self).get_fieldsets(request, obj) return super(PostAdmin, self).get_fieldsets(request, obj)
@ -130,6 +168,9 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
@property @property
def declared_fieldsets(self): def declared_fieldsets(self):
"""
Fieldsets configuration
"""
return [ return [
(None, { (None, {
'fields': ('type', 'namespace', 'app_title', 'object_name') 'fields': ('type', 'namespace', 'app_title', 'object_name')

View file

@ -3,6 +3,7 @@ from __future__ import absolute_import, print_function, unicode_literals
from cms.toolbar_base import CMSToolbar from cms.toolbar_base import CMSToolbar
from cms.toolbar_pool import toolbar_pool from cms.toolbar_pool import toolbar_pool
from cms.utils.urlutils import admin_reverse
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.translation import override, ugettext_lazy as _ from django.utils.translation import override, ugettext_lazy as _
@ -33,6 +34,22 @@ class BlogToolbar(CMSToolbar):
'admin:djangocms_blog_post_change', args=(current_post.pk,)), 'admin:djangocms_blog_post_change', args=(current_post.pk,)),
active=True) active=True)
def add_publish_button(self):
"""
Adds the publish button to the toolbar if the current post is unpublished
"""
current_post = getattr(self.request, get_setting('CURRENT_POST_IDENTIFIER'), None)
if (self.toolbar.edit_mode and current_post and
not current_post.publish and
self.request.user.has_perm('djangocms_blog.change_post')
): # pragma: no cover # NOQA
classes = ['cms-btn-action', 'blog-publish']
title = _('Publish {0} now').format(current_post.app_config.object_name)
url = admin_reverse('djangocms_blog_publish_article', args=(current_post.pk,))
self.toolbar.add_button(title, url=url, extra_classes=classes, side=self.toolbar.RIGHT)
def post_template_populate(self): def post_template_populate(self):
current_post = getattr(self.request, get_setting('CURRENT_POST_IDENTIFIER'), None) current_post = getattr(self.request, get_setting('CURRENT_POST_IDENTIFIER'), None)
if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA
@ -52,3 +69,4 @@ class BlogToolbar(CMSToolbar):
menu.remove_item(pagetags) menu.remove_item(pagetags)
except ImportError: except ImportError:
pass pass
self.add_publish_button()

View file

@ -1,21 +1,34 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
from cms.apphook_pool import apphook_pool
from cms.menu_bases import CMSAttachMenu from cms.menu_bases import CMSAttachMenu
from django.core.urlresolvers import resolve
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from django.utils.translation import get_language_from_request, ugettext_lazy as _ from django.utils.translation import get_language_from_request, ugettext_lazy as _
from menus.base import NavigationNode from menus.base import Modifier, NavigationNode
from menus.menu_pool import menu_pool from menus.menu_pool import menu_pool
from .cms_appconfig import BlogConfig from .cms_appconfig import BlogConfig
from .models import BlogCategory, Post from .models import BlogCategory, Post
from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS, get_setting
class BlogCategoryMenu(CMSAttachMenu): class BlogCategoryMenu(CMSAttachMenu):
"""
Main menu class
Handles all types of blog menu
"""
name = _('Blog menu') name = _('Blog menu')
def get_nodes(self, request): def get_nodes(self, request):
"""
Generates the nodelist
:param request:
:return: list of nodes
"""
nodes = [] nodes = []
language = get_language_from_request(request, check_path=True) language = get_language_from_request(request, check_path=True)
@ -40,9 +53,12 @@ class BlogCategoryMenu(CMSAttachMenu):
node = NavigationNode( node = NavigationNode(
category.name, category.name,
category.get_absolute_url(), category.get_absolute_url(),
'%s-%s' % (category.__class__.__name__, category.pk), '{0}-{1}'.format(category.__class__.__name__, category.pk),
('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent (
else None) '{0}-{1}'.format(
category.__class__.__name__, category.parent.id
) if category.parent else None
)
) )
nodes.append(node) nodes.append(node)
@ -57,10 +73,10 @@ class BlogCategoryMenu(CMSAttachMenu):
if categories_menu: if categories_menu:
category = post.categories.first() category = post.categories.first()
if category: if category:
parent = '%s-%s' % (category.__class__.__name__, category.pk) parent = '{0}-{1}'.format(category.__class__.__name__, category.pk)
post_id = '%s-%s' % (post.__class__.__name__, post.pk), post_id = '{0}-{1}'.format(post.__class__.__name__, post.pk),
else: else:
post_id = '%s-%s' % (post.__class__.__name__, post.pk), post_id = '{0}-{1}'.format(post.__class__.__name__, post.pk),
if post_id: if post_id:
node = NavigationNode( node = NavigationNode(
post.get_title(), post.get_title(),
@ -75,7 +91,54 @@ class BlogCategoryMenu(CMSAttachMenu):
menu_pool.register_menu(BlogCategoryMenu) 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):
"""
Actual modifier function
:param request: request
:param nodes: complete list of nodes
:param namespace: Menu namespace
:param root_id: eventual root_id
:param post_cut: flag for modifier stage
:param breadcrumb: flag for modifier stage
:return: nodeslist
"""
app = None
config = None
if getattr(request, 'current_page', None) and request.current_page.application_urls:
app = apphook_pool.get_apphook(request.current_page.application_urls)
if app and app.app_config:
namespace = resolve(request.path).namespace
config = app.get_config(namespace)
if config and config.menu_structure != MENU_TYPE_CATEGORIES:
return nodes
if post_cut:
return nodes
current_post = getattr(request, get_setting('CURRENT_POST_IDENTIFIER'), None)
category = None
if current_post and current_post.__class__ == Post:
category = current_post.categories.first()
if not category:
return nodes
for node in nodes:
if '{0}-{1}'.format(category.__class__.__name__, category.pk) == node.id:
node.selected = True
return nodes
menu_pool.register_modifier(BlogNavModifier)
def clear_menu_cache(**kwargs): def clear_menu_cache(**kwargs):
"""
Empty menu cache when saving categories
"""
menu_pool.clear(all=True) menu_pool.clear(all=True)
post_save.connect(clear_menu_cache, sender=BlogCategory) post_save.connect(clear_menu_cache, sender=BlogCategory)

View file

@ -8,13 +8,17 @@ import filer.fields.image
import meta_mixin.models import meta_mixin.models
import taggit_autosuggest.managers import taggit_autosuggest.managers
from django.conf import settings from django.conf import settings
from django.db import models, migrations from django.db import migrations, models
from filer.settings import FILER_IMAGE_MODEL
ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or 'filer.Image'
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL),
('cms', '__latest__'), ('cms', '__latest__'),
('taggit', '__latest__'), ('taggit', '__latest__'),
('filer', '__latest__'), ('filer', '__latest__'),
@ -90,7 +94,7 @@ class Migration(migrations.Migration):
('author', models.ForeignKey(related_name='djangocms_blog_post_author', verbose_name='Author', blank=True, to=settings.AUTH_USER_MODEL, null=True)), ('author', models.ForeignKey(related_name='djangocms_blog_post_author', verbose_name='Author', blank=True, to=settings.AUTH_USER_MODEL, null=True)),
('categories', models.ManyToManyField(related_name='blog_posts', verbose_name='category', to='djangocms_blog.BlogCategory')), ('categories', models.ManyToManyField(related_name='blog_posts', verbose_name='category', to='djangocms_blog.BlogCategory')),
('content', cms.models.fields.PlaceholderField(slotname='post_content', editable=False, to='cms.Placeholder', null=True)), ('content', cms.models.fields.PlaceholderField(slotname='post_content', editable=False, to='cms.Placeholder', null=True)),
('main_image', filer.fields.image.FilerImageField(related_name='djangocms_blog_post_image', verbose_name='Main image', blank=True, to='filer.Image', null=True)), ('main_image', filer.fields.image.FilerImageField(related_name='djangocms_blog_post_image', verbose_name='Main image', blank=True, to=ACTUAL_FILER_IMAGE_MODEL, null=True)),
('main_image_full', models.ForeignKey(related_name='djangocms_blog_post_full', verbose_name='Main image full', blank=True, to='cmsplugin_filer_image.ThumbnailOption', null=True)), ('main_image_full', models.ForeignKey(related_name='djangocms_blog_post_full', verbose_name='Main image full', blank=True, to='cmsplugin_filer_image.ThumbnailOption', null=True)),
('main_image_thumbnail', models.ForeignKey(related_name='djangocms_blog_post_thumbnail', verbose_name='Main image thumbnail', blank=True, to='cmsplugin_filer_image.ThumbnailOption', null=True)), ('main_image_thumbnail', models.ForeignKey(related_name='djangocms_blog_post_thumbnail', verbose_name='Main image thumbnail', blank=True, to='cmsplugin_filer_image.ThumbnailOption', null=True)),
('tags', taggit_autosuggest.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ('tags', taggit_autosuggest.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),

View file

@ -1,14 +1,18 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
import filer.fields.image
import django.db.models.deletion import django.db.models.deletion
import filer.fields.image
from django.db import migrations, models
from filer.settings import FILER_IMAGE_MODEL
ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or 'filer.Image'
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL),
('djangocms_blog', '0003_auto_20141201_2252'), ('djangocms_blog', '0003_auto_20141201_2252'),
] ]
@ -16,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='post', model_name='post',
name='main_image', name='main_image',
field=filer.fields.image.FilerImageField(related_name='djangocms_blog_post_image', on_delete=django.db.models.deletion.SET_NULL, verbose_name='Main image', blank=True, to='filer.Image', null=True), field=filer.fields.image.FilerImageField(related_name='djangocms_blog_post_image', on_delete=django.db.models.deletion.SET_NULL, verbose_name='Main image', blank=True, to=ACTUAL_FILER_IMAGE_MODEL, null=True),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterField( migrations.AlterField(

View file

@ -1,18 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from django.db import models, migrations
import filer.fields.image
import django.utils.timezone
from django.conf import settings
import djangocms_text_ckeditor.fields
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone
import djangocms_text_ckeditor.fields
import filer.fields.image
from django.conf import settings
from django.db import migrations, models
from filer.settings import FILER_IMAGE_MODEL
ACTUAL_FILER_IMAGE_MODEL = FILER_IMAGE_MODEL or 'filer.Image'
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('djangocms_blog', '0006_auto_20150214_1907'), ('djangocms_blog', '0006_auto_20150214_1907'),
migrations.swappable_dependency(ACTUAL_FILER_IMAGE_MODEL),
] ]
operations = [ operations = [
@ -99,7 +103,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='post', model_name='post',
name='main_image', name='main_image',
field=filer.fields.image.FilerImageField(on_delete=django.db.models.deletion.SET_NULL, blank=True, verbose_name='main image', to='filer.Image', related_name='djangocms_blog_post_image', null=True), field=filer.fields.image.FilerImageField(on_delete=django.db.models.deletion.SET_NULL, blank=True, verbose_name='main image', to=ACTUAL_FILER_IMAGE_MODEL, related_name='djangocms_blog_post_image', null=True),
preserve_default=True, preserve_default=True,
), ),
migrations.AlterField( migrations.AlterField(

View file

@ -4,37 +4,9 @@ from __future__ import unicode_literals
import aldryn_apphooks_config.fields import aldryn_apphooks_config.fields
import app_data.fields import app_data.fields
import djangocms_text_ckeditor.fields import djangocms_text_ckeditor.fields
from cms.models import Page
from cms.utils.i18n import get_language_list
from django.db import models, migrations 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')
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_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(apps, schema_editor):
# No need for backward data migration
pass
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
@ -120,5 +92,4 @@ class Migration(migrations.Migration):
name='sites', 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)'), 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)
] ]

View file

@ -0,0 +1,43 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from cms.models import Page
from cms.utils.i18n import get_language_list
from django.db import migrations, models
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')
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, created = BlogConfig.objects.get_or_create(namespace=page.application_namespace)
if not BlogConfigTranslation.objects.exists():
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.filter(app_config__isnull=True):
item.app_config = config
item.save()
def backwards(apps, schema_editor):
# No need for backward data migration
pass
class Migration(migrations.Migration):
dependencies = [
('djangocms_blog', '0013_auto_20160201_2235'),
]
operations = [
migrations.RunPython(forwards, backwards),
]

View file

@ -27,7 +27,7 @@ setup(
], ],
include_package_data=True, include_package_data=True,
install_requires=[ install_requires=[
'django-parler>=1.2', 'django-parler>=1.5',
'django-cms>3.0.11', 'django-cms>3.0.11',
'django-taggit>=0.12.2', 'django-taggit>=0.12.2',
'django-filer', 'django-filer',

View file

@ -29,6 +29,7 @@ class MenuTest(BaseTest):
self.cats.append(cat) self.cats.append(cat)
activate('en') activate('en')
menu_pool.clear(all=True)
menu_pool.discover_menus() menu_pool.discover_menus()
# All cms menu modifiers should be removed from menu_pool.modifiers # All cms menu modifiers should be removed from menu_pool.modifiers
# so that they do not interfere with our menu nodes # so that they do not interfere with our menu nodes
@ -139,22 +140,55 @@ class MenuTest(BaseTest):
(PostDetailView, 'slug', posts[0], posts[0].categories.first()), (PostDetailView, 'slug', posts[0], posts[0].categories.first()),
(CategoryEntriesView, 'category', self.cats[2], self.cats[2]) (CategoryEntriesView, 'category', self.cats[2], self.cats[2])
) )
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE
self.app_config_1.save()
for view_cls, kwarg, obj, cat in tests: for view_cls, kwarg, obj, cat in tests:
request = self.get_page_request(pages[1], self.user, path=obj.get_absolute_url())
with smart_override('en'): with smart_override('en'):
with switch_language(obj, 'en'): with switch_language(obj, 'en'):
request = self.get_page_request(
pages[1], self.user, path=obj.get_absolute_url()
)
cache.clear()
menu_pool.clear(all=True)
view_obj = view_cls() view_obj = view_cls()
view_obj.request = request view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request) view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.app_config = self.app_config_1 view_obj.app_config = self.app_config_1
view_obj.kwargs = {kwarg: obj.slug} view_obj.kwargs = {kwarg: obj.slug}
view_obj.get(request) view_obj.get(request)
view_obj.get_context_data()
# check if selected menu node points to cat # check if selected menu node points to cat
nodes = menu_pool.get_nodes(request) nodes = menu_pool.get_nodes(request)
found = False found = []
for node in nodes: for node in nodes:
if node.selected: if node.selected:
self.assertEqual(node.url, obj.get_absolute_url()) found.append(node.get_absolute_url())
found = True self.assertTrue(obj.get_absolute_url() in found)
break
self.assertTrue(found) self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES
self.app_config_1.save()
for view_cls, kwarg, obj, cat in tests:
with smart_override('en'):
with switch_language(obj, 'en'):
request = self.get_page_request(
pages[1], self.user, path=obj.get_absolute_url()
)
cache.clear()
menu_pool.clear(all=True)
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)
view_obj.get_context_data()
# check if selected menu node points to cat
nodes = menu_pool.get_nodes(request)
found = []
for node in nodes:
if node.selected:
found.append(node.get_absolute_url())
self.assertTrue(cat.get_absolute_url() in found)
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE
self.app_config_1.save()

View file

@ -34,6 +34,8 @@ class AdminTest(BaseTest):
admin.autodiscover() admin.autodiscover()
def test_admin_post_views(self): def test_admin_post_views(self):
self.get_pages()
post_admin = admin.site._registry[Post] post_admin = admin.site._registry[Post]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
@ -50,6 +52,28 @@ class AdminTest(BaseTest):
self.assertContains(response, '<input id="id_slug" maxlength="50" name="slug" type="text" value="first-post" />') self.assertContains(response, '<input id="id_slug" maxlength="50" name="slug" type="text" value="first-post" />')
self.assertContains(response, '<option value="%s" selected="selected">Blog / sample_app</option>' % self.app_config_1.pk) self.assertContains(response, '<option value="%s" selected="selected">Blog / sample_app</option>' % self.app_config_1.pk)
# Test for publish view
post.publish = False
post.save()
response = post_admin.publish_post(request, str(post.pk))
# Redirects to current post
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], post.get_absolute_url())
post = self.reload_model(post)
# post is publshed
self.assertTrue(post.publish)
# Non-existing post is redirected to posts list
response = post_admin.publish_post(request, str('1000000'))
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], reverse('djangocms_blog:posts-latest'))
# unless a referer is set
request.META['HTTP_REFERER'] = '/'
response = post_admin.publish_post(request, str('1000000'))
self.assertEqual(response.status_code, 302)
self.assertEqual(response['Location'], '/')
def test_admin_blogconfig_views(self): def test_admin_blogconfig_views(self):
post_admin = admin.site._registry[BlogConfig] post_admin = admin.site._registry[BlogConfig]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False) request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
@ -67,7 +91,7 @@ class AdminTest(BaseTest):
for fieldname in BlogConfigForm.base_fields: for fieldname in BlogConfigForm.base_fields:
self.assertContains(response, 'id="id_config-%s"' % fieldname) self.assertContains(response, 'id="id_config-%s"' % fieldname)
self.assertContains(response, '<input id="id_config-og_app_id" maxlength="200" name="config-og_app_id" type="text" />') self.assertContains(response, '<input id="id_config-og_app_id" maxlength="200" name="config-og_app_id" type="text" />')
self.assertContains(response, '<input class="vTextField" id="id_namespace" maxlength="100" name="namespace" type="text" value="sample_app" />') self.assertContains(response, 'sample_app')
def test_admin_category_views(self): def test_admin_category_views(self):
post_admin = admin.site._registry[BlogCategory] post_admin = admin.site._registry[BlogCategory]

View file

@ -1,8 +1,9 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
from cms.toolbar.items import ModalItem from cms.toolbar.items import ButtonList, ModalItem
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.utils.encoding import force_text
from djangocms_blog.models import BLOG_CURRENT_POST_IDENTIFIER from djangocms_blog.models import BLOG_CURRENT_POST_IDENTIFIER
@ -20,9 +21,39 @@ class ToolbarTest(BaseTest):
pages = self.get_pages() pages = self.get_pages()
request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True) request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True)
setattr(request, BLOG_CURRENT_POST_IDENTIFIER, posts[0]) setattr(request, BLOG_CURRENT_POST_IDENTIFIER, posts[0])
posts[0].publish = False
posts[0].save()
toolbar = CMSToolbar(request) toolbar = CMSToolbar(request)
toolbar.populate()
toolbar.post_template_populate()
toolbar.get_left_items() toolbar.get_left_items()
blog_menu = toolbar.menus['djangocms_blog'] 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_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_add'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(posts[0].pk,)))), 1) self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(posts[0].pk,)))), 1)
# Publish button only appears if current post is unpublished
right = toolbar.get_right_items()
buttons = sum([item.buttons for item in right if isinstance(item, ButtonList)], [])
self.assertTrue([button for button in buttons if force_text(button.name) == 'Publish Blog now'])
# Publish button does not appears if current post is published
posts[0].publish = True
posts[0].save()
toolbar = CMSToolbar(request)
toolbar.populate()
toolbar.post_template_populate()
right = toolbar.get_right_items()
buttons = sum([item.buttons for item in right if isinstance(item, ButtonList)], [])
self.assertFalse([button for button in buttons if force_text(button.name) == 'Publish Blog now'])
# Publish button does not appears if other posts but the current one are unpublished
posts[1].publish = True
posts[1].save()
toolbar = CMSToolbar(request)
toolbar.populate()
toolbar.post_template_populate()
right = toolbar.get_right_items()
buttons = sum([item.buttons for item in right if isinstance(item, ButtonList)], [])
self.assertFalse([button for button in buttons if force_text(button.name) == 'Publish Blog now'])