Merge pull request #151 from nephila/feature/menu_structure
Add menu structure configuration
This commit is contained in:
commit
741dc24ac7
9 changed files with 231 additions and 85 deletions
97
README.rst
97
README.rst
|
@ -2,28 +2,34 @@
|
||||||
djangocms-blog
|
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
|
.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg?style=flat-square
|
||||||
:target: https://pypi.python.org/pypi/djangocms-blog
|
:target: https://pypi.python.org/pypi/djangocms-blog
|
||||||
:alt: Latest PyPI version
|
:alt: Monthly downloads
|
||||||
|
|
||||||
.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg
|
.. image:: https://img.shields.io/pypi/pyversions/djangocms-blog.svg?style=flat-square
|
||||||
:target: https://travis-ci.org/nephila/djangocms-blog
|
:target: https://pypi.python.org/pypi/djangocms-blog
|
||||||
:alt: Latest Travis CI build status
|
:alt: Python versions
|
||||||
|
|
||||||
.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg
|
.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg?style=flat-square
|
||||||
:target: https://pypi.python.org/pypi/djangocms-blog
|
:target: https://travis-ci.org/nephila/djangocms-blog
|
||||||
:alt: Monthly downloads
|
:alt: Latest Travis CI build status
|
||||||
|
|
||||||
.. image:: https://coveralls.io/repos/nephila/djangocms-blog/badge.png
|
.. image:: https://img.shields.io/coveralls/nephila/djangocms-blog/master.svg?style=flat-square
|
||||||
:target: https://coveralls.io/r/nephila/djangocms-blog
|
:target: https://coveralls.io/r/nephila/djangocms-blog?branch=master
|
||||||
:alt: Test coverage
|
: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
|
:target: https://codeclimate.com/github/nephila/djangocms-blog
|
||||||
:alt: Code Climate
|
:alt: Code Climate
|
||||||
|
|
||||||
|
|
||||||
A djangoCMS 3 blog application.
|
A djangoCMS 3 blog application.
|
||||||
|
|
||||||
Supported Django versions:
|
Supported Django versions:
|
||||||
|
@ -197,6 +203,7 @@ Features
|
||||||
* Support for Twitter cards, Open Graph and Google+ snippets meta tags
|
* Support for Twitter cards, Open Graph and Google+ snippets meta tags
|
||||||
* Optional support for simpler TextField-based content editing
|
* Optional support for simpler TextField-based content editing
|
||||||
* Multisite support (posts can be visible in one or more Django sites on the same project)
|
* Multisite support (posts can be visible in one or more Django sites on the same project)
|
||||||
|
* Per-apphook configuration
|
||||||
|
|
||||||
Import from Wordpress
|
Import from Wordpress
|
||||||
+++++++++++++++++++++
|
+++++++++++++++++++++
|
||||||
|
@ -206,15 +213,8 @@ https://pypi.python.org/pypi/the-real-django-wordpress and
|
||||||
this gist https://gist.github.com/yakky/11336204 as a base.
|
this gist https://gist.github.com/yakky/11336204 as a base.
|
||||||
|
|
||||||
|
|
||||||
Settings
|
Global 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)
|
|
||||||
* BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post lists;
|
* 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;
|
it's a dictionary with ``size``, ``crop`` and ``upscale`` keys;
|
||||||
(default: ``{'size': '120x120', 'crop': True,'upscale': False}``)
|
(default: ``{'size': '120x120', 'crop': True,'upscale': False}``)
|
||||||
|
@ -224,27 +224,62 @@ Settings
|
||||||
* BLOG_PAGINATION: Number of post per page; (default: 10)
|
* BLOG_PAGINATION: Number of post per page; (default: 10)
|
||||||
* BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin; (default: 5)
|
* 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_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_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_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_APPID: Facebook Application ID
|
||||||
* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author
|
* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author
|
||||||
* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher
|
* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher
|
||||||
* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author
|
* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author
|
||||||
* BLOG_FB_AUTHOR: 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_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_SITE: Twitter account of the site
|
||||||
* BLOG_TWITTER_AUTHOR: Twitter account of the post author
|
* BLOG_TWITTER_AUTHOR: Twitter account of the post author
|
||||||
* BLOG_GPLUS_TYPE: Google+ Snippet type for the post object; (default: Blog)
|
* 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_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
|
Known djangocms-blog websites
|
||||||
+++++++++++++++++++++++++++++
|
+++++++++++++++++++++++++++++
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
__author__ = 'Iacopo Spalletti'
|
__author__ = 'Iacopo Spalletti'
|
||||||
__email__ = 'i.spalletti@nephila.it'
|
__email__ = 'i.spalletti@nephila.it'
|
||||||
__version__ = '0.5.1.dev1'
|
__version__ = '0.5.1.dev2'
|
||||||
|
|
|
@ -144,6 +144,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
|
||||||
('Layout', {
|
('Layout', {
|
||||||
'fields': (
|
'fields': (
|
||||||
'config.paginate_by', 'config.url_patterns', 'config.template_prefix',
|
'config.paginate_by', 'config.url_patterns', 'config.template_prefix',
|
||||||
|
'config.menu_structure',
|
||||||
),
|
),
|
||||||
'classes': ('collapse',)
|
'classes': ('collapse',)
|
||||||
}),
|
}),
|
||||||
|
@ -155,7 +156,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
|
||||||
('Open Graph', {
|
('Open Graph', {
|
||||||
'fields': (
|
'fields': (
|
||||||
'config.og_type', 'config.og_app_id', 'config.og_profile_id',
|
'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', {
|
('Twitter', {
|
||||||
|
|
|
@ -9,7 +9,7 @@ from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from parler.models import TranslatableModel, TranslatedFields
|
from parler.models import TranslatableModel, TranslatedFields
|
||||||
|
|
||||||
from .settings import get_setting
|
from .settings import MENU_TYPE_COMPLETE, get_setting
|
||||||
|
|
||||||
|
|
||||||
class BlogConfig(TranslatableModel, AppHookConfig):
|
class BlogConfig(TranslatableModel, AppHookConfig):
|
||||||
|
@ -54,41 +54,57 @@ class BlogConfigForm(AppDataForm):
|
||||||
label=_('Template prefix'), required=False, initial='',
|
label=_('Template prefix'), required=False, initial='',
|
||||||
help_text=_('Alternative directory to load the blog templates from')
|
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(
|
object_type = forms.ChoiceField(
|
||||||
label=_('Object type'), required=False,
|
label=_('Object type'), required=False,
|
||||||
choices=get_setting('TYPES'), initial=get_setting('TYPE')
|
choices=get_setting('TYPES'), initial=get_setting('TYPE')
|
||||||
)
|
)
|
||||||
og_type = forms.ChoiceField(
|
og_type = forms.ChoiceField(
|
||||||
label=_('Facebook type'), required=False,
|
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(
|
og_app_id = forms.CharField(
|
||||||
max_length=200, label=_('Facebook application ID'), required=False,
|
max_length=200, label=_('Facebook application ID'), required=False,
|
||||||
|
initial=get_setting('FB_PROFILE_ID')
|
||||||
)
|
)
|
||||||
og_profile_id = forms.CharField(
|
og_profile_id = forms.CharField(
|
||||||
max_length=200, label=_('Facebook profile ID'), required=False,
|
max_length=200, label=_('Facebook profile ID'), required=False,
|
||||||
|
initial=get_setting('FB_PROFILE_ID')
|
||||||
)
|
)
|
||||||
og_publisher = forms.CharField(
|
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(
|
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(
|
twitter_type = forms.ChoiceField(
|
||||||
label=_('Twitter type'), required=False,
|
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(
|
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(
|
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(
|
gplus_type = forms.ChoiceField(
|
||||||
label=_('Google+ type'), required=False,
|
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(
|
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)
|
setup_config(BlogConfigForm, BlogConfig)
|
||||||
|
|
|
@ -7,7 +7,9 @@ from django.utils.translation import get_language_from_request, ugettext_lazy as
|
||||||
from menus.base import NavigationNode
|
from menus.base import NavigationNode
|
||||||
from menus.menu_pool import menu_pool
|
from menus.menu_pool import menu_pool
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
class BlogCategoryMenu(CMSAttachMenu):
|
class BlogCategoryMenu(CMSAttachMenu):
|
||||||
|
@ -18,34 +20,52 @@ class BlogCategoryMenu(CMSAttachMenu):
|
||||||
|
|
||||||
language = get_language_from_request(request, check_path=True)
|
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:
|
if hasattr(self, 'instance') and self.instance:
|
||||||
categories = categories.namespace(self.instance.application_namespace)
|
config = BlogConfig.objects.get(namespace=self.instance.application_namespace)
|
||||||
categories = categories.active_translations(language).distinct()
|
if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES):
|
||||||
categories = categories.order_by('parent__id', 'translations__name')
|
categories_menu = True
|
||||||
for category in categories:
|
if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS):
|
||||||
node = NavigationNode(
|
posts_menu = True
|
||||||
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)
|
|
||||||
|
|
||||||
posts = Post.objects
|
if categories_menu:
|
||||||
if hasattr(self, 'instance') and self.instance:
|
categories = BlogCategory.objects
|
||||||
posts = posts.namespace(self.instance.application_namespace)
|
if config:
|
||||||
posts = posts.active_translations(language).distinct()
|
categories = categories.namespace(self.instance.application_namespace)
|
||||||
for post in posts:
|
categories = categories.active_translations(language).distinct()
|
||||||
category = post.categories.first()
|
categories = categories.order_by('parent__id', 'translations__name')
|
||||||
node = NavigationNode(
|
for category in categories:
|
||||||
post.get_title(),
|
node = NavigationNode(
|
||||||
post.get_absolute_url(language),
|
category.name,
|
||||||
'%s-%s' % (post.__class__.__name__, category.pk),
|
category.get_absolute_url(),
|
||||||
'%s-%s' % (category.__class__.__name__, category.pk)
|
'%s-%s' % (category.__class__.__name__, category.pk),
|
||||||
)
|
('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent
|
||||||
nodes.append(node)
|
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
|
return nodes
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,7 @@ class Post(ModelMeta, TranslatableModel):
|
||||||
'og_profile_id': 'get_meta_attribute',
|
'og_profile_id': 'get_meta_attribute',
|
||||||
'og_publisher': 'get_meta_attribute',
|
'og_publisher': 'get_meta_attribute',
|
||||||
'og_author_url': 'get_meta_attribute',
|
'og_author_url': 'get_meta_attribute',
|
||||||
|
'og_author': 'get_meta_attribute',
|
||||||
'twitter_type': 'get_meta_attribute',
|
'twitter_type': 'get_meta_attribute',
|
||||||
'twitter_site': 'get_meta_attribute',
|
'twitter_site': 'get_meta_attribute',
|
||||||
'twitter_author': 'get_meta_attribute',
|
'twitter_author': 'get_meta_attribute',
|
||||||
|
|
|
@ -1,18 +1,17 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
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):
|
def get_setting(name):
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from meta_mixin import settings as meta_settings
|
from meta_mixin import settings as meta_settings
|
||||||
|
|
||||||
OBJECT_TYPES = (
|
|
||||||
('Article', _('Article')),
|
|
||||||
('Website', _('Website')),
|
|
||||||
)
|
|
||||||
BLOG_TYPES = getattr(settings, 'BLOG_TYPES', OBJECT_TYPES)
|
|
||||||
|
|
||||||
PERMALINKS = (
|
PERMALINKS = (
|
||||||
('full_date', _('Full date')),
|
('full_date', _('Full date')),
|
||||||
('short_date', _('Year / Month')),
|
('short_date', _('Year / Month')),
|
||||||
|
@ -25,7 +24,12 @@ def get_setting(name):
|
||||||
'category': r'^(?P<category>\w[-\w]*)/(?P<slug>\w[-\w]*)/$',
|
'category': r'^(?P<category>\w[-\w]*)/(?P<slug>\w[-\w]*)/$',
|
||||||
'slug': r'^(?P<slug>\w[-\w]*)/$',
|
'slug': r'^(?P<slug>\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 = {
|
default = {
|
||||||
'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
|
'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
|
||||||
'size': '120x120',
|
'size': '120x120',
|
||||||
|
@ -39,28 +43,28 @@ def get_setting(name):
|
||||||
'upscale': False
|
'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_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10),
|
||||||
'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5),
|
'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5),
|
||||||
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
|
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
|
||||||
settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
|
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_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_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_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_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_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_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'),
|
||||||
'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
|
'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
|
||||||
'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'),
|
'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'summary'),
|
||||||
'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', BLOG_TYPES),
|
'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_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE),
|
||||||
'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
|
'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
|
||||||
'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'),
|
'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_TYPE', 'Blog'),
|
||||||
'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', BLOG_TYPES),
|
'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', meta_settings.GPLUS_TYPES),
|
||||||
'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
|
'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
|
||||||
'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
|
'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
|
||||||
'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
|
'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
|
||||||
|
|
4
setup.py
4
setup.py
|
@ -47,8 +47,8 @@ setup(
|
||||||
'djangocms-text-ckeditor',
|
'djangocms-text-ckeditor',
|
||||||
'cmsplugin-filer',
|
'cmsplugin-filer',
|
||||||
'django-meta>=0.2',
|
'django-meta>=0.2',
|
||||||
'django-meta-mixin>=0.1.1',
|
'django-meta-mixin>=0.2.1',
|
||||||
'aldryn-apphooks-config',
|
'aldryn-apphooks-config>=0.2.6',
|
||||||
],
|
],
|
||||||
license='BSD',
|
license='BSD',
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
|
|
|
@ -2,10 +2,14 @@
|
||||||
from __future__ import absolute_import, print_function, unicode_literals
|
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.core.cache import cache
|
||||||
from django.utils.translation import activate
|
from django.utils.translation import activate
|
||||||
from menus.menu_pool import menu_pool
|
from menus.menu_pool import menu_pool
|
||||||
from parler.utils.context import smart_override, switch_language
|
from parler.utils.context import smart_override, switch_language
|
||||||
|
|
||||||
|
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 djangocms_blog.views import CategoryEntriesView, PostDetailView
|
||||||
|
|
||||||
from . import BaseTest
|
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)])
|
cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)])
|
||||||
self.assertTrue(cats_url.issubset(nodes_url))
|
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):
|
def test_modifier(self):
|
||||||
"""
|
"""
|
||||||
Tests if correct category is selected in the menu
|
Tests if correct category is selected in the menu
|
||||||
|
|
Loading…
Reference in a new issue