Merge branch 'develop' of github.com:nephila/djangocms-blog into develop
This commit is contained in:
commit
35f2b47905
15 changed files with 311 additions and 64 deletions
|
@ -1,3 +1,4 @@
|
||||||
tests/*
|
tests/*
|
||||||
docs/*
|
docs/*
|
||||||
djangocms_blog/south_migrations/*
|
djangocms_blog/south_migrations/*
|
||||||
|
djangocms_blog/migrations/*
|
||||||
|
|
|
@ -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)
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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')),
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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)
|
|
||||||
]
|
]
|
||||||
|
|
43
djangocms_blog/migrations/0014_auto_20160215_1331.py
Normal file
43
djangocms_blog/migrations/0014_auto_20160215_1331.py
Normal 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),
|
||||||
|
]
|
2
setup.py
2
setup.py
|
@ -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',
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
Loading…
Reference in a new issue