Merge pull request #147 from nephila/feature/appcnfig

Add aldryn-apphooks config
This commit is contained in:
Iacopo Spalletti 2015-10-18 14:09:45 +02:00
commit 134b1e0484
36 changed files with 2771 additions and 801 deletions

View file

@ -5,6 +5,7 @@ language: python
sudo: false
python:
- 3.5
- 3.4
- 3.3
- 2.7
@ -12,16 +13,16 @@ python:
env:
matrix:
- DJANGO='django16' CMS='cms30'
- DJANGO='django16' CMS='cms31'
- DJANGO='django16' CMS='cms32'
- DJANGO='django17' CMS='cms30'
- DJANGO='django17' CMS='cms31'
- DJANGO='django17' CMS='cms32'
- DJANGO='django18' CMS='cms31'
- DJANGO='django18' CMS='cms32'
- TOXENV='pep8'
- TOXENV='isort'
- DJANGO='django18' CMS='cms32'
- DJANGO='django18' CMS='cms31'
- DJANGO='django17' CMS='cms32'
- DJANGO='django17' CMS='cms31'
- DJANGO='django17' CMS='cms30'
- DJANGO='django16' CMS='cms32'
- DJANGO='django16' CMS='cms31'
- DJANGO='django16' CMS='cms30'
# command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors
@ -31,12 +32,17 @@ install:
- "if [[ $TRAVIS_PYTHON_VERSION == '2.7' ]]; then export PYVER=py27; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.3' ]]; then export PYVER=py33; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.4' ]]; then export PYVER=py34; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.5' ]]; then export PYVER=py35; fi"
- "if [[ ${DJANGO}z != 'z' ]]; then export TOXENV=$PYVER-$DJANGO-$CMS; fi"
# command to run tests, e.g. python setup.py test
script: COMMAND='coverage run' tox -e$TOXENV
after_success: coveralls
before_install:
- pip install codecov
after_success:
- codecov
- coveralls
matrix:
exclude:
@ -52,6 +58,10 @@ matrix:
env: TOXENV='pep8'
- python: 3.3
env: TOXENV='isort'
- python: 3.4
env: TOXENV='pep8'
- python: 3.4
env: TOXENV='isort'
- python: 2.6
env: DJANGO='django17' CMS='cms30'
- python: 2.6
@ -62,6 +72,18 @@ matrix:
env: DJANGO='django18' CMS='cms31'
- python: 2.6
env: DJANGO='django18' CMS='cms32'
- python: 3.5
env: DJANGO='django16' CMS='cms30'
- python: 3.5
env: DJANGO='django16' CMS='cms31'
- python: 3.5
env: DJANGO='django16' CMS='cms32'
- python: 3.5
env: DJANGO='django17' CMS='cms30'
- python: 3.5
env: DJANGO='django17' CMS='cms31'
- python: 3.5
env: DJANGO='django17' CMS='cms32'
allow_failures:
- python: 2.6
@ -84,3 +106,5 @@ matrix:
env: DJANGO='django17' CMS='cms32'
- python: 3.4
env: DJANGO='django18' CMS='cms32'
- python: 3.5
env: DJANGO='django18' CMS='cms32'

View file

@ -2,28 +2,34 @@
djangocms-blog
==============
.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg?style=flat-square
:target: https://pypi.python.org/pypi/djangocms-blog
:alt: Latest PyPI version
.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg
:target: https://pypi.python.org/pypi/djangocms-blog
:alt: Latest PyPI version
.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg?style=flat-square
:target: https://pypi.python.org/pypi/djangocms-blog
:alt: Monthly downloads
.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg
:target: https://travis-ci.org/nephila/djangocms-blog
:alt: Latest Travis CI build status
.. image:: https://img.shields.io/pypi/pyversions/djangocms-blog.svg?style=flat-square
:target: https://pypi.python.org/pypi/djangocms-blog
:alt: Python versions
.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg
:target: https://pypi.python.org/pypi/djangocms-blog
:alt: Monthly downloads
.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg?style=flat-square
:target: https://travis-ci.org/nephila/djangocms-blog
:alt: Latest Travis CI build status
.. image:: https://coveralls.io/repos/nephila/djangocms-blog/badge.png
:target: https://coveralls.io/r/nephila/djangocms-blog
:alt: Test coverage
.. image:: https://img.shields.io/coveralls/nephila/djangocms-blog/master.svg?style=flat-square
:target: https://coveralls.io/r/nephila/djangocms-blog?branch=master
:alt: Test coverage
.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg
.. image:: https://img.shields.io/codecov/c/github/nephila/djangocms-blog/master.svg?style=flat-square
:target: https://codecov.io/github/nephila/djangocms-blog
:alt: Test coverage
.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg?style=flat-square
:target: https://codeclimate.com/github/nephila/djangocms-blog
:alt: Code Climate
A djangoCMS 3 blog application.
Supported Django versions:
@ -46,6 +52,19 @@ Supported django CMS versions:
on South anymore; please install it separately if using this
application on Django 1.6.
Features
--------
* Placeholder content editing
* Frontend editing using django CMS 3.0 frontend editor
* Multilingual support using django-parler
* Support for Twitter cards, Open Graph and Google+ snippets meta tags
* Optional support for simpler TextField-based content editing
* Multisite support (posts can be visible in one or more Django sites on the
same project)
* Per-Apphook configuration
* Per-Apphook templates set
* Auto Apphook setup
Quickstart
----------
@ -85,6 +104,7 @@ Please, refer to each application documentation on details.
* django-filer: http://django-filer.readthedocs.org
* django-meta: https://github.com/nephila/django-meta#installation
* django-meta-mixin: https://github.com/nephila/django-meta-mixin#installation
* django-parler: http://django-parler.readthedocs.org/en/latest/quickstart.html#configuration
* django-taggit-autosuggest: https://bitbucket.org/fabian/django-taggit-autosuggest
@ -109,13 +129,14 @@ suited for your deployment.
META_SITE_PROTOCOL = 'http'
META_USE_SITES = True
* If you are using Django 1.7+, be aware than ``filer`` < 0.9.10, ``cmsplugin_filer``
and ``django-cms`` < 3.1 currently requires you to setup ``MIGRATION_MODULES`` in settings::
* If you are using Django 1.7+, be aware than ``filer`` < 0.9.10,
``cmsplugin_filer`` and ``django-cms`` < 3.1 currently requires you to
setup ``MIGRATION_MODULES`` in settings::
MIGRATION_MODULES = {
'cms': 'cms.migrations_django', # only for django CMS 3.0
'menus': 'menus.migrations_django', # only for django CMS 3.0
'filer': 'filer.migrations_django', # only for django filer 0.9.9 and below
'filer': 'filer.migrations_django', # only for django filer up to 0.9.9
'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django',
}
@ -139,10 +160,12 @@ suited for your deployment.
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
* To start your blog you need to use `AppHooks from django CMS <http://django-cms.readthedocs.org/en/support-3.0.x/how_to/apphooks.html>`_
to add the blog to a django CMS page:
to add the blog to a django CMS page; this step is not required when using
**Auto setup** (see below):
* Create a new django CMS page
* Go to Advanced settings and select Blog from the Application selector;
* Go to Advanced settings and select Blog from the Application selector and
create an application configuration;
* Eventually customise the Application instance name;
* Publish the page
* Restart the project instance to properly load blog urls.
@ -154,26 +177,165 @@ suited for your deployment.
* Create a new blog entry in django admin backend or from the toolbar
* Click on "view on site" button to view the post detail page
* Edit the post via djangocms frontend by adding / editing plugins
* Publish the blog post by flagging the "Publish" switch in the blog post admin
* Publish the blog post by flagging the "Publish" switch in the blog post
admin
Configurable permalinks
+++++++++++++++++++++++
Blog comes with four different styles of permalinks styles:
* Full date: ``YYYY/MM/DD/SLUG``
* Year / Month: ``YYYY/MM/SLUG``
* Category: ``CATEGORY/SLUG``
* Just slug: ``SLUG``
As all the styles are loaded in the urlconf, the latter two does not allow
to have CMS pages beneath the page the blog is attached to. If you want to
do this, you have to override the default urlconfs by setting something
like the following in the project settings::
BLOG_PERMALINK_URLS = {
'full_date': r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$',
'short_date': r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<slug>\w[-\w]*)/$',
'category': r'^post/(?P<category>\w[-\w]*)/(?P<slug>\w[-\w]*)/$',
'slug': r'^post/(?P<slug>\w[-\w]*)/$',
}
And change ``post/`` with the desired prefix.
Templates
+++++++++
To ease the template customisations a ``djangocms_blog/base.html`` template is used by all the blog templates;
the templates itself extends a ``base.html`` template; content is pulled in the ``content`` block.
If you need to define a different base template, or if your base template does not defines a ``content`` block,
copy in your template directory ``djangocms_blog/base.html`` and customise it according to your
needs; the other application templates will use the newly created template and will ignore the bundled one.
To ease the template customisations a ``djangocms_blog/base.html`` template is
used by all the blog templates; the templates itself extends a ``base.html``
template; content is pulled in the ``content`` block.
If you need to define a different base template, or if your base template does
not defines a ``content`` block, copy in your template directory
``djangocms_blog/base.html`` and customise it according to your needs; the
other application templates will use the newly created base template and
will ignore the bundled one.
Features
--------
Templates set
+++++++++++++
By using **Apphook configuration** you can define a different templates set.
To use this feature provide a directory name in **Template prefix** field in
the **Apphook configuration** admin (in *Layout* section): it will be the
root of your custom templates set.
Auto setup
++++++++++
``djangocms_blog`` can install and configue itself if it does not find any
attached instance of itself.
This feature is enable by default and will create:
* a ``BlogConfig`` with default values
* a ``Blog`` CMS page and will attach ``djangocms_blog`` instance to it
* a **home page** is no, home is found.
All the items will be created in every language configured for the website
and the pages will be published. If not using **aldryn-apphook-reload** or
**django CMS 3.2** auto-reload middleware you are required to reload the
project instance after this.
This will only work for the current website as detected by
``Site.objects.get_current()``.
The auto setup is execute once for each server start but it will skip any
action if a ``BlogConfig`` instance is found.
Global Settings
---------------
* BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post
lists; it's a dictionary with ``size``, ``crop`` and ``upscale`` keys;
(default: ``{'size': '120x120', 'crop': True,'upscale': False}``)
* BLOG_IMAGE_FULL_SIZE: Size of the main image when shown on the post
detail; it's a dictionary with ``size``, ``crop`` and ``upscale`` keys;
(default: ``{'size': '640x120', 'crop': True,'upscale': False}``)
* BLOG_PAGINATION: Number of post per page; (default: ``10``)
* BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin;
(default: ``5'')
* BLOG_POSTS_LIST_TRUNCWORDS_COUNT: Default number of words shown for
abstract in the post list; (default: ``100``)
* BLOG_TYPE: Generic type for the post object; (default: ``Article``)
* BLOG_TYPES: Choices of available blog types;
(default: to ``META_OBJECT_TYPES`` defined in `django-meta-mixin settings`_)
* BLOG_FB_TYPE: Open Graph type for the post object; (default: ``Article``)
* BLOG_FB_TYPES: Choices of available blog types;
(default: to ``META_FB_TYPES`` defined in `django-meta-mixin settings`_)
* BLOG_FB_APPID: Facebook Application ID
* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author
* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher
* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author
* BLOG_FB_AUTHOR: Facebook profile URL of the post author
* BLOG_TWITTER_TYPE: Twitter Card type for the post object;
(default: ``Summary``)
* BLOG_TWITTER_TYPES: Choices of available blog types for twitter;
(default: to ``META_TWITTER_TYPES`` defined in `django-meta-mixin settings`_)
* BLOG_TWITTER_SITE: Twitter account of the site
* BLOG_TWITTER_AUTHOR: Twitter account of the post author
* BLOG_GPLUS_TYPE: Google+ Snippet type for the post object;
(default: ``Blog``)
* BLOG_GPLUS_TYPES: Choices of available blog types for twitter;
(default: to ``META_GPLUS_TYPES`` defined in `django-meta-mixin settings`_)
* BLOG_GPLUS_AUTHOR: Google+ account of the post author
* BLOG_ENABLE_COMMENTS: Whether to enable comments by default on posts;
while ``djangocms_blog`` does not ship any comment system, this flag
can be used to control the chosen comments framework; (default: ``True``)
* BLOG_USE_ABSTRACT: Use an abstract field for the post; if ``False``
no abstract field is available for every post; (default: ``True``)
* BLOG_USE_PLACEHOLDER: Post content is managed via placeholder;
if ``False`` a simple HTMLField is used; (default: ``True``)
* BLOG_MULTISITE: Add support for multisite setup; (default: ``True``)
* BLOG_MENU_TYPE: Structure of the Blog menu;
(default: ``Posts and Categories``)
* BLOG_AUTHOR_DEFAULT: Use a default if not specified; if set to ``True`` the
current user is set as the default author, if set to ``False`` no default
author is set, if set to a string the user with the provided username is
used; (default: ``True``)
* BLOG_DEFAULT_PUBLISHED: If posts are marked as published by default;
(default: ``False``)
* BLOG_AVAILABLE_PERMALINK_STYLES: Choices of permalinks styles;
* BLOG_PERMALINK_URLS: URLConf corresponding to
BLOG_AVAILABLE_PERMALINK_STYLES;
* BLOG_AUTO_SETUP: Enable the blog **Auto setup** feature; (default: ``True``)
* BLOG_AUTO_HOME_TITLE: Title of the home page created by **Auto setup**;
(default: ``Home``)
* BLOG_AUTO_BLOG_TITLE: Title of the blog page created by **Auto setup**;
(default: ``Blog``)
* BLOG_AUTO_APP_TITLE: Title of the ``BlogConfig`` instance created by
**Auto setup**; (default: ``Blog``)
Per-Apphook settings
--------------------
* application title: Free text title that can be used as title in templates;
* Post published by default: Per-Apphook setting for BLOG_DEFAULT_PUBLISHED;
* Permalink structure: Per-Apphook setting for
BLOG_AVAILABLE_PERMALINK_STYLES;
* Use placeholder and plugins for article body: Per-Apphook setting for
BLOG_USE_PLACEHOLDER;
* Use abstract field: Per-Apphook setting for BLOG_USE_ABSTRACT;
* Set author: Per-Apphook setting for BLOG_AUTHOR_DEFAULT;
* Paginate sizePer-Apphook setting for BLOG_PAGINATION;
* Template prefix: Alternative directory to load the blog templates from;
* Menu structure: Per-Apphook setting for BLOG_MENU_TYPE
* Object type: Per-Apphook setting for BLOG_TYPE
* Facebook type: Per-Apphook setting for BLOG_FB_TYPE
* Facebook application ID: Per-Apphook setting for BLOG_FB_APP_ID
* Facebook profile ID: Per-Apphook setting for BLOG_FB_PROFILE_ID
* Facebook page URL: Per-Apphook setting for BLOG_FB_PUBLISHER
* Facebook author URL: Per-Apphook setting for BLOG_AUTHOR_URL
* Facebook author: Per-Apphook setting for BLOG_AUTHOR
* Twitter type: Per-Apphook setting for BLOG_TWITTER_TYPE
* Twitter site handle: Per-Apphook setting for BLOG_TWITTER_SITE
* Twitter author handle: Per-Apphook setting for BLOG_TWITTER_AUTHOR
* Google+ type: Per-Apphook setting for BLOG_GPLUS_TYPE
* Google+ author name: Per-Apphook setting for BLOG_GPLUS_AUTHOR
* Placeholder content editing
* Frontend editing using django CMS 3.0 frontend editor
* Multilingual support using django-parler
* Support for Twitter cards, Open Graph and Google+ snippets meta tags
* Optional support for simpler TextField-based content editing
* Multisite support (posts can be visible in one or more Django sites on the same project)
Import from Wordpress
+++++++++++++++++++++
@ -182,49 +344,11 @@ If you want to import content from existing wordpress blog, check
https://pypi.python.org/pypi/the-real-django-wordpress and
this gist https://gist.github.com/yakky/11336204 as a base.
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;
it's a dictionary with ``size``, ``crop`` and ``upscale`` keys;
(default: ``{'size': '120x120', 'crop': True,'upscale': False}``)
* BLOG_IMAGE_FULL_SIZE: Size of the main image when shown on the post detail;
it's a dictionary with ``size``, ``crop`` and ``upscale`` keys;
(default: ``{'size': '640x120', 'crop': True,'upscale': False}``)
* BLOG_PAGINATION: Number of post per page; (default: 10)
* BLOG_LATEST_POSTS: Default number of post in the **Latest post** plugin; (default: 5)
* BLOG_POSTS_LIST_TRUNCWORDS_COUNT: Default number of words shown for abstract in the post list; (default: 100)
* BLOG_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_FB_TYPE: Open Graph type for the post object; (default: Article)
* BLOG_FB_APPID: Facebook Application ID
* BLOG_FB_PROFILE_ID: Facebook profile ID of the post author
* BLOG_FB_PUBLISHER: Facebook URL of the blog publisher
* BLOG_FB_AUTHOR_URL: Facebook profile URL of the post author
* BLOG_FB_AUTHOR: Facebook profile URL of the post author
* BLOG_TWITTER_TYPE: Twitter Card type for the post object; (default: Summary)
* BLOG_TWITTER_SITE: Twitter account of the site
* BLOG_TWITTER_AUTHOR: Twitter account of the post author
* BLOG_GPLUS_TYPE: Google+ Snippet type for the post object; (default: Blog)
* BLOG_GPLUS_AUTHOR: Google+ account of the post author
Known djangocms-blog websites
+++++++++++++++++++++++++++++
* http://nephila.co.uk/blog
* https://blog.ungleich.ch/
.. _django-meta-mixin settings: https://github.com/nephila/django-meta-mixin#settings

View file

@ -18,6 +18,8 @@ HELPER_SETTINGS = dict(
'cmsplugin_filer_image',
'taggit',
'taggit_autosuggest',
'aldryn_apphooks_config',
'tests.test_utils',
],
LANGUAGE_CODE='en',
LANGUAGES=(
@ -71,6 +73,9 @@ HELPER_SETTINGS = dict(
MIGRATION_MODULES={
'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django',
},
CMS_TEMPLATES=(
('blog.html', 'Blog template'),
),
META_SITE_PROTOCOL='http',
META_SITE_DOMAIN='example.com',
META_USE_OG_PROPERTIES=True,

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
__author__ = 'Iacopo Spalletti'
__email__ = 'i.spalletti@nephila.it'
__version__ = '0.5.1.dev1'
__version__ = '0.6.0.dev1'
default_app_config = 'djangocms_blog.apps.BlogAppConfig'

View file

@ -3,13 +3,17 @@ from __future__ import absolute_import, print_function, unicode_literals
from copy import deepcopy
from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig
from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin
from django import forms
from django.conf import settings
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.utils.translation import ugettext_lazy as _
from parler.admin import TranslatableAdmin
from .cms_appconfig import BlogConfig
from .forms import PostAdminForm
from .models import BlogCategory, Post
from .settings import get_setting
@ -20,27 +24,33 @@ except ImportError:
pass
class BlogCategoryAdmin(EnhancedModelAdminMixin, TranslatableAdmin):
class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin):
def get_prepopulated_fields(self, request, obj=None):
app_config_default = self._app_config_select(request, obj)
if app_config_default is None and request.method == 'GET':
return {}
return {'slug': ('name',)}
class Media:
css = {
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL,
'djangocms_blog_admin.css'),)
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
}
class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
PlaceholderAdminMixin, TranslatableAdmin):
list_display = ['title', 'author', 'date_published', 'date_published_end']
class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
ModelAppHookConfig, TranslatableAdmin):
form = PostAdminForm
list_display = [
'title', 'author', 'date_published', 'app_config', 'languages', 'date_published_end'
]
list_filter = ('app_config',)
date_hierarchy = 'date_published'
raw_id_fields = ['author']
frontend_editable_fields = ('title', 'abstract', 'post_text')
enhance_exclude = ('main_image', 'tags')
_fieldsets = [
(None, {
'fields': [('title', 'categories', 'publish')]
'fields': [('title', 'categories', 'publish', 'app_config')]
}),
('Info', {
'fields': (['slug', 'tags'],
@ -57,6 +67,13 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
}),
]
app_config_values = {
'default_published': 'publish'
}
def languages(self, obj):
return ','.join(obj.get_available_languages())
def formfield_for_dbfield(self, db_field, **kwargs):
field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'meta_description':
@ -68,11 +85,25 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
return field
def get_fieldsets(self, request, obj=None):
app_config_default = self._app_config_select(request, obj)
if app_config_default is None and request.method == 'GET':
return super(PostAdmin, self).get_fieldsets(request, obj)
if not obj:
config = app_config_default
else:
config = obj.app_config
fsets = deepcopy(self._fieldsets)
if get_setting('USE_ABSTRACT'):
fsets[0][1]['fields'].append('abstract')
if not get_setting('USE_PLACEHOLDER'):
fsets[0][1]['fields'].append('post_text')
if config:
if config.use_abstract:
fsets[0][1]['fields'].append('abstract')
if not config.use_placeholder:
fsets[0][1]['fields'].append('post_text')
else:
if get_setting('USE_ABSTRACT'):
fsets[0][1]['fields'].append('abstract')
if not get_setting('USE_PLACEHOLDER'):
fsets[0][1]['fields'].append('post_text')
if get_setting('MULTISITE'):
fsets[1][1]['fields'][0].append('sites')
if request.user.is_superuser:
@ -83,7 +114,7 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
return {'slug': ('title',)}
def save_model(self, request, obj, form, change):
if not obj.author_id and get_setting('AUTHOR_DEFAULT'):
if not obj.author_id and obj.app_config.set_author:
if get_setting('AUTHOR_DEFAULT') is True:
user = request.user
else:
@ -93,10 +124,63 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
class Media:
css = {
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL,
'djangocms_blog_admin.css'),)
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
}
class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
@property
def declared_fieldsets(self):
return [
(None, {
'fields': ('type', 'namespace', 'app_title')
}),
('Generic', {
'fields': (
'config.default_published', 'config.use_placeholder', 'config.use_abstract',
'config.set_author',
)
}),
('Layout', {
'fields': (
'config.paginate_by', 'config.url_patterns', 'config.template_prefix',
'config.menu_structure',
),
'classes': ('collapse',)
}),
('Meta', {
'fields': (
'config.object_type',
)
}),
('Open Graph', {
'fields': (
'config.og_type', 'config.og_app_id', 'config.og_profile_id',
'config.og_publisher', 'config.og_author_url', 'config.og_author',
),
'description': _(
'You can provide plain strings, Post model attribute or method names'
)
}),
('Twitter', {
'fields': (
'config.twitter_type', 'config.twitter_site', 'config.twitter_author',
),
'description': _(
'You can provide plain strings, Post model attribute or method names'
)
}),
('Google+', {
'fields': (
'config.gplus_type', 'config.gplus_author',
),
'description': _(
'You can provide plain strings, Post model attribute or method names'
)
}),
]
admin.site.register(BlogCategory, BlogCategoryAdmin)
admin.site.register(Post, PostAdmin)
admin.site.register(BlogConfig, BlogConfigAdmin)

65
djangocms_blog/apps.py Normal file
View file

@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from django.utils.translation import ugettext_lazy as _
try:
from django.apps import AppConfig
except ImportError:
class AppConfig(object):
pass
class BlogAppConfig(AppConfig):
name = 'djangocms_blog'
verbose_name = _('django CMS Blog')
@staticmethod
def setup():
from cms.api import create_page, create_title
from cms.exceptions import NoHomeFound
from cms.models import Page
from cms.utils import get_language_list
from cms.utils.conf import get_templates
from django.utils.translation import override
from .cms_appconfig import BlogConfig
from .settings import get_setting
if get_setting('AUTO_SETUP'):
configs = BlogConfig.objects.all()
if not configs.exists():
config = BlogConfig.objects.create(namespace='Blog')
langs = get_language_list()
blog = None
for lang in langs:
with override(lang):
config.set_current_language(lang)
config.app_title = get_setting('AUTO_APP_TITLE')
config.save()
default_template = get_templates()[0][0]
try:
home = Page.objects.get_home()
except NoHomeFound:
home = None
if not home:
home = create_page(
get_setting('AUTO_HOME_TITLE'), language=lang,
template=default_template, in_navigation=True, published=True
)
elif lang not in home.get_languages():
create_title(
language=lang, title=get_setting('AUTO_HOME_TITLE'), page=home
)
home.publish(lang)
if not blog:
blog = create_page(
get_setting('AUTO_BLOG_TITLE'), language=lang, apphook='BlogApp',
apphook_namespace=config.namespace, parent=home,
template=default_template, in_navigation=True, published=True
)
else:
create_title(
language=lang, title=get_setting('AUTO_BLOG_TITLE'), page=blog
)
blog.publish(lang)

View file

@ -1,17 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from cms.app_base import CMSApp
from aldryn_apphooks_config.app_base import CMSConfigApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
from .cms_appconfig import BlogConfig
from .menu import BlogCategoryMenu
class BlogApp(CMSApp):
class BlogApp(CMSConfigApp):
name = _('Blog')
urls = ['djangocms_blog.urls']
app_name = 'djangocms_blog'
app_config = BlogConfig
menus = [BlogCategoryMenu]
apphook_pool.register(BlogApp)

View file

@ -0,0 +1,110 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from aldryn_apphooks_config.models import AppHookConfig
from aldryn_apphooks_config.utils import setup_config
from app_data import AppDataForm
from django import forms
from django.db import models
from django.utils.translation import ugettext_lazy as _
from parler.models import TranslatableModel, TranslatedFields
from .settings import MENU_TYPE_COMPLETE, get_setting
class BlogConfig(TranslatableModel, AppHookConfig):
"""
Adds some translatable, per-app-instance fields.
"""
translations = TranslatedFields(
app_title=models.CharField(_('application title'), max_length=234),
)
def get_app_title(self):
return getattr(self, 'app_title', _('untitled'))
class BlogConfigForm(AppDataForm):
default_published = forms.BooleanField(
label=_('Post published by default'), required=False,
initial=get_setting('DEFAULT_PUBLISHED')
)
url_patterns = forms.ChoiceField(
label=_('Permalink structure'), required=False,
initial=get_setting('AVAILABLE_PERMALINK_STYLES')[0][0],
choices=get_setting('AVAILABLE_PERMALINK_STYLES')
)
use_placeholder = forms.BooleanField(
label=_('Use placeholder and plugins for article body'), required=False,
initial=get_setting('USE_PLACEHOLDER')
)
use_abstract = forms.BooleanField(
label=_('Use abstract field'), required=False,
initial=get_setting('USE_ABSTRACT')
)
set_author = forms.BooleanField(
label=_('Set author'), required=False, help_text=_('Set author by default'),
initial=get_setting('AUTHOR_DEFAULT')
)
paginate_by = forms.IntegerField(
label=_('Paginate size'), required=False, initial=get_setting('PAGINATION'),
help_text=_('When paginating list views, how many articles per page?')
)
template_prefix = forms.CharField(
label=_('Template prefix'), required=False, initial='',
help_text=_('Alternative directory to load the blog templates from')
)
menu_structure = forms.ChoiceField(
label=_('Menu structure'), required=True,
choices=get_setting('MENU_TYPES'), initial=MENU_TYPE_COMPLETE,
help_text=_('Structure of the django CMS menu')
)
object_type = forms.ChoiceField(
label=_('Object type'), required=False,
choices=get_setting('TYPES'), initial=get_setting('TYPE')
)
og_type = forms.ChoiceField(
label=_('Facebook type'), required=False,
choices=get_setting('FB_TYPES'), initial=get_setting('FB_TYPE')
)
og_app_id = forms.CharField(
max_length=200, label=_('Facebook application ID'), required=False,
initial=get_setting('FB_PROFILE_ID')
)
og_profile_id = forms.CharField(
max_length=200, label=_('Facebook profile ID'), required=False,
initial=get_setting('FB_PROFILE_ID')
)
og_publisher = forms.CharField(
max_length=200, label=_('Facebook page URL'), required=False,
initial=get_setting('FB_PUBLISHER')
)
og_author_url = forms.CharField(
max_length=200, label=_('Facebook author URL'), required=False,
initial=get_setting('FB_AUTHOR_URL')
)
og_author = forms.CharField(
max_length=200, label=_('Facebook author'), required=False,
initial=get_setting('FB_AUTHOR')
)
twitter_type = forms.ChoiceField(
label=_('Twitter type'), required=False,
choices=get_setting('TWITTER_TYPES'), initial=get_setting('TWITTER_TYPE')
)
twitter_site = forms.CharField(
max_length=200, label=_('Twitter site handle'), required=False,
initial=get_setting('TWITTER_SITE')
)
twitter_author = forms.CharField(
max_length=200, label=_('Twitter author handle'), required=False,
initial=get_setting('TWITTER_AUTHOR')
)
gplus_type = forms.ChoiceField(
label=_('Google+ type'), required=False,
choices=get_setting('GPLUS_TYPES'), initial=get_setting('GPLUS_TYPE')
)
gplus_author = forms.CharField(
max_length=200, label=_('Google+ author name'), required=False,
initial=get_setting('GPLUS_AUTHOR')
)
setup_config(BlogConfigForm, BlogConfig)

View file

@ -1,32 +1,39 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from cms.models.pluginmodel import CMSPlugin
import os.path
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
from django.utils.translation import ugettext_lazy as _
from .forms import LatestEntriesForm
from .models import AuthorEntriesPlugin, BlogCategory, LatestPostsPlugin, Post
from .models import AuthorEntriesPlugin, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, Post
from .settings import get_setting
class BlogPlugin(CMSPluginBase):
module = 'Blog'
def get_render_template(self, context, instance, placeholder):
if instance.app_config.template_prefix:
return os.path.join(instance.app_config.template_prefix, self.base_render_template)
else:
return os.path.join('djangocms_blog', self.base_render_template)
class BlogLatestEntriesPlugin(BlogPlugin):
"""
Non cached plugin which returns the latest posts taking into account the
user / toolbar state
"""
render_template = 'djangocms_blog/plugins/latest_entries.html'
name = _('Latest Blog Articles')
model = LatestPostsPlugin
form = LatestEntriesForm
filter_horizontal = ('categories',)
fields = ('latest_posts', 'tags', 'categories')
cache = False
base_render_template = 'plugins/latest_entries.html'
def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPlugin, self).render(context, instance, placeholder)
@ -39,12 +46,12 @@ class BlogLatestEntriesPluginCached(BlogPlugin):
"""
Cached plugin which returns the latest published posts
"""
render_template = 'djangocms_blog/plugins/latest_entries.html'
name = _('Latest Blog Articles')
model = LatestPostsPlugin
form = LatestEntriesForm
filter_horizontal = ('categories',)
fields = ('latest_posts', 'tags', 'categories')
base_render_template = 'plugins/latest_entries.html'
def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder)
@ -57,8 +64,7 @@ class BlogAuthorPostsPlugin(BlogPlugin):
module = _('Blog')
name = _('Author Blog Articles')
model = AuthorEntriesPlugin
form = LatestEntriesForm
render_template = 'djangocms_blog/plugins/authors.html'
base_render_template = 'plugins/authors.html'
filter_horizontal = ['authors']
def render(self, context, instance, placeholder):
@ -70,36 +76,47 @@ class BlogAuthorPostsPlugin(BlogPlugin):
class BlogTagsPlugin(BlogPlugin):
module = _('Blog')
name = _('Tags')
model = CMSPlugin
render_template = 'djangocms_blog/plugins/tags.html'
model = GenericBlogPlugin
base_render_template = 'plugins/tags.html'
def render(self, context, instance, placeholder):
context = super(BlogTagsPlugin, self).render(context, instance, placeholder)
context['tags'] = Post.objects.tag_cloud(queryset=Post.objects.published())
qs = Post._default_manager
qs_post = qs
if instance.app_config:
qs_post = qs_post.namespace(instance.app_config.namespace)
context['tags'] = qs.tag_cloud(queryset=qs_post.published())
return context
class BlogCategoryPlugin(BlogPlugin):
module = _('Blog')
name = _('Categories')
model = CMSPlugin
render_template = 'djangocms_blog/plugins/categories.html'
model = GenericBlogPlugin
base_render_template = 'plugins/categories.html'
def render(self, context, instance, placeholder):
context = super(BlogCategoryPlugin, self).render(context, instance, placeholder)
context['categories'] = BlogCategory.objects.all()
qs = BlogCategory._default_manager
if instance.app_config:
qs = qs.namespace(instance.app_config.namespace)
context['categories'] = qs
return context
class BlogArchivePlugin(BlogPlugin):
module = _('Blog')
name = _('Archive')
model = CMSPlugin
render_template = 'djangocms_blog/plugins/archive.html'
model = GenericBlogPlugin
base_render_template = 'plugins/archive.html'
def render(self, context, instance, placeholder):
context = super(BlogArchivePlugin, self).render(context, instance, placeholder)
context['dates'] = Post.objects.get_months(queryset=Post.objects.published())
qs = Post._default_manager
qs_post = qs
if instance.app_config:
qs_post = qs.namespace(instance.app_config.namespace)
context['dates'] = qs.get_months(queryset=qs_post.published())
return context

View file

@ -4,9 +4,9 @@ from __future__ import absolute_import, print_function, unicode_literals
from cms.toolbar_base import CMSToolbar
from cms.toolbar_pool import toolbar_pool
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import override, ugettext_lazy as _
from .models import BLOG_CURRENT_POST_IDENTIFIER
from .models import BLOG_CURRENT_NAMESPACE, BLOG_CURRENT_POST_IDENTIFIER
@toolbar_pool.register
@ -17,16 +17,21 @@ class BlogToolbar(CMSToolbar):
if not self.request.user.has_perm('djangocms_blog.add_post'):
return # pragma: no cover
admin_menu = self.toolbar.get_or_create_menu('djangocms_blog', _('Blog'))
url = reverse('admin:djangocms_blog_post_changelist')
admin_menu.add_modal_item(_('Post list'), url=url)
url = reverse('admin:djangocms_blog_post_add')
admin_menu.add_modal_item(_('Add post'), url=url)
with override(self.current_lang):
url = reverse('admin:djangocms_blog_post_changelist')
admin_menu.add_modal_item(_('Post list'), url=url)
url = reverse('admin:djangocms_blog_post_add')
admin_menu.add_modal_item(_('Add post'), url=url)
current_config = getattr(self.request, BLOG_CURRENT_NAMESPACE, None)
if current_config:
url = reverse('admin:djangocms_blog_blogconfig_change', args=(current_config.pk,))
admin_menu.add_modal_item(_('Edit configuration'), url=url)
current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None)
if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA
admin_menu.add_modal_item(_('Edit Post'), reverse(
'admin:djangocms_blog_post_change', args=(current_post.pk,)),
active=True)
current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None)
if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA
admin_menu.add_modal_item(_('Edit Post'), reverse(
'admin:djangocms_blog_post_change', args=(current_post.pk,)),
active=True)
def post_template_populate(self):
current_post = getattr(self.request, BLOG_CURRENT_POST_IDENTIFIER, None)

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from aldryn_apphooks_config.utils import get_app_instance
from django.contrib.sites.models import Site
from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse
@ -12,14 +13,18 @@ from .settings import get_setting
class LatestEntriesFeed(Feed):
def __call__(self, request, *args, **kwargs):
self.namespace, self.config = get_app_instance(request)
return super(LatestEntriesFeed, self).__call__(request, *args, **kwargs)
def link(self):
return reverse('djangocms_blog:posts-latest')
return reverse('%s:posts-latest' % self.namespace, current_app=self.namespace)
def title(self):
return _('Blog articles on %(site_name)s') % {'site_name': Site.objects.get_current().name}
def items(self, obj=None):
return Post.objects.published().order_by('-date_published')[:10]
return Post.objects.namespace(self.namespace).published().order_by('-date_published')[:10]
def item_title(self, item):
return item.safe_translation_getter('title')

View file

@ -3,8 +3,11 @@ from __future__ import absolute_import, print_function, unicode_literals
from django import forms
from django.conf import settings
from parler.forms import TranslatableModelForm
from taggit_autosuggest.widgets import TagAutoSuggest
from .models import BlogCategory, BlogConfig, Post
class LatestEntriesForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
@ -16,3 +19,29 @@ class LatestEntriesForm(forms.ModelForm):
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL,
'djangocms_blog_admin.css'),)
}
class PostAdminForm(TranslatableModelForm):
class Meta:
model = Post
fields = '__all__'
def __init__(self, *args, **kwargs):
super(PostAdminForm, self).__init__(*args, **kwargs)
qs = BlogCategory.objects
if getattr(self.instance, 'app_config_id', None):
qs = qs.namespace(self.instance.app_config.namespace)
elif 'initial' in kwargs and 'app_config' in kwargs['initial']:
config = BlogConfig.objects.get(pk=kwargs['initial']['app_config'])
qs = qs.namespace(config.namespace)
if 'categories' in self.fields:
self.fields['categories'].queryset = qs
if 'app_config' in self.fields:
# Don't allow app_configs to be added here. The correct way to add an
# apphook-config is to create an apphook on a cms Page.
self.fields['app_config'].widget.can_add_related = False

View file

@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-30 12:50+0100\n"
"POT-Creation-Date: 2015-09-30 09:56+0200\n"
"PO-Revision-Date: 2014-03-05 18:09+0100\n"
"Last-Translator: Iacopo Spalletti\n"
"Language-Team: Italian <i.spalletti@nephila.it>\n"
@ -17,189 +17,361 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.5\n"
#: cms_app.py:8 cms_plugins.py:32 cms_plugins.py:45 cms_plugins.py:56
#: cms_plugins.py:67 cms_toolbar.py:18
#: cms_app.py:13 cms_plugins.py:64 cms_plugins.py:77 cms_plugins.py:93
#: cms_plugins.py:108 cms_toolbar.py:19
msgid "Blog"
msgstr "Blog"
#: cms_plugins.py:20
#: cms_appconfig.py:20
msgid "application title"
msgstr ""
#: cms_appconfig.py:24
#, fuzzy
msgid "untitled"
msgstr "Title"
#: cms_appconfig.py:29
msgid "Post published by default"
msgstr ""
#: cms_appconfig.py:33
msgid "Permalink structure"
msgstr ""
#: cms_appconfig.py:38
msgid "Use placeholder and plugins for article body"
msgstr ""
#: cms_appconfig.py:42
msgid "Use abstract field"
msgstr ""
#: cms_appconfig.py:46
#, fuzzy
msgid "Set author"
msgstr "Author"
#: cms_appconfig.py:46
msgid "Set author by default"
msgstr ""
#: cms_appconfig.py:50
msgid "Paginate size"
msgstr ""
#: cms_appconfig.py:51
msgid "When paginating list views, how many articles per page?"
msgstr ""
#: cms_appconfig.py:54
msgid "Template prefix"
msgstr ""
#: cms_appconfig.py:55
msgid "Alternative directory to load the blog templates from"
msgstr ""
#: cms_appconfig.py:58
msgid "Object type"
msgstr ""
#: cms_appconfig.py:62
msgid "Facebook type"
msgstr ""
#: cms_appconfig.py:66
msgid "Facebook application ID"
msgstr ""
#: cms_appconfig.py:69
msgid "Facebook profile ID"
msgstr ""
#: cms_appconfig.py:72
msgid "Facebook page URL"
msgstr ""
#: cms_appconfig.py:75
msgid "Facebook author URL"
msgstr ""
#: cms_appconfig.py:78
msgid "Twitter type"
msgstr ""
#: cms_appconfig.py:82
msgid "Twitter site handle"
msgstr ""
#: cms_appconfig.py:85
msgid "Twitter author handle"
msgstr ""
#: cms_appconfig.py:88
msgid "Google+ type"
msgstr ""
#: cms_appconfig.py:92
msgid "Google+ author name"
msgstr ""
#: cms_plugins.py:30 cms_plugins.py:49
msgid "Latest Blog Articles"
msgstr "Latest Blog Articles"
#: cms_plugins.py:33
#: cms_plugins.py:65
msgid "Author Blog Articles"
msgstr "Author Blog Articles"
#: cms_plugins.py:46 templates/djangocms_blog/plugins/tags.html:4
#: cms_plugins.py:78 templates/djangocms_blog/plugins/tags.html:3
msgid "Tags"
msgstr "Tags"
#: cms_plugins.py:57 templates/djangocms_blog/plugins/categories.html:4
#: cms_plugins.py:94 templates/djangocms_blog/plugins/categories.html:3
msgid "Categories"
msgstr "Categories"
#: cms_plugins.py:68 templates/djangocms_blog/post_list.html:12
#: templates/djangocms_blog/plugins/archive.html:4
#: cms_plugins.py:109 templates/djangocms_blog/post_list.html:12
#: templates/djangocms_blog/plugins/archive.html:3
msgid "Archive"
msgstr "Archive"
#: cms_toolbar.py:20
#: cms_toolbar.py:22
msgid "Post list"
msgstr "Post list"
#: cms_toolbar.py:22
#: cms_toolbar.py:24
msgid "Add post"
msgstr "Add post"
#: cms_toolbar.py:26
#: cms_toolbar.py:28
msgid "Edit configuration"
msgstr ""
#: cms_toolbar.py:32
msgid "Edit Post"
msgstr ""
#: feeds.py:16
#: feeds.py:24
#, python-format
msgid "Blog articles on %(site_name)s"
msgstr "Blog articles on %(site_name)s"
#: models.py:31
#: menu.py:14
#, fuzzy
msgid "Blog Category menu"
msgstr "blog category"
#: models.py:34
msgid "parent"
msgstr "parent"
#: models.py:33
#: models.py:35
msgid "created at"
msgstr "created at"
#: models.py:34
#: models.py:36
msgid "modified at"
msgstr "modified at"
#: models.py:37
#: models.py:37 models.py:120 models.py:253
msgid "app. config"
msgstr ""
#: models.py:40
msgid "name"
msgstr "name"
#: models.py:38 models.py:105
#: models.py:41 models.py:124
msgid "slug"
msgstr "slug"
#: models.py:45
#: models.py:48
msgid "blog category"
msgstr "blog category"
#: models.py:46
#: models.py:49
msgid "blog categories"
msgstr "blog categories"
#: models.py:70
msgid "Author"
#: models.py:89
#, fuzzy
msgid "author"
msgstr "Author"
#: models.py:75
msgid "Published Since"
#: models.py:92
#, fuzzy
msgid "created"
msgstr "created at"
#: models.py:93
#, fuzzy
msgid "last modified"
msgstr "modified at"
#: models.py:94
#, fuzzy
msgid "published since"
msgstr "Published Since"
#: models.py:77
msgid "Published Until"
#: models.py:96
#, fuzzy
msgid "published until"
msgstr "Published Until"
#: models.py:79
msgid "Publish"
#: models.py:98
#, fuzzy
msgid "publish"
msgstr "Publish"
#: models.py:80
#: models.py:99
msgid "category"
msgstr "category"
#: models.py:82
msgid "Main image"
#: models.py:101
#, fuzzy
msgid "main image"
msgstr "Main image"
#: models.py:85
msgid "Main image thumbnail"
#: models.py:105
#, fuzzy
msgid "main image thumbnail"
msgstr "Main image thumbnail"
#: models.py:89
msgid "Main image full"
#: models.py:110
#, fuzzy
msgid "main image full"
msgstr "Main image full"
#: models.py:93
msgid "Enable comments on post"
#: models.py:114
msgid "enable comments on post"
msgstr ""
#: models.py:96
#: models.py:116
msgid "Site(s)"
msgstr ""
#: models.py:98
#: models.py:117
msgid ""
"Select sites in which to show the post. If none is set it will be visible in "
"all the configured sites."
msgstr ""
#: models.py:104
msgid "Title"
#: models.py:123
#, fuzzy
msgid "title"
msgstr "Title"
#: models.py:106
msgid "Abstract"
#: models.py:125
msgid "abstract"
msgstr ""
#: models.py:107
msgid "Post meta description"
msgstr "Post meta description"
#: models.py:109
#: models.py:126
#, fuzzy
msgid "Post meta keywords"
msgid "post meta description"
msgstr "Post meta description"
#: models.py:111
#: models.py:128
#, fuzzy
msgid "Post meta title"
msgid "post meta keywords"
msgstr "Post meta description"
#: models.py:112
#: models.py:130
#, fuzzy
msgid "post meta title"
msgstr "Post meta description"
#: models.py:131
msgid "used in title tag and social sharing"
msgstr ""
#: models.py:115
msgid "Text"
msgstr "Text"
#: models.py:134
msgid "text"
msgstr ""
#: models.py:178
#: models.py:170
msgid "blog article"
msgstr "blog article"
#: models.py:179
#: models.py:171
msgid "blog articles"
msgstr "blog articles"
#: models.py:222 models.py:250
msgid "Articles"
msgstr "Articles"
#: models.py:269
msgid "generic blog plugin"
msgstr ""
#: models.py:223
#: models.py:273 models.py:306
#, fuzzy
msgid "articles"
msgstr "0 articles"
#: models.py:274
msgid "The number of latests articles to be displayed."
msgstr "The number of latests articles to be displayed."
#: models.py:225
#: models.py:276
msgid "filter by tag"
msgstr ""
#: models.py:277
msgid "Show only the blog articles tagged with chosen tags."
msgstr "Show only the blog articles tagged with chosen tags."
#: models.py:227
#: models.py:280
#, fuzzy
msgid "filter by category"
msgstr "blog category"
#: models.py:281
msgid "Show only the blog articles tagged with chosen categories."
msgstr "Show only the blog articles tagged with chosen categories."
#: models.py:246 templates/djangocms_blog/plugins/authors.html:3
msgid "Authors"
#: models.py:285
#, python-format
msgid "%s latest articles by tag"
msgstr ""
#: models.py:302
#, fuzzy
msgid "authors"
msgstr "Authors"
#: models.py:251
#: models.py:307
msgid "The number of author articles to be displayed."
msgstr "The number of author articles to be displayed."
#: templates/djangocms_blog/post_detail.html:18
#: templates/djangocms_blog/includes/blog_item.html:11
msgid "by"
msgstr "by"
#: models.py:311
#, python-format
msgid "%s latest articles by author"
msgstr ""
#: settings.py:11
#, fuzzy
msgid "Article"
msgstr "Articles"
#: settings.py:12
msgid "Website"
msgstr ""
#: settings.py:17
msgid "Full date"
msgstr ""
#: settings.py:18
msgid "Year / Month"
msgstr ""
#: settings.py:19 templates/djangocms_blog/post_list.html:14
msgid "Category"
msgstr "Category"
#: settings.py:20
#, fuzzy
msgid "Just slug"
msgstr "slug"
#: templates/djangocms_blog/post_list.html:11
msgid "Articles by"
@ -209,16 +381,11 @@ msgstr "Articles by"
msgid "Tag"
msgstr "Tag"
#: templates/djangocms_blog/post_list.html:14
msgid "Category"
msgstr "Category"
#: templates/djangocms_blog/post_list.html:21
#: templates/djangocms_blog/plugins/archive.html:27
#: templates/djangocms_blog/plugins/archive.html:26
#: templates/djangocms_blog/plugins/authors.html:15
#: templates/djangocms_blog/plugins/categories.html:16
#: templates/djangocms_blog/plugins/latest_entries.html:7
#: templates/djangocms_blog/plugins/tags.html:16
#: templates/djangocms_blog/plugins/tags.html:15
msgid "No article found."
msgstr "No article found."
@ -226,43 +393,59 @@ msgstr "No article found."
msgid "Back"
msgstr "Back"
#: templates/djangocms_blog/post_list.html:30
#: templates/djangocms_blog/post_list.html:29
msgid "previous"
msgstr "previous"
#: templates/djangocms_blog/post_list.html:33
#: templates/djangocms_blog/post_list.html:32
msgid "Page"
msgstr "Page"
#: templates/djangocms_blog/post_list.html:33
#: templates/djangocms_blog/post_list.html:32
msgid "of"
msgstr "of"
#: templates/djangocms_blog/post_list.html:36
#: templates/djangocms_blog/post_list.html:35
msgid "next"
msgstr "next"
#: templates/djangocms_blog/includes/blog_item.html:46
#: templates/djangocms_blog/includes/blog_item.html:24
msgid "read more"
msgstr "read more"
#: templates/djangocms_blog/plugins/archive.html:18
#: templates/djangocms_blog/includes/blog_meta.html:6
msgid "by"
msgstr "by"
#: templates/djangocms_blog/plugins/archive.html:17
#: templates/djangocms_blog/plugins/authors.html:10
#: templates/djangocms_blog/plugins/categories.html:11
#: templates/djangocms_blog/plugins/tags.html:11
#: templates/djangocms_blog/plugins/categories.html:10
#: templates/djangocms_blog/plugins/tags.html:10
#, python-format
msgid "1 article"
msgid_plural "%(articles)s articles"
msgstr[0] "1 article"
msgstr[1] "%(articles)s articles"
#: templates/djangocms_blog/plugins/archive.html:19
#: templates/djangocms_blog/plugins/archive.html:18
#: templates/djangocms_blog/plugins/authors.html:11
#: templates/djangocms_blog/plugins/categories.html:12
#: templates/djangocms_blog/plugins/tags.html:12
#: templates/djangocms_blog/plugins/categories.html:11
#: templates/djangocms_blog/plugins/tags.html:11
msgid "0 articles"
msgstr "0 articles"
#: templates/djangocms_blog/plugins/authors.html:3
msgid "Authors"
msgstr "Authors"
#: templates/djangocms_blog/plugins/categories.html:15
#, fuzzy
msgid "No categories found."
msgstr "No article found."
#~ msgid "Text"
#~ msgstr "Text"
#~ msgid "blog post"
#~ msgstr "blog post"

View file

@ -2,10 +2,12 @@
from __future__ import absolute_import, print_function, unicode_literals
import django
from aldryn_apphooks_config.managers.parler import (
AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset,
)
from django.contrib.sites.models import Site
from django.db import models
from django.utils.timezone import now
from parler.managers import TranslatableQuerySet, TranslationManager
try:
from collections import Counter
@ -21,7 +23,7 @@ class TaggedFilterItem(object):
o con gli stessi tag di un model o un queryset
"""
tags = self._taglist(other_model, queryset)
return self.get_queryset().filter(taglist__in=tags)
return self.get_queryset().filter(tags__in=tags).distinct()
def _taglist(self, other_model=None, queryset=None):
"""
@ -29,21 +31,21 @@ class TaggedFilterItem(object):
o queryset passati come argomento
"""
from taggit.models import TaggedItem
filtro = None
filter = None
if queryset is not None:
filtro = set()
filter = set()
for item in queryset.all():
filtro.update(item.tags.all())
filtro = set([tag.id for tag in filtro])
filter.update(item.tags.all())
filter = set([tag.id for tag in filter])
elif other_model is not None:
filtro = set(TaggedItem.objects.filter(
filter = set(TaggedItem.objects.filter(
content_type__model=other_model.__name__.lower()
).values_list('tag_id', flat=True))
tags = set(TaggedItem.objects.filter(
content_type__model=self.model.__name__.lower()
).values_list('tag_id', flat=True))
if filtro is not None:
tags = tags.intersection(filtro)
if filter is not None:
tags = tags.intersection(filter)
return list(tags)
def tag_list(self, other_model=None, queryset=None):
@ -76,7 +78,7 @@ class TaggedFilterItem(object):
return sorted(tags, key=lambda x: -x.count)
class GenericDateQuerySet(TranslatableQuerySet):
class GenericDateQuerySet(AppHookConfigTranslatableQueryset):
start_date_field = 'date_published'
end_date_field = 'date_published_end'
publish_field = 'publish'
@ -120,7 +122,7 @@ class GenericDateQuerySet(TranslatableQuerySet):
return self.active_translations(language_code=language).on_site()
class GenericDateTaggedManager(TaggedFilterItem, TranslationManager):
class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager):
use_for_related_fields = True
queryset_class = GenericDateQuerySet

View file

@ -3,66 +3,75 @@ from __future__ import absolute_import, print_function, unicode_literals
from cms.menu_bases import CMSAttachMenu
from django.db.models.signals import post_delete, post_save
from django.utils.translation import get_language, ugettext_lazy as _
from menus.base import Modifier, NavigationNode
from django.utils.translation import get_language_from_request, ugettext_lazy as _
from menus.base import NavigationNode
from menus.menu_pool import menu_pool
from .models import BlogCategory
from .cms_appconfig import BlogConfig
from .models import BlogCategory, Post
from .settings import MENU_TYPE_CATEGORIES, MENU_TYPE_COMPLETE, MENU_TYPE_POSTS
class BlogCategoryMenu(CMSAttachMenu):
name = _('Blog Category menu')
name = _('Blog menu')
def get_nodes(self, request):
nodes = []
qs = BlogCategory.objects.translated(get_language())
qs = qs.order_by('parent__id', 'translations__name')
for category in qs:
node = NavigationNode(
category.name,
category.get_absolute_url(),
category.pk,
category.parent_id
)
nodes.append(node)
language = get_language_from_request(request, check_path=True)
categories_menu = False
posts_menu = False
config = False
if hasattr(self, 'instance') and self.instance:
config = BlogConfig.objects.get(namespace=self.instance.application_namespace)
if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_CATEGORIES):
categories_menu = True
if config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS):
posts_menu = True
if categories_menu:
categories = BlogCategory.objects
if config:
categories = categories.namespace(self.instance.application_namespace)
categories = categories.active_translations(language).distinct()
categories = categories.order_by('parent__id', 'translations__name')
for category in categories:
node = NavigationNode(
category.name,
category.get_absolute_url(),
'%s-%s' % (category.__class__.__name__, category.pk),
('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent
else None)
)
nodes.append(node)
if posts_menu:
posts = Post.objects
if hasattr(self, 'instance') and self.instance:
posts = posts.namespace(self.instance.application_namespace)
posts = posts.active_translations(language).distinct()
for post in posts:
if categories_menu:
category = post.categories.first()
parent = '%s-%s' % (category.__class__.__name__, category.pk)
post_id = '%s-%s' % (post.__class__.__name__, post.pk),
else:
parent = None
post_id = '%s-%s' % (post.__class__.__name__, post.pk),
node = NavigationNode(
post.get_title(),
post.get_absolute_url(language),
post_id,
parent
)
nodes.append(node)
return nodes
menu_pool.register_menu(BlogCategoryMenu)
class BlogNavModifier(Modifier):
"""
This navigation modifier makes sure that when
a particular blog post is viewed,
a corresponding category is selected in menu
"""
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
if post_cut:
return nodes
if not hasattr(request, 'toolbar'):
return nodes
models = ('djangocms_blog.post', 'djangocms_blog.blogcategory')
model = request.toolbar.get_object_model()
if model not in models:
return nodes
if model == 'djangocms_blog.blogcategory':
cat = request.toolbar.obj
else:
cat = request.toolbar.obj.categories.first()
if not cat:
return nodes
for node in nodes:
if (node.namespace.startswith(BlogCategoryMenu.__name__) and
cat.pk == node.id):
node.selected = True
# no break here because django-cms maintains two menu structures
# for every apphook (attached to published page and draft page)
return nodes
menu_pool.register_modifier(BlogNavModifier)
def clear_menu_cache(**kwargs):
menu_pool.clear(all=True)

View file

@ -0,0 +1,124 @@
#-*- coding: utf-8 -*-
from __future__ import unicode_literals
import aldryn_apphooks_config.fields
import app_data.fields
import djangocms_text_ckeditor.fields
from cms.models import Page
from cms.utils.i18n import get_language_list
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):
dependencies = [
('cms', '__latest__'),
('djangocms_blog', '0009_latestpostsplugin_tags_new'),
]
operations = [
migrations.CreateModel(
name='BlogConfig',
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('type', models.CharField(verbose_name='type', max_length=100)),
('namespace', models.CharField(default=None, verbose_name='instance namespace', unique=True, max_length=100)),
('app_data', app_data.fields.AppDataField(editable=False, default='{}')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='BlogConfigTranslation',
fields=[
('id', models.AutoField(auto_created=True, verbose_name='ID', serialize=False, primary_key=True)),
('language_code', models.CharField(db_index=True, verbose_name='Language', max_length=15)),
('app_title', models.CharField(verbose_name='application title', max_length=234)),
('master', models.ForeignKey(editable=False, to='djangocms_blog.BlogConfig', related_name='translations', null=True)),
],
options={
'verbose_name': 'blog config Translation',
'db_table': 'djangocms_blog_blogconfig_translation',
'default_permissions': (),
'db_tablespace': '',
'managed': True,
},
),
migrations.CreateModel(
name='GenericBlogPlugin',
fields=[
('cmsplugin_ptr', models.OneToOneField(parent_link=True, serialize=False, primary_key=True, auto_created=True, to='cms.CMSPlugin')),
('app_config', aldryn_apphooks_config.fields.AppHookConfigField(verbose_name='app. config', blank=True, to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default')),
],
options={
'abstract': False,
},
bases=('cms.cmsplugin',),
),
migrations.AlterField(
model_name='posttranslation',
name='abstract',
field=djangocms_text_ckeditor.fields.HTMLField(default='', verbose_name='abstract', blank=True),
),
migrations.AddField(
model_name='authorentriesplugin',
name='app_config',
field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, blank=True, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'),
preserve_default=False,
),
migrations.AddField(
model_name='blogcategory',
name='app_config',
field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'),
preserve_default=False,
),
migrations.AddField(
model_name='latestpostsplugin',
name='app_config',
field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, blank=True, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'),
preserve_default=False,
),
migrations.AddField(
model_name='post',
name='app_config',
field=aldryn_apphooks_config.fields.AppHookConfigField(default=None, verbose_name='app. config', to='djangocms_blog.BlogConfig', help_text='When selecting a value, the form is reloaded to get the updated default'),
preserve_default=False,
),
migrations.AlterUniqueTogether(
name='blogconfigtranslation',
unique_together=set([('language_code', 'master')]),
),
migrations.AlterField(
model_name='post',
name='sites',
field=models.ManyToManyField(to='sites.Site', help_text='Select sites in which to show the post. If none is set it will be visible in all the configured sites.', blank=True, verbose_name='Site(s)'),
),
migrations.RunPython(forwards, backwards)
]

View file

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from aldryn_apphooks_config.fields import AppHookConfigField
from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableManager
from cms.models import CMSPlugin, PlaceholderField
from django.conf import settings as dj_settings
from django.core.urlresolvers import reverse
@ -13,14 +15,15 @@ from django.utils.translation import get_language, ugettext_lazy as _
from djangocms_text_ckeditor.fields import HTMLField
from filer.fields.image import FilerImageField
from meta_mixin.models import ModelMeta
from parler.managers import TranslationManager
from parler.models import TranslatableModel, TranslatedFields
from taggit_autosuggest.managers import TaggableManager
from .cms_appconfig import BlogConfig
from .managers import GenericDateTaggedManager
from .settings import get_setting
BLOG_CURRENT_POST_IDENTIFIER = 'djangocms_post_current'
BLOG_CURRENT_NAMESPACE = 'djangocms_post_current_config'
@python_2_unicode_compatible
@ -28,10 +31,12 @@ class BlogCategory(TranslatableModel):
"""
Blog category
"""
parent = models.ForeignKey('self', verbose_name=_('parent'), null=True,
blank=True)
parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True)
date_created = models.DateTimeField(_('created at'), auto_now_add=True)
date_modified = models.DateTimeField(_('modified at'), auto_now=True)
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config')
)
translations = TranslatedFields(
name=models.CharField(_('name'), max_length=255),
@ -39,7 +44,7 @@ class BlogCategory(TranslatableModel):
meta={'unique_together': (('language_code', 'slug'),)}
)
objects = TranslationManager()
objects = AppHookConfigTranslatableManager()
class Meta:
verbose_name = _('blog category')
@ -47,16 +52,23 @@ class BlogCategory(TranslatableModel):
@property
def count(self):
return self.blog_posts.published().count()
return self.blog_posts.namespace(self.app_config.namespace).published().count()
def get_absolute_url(self):
lang = get_language()
if self.has_translation(lang):
def get_absolute_url(self, lang=None):
if not lang:
lang = get_language()
if self.has_translation(lang, ):
slug = self.safe_translation_getter('slug', language_code=lang)
return reverse('djangocms_blog:posts-category', kwargs={'category': slug})
return reverse(
'%s:posts-category' % self.app_config.namespace,
kwargs={'category': slug},
current_app=self.app_config.namespace
)
# in case category doesn't exist in this language, gracefully fallback
# to posts-latest
return reverse('djangocms_blog:posts-latest')
return reverse(
'%s:posts-latest' % self.app_config.namespace, current_app=self.app_config.namespace
)
def __str__(self):
return self.safe_translation_getter('name')
@ -81,9 +93,9 @@ class Post(ModelMeta, TranslatableModel):
date_created = models.DateTimeField(_('created'), auto_now_add=True)
date_modified = models.DateTimeField(_('last modified'), auto_now=True)
date_published = models.DateTimeField(_('published Since'),
date_published = models.DateTimeField(_('published since'),
default=timezone.now)
date_published_end = models.DateTimeField(_('published Until'), null=True,
date_published_end = models.DateTimeField(_('published until'), null=True,
blank=True)
publish = models.BooleanField(_('publish'), default=False)
categories = models.ManyToManyField('djangocms_blog.BlogCategory', verbose_name=_('category'),
@ -104,10 +116,12 @@ class Post(ModelMeta, TranslatableModel):
enable_comments = models.BooleanField(verbose_name=_('enable comments on post'),
default=get_setting('ENABLE_COMMENTS'))
sites = models.ManyToManyField('sites.Site', verbose_name=_('Site(s)'), blank=True,
null=True,
help_text=_('Select sites in which to show the post. '
u'If none is set it will be '
u'visible in all the configured sites.'))
'If none is set it will be '
'visible in all the configured sites.'))
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config')
)
translations = TranslatedFields(
title=models.CharField(_('title'), max_length=255),
@ -132,23 +146,24 @@ class Post(ModelMeta, TranslatableModel):
_metadata = {
'title': 'get_title',
'description': 'get_description',
'keywords': 'get_keywords',
'og_description': 'get_description',
'twitter_description': 'get_description',
'gplus_description': 'get_description',
'keywords': 'get_keywords',
'locale': None,
'locale': 'get_locale',
'image': 'get_image_full_url',
'object_type': get_setting('TYPE'),
'og_type': get_setting('FB_TYPE'),
'og_app_id': get_setting('FB_APPID'),
'og_profile_id': get_setting('FB_PROFILE_ID'),
'og_publisher': get_setting('FB_PUBLISHER'),
'og_author_url': get_setting('FB_AUTHOR_URL'),
'twitter_type': get_setting('TWITTER_TYPE'),
'twitter_site': get_setting('TWITTER_SITE'),
'twitter_author': get_setting('TWITTER_AUTHOR'),
'gplus_type': get_setting('GPLUS_TYPE'),
'gplus_author': get_setting('GPLUS_AUTHOR'),
'object_type': 'get_meta_attribute',
'og_type': 'get_meta_attribute',
'og_app_id': 'get_meta_attribute',
'og_profile_id': 'get_meta_attribute',
'og_publisher': 'get_meta_attribute',
'og_author_url': 'get_meta_attribute',
'og_author': 'get_meta_attribute',
'twitter_type': 'get_meta_attribute',
'twitter_site': 'get_meta_attribute',
'twitter_author': 'get_meta_attribute',
'gplus_type': 'get_meta_attribute',
'gplus_author': 'get_meta_attribute',
'published_time': 'date_published',
'modified_time': 'date_modified',
'expiration_time': 'date_published_end',
@ -165,19 +180,49 @@ class Post(ModelMeta, TranslatableModel):
def __str__(self):
return self.safe_translation_getter('title')
def get_absolute_url(self):
kwargs = {'year': self.date_published.year,
'month': '%02d' % self.date_published.month,
'day': '%02d' % self.date_published.day,
'slug': self.safe_translation_getter('slug',
language_code=get_language(),
any_language=True)}
return reverse('djangocms_blog:post-detail', kwargs=kwargs)
def get_absolute_url(self, lang=None):
if not lang:
lang = get_language()
category = self.categories.first()
kwargs = {}
urlconf = get_setting('PERMALINK_URLS')[self.app_config.url_patterns]
if '<year>' in urlconf:
kwargs['year'] = self.date_published.year
if '<month>' in urlconf:
kwargs['month'] = '%02d' % self.date_published.month
if '<day>' in urlconf:
kwargs['day'] = '%02d' % self.date_published.day
if '<slug>' in urlconf:
kwargs['slug'] = self.safe_translation_getter('slug', language_code=lang, any_language=True) # NOQA
if '<category>' in urlconf:
kwargs['category'] = category.safe_translation_getter('slug', language_code=lang, any_language=True) # NOQA
return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs)
def save(self, *args, **kwargs):
if not self.slug and self.title:
self.slug = slugify(self.title)
super(Post, self).save(*args, **kwargs)
def get_meta_attribute(self, param):
"""
Retrieves django-meta attributes from apphook config instance
:param param: django-meta attribute passed as key
"""
attr = None
value = getattr(self.app_config, param)
if value:
attr = getattr(self, value, None)
if attr is not None:
if callable(attr):
try:
data = attr(param)
except TypeError:
data = attr()
else:
data = attr
else:
data = value
return data
def save_translation(self, translation, *args, **kwargs):
if not translation.slug and translation.title:
translation.slug = slugify(translation.title)
super(Post, self).save_translation(translation, *args, **kwargs)
def get_title(self):
title = self.safe_translation_getter('meta_title', any_language=True)
@ -188,6 +233,9 @@ class Post(ModelMeta, TranslatableModel):
def get_keywords(self):
return self.safe_translation_getter('meta_keywords').strip().split(',')
def get_locale(self):
return self.get_current_language()
def get_description(self):
description = self.safe_translation_getter('meta_description', any_language=True)
if not description:
@ -224,23 +272,28 @@ class Post(ModelMeta, TranslatableModel):
@python_2_unicode_compatible
class BasePostPlugin(CMSPlugin):
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config'), blank=True
)
class Meta:
abstract = True
def post_queryset(self, request=None):
language = get_language()
posts = Post._default_manager.active_translations(language_code=language)
posts = Post._default_manager
if self.app_config:
posts = posts.namespace(self.app_config.namespace)
posts = posts.active_translations(language_code=language)
if not request or not getattr(request, 'toolbar', False) or not request.toolbar.edit_mode:
posts = posts.published()
return posts
def __str__(self):
return force_text(self.latest_posts)
return _('generic blog plugin')
class LatestPostsPlugin(BasePostPlugin):
latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'),
help_text=_('The number of latests '
u'articles to be displayed.'))
@ -292,6 +345,16 @@ class AuthorEntriesPlugin(BasePostPlugin):
authors = self.authors.all()
for author in authors:
author.count = 0
if author.djangocms_blog_post_author.filter(publish=True).exists():
author.count = author.djangocms_blog_post_author.filter(publish=True).count()
qs = author.djangocms_blog_post_author
if self.app_config:
qs = qs.namespace(self.app_config.namespace)
count = qs.filter(publish=True).count()
if count:
author.count = count
return authors
class GenericBlogPlugin(BasePostPlugin):
class Meta:
abstract = False

View file

@ -1,11 +1,35 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
MENU_TYPE_COMPLETE = 'complete'
MENU_TYPE_CATEGORIES = 'categories'
MENU_TYPE_POSTS = 'posts'
MENU_TYPE_NONE = 'none'
def get_setting(name):
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from meta_mixin import settings as meta_settings
PERMALINKS = (
('full_date', _('Full date')),
('short_date', _('Year / Month')),
('category', _('Category')),
('slug', _('Just slug')),
)
PERMALINKS_URLS = {
'full_date': r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$',
'short_date': r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<slug>\w[-\w]*)/$',
'category': r'^(?P<category>\w[-\w]*)/(?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 = {
'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
'size': '120x120',
@ -19,38 +43,41 @@ def get_setting(name):
'upscale': False
}),
'BLOG_TAGCLOUD_MIN': getattr(settings, 'BLOG_TAGCLOUD_MIN', 1),
'BLOG_TAGCLOUD_MAX': getattr(settings, 'BLOG_TAGCLOUD_MAX', 10),
'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10),
'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5),
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(settings,
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT',
100),
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
),
'BLOG_MENU_TYPE': MENU_TYPES,
'BLOG_MENU_TYPES': MENU_TYPES,
'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
'BLOG_TYPES': meta_settings.OBJECT_TYPES,
'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'),
'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID',
meta_settings.FB_APPID),
'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID',
meta_settings.FB_PROFILE_ID),
'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER',
meta_settings.FB_PUBLISHER),
'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL',
'get_author_url'),
'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR',
'get_author_name'),
'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'),
'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE',
meta_settings.TWITTER_SITE),
'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR',
'get_author_twitter'),
'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY',
'Blog'),
'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR',
'get_author_gplus'),
'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', meta_settings.FB_TYPES),
'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID),
'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID),
'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER),
'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'),
'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'summary'),
'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', meta_settings.TWITTER_TYPES),
'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE),
'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_TYPE', 'Blog'),
'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', meta_settings.GPLUS_TYPES),
'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True),
'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True),
'BLOG_AUTHOR_DEFAULT': getattr(settings, 'BLOG_AUTHOR_DEFAULT', True),
'BLOG_DEFAULT_PUBLISHED': getattr(settings, 'BLOG_DEFAULT_PUBLISHED', False),
'BLOG_AVAILABLE_PERMALINK_STYLES': getattr(settings, 'BLOG_AVAILABLE_PERMALINK_STYLES', PERMALINKS), # NOQA
'BLOG_PERMALINK_URLS': getattr(settings, 'BLOG_PERMALINK_URLS', PERMALINKS_URLS),
'BLOG_AUTO_SETUP': getattr(settings, 'BLOG_AUTO_SETUP', True),
'BLOG_AUTO_HOME_TITLE': getattr(settings, 'BLOG_AUTO_HOME_TITLE', 'Home'),
'BLOG_AUTO_BLOG_TITLE': getattr(settings, 'BLOG_AUTO_BLOG_TITLE', 'Blog'),
'BLOG_AUTO_APP_TITLE': getattr(settings, 'BLOG_AUTO_APP_TITLE', 'Blog'),
}
return default['BLOG_%s' % name]

View file

@ -0,0 +1,285 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'GenericBlogPlugin'
db.create_table(u'djangocms_blog_genericblogplugin', (
(u'cmsplugin_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['cms.CMSPlugin'], unique=True, primary_key=True)),
('app_config', self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True)),
))
db.send_create_signal(u'djangocms_blog', ['GenericBlogPlugin'])
# Adding model 'BlogConfig'
db.create_table(u'djangocms_blog_blogconfig', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('type', self.gf('django.db.models.fields.CharField')(max_length=100)),
('namespace', self.gf('django.db.models.fields.CharField')(default=None, unique=True, max_length=100)),
('app_data', self.gf('app_data.fields.AppDataField')(default='{}')),
))
db.send_create_signal(u'djangocms_blog', ['BlogConfig'])
# Adding model 'BlogConfigTranslation'
db.create_table(u'djangocms_blog_blogconfig_translation', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('language_code', self.gf('django.db.models.fields.CharField')(max_length=15, db_index=True)),
('app_title', self.gf('django.db.models.fields.CharField')(max_length=234)),
(u'master', self.gf('django.db.models.fields.related.ForeignKey')(related_name='translations', null=True, to=orm['djangocms_blog.BlogConfig'])),
))
db.send_create_signal(u'djangocms_blog', ['BlogConfigTranslation'])
# Adding unique constraint on 'BlogConfigTranslation', fields ['language_code', u'master']
db.create_unique(u'djangocms_blog_blogconfig_translation', ['language_code', u'master_id'])
# Adding field 'BlogCategory.app_config'
db.add_column(u'djangocms_blog_blogcategory', 'app_config',
self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True),
keep_default=False)
# Adding field 'Post.app_config'
db.add_column(u'djangocms_blog_post', 'app_config',
self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True),
keep_default=False)
# Adding field 'LatestPostsPlugin.app_config'
db.add_column(u'djangocms_blog_latestpostsplugin', 'app_config',
self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True),
keep_default=False)
# Adding field 'AuthorEntriesPlugin.app_config'
db.add_column(u'djangocms_blog_authorentriesplugin', 'app_config',
self.gf('aldryn_apphooks_config.fields.AppHookConfigField')(to=orm['djangocms_blog.BlogConfig'], null=True, blank=True),
keep_default=False)
def backwards(self, orm):
# Removing unique constraint on 'BlogConfigTranslation', fields ['language_code', u'master']
db.delete_unique(u'djangocms_blog_blogconfig_translation', ['language_code', u'master_id'])
# Deleting model 'GenericBlogPlugin'
db.delete_table(u'djangocms_blog_genericblogplugin')
# Deleting model 'BlogConfig'
db.delete_table(u'djangocms_blog_blogconfig')
# Deleting model 'BlogConfigTranslation'
db.delete_table(u'djangocms_blog_blogconfig_translation')
# Deleting field 'BlogCategory.app_config'
db.delete_column(u'djangocms_blog_blogcategory', 'app_config_id')
# Deleting field 'Post.app_config'
db.delete_column(u'djangocms_blog_post', 'app_config_id')
# Deleting field 'LatestPostsPlugin.app_config'
db.delete_column(u'djangocms_blog_latestpostsplugin', 'app_config_id')
# Deleting field 'AuthorEntriesPlugin.app_config'
db.delete_column(u'djangocms_blog_authorentriesplugin', 'app_config_id')
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'cms.cmsplugin': {
'Meta': {'object_name': 'CMSPlugin'},
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
},
'cms.placeholder': {
'Meta': {'object_name': 'Placeholder'},
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'cmsplugin_filer_image.thumbnailoption': {
'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'},
'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'height': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'width': ('django.db.models.fields.IntegerField', [], {})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_blog.authorentriesplugin': {
'Meta': {'object_name': 'AuthorEntriesPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'})
},
u'djangocms_blog.blogcategory': {
'Meta': {'object_name': 'BlogCategory'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'})
},
u'djangocms_blog.blogcategorytranslation': {
'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'})
},
u'djangocms_blog.blogconfig': {
'Meta': {'object_name': 'BlogConfig'},
'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'unique': 'True', 'max_length': '100'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_blog.blogconfigtranslation': {
'Meta': {'unique_together': "[(u'language_code', u'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "u'djangocms_blog_blogconfig_translation'"},
'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogConfig']"})
},
u'djangocms_blog.genericblogplugin': {
'Meta': {'object_name': 'GenericBlogPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
},
u'djangocms_blog.latestpostsplugin': {
'Meta': {'object_name': 'LatestPostsPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'})
},
u'djangocms_blog.post': {
'Meta': {'ordering': "(u'-date_published', u'-date_created')", 'object_name': 'Post'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}),
'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}),
'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}),
'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}),
'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}),
'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}),
'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'})
},
u'djangocms_blog.posttranslation': {
'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"},
'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}),
'meta_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
'meta_title': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
u'filer.file': {
'Meta': {'object_name': 'File'},
'_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}),
'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}),
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}),
'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
u'filer.folder': {
'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}),
u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'filer.image': {
'Meta': {'object_name': 'Image'},
'_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}),
'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'related_url': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'})
},
u'sites.site': {
'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['djangocms_blog']

View file

@ -0,0 +1,236 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
from cms.models import Page
from cms.utils.i18n import get_language_list
class Migration(DataMigration):
def forwards(self, orm):
BlogConfig = orm['djangocms_blog.BlogConfig']
BlogConfigTranslation = orm['djangocms_blog.BlogConfigTranslation']
Post = orm['djangocms_blog.Post']
BlogCategory = orm['djangocms_blog.BlogCategory']
GenericBlogPlugin = orm['djangocms_blog.GenericBlogPlugin']
LatestPostsPlugin = orm['djangocms_blog.LatestPostsPlugin']
AuthorEntriesPlugin = orm['djangocms_blog.AuthorEntriesPlugin']
config = None
for page in Page.objects.drafts().filter(application_urls='BlogApp'):
config = BlogConfig.objects.create(namespace=page.application_namespace)
for lang in get_language_list():
title = page.get_title(lang)
translation = BlogConfigTranslation.objects.create(language_code=lang, master_id=config.pk, app_title=title)
if config:
for model in (Post, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, AuthorEntriesPlugin):
for item in model.objects.all():
item.app_config = config
item.save()
def backwards(self, orm):
# No need for backward data migration
pass
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'cms.cmsplugin': {
'Meta': {'object_name': 'CMSPlugin'},
'changed_date': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'creation_date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.CMSPlugin']", 'null': 'True', 'blank': 'True'}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'placeholder': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['cms.Placeholder']", 'null': 'True'}),
'plugin_type': ('django.db.models.fields.CharField', [], {'max_length': '50', 'db_index': 'True'}),
'position': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True', 'blank': 'True'})
},
'cms.placeholder': {
'Meta': {'object_name': 'Placeholder'},
'default_width': ('django.db.models.fields.PositiveSmallIntegerField', [], {'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'slot': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
},
u'cmsplugin_filer_image.thumbnailoption': {
'Meta': {'ordering': "(u'width', u'height')", 'object_name': 'ThumbnailOption'},
'crop': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'height': ('django.db.models.fields.IntegerField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'upscale': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'width': ('django.db.models.fields.IntegerField', [], {})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_blog.authorentriesplugin': {
'Meta': {'object_name': 'AuthorEntriesPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'})
},
u'djangocms_blog.blogcategory': {
'Meta': {'object_name': 'BlogCategory'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'null': 'True', 'blank': 'True'})
},
u'djangocms_blog.blogcategorytranslation': {
'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'BlogCategoryTranslation', 'db_table': "u'djangocms_blog_blogcategory_translation'"},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogCategory']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'})
},
u'djangocms_blog.blogconfig': {
'Meta': {'object_name': 'BlogConfig'},
'app_data': ('app_data.fields.AppDataField', [], {'default': "'{}'"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'namespace': ('django.db.models.fields.CharField', [], {'default': 'None', 'unique': 'True', 'max_length': '100'}),
'type': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
u'djangocms_blog.blogconfigtranslation': {
'Meta': {'unique_together': "[(u'language_code', u'master')]", 'object_name': 'BlogConfigTranslation', 'db_table': "u'djangocms_blog_blogconfig_translation'"},
'app_title': ('django.db.models.fields.CharField', [], {'max_length': '234'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.BlogConfig']"})
},
u'djangocms_blog.genericblogplugin': {
'Meta': {'object_name': 'GenericBlogPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'})
},
u'djangocms_blog.latestpostsplugin': {
'Meta': {'object_name': 'LatestPostsPlugin'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True', 'blank': 'True'}),
'categories': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['djangocms_blog.BlogCategory']", 'symmetrical': 'False', 'blank': 'True'}),
u'cmsplugin_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['cms.CMSPlugin']", 'unique': 'True', 'primary_key': 'True'}),
'latest_posts': ('django.db.models.fields.IntegerField', [], {'default': '5'})
},
u'djangocms_blog.post': {
'Meta': {'ordering': "(u'-date_published', u'-date_created')", 'object_name': 'Post'},
'app_config': ('aldryn_apphooks_config.fields.AppHookConfigField', [], {'to': u"orm['djangocms_blog.BlogConfig']", 'null': 'True'}),
'author': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_author'", 'null': 'True', 'to': u"orm['auth.User']"}),
'categories': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "u'blog_posts'", 'symmetrical': 'False', 'to': u"orm['djangocms_blog.BlogCategory']"}),
'content': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'post_content'", 'null': 'True', 'to': "orm['cms.Placeholder']"}),
'date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'date_modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'date_published': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'date_published_end': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'enable_comments': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'main_image': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_image'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['filer.Image']"}),
'main_image_full': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_full'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}),
'main_image_thumbnail': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'djangocms_blog_post_thumbnail'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['cmsplugin_filer_image.ThumbnailOption']"}),
'publish': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'sites': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['sites.Site']", 'symmetrical': 'False', 'blank': 'True'})
},
u'djangocms_blog.posttranslation': {
'Meta': {'unique_together': "[(u'language_code', u'slug'), (u'language_code', u'master')]", 'object_name': 'PostTranslation', 'db_table': "u'djangocms_blog_post_translation'"},
'abstract': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'language_code': ('django.db.models.fields.CharField', [], {'max_length': '15', 'db_index': 'True'}),
u'master': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'translations'", 'null': 'True', 'to': u"orm['djangocms_blog.Post']"}),
'meta_description': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
'meta_keywords': ('django.db.models.fields.TextField', [], {'default': "u''", 'blank': 'True'}),
'meta_title': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
'post_text': ('djangocms_text_ckeditor.fields.HTMLField', [], {'default': "u''", 'blank': 'True'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'blank': 'True'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'})
},
u'filer.file': {
'Meta': {'object_name': 'File'},
'_file_size': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'file': ('django.db.models.fields.files.FileField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'folder': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'all_files'", 'null': 'True', 'to': u"orm['filer.Folder']"}),
'has_all_mandatory_data': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_public': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '255', 'blank': 'True'}),
'original_filename': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'owned_files'", 'null': 'True', 'to': u"orm['auth.User']"}),
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "u'polymorphic_filer.file_set+'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'sha1': ('django.db.models.fields.CharField', [], {'default': "u''", 'max_length': '40', 'blank': 'True'}),
'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
u'filer.folder': {
'Meta': {'ordering': "(u'name',)", 'unique_together': "((u'parent', u'name'),)", 'object_name': 'Folder'},
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
u'level': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
u'lft': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'filer_owned_folders'", 'null': 'True', 'to': u"orm['auth.User']"}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "u'children'", 'null': 'True', 'to': u"orm['filer.Folder']"}),
u'rght': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
u'tree_id': ('django.db.models.fields.PositiveIntegerField', [], {'db_index': 'True'}),
'uploaded_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'})
},
'filer.image': {
'Meta': {'object_name': 'Image'},
'_height': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'_width': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
'author': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'date_taken': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'default_alt_text': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
'default_caption': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
u'file_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['filer.File']", 'unique': 'True', 'primary_key': 'True'}),
'must_always_publish_author_credit': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'must_always_publish_copyright': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'related_url': ('django.db.models.fields.CharField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
'subject_location': ('django.db.models.fields.CharField', [], {'default': 'None', 'max_length': '64', 'null': 'True', 'blank': 'True'})
},
u'sites.site': {
'Meta': {'ordering': "(u'domain',)", 'object_name': 'Site', 'db_table': "u'django_site'"},
'domain': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '255'})
}
}
complete_apps = ['djangocms_blog']

View file

@ -21,7 +21,7 @@
</div>
{% endif %}
{% endspaceless %}
{% if use_placeholder %}
{% if post.app_config.use_placeholder %}
<div class="blog-content">{% render_placeholder post.content %}</div>
{% else %}
<div class="blog-content">{% render_model post "post_text" "post_text" %}</div>

View file

@ -3,12 +3,26 @@ from __future__ import absolute_import, print_function, unicode_literals
from django.conf.urls import patterns, url
from .apps import BlogAppConfig
from .feeds import LatestEntriesFeed, TagFeed
from .settings import get_setting
from .views import (
AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView,
TaggedListView,
)
def get_urls():
urls = get_setting('PERMALINK_URLS')
details = []
for urlconf in urls.values():
details.append(
url(urlconf, PostDetailView.as_view(), name='post-detail'),
)
return details
detail_urls = get_urls()
urlpatterns = patterns(
'',
url(r'^$',
@ -19,8 +33,7 @@ urlpatterns = patterns(
PostArchiveView.as_view(), name='posts-archive'),
url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/$',
PostArchiveView.as_view(), name='posts-archive'),
url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/(?P<slug>\w[-\w]*)/$',
PostDetailView.as_view(), name='post-detail'),
) + detail_urls + [
url(r'^author/(?P<username>[\w\.@+-]+)/$',
AuthorEntriesView.as_view(), name='posts-author'),
url(r'^category/(?P<category>[\w\.@+-]+)/$',
@ -29,4 +42,6 @@ urlpatterns = patterns(
TaggedListView.as_view(), name='posts-tagged'),
url(r'^tag/(?P<tag>[-\w]+)/feed/$',
TagFeed(), name='posts-tagged-feed'),
)
]
BlogAppConfig.setup()

View file

@ -1,38 +1,61 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import os.path
from aldryn_apphooks_config.mixins import AppConfigMixin
from django.contrib.auth import get_user_model
from django.core.urlresolvers import resolve
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.utils.timezone import now
from django.utils.translation import get_language
from django.views.generic import DetailView, ListView
from parler.views import TranslatableSlugMixin, ViewUrlMixin
from .models import BLOG_CURRENT_POST_IDENTIFIER, BlogCategory, Post
from .models import BLOG_CURRENT_NAMESPACE, BLOG_CURRENT_POST_IDENTIFIER, BlogCategory, Post
from .settings import get_setting
User = get_user_model()
class BaseBlogView(ViewUrlMixin):
class BaseBlogView(AppConfigMixin, ViewUrlMixin):
def get_view_url(self):
if not self.view_url_name:
raise ImproperlyConfigured(
'Missing `view_url_name` attribute on {0}'.format(self.__class__.__name__)
)
return reverse(
self.view_url_name,
args=self.args,
kwargs=self.kwargs,
current_app=self.namespace
)
def get_queryset(self):
language = get_language()
queryset = self.model._default_manager.all().active_translations(language_code=language)
queryset = self.model._default_manager.namespace(
self.namespace
).active_translations(
language_code=language
)
if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
queryset = queryset.published()
setattr(self.request, BLOG_CURRENT_NAMESPACE, self.config)
return queryset
def render_to_response(self, context, **response_kwargs):
response_kwargs['current_app'] = resolve(self.request.path).namespace
return super(BaseBlogView, self).render_to_response(context, **response_kwargs)
def get_template_names(self):
if self.config.template_prefix:
return os.path.join(self.config.template_prefix, self.base_template_name)
else:
return os.path.join('djangocms_blog', self.base_template_name)
class PostListView(BaseBlogView, ListView):
model = Post
context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html'
paginate_by = get_setting('PAGINATION')
base_template_name = 'post_list.html'
view_url_name = 'djangocms_blog:posts-latest'
def get_context_data(self, **kwargs):
@ -40,11 +63,17 @@ class PostListView(BaseBlogView, ListView):
context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT')
return context
def get_paginate_by(self, queryset):
if self.config.paginate_by:
return self.config.paginate_by
else:
return get_setting('PAGINATION')
class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
model = Post
context_object_name = 'post'
template_name = 'djangocms_blog/post_detail.html'
base_template_name = 'post_detail.html'
slug_field = 'slug'
view_url_name = 'djangocms_blog:post-detail'
@ -71,7 +100,7 @@ class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
class PostArchiveView(BaseBlogView, ListView):
model = Post
context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html'
base_template_name = 'post_list.html'
date_field = 'date_published'
allow_empty = True
allow_future = True
@ -99,7 +128,7 @@ class PostArchiveView(BaseBlogView, ListView):
class TaggedListView(BaseBlogView, ListView):
model = Post
context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html'
base_template_name = 'post_list.html'
paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-tagged'
@ -118,7 +147,7 @@ class TaggedListView(BaseBlogView, ListView):
class AuthorEntriesView(BaseBlogView, ListView):
model = Post
context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html'
base_template_name = 'post_list.html'
paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-authors'
@ -138,7 +167,7 @@ class AuthorEntriesView(BaseBlogView, ListView):
class CategoryEntriesView(BaseBlogView, ListView):
model = Post
context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html'
base_template_name = 'post_list.html'
_category = None
paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-category'

View file

@ -47,7 +47,8 @@ setup(
'djangocms-text-ckeditor',
'cmsplugin-filer',
'django-meta>=0.2',
'django-meta-mixin>=0.1.1',
'django-meta-mixin>=0.2.1',
'aldryn-apphooks-config>=0.2.6',
],
license='BSD',
zip_safe=False,
@ -73,5 +74,6 @@ setup(
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
],
)

View file

@ -1,151 +1 @@
# -*- coding: utf-8 -*-
"""
Tests for `djangocms_blog` module.
"""
from __future__ import absolute_import, print_function, unicode_literals
from cmsplugin_filer_image.models import ThumbnailOption
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from django.utils.translation import activate
from djangocms_helper.base_test import BaseTestCase
from djangocms_blog.models import BlogCategory, Post
User = get_user_model()
def _get_cat_pk(lang, name):
return lambda: BlogCategory.objects.translated(lang, name=name).get().pk
class BaseTest(BaseTestCase):
"""
Base class with utility function
"""
category_1 = None
thumb_1 = None
thumb_2 = None
_pages_data = (
{'en': {'title': 'page one', 'template': 'page.html', 'publish': True},
'fr': {'title': 'page un', 'publish': True},
'it': {'title': 'pagina uno', 'publish': True}},
{'en': {'title': 'page two', 'template': 'page.html', 'publish': True},
'fr': {'title': 'page deux', 'publish': True},
'it': {'title': 'pagina due', 'publish': True}},
)
data = {
'it': [
{'title': u'Primo post', 'abstract': u'<p>prima riga</p>',
'description': u'Questa è la descrizione', 'keywords': u'keyword1, keyword2',
'text': u'Testo del post'},
{'title': u'Secondo post', 'abstract': u'<p>prima riga del secondo post</p>',
'description': u'Descrizione del secondo post', 'keywords': u'keyword3, keyword4',
'text': u'Testo del secondo post'},
{'title': u'Terzo post', 'abstract': u'<p>prima riga del terzo post</p>',
'description': u'Descrizione del terzo post', 'keywords': u'keyword5, keyword6',
'text': u'Testo del terzo post'},
],
'en': [
{'title': u'First post', 'abstract': u'<p>first line</p>',
'description': u'This is the description', 'keywords': u'keyword1, keyword2',
'text': u'Post text'},
{'title': u'Second post', 'abstract': u'<p>second post first line</p>',
'description': u'Second post description', 'keywords': u'keyword3, keyword4',
'text': u'Second post text'},
{'title': u'Third post', 'abstract': u'<p>third post first line</p>',
'description': u'third post description', 'keywords': u'keyword5, keyword6',
'text': u'Third post text'}
]
}
cat_data = {
'it': [
{'name': u'Fortissimo'},
{'name': u'Pianississimo'},
{'name': u'Mezzo'},
{'name': u'Forte', 'parent_id': _get_cat_pk('it', 'Mezzo')},
],
'en': [
{'name': u'Very loud'},
{'name': u'Very very silent'},
{'name': u'Almost'},
{'name': u'Loud', 'parent_id': _get_cat_pk('en', 'Almost')},
{'name': u'Silent', 'parent_id': _get_cat_pk('en', 'Almost')},
]
}
@classmethod
def setUpClass(cls):
super(BaseTest, cls).setUpClass()
cls.site_2 = Site.objects.create(domain='http://example2.com', name='example 2')
def setUp(self):
activate('en')
super(BaseTest, self).setUp()
self.category_1 = BlogCategory.objects.create(name=u'category 1')
self.category_1.set_current_language('it', initialize=True)
self.category_1.name = u'categoria 1'
self.category_1.save()
self.thumb_1 = ThumbnailOption.objects.create(
name='base', width=100, height=100, crop=True, upscale=False
)
self.thumb_2 = ThumbnailOption.objects.create(
name='main', width=200, height=200, crop=False, upscale=False
)
self.img = self.create_filer_image_object()
def tearDown(self):
for post in Post.objects.all():
post.delete()
super(BaseTest, self).tearDown()
def _get_category(self, data, category=None, lang='en'):
for k, v in data.items():
if hasattr(v, '__call__'):
data[k] = v()
if not category:
category = BlogCategory.objects.create(**data)
else:
category.set_current_language(lang)
for attr, val in data.items():
setattr(category, attr, val)
category.save()
return category
def _get_post(self, data, post=None, lang='en', sites=None):
if not post:
post_data = {
'author': self.user,
'title': data['title'],
'abstract': data['abstract'],
'meta_description': data['description'],
'meta_keywords': data['keywords'],
}
post = Post.objects.create(**post_data)
else:
post.set_current_language(lang)
post.title = data['title']
post.abstract = data['abstract']
post.meta_description = data['description']
post.meta_keywords = data['keywords']
post.save()
post.categories.add(self.category_1)
if sites:
for site in sites:
post.sites.add(site)
return post
def get_posts(self, sites=None):
post1 = self._get_post(self.data['en'][0], sites=sites)
post1 = self._get_post(self.data['it'][0], post1, 'it')
post1.publish = True
post1.main_image = self.img
post1.save()
post2 = self._get_post(self.data['en'][1], sites=sites)
post2 = self._get_post(self.data['it'][1], post2, 'it')
post2.main_image = self.img
post2.save()
return post1, post2

185
tests/base.py Normal file
View file

@ -0,0 +1,185 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from copy import deepcopy
from cmsplugin_filer_image.models import ThumbnailOption
from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site
from djangocms_helper.base_test import BaseTestCase
from parler.utils.context import smart_override
from djangocms_blog.cms_appconfig import BlogConfig
from djangocms_blog.models import BlogCategory, Post
User = get_user_model()
def _get_cat_pk(lang, name):
return lambda: BlogCategory.objects.translated(lang, name=name).get().pk
class BaseTest(BaseTestCase):
"""
Base class with utility function
"""
category_1 = None
thumb_1 = None
thumb_2 = None
_pages_data = (
{'en': {'title': 'page one', 'template': 'blog.html', 'publish': True},
'fr': {'title': 'page un', 'publish': True},
'it': {'title': 'pagina uno', 'publish': True}},
{'en': {'title': 'page two', 'template': 'blog.html', 'publish': True,
'apphook': 'BlogApp', 'apphook_namespace': 'sample_app'},
'fr': {'title': 'page deux', 'publish': True},
'it': {'title': 'pagina due', 'publish': True}},
{'en': {'title': 'page three', 'template': 'blog.html', 'publish': True,
'apphook': 'BlogApp', 'apphook_namespace': 'sample_app2'},
'fr': {'title': 'page trois', 'publish': True},
'it': {'title': 'pagina tre', 'publish': True}},
)
_post_data = (
{'en': {'title': 'First post', 'abstract': '<p>first line</p>',
'description': 'This is the description', 'keywords': 'keyword1, keyword2',
'text': 'Post text', 'app_config': 'sample_app', 'publish': True},
'it': {'title': 'Primo post', 'abstract': '<p>prima riga</p>',
'description': 'Questa è la descrizione', 'keywords': 'keyword1, keyword2',
'text': 'Testo del post'},
},
{'en': {'title': 'Second post', 'abstract': '<p>second post first line</p>',
'description': 'Second post description', 'keywords': 'keyword3, keyword4',
'text': 'Second post text', 'app_config': 'sample_app', 'publish': False},
'it': {'title': 'Secondo post', 'abstract': '<p>prima riga del secondo post</p>',
'description': 'Descrizione del secondo post', 'keywords': 'keyword3, keyword4',
'text': 'Testo del secondo post', 'app_config': 'sample_app'},
},
{'en': {'title': 'Third post', 'abstract': '<p>third post first line</p>',
'description': 'third post description', 'keywords': 'keyword5, keyword6',
'text': 'Third post text', 'app_config': 'sample_app', 'publish': False},
'it': {'title': 'Terzo post', 'abstract': '<p>prima riga del terzo post</p>',
'description': 'Descrizione del terzo post', 'keywords': 'keyword5, keyword6',
'text': 'Testo del terzo post'},
},
{'en': {'title': 'Different appconfig', 'abstract': '<p>Different appconfig first line</p>',
'description': 'Different appconfig description', 'keywords': 'keyword5, keyword6',
'text': 'Different appconfig text', 'app_config': 'sample_app2', 'publish': True},
'it': {'title': 'Altro appconfig', 'abstract': '<p>prima riga del Altro appconfig</p>',
'description': 'Descrizione Altro appconfig', 'keywords': 'keyword5, keyword6',
'text': 'Testo del Altro appconfig'},
},
)
_categories_data = (
{'en': {'name': 'Very loud', 'app_config': 'sample_app'},
'it': {'name': 'Fortissimo'},
},
{'en': {'name': 'Very very silent', 'app_config': 'sample_app'},
'it': {'name': 'Pianississimo'},
},
{'en': {'name': 'Almost', 'app_config': 'sample_app'},
'it': {'name': 'Mezzo'},
},
{'en': {'name': 'Loud', 'parent_id': _get_cat_pk('en', 'Almost'), 'app_config': 'sample_app'},
'it': {'name': 'Forte', 'parent_id': _get_cat_pk('it', 'Mezzo')},
},
{'en': {'name': 'Silent', 'parent_id': _get_cat_pk('en', 'Almost'), 'app_config': 'sample_app'},
},
{'en': {'name': 'Drums', 'app_config': 'sample_app2'},
'it': {'name': 'Tamburi'},
},
{'en': {'name': 'Guitars', 'app_config': 'sample_app2'},
'it': {'name': 'Chitarre'},
},
)
@classmethod
def setUpClass(cls):
super(BaseTest, cls).setUpClass()
cls.thumb_1 = ThumbnailOption.objects.create(
name='base', width=100, height=100, crop=True, upscale=False
)
cls.thumb_2 = ThumbnailOption.objects.create(
name='main', width=200, height=200, crop=False, upscale=False
)
cls.app_config_1 = BlogConfig.objects.create(
namespace='sample_app', app_title='app1'
)
cls.app_config_2 = BlogConfig.objects.create(
namespace='sample_app2', app_title='app2'
)
cls.app_config_1.app_data.config.paginate_by = 1
cls.app_config_1.save()
cls.app_config_2.app_data.config.paginate_by = 2
cls.app_config_2.save()
cls.app_configs = {
'sample_app': cls.app_config_1,
'sample_app2': cls.app_config_2,
}
cls.category_1 = BlogCategory.objects.create(name='category 1', app_config=cls.app_config_1)
cls.category_1.set_current_language('it', initialize=True)
cls.category_1.name = 'categoria 1'
cls.category_1.save()
cls.site_2 = Site.objects.create(domain='http://example2.com', name='example 2')
@classmethod
def tearDownClass(cls):
super(BaseTest, cls).tearDownClass()
BlogConfig.objects.all().delete()
BlogCategory.objects.all().delete()
ThumbnailOption.objects.all().delete()
def _get_category(self, data, category=None, lang='en'):
data = deepcopy(data)
for k, v in data.items():
if hasattr(v, '__call__'):
data[k] = v()
if not category:
with smart_override(lang):
data['app_config'] = self.app_configs[data['app_config']]
category = BlogCategory.objects.create(**data)
else:
category.set_current_language(lang, initialize=True)
for attr, val in data.items():
setattr(category, attr, val)
category.save()
return category
def _get_post(self, data, post=None, lang='en', sites=None):
if not post:
post_data = {
'author': self.user,
'title': data['title'],
'abstract': data['abstract'],
'meta_description': data['description'],
'meta_keywords': data['keywords'],
'app_config': self.app_configs[data['app_config']]
}
post = Post.objects.create(**post_data)
else:
post.create_translation(
lang,
title=data['title'],
abstract=data['abstract'],
meta_description=data['description'],
meta_keywords=data['keywords']
)
post = self.reload_model(post)
post.categories.add(self.category_1)
if sites:
for site in sites:
post.sites.add(site)
return post
def get_posts(self, sites=None):
posts = []
for post in self._post_data:
post1 = self._get_post(post['en'], sites=sites)
post1 = self._get_post(post['it'], post=post1, lang='it')
post1.publish = post['en']['publish']
post1.main_image = self.create_filer_image_object()
post1.save()
posts.append(post1)
return posts

View file

@ -1,25 +1,30 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import copy
from aldryn_apphooks_config.utils import get_app_instance
from django.core.cache import cache
from django.utils.translation import activate
from menus.menu_pool import menu_pool
from parler.utils.context import switch_language
from parler.utils.context import 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 . import BaseTest
from .base import BaseTest
class MenuTest(BaseTest):
cats = []
def setUp(self):
super(MenuTest, self).setUp()
self.cats = [self.category_1]
for i, cat_data in enumerate(self.cat_data['en']):
cat = self._get_category(cat_data)
if i < len(self.cat_data['it']):
cat = self._get_category(self.cat_data['it'][i], cat, 'it')
for i, lang_data in enumerate(self._categories_data):
cat = self._get_category(lang_data['en'])
if 'it' in lang_data:
cat = self._get_category(lang_data['it'], cat, 'it')
self.cats.append(cat)
activate('en')
@ -32,54 +37,111 @@ class MenuTest(BaseTest):
"""
Tests if all categories are present in the menu
"""
self.get_posts()
self.get_pages()
for lang in ('en', 'it'):
request = self.get_page_request(None, self.user,
r'/%s/blog/' % lang, edit=False)
activate(lang)
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_copy = copy.deepcopy(nodes)
for cat in self.cats:
if not cat.has_translation(lang):
continue
with switch_language(cat, lang):
# find in node list
found = None
for node in nodes_copy:
if node.url == cat.get_absolute_url():
found = node
break
self.assertIsNotNone(found)
nodes_copy.remove(found)
self.assertEqual(node.id, cat.id)
self.assertEqual(node.title, cat.name)
# check that all categories were found in menu
self.assertEqual(len(nodes_copy), 0)
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
with smart_override(lang):
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_url = set([node.url for node in nodes])
cats_url = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)])
self.assertTrue(cats_url.issubset(nodes_url))
def test_menu_options(self):
"""
Tests menu structure based on menu_structure configuration
"""
posts = self.get_posts()
self.get_pages()
cats_url = {}
posts_url = {}
languages = ('en', 'it')
for lang in languages:
with smart_override(lang):
cats_url[lang] = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang)])
posts_url[lang] = set([post.get_absolute_url() for post in posts if post.has_translation(lang) and post.app_config == self.app_config_1])
# No item in the menu
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_NONE
self.app_config_1.save()
cache.clear()
for lang in languages:
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
with smart_override(lang):
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_url = set([node.url for node in nodes])
self.assertFalse(cats_url[lang].issubset(nodes_url))
self.assertFalse(posts_url[lang].issubset(nodes_url))
# Only posts in the menu
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_POSTS
self.app_config_1.save()
cache.clear()
for lang in languages:
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
with smart_override(lang):
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_url = set([node.url for node in nodes])
self.assertFalse(cats_url[lang].issubset(nodes_url))
self.assertTrue(posts_url[lang].issubset(nodes_url))
# Only categories in the menu
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_CATEGORIES
self.app_config_1.save()
cache.clear()
for lang in languages:
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
with smart_override(lang):
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_url = set([node.url for node in nodes])
self.assertTrue(cats_url[lang].issubset(nodes_url))
self.assertFalse(posts_url[lang].issubset(nodes_url))
# Both types in the menu
self.app_config_1.app_data.config.menu_structure = MENU_TYPE_COMPLETE
self.app_config_1.save()
cache.clear()
for lang in languages:
request = self.get_page_request(None, self.user, r'/%s/page-two/' % lang)
with smart_override(lang):
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
nodes_url = set([node.url for node in nodes])
self.assertTrue(cats_url[lang].issubset(nodes_url))
self.assertTrue(posts_url[lang].issubset(nodes_url))
def test_modifier(self):
"""
Tests if correct category is selected in the menu
according to context (view object)
"""
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
tests = (
# view class, view kwarg, view object, category
(PostDetailView, 'slug', post1, post1.categories.first()),
(PostDetailView, 'slug', posts[0], posts[0].categories.first()),
(CategoryEntriesView, 'category', self.cats[2], self.cats[2])
)
for view_cls, kwarg, obj, cat in tests:
request = self.get_page_request(None, self.user, r'/en/blog/', edit=False)
activate('en')
with switch_language(obj, 'en'):
view_obj = view_cls()
view_obj.request = request
view_obj.kwargs = {kwarg: obj.slug}
view_obj.get(request)
# check if selected menu node points to cat
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
found = False
for node in nodes:
if node.selected:
self.assertEqual(node.url, cat.get_absolute_url())
found = True
break
self.assertTrue(found)
request = self.get_page_request(pages[1], self.user, path=obj.get_absolute_url())
with smart_override('en'):
with switch_language(obj, 'en'):
view_obj = view_cls()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.app_config = self.app_config_1
view_obj.kwargs = {kwarg: obj.slug}
view_obj.get(request)
# check if selected menu node points to cat
nodes = menu_pool.get_nodes(request, namespace='BlogCategoryMenu')
found = False
for node in nodes:
if node.selected:
self.assertEqual(node.url, obj.get_absolute_url())
found = True
break
self.assertTrue(found)

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import re
from copy import deepcopy
import parler
@ -12,37 +13,109 @@ from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.utils.encoding import force_text
from django.utils.html import strip_tags
from django.utils.timezone import now
from django.utils.translation import get_language, override
from djangocms_helper.utils import CMS_30
from taggit.models import Tag
from djangocms_blog.models import Post
from djangocms_blog.cms_appconfig import BlogConfig, BlogConfigForm
from djangocms_blog.models import BlogCategory, Post
from djangocms_blog.settings import get_setting
from . import BaseTest
from .base import BaseTest
class AdminTest(BaseTest):
def test_admin_fieldsets(self):
request = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False)
def setUp(self):
super(AdminTest, self).setUp()
admin.autodiscover()
def test_admin_post_views(self):
post_admin = admin.site._registry[Post]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
with self.settings(BLOG_USE_PLACEHOLDER=True):
fsets = post_admin.get_fieldsets(request)
self.assertFalse('post_text' in fsets[0][1]['fields'])
post = self._get_post(self._post_data[0]['en'])
post = self._get_post(self._post_data[0]['it'], post, 'it')
with self.settings(BLOG_USE_PLACEHOLDER=False):
fsets = post_admin.get_fieldsets(request)
self.assertTrue('post_text' in fsets[0][1]['fields'])
# Add view only contains the apphook selection widget
response = post_admin.add_view(request)
self.assertNotContains(response, '<input id="id_slug" maxlength="50" name="slug" type="text"')
self.assertContains(response, '<option value="%s">Blog / sample_app</option>' % self.app_config_1.pk)
with self.settings(BLOG_USE_ABSTRACT=True):
fsets = post_admin.get_fieldsets(request)
self.assertTrue('abstract' in fsets[0][1]['fields'])
with self.settings(BLOG_USE_ABSTRACT=False):
fsets = post_admin.get_fieldsets(request)
self.assertFalse('abstract' in fsets[0][1]['fields'])
# Changeview is 'normal'
response = post_admin.change_view(request, str(post.pk))
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)
def test_admin_blogconfig_views(self):
post_admin = admin.site._registry[BlogConfig]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
# Add view only has an empty form - no type
response = post_admin.add_view(request)
self.assertNotContains(response, 'djangocms_blog.cms_appconfig.BlogConfig')
self.assertContains(response, '<input class="vTextField" id="id_namespace" maxlength="100" name="namespace" type="text" />')
# Changeview is 'normal', with a few preselected items
response = post_admin.change_view(request, str(self.app_config_1.pk))
self.assertContains(response, 'djangocms_blog.cms_appconfig.BlogConfig')
self.assertContains(response, '<option value="Article" selected="selected">Article</option>')
# check that all the form fields are visible in the admin
for fieldname in BlogConfigForm.base_fields:
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 class="vTextField" id="id_namespace" maxlength="100" name="namespace" type="text" value="sample_app" />')
def test_admin_category_views(self):
post_admin = admin.site._registry[BlogCategory]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
# Add view only has an empty form - no type
response = post_admin.add_view(request)
self.assertNotContains(response, '<input class="vTextField" id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
self.assertContains(response, '<option value="%s">Blog / sample_app</option>' % self.app_config_1.pk)
# Changeview is 'normal', with a few preselected items
response = post_admin.change_view(request, str(self.category_1.pk))
# response.render()
# print(response.content.decode('utf-8'))
self.assertContains(response, '<input class="vTextField" id="id_name" maxlength="255" name="name" type="text" value="category 1" />')
self.assertContains(response, '<option value="%s" selected="selected">Blog / sample_app</option>' % self.app_config_1.pk)
def test_admin_fieldsets(self):
post_admin = admin.site._registry[Post]
request = self.get_page_request('/', self.user_staff, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False)
# Use placeholder
self.app_config_1.app_data.config.use_placeholder = True
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request)
self.assertFalse('post_text' in fsets[0][1]['fields'])
self.app_config_1.app_data.config.use_placeholder = False
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request)
self.assertTrue('post_text' in fsets[0][1]['fields'])
self.app_config_1.app_data.config.use_placeholder = True
self.app_config_1.save()
# Use abstract
self.app_config_1.app_data.config.use_abstract = True
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request)
self.assertTrue('abstract' in fsets[0][1]['fields'])
self.app_config_1.app_data.config.use_abstract = False
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request)
self.assertFalse('abstract' in fsets[0][1]['fields'])
self.app_config_1.app_data.config.use_abstract = True
self.app_config_1.save()
with self.settings(BLOG_MULTISITE=True):
fsets = post_admin.get_fieldsets(request)
@ -51,49 +124,65 @@ class AdminTest(BaseTest):
fsets = post_admin.get_fieldsets(request)
self.assertFalse('sites' in fsets[1][1]['fields'][0])
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
request = self.get_page_request('/', self.user, r'/en/blog/?app_config=%s' % self.app_config_1.pk, edit=False)
fsets = post_admin.get_fieldsets(request)
self.assertTrue('author' in fsets[1][1]['fields'][0])
with self.login_user_context(self.user):
request = self.get_request('/', 'en', user=self.user, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk)
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertContains(response, '<option value="%s">%s</option>' % (
self.category_1.pk, self.category_1.safe_translation_getter('name', language_code='en')
))
def test_admin_auto_author(self):
page1, page2 = self.get_pages()
data = deepcopy(self.data['en'][0])
pages = self.get_pages()
data = deepcopy(self._post_data[0]['en'])
with self.login_user_context(self.user):
with self.settings(BLOG_AUTHOR_DEFAULT=True):
data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk
request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertEqual(response.status_code, 302)
self.assertEqual(Post.objects.count(), 1)
self.assertEqual(Post.objects.get(translations__slug='first-post').author_id,
request.user.pk)
self.app_config_1.app_data.config.set_author = True
self.app_config_1.save()
data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk
data['app_config'] = self.app_config_1.pk
request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk)
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertEqual(response.status_code, 302)
self.assertEqual(Post.objects.count(), 1)
self.assertEqual(Post.objects.get(translations__slug='first-post').author_id, request.user.pk)
with self.settings(BLOG_AUTHOR_DEFAULT=False):
data = deepcopy(self.data['en'][1])
data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk
request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertEqual(response.status_code, 302)
self.assertEqual(Post.objects.count(), 2)
self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None)
self.app_config_1.app_data.config.set_author = False
self.app_config_1.save()
data = deepcopy(self._post_data[1]['en'])
data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk
data['app_config'] = self.app_config_1.pk
request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk)
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
response = post_admin.add_view(request)
self.assertEqual(response.status_code, 302)
self.assertEqual(Post.objects.count(), 2)
self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None)
with self.settings(BLOG_AUTHOR_DEFAULT='staff'):
data = deepcopy(self.data['en'][2])
self.app_config_1.app_data.config.set_author = True
self.app_config_1.save()
data = deepcopy(self._post_data[2]['en'])
data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk
request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
data['app_config'] = self.app_config_1.pk
request = self.post_request(pages[0], 'en', user=self.user, data=data, path=r'/en/blog/?app_config=%s' % self.app_config_1.pk)
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
@ -103,13 +192,13 @@ class AdminTest(BaseTest):
self.assertEqual(Post.objects.get(translations__slug='third-post').author.username, 'staff')
def test_admin_post_text(self):
page1, page2 = self.get_pages()
post = self._get_post(self.data['en'][0])
pages = self.get_pages()
post = self._get_post(self._post_data[0]['en'])
with self.login_user_context(self.user):
with self.settings(BLOG_USE_PLACEHOLDER=False):
data = {'post_text': 'ehi text'}
request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
data = {'post_text': 'ehi text', 'title': 'some title'}
request = self.post_request(pages[0], 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
msg_mid = MessageMiddleware()
msg_mid.process_request(request)
post_admin = admin.site._registry[Post]
@ -122,9 +211,14 @@ class AdminTest(BaseTest):
class ModelsTest(BaseTest):
def test_model_attributes(self):
post = self._get_post(self.data['en'][0])
post = self._get_post(self.data['it'][0], post, 'it')
post.main_image = self.img
self.get_pages()
self.app_config_1.app_data.config.gplus_author = 'RandomJoe'
self.app_config_1.save()
post = self._get_post(self._post_data[0]['en'])
post = self._get_post(self._post_data[0]['it'], post, 'it')
post.main_image = self.create_filer_image_object()
post.save()
post.set_current_language('en')
meta_en = post.as_meta()
@ -134,6 +228,14 @@ class ModelsTest(BaseTest):
self.assertEqual(meta_en.description, post.meta_description)
self.assertEqual(meta_en.keywords, post.meta_keywords.split(','))
self.assertEqual(meta_en.published_time, post.date_published)
self.assertEqual(meta_en.locale, 'en')
self.assertEqual(meta_en.twitter_site, '')
self.assertEqual(meta_en.twitter_author, '')
self.assertEqual(meta_en.twitter_type, 'summary')
self.assertEqual(meta_en.gplus_author, 'RandomJoe')
self.assertEqual(meta_en.gplus_type, 'Blog')
self.assertEqual(meta_en.og_type, 'Article')
self.assertEqual(meta_en.facebook_app_id, None)
post.set_current_language('it')
meta_it = post.as_meta()
self.assertEqual(meta_it.title, post.title)
@ -147,7 +249,11 @@ class ModelsTest(BaseTest):
'month': '%02d' % post.date_published.month,
'day': '%02d' % post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=get_language())}
url_en = reverse('djangocms_blog:post-detail', kwargs=kwargs)
url_en = reverse(
'%s:post-detail' % self.app_config_1.namespace,
kwargs=kwargs,
current_app=self.app_config_1
)
self.assertEqual(url_en, post.get_absolute_url())
with override('it'):
@ -156,7 +262,11 @@ class ModelsTest(BaseTest):
'month': '%02d' % post.date_published.month,
'day': '%02d' % post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=get_language())}
url_it = reverse('djangocms_blog:post-detail', kwargs=kwargs)
url_it = reverse(
'%s:post-detail' % self.app_config_1.namespace,
kwargs=kwargs,
current_app=self.app_config_1
)
self.assertEqual(url_it, post.get_absolute_url())
self.assertNotEqual(url_it, url_en)
@ -185,9 +295,47 @@ class ModelsTest(BaseTest):
post.meta_title = 'meta title'
self.assertEqual(post.get_title(), 'meta title')
def test_urls(self):
self.get_pages()
post = self._get_post(self._post_data[0]['en'])
post = self._get_post(self._post_data[0]['it'], post, 'it')
# default
self.assertTrue(re.match(r'.*\d{4}/\d{2}/\d{2}/%s/$' % post.slug, post.get_absolute_url()))
# full date
self.app_config_1.app_data.config.url_patterns = 'full_date'
self.app_config_1.save()
post.app_config = self.app_config_1
self.assertTrue(re.match(r'.*\d{4}/\d{2}/\d{2}/%s/$' % post.slug, post.get_absolute_url()))
# short date
self.app_config_1.app_data.config.url_patterns = 'short_date'
self.app_config_1.save()
post.app_config = self.app_config_1
self.assertTrue(re.match(r'.*\d{4}/\d{2}/%s/$' % post.slug, post.get_absolute_url()))
# category
self.app_config_1.app_data.config.url_patterns = 'category'
self.app_config_1.save()
post.app_config = self.app_config_1
self.assertTrue(re.match(r'.*/\w[-\w]*/%s/$' % post.slug, post.get_absolute_url()))
self.assertTrue(
re.match(
r'.*%s/%s/$' % (post.categories.first().slug, post.slug),
post.get_absolute_url()
)
)
# slug only
self.app_config_1.app_data.config.url_patterns = 'category'
self.app_config_1.save()
post.app_config = self.app_config_1
self.assertTrue(re.match(r'.*/%s/$' % post.slug, post.get_absolute_url()))
def test_manager(self):
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
# default queryset, published and unpublished posts
months = Post.objects.get_months()
@ -211,6 +359,7 @@ class ModelsTest(BaseTest):
post2.save()
self.assertEqual(len(Post.objects.available()), 2)
self.assertEqual(len(Post.objects.published()), 1)
self.assertEqual(len(Post.objects.published_future()), 2)
self.assertEqual(len(Post.objects.archived()), 0)
# If post is published but end publishing date is in the past
@ -222,7 +371,7 @@ class ModelsTest(BaseTest):
self.assertEqual(len(Post.objects.archived()), 1)
# counting with language fallback enabled
self._get_post(self.data['it'][0], post1, 'it')
self._get_post(self._post_data[0]['it'], post1, 'it')
self.assertEqual(len(Post.objects.filter_by_language('it')), 2)
# No fallback
@ -235,8 +384,8 @@ class ModelsTest(BaseTest):
parler.appsettings.PARLER_LANGUAGES[Site.objects.get_current().pk][index]['hide_untranslated'] = False
def test_tag_cloud(self):
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
@ -269,15 +418,24 @@ class ModelsTest(BaseTest):
self.assertEqual(set(Post.objects.tag_cloud()), set(tags_1))
self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags))
tags1 = set(Post.objects.tag_list(Post))
tags2 = set(Tag.objects.all())
self.assertEqual(tags1, tags2)
self.assertEqual(
list(Post.objects.tagged(queryset=Post.objects.filter(pk=post1.pk)).order_by('pk').values_list('pk')),
list(Post.objects.filter(pk__in=(post1.pk, post2.pk)).order_by('pk').values_list('pk'))
)
def test_plugin_latest(self):
post1 = self._get_post(self.data['en'][0])
self._get_post(self.data['en'][1])
post1 = self._get_post(self._post_data[0]['en'])
self._get_post(self._post_data[1]['en'])
post1.tags.add('tag 1')
post1.save()
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False)
request_auth = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False)
request_edit = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=True)
plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en')
plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
# unauthenticated users get no post
@ -292,11 +450,11 @@ class ModelsTest(BaseTest):
self.assertEqual(len(plugin.get_posts(request)), 1)
def test_copy_plugin_latest(self):
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
tag1 = Tag.objects.create(name='tag 1')
tag2 = Tag.objects.create(name='tag 2')
plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en')
plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
plugin.tags.add(tag1)
plugin.tags.add(tag2)
if CMS_30:
@ -309,10 +467,10 @@ class ModelsTest(BaseTest):
self.assertEqual(set(new[0].tags.all()), set(plugin.tags.all()))
def test_plugin_author(self):
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False)
plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en')
plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
plugin.authors.add(self.user)
self.assertEqual(len(plugin.get_posts(request)), 0)
self.assertEqual(plugin.get_authors()[0].count, 0)
@ -328,9 +486,9 @@ class ModelsTest(BaseTest):
self.assertEqual(plugin.get_authors()[0].count, 2)
def test_copy_plugin_author(self):
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en')
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
plugin.authors.add(self.user)
if CMS_30:
plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position'))
@ -342,9 +500,9 @@ class ModelsTest(BaseTest):
def test_multisite(self):
with override('en'):
post1 = self._get_post(self.data['en'][0], sites=(self.site_1,))
post2 = self._get_post(self.data['en'][1], sites=(self.site_2,))
post3 = self._get_post(self.data['en'][2], sites=(self.site_2, self.site_1))
post1 = self._get_post(self._post_data[0]['en'], sites=(self.site_1,))
post2 = self._get_post(self._post_data[1]['en'], sites=(self.site_2,))
post3 = self._get_post(self._post_data[2]['en'], sites=(self.site_2, self.site_1))
self.assertEqual(len(Post.objects.all()), 3)
with self.settings(**{'SITE_ID': self.site_1.pk}):
@ -353,3 +511,25 @@ class ModelsTest(BaseTest):
with self.settings(**{'SITE_ID': self.site_2.pk}):
self.assertEqual(len(Post.objects.all().on_site()), 2)
self.assertEqual(set(list(Post.objects.all().on_site())), set([post2, post3]))
def test_str_repr(self):
post1 = self._get_post(self._post_data[0]['en'])
post1.meta_description = ''
post1.main_image = None
post1.save()
self.assertEqual(force_text(post1), post1.title)
self.assertEqual(post1.get_description(), strip_tags(post1.abstract))
self.assertEqual(post1.get_image_full_url(), '')
self.assertEqual(post1.get_author(), self.user)
self.assertEqual(force_text(post1.categories.first()), 'category 1')
plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
self.assertEqual(force_text(plugin.__str__()), '5 latest articles by author')
plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
self.assertEqual(force_text(plugin.__str__()), '5 latest articles by tag')
plugin = add_plugin(post1.content, 'BlogArchivePlugin', language='en', app_config=self.app_config_1)
self.assertEqual(force_text(plugin.__str__()), 'generic blog plugin')

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import os.path
import re
from cms.api import add_plugin
@ -10,79 +11,80 @@ from taggit.models import Tag
from djangocms_blog.models import BlogCategory
from . import BaseTest
from .base import BaseTest
class PluginTest(BaseTest):
def test_plugin_latest(self):
page1, page2 = self.get_pages()
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1.tags.add('tag 1')
post1.publish = True
post1.save()
ph = page1.placeholders.get(slot='content')
pages = self.get_pages()
posts = self.get_posts()
posts[0].tags.add('tag 1')
posts[0].publish = True
posts[0].save()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en')
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph)
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1)
try:
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1)
except AssertionError:
self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-1') > -1)
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
self.assertTrue(rendered.find('<p>first line</p>') > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -1)
self.assertTrue(rendered.find(post1.get_absolute_url()) > -1)
self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)
category_2 = BlogCategory.objects.create(name=u'category 2')
category_2 = BlogCategory.objects.create(name='category 2', app_config=self.app_config_1)
category_2.set_current_language('it', initialize=True)
category_2.name = u'categoria 2'
category_2.name = 'categoria 2'
category_2.save()
category_2.set_current_language('en')
post2.categories.add(category_2)
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en')
posts[1].categories.add(category_2)
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
plugin.categories.add(category_2)
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph)
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1)
try:
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1)
except AssertionError:
self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-2') > -1)
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-category', kwargs={'category': category_2.slug})) > -1)
self.assertTrue(rendered.find('<p>second post first line</p>') > -1)
self.assertTrue(rendered.find('<article id="post-second-post"') > -1)
self.assertTrue(rendered.find(post2.get_absolute_url()) > -1)
self.assertTrue(rendered.find(posts[1].get_absolute_url()) > -1)
def test_plugin_authors(self):
page1, page2 = self.get_pages()
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1.publish = True
post1.save()
post2.publish = True
post2.save()
ph = page1.placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en')
plugin.authors.add(self.user)
pages = self.get_pages()
posts = self.get_posts()
posts[0].publish = True
posts[0].save()
posts[1].publish = True
posts[1].save()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph)
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-author', kwargs={'username': self.user.get_username()})) > -1)
self.assertTrue(rendered.find('2 articles') > -1)
self.assertTrue(rendered.find('No article found') > -1)
def test_plugin_tags(self):
page1, page2 = self.get_pages()
post1 = self._get_post(self.data['en'][0])
post2 = self._get_post(self.data['en'][1])
post1.tags.add('tag 1', 'tag 2', 'test tag')
post1.publish = True
post1.save()
post2.tags.add('test tag', 'another tag')
post2.publish = True
post2.save()
ph = page1.placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogTagsPlugin', language='en')
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
pages = self.get_pages()
posts = self.get_posts()
posts[0].tags.add('tag 1', 'tag 2', 'test tag')
posts[0].publish = True
posts[0].save()
posts[1].tags.add('test tag', 'another tag')
posts[1].publish = True
posts[1].save()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogTagsPlugin', language='en', app_config=self.app_config_1)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph)
for tag in Tag.objects.all():
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
@ -94,38 +96,55 @@ class PluginTest(BaseTest):
self.assertEqual(len(rx.findall(rendered)), 1)
def test_blog_category_plugin(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
post1.publish = True
post1.save()
post2.publish = True
post2.save()
ph = page1.placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en')
pages = self.get_pages()
posts = self.get_posts()
posts[0].publish = True
posts[0].save()
posts[1].publish = True
posts[1].save()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en', app_config=self.app_config_1)
plugin_class = plugin.get_plugin_class_instance()
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
context = plugin_class.render(context, plugin, ph)
self.assertTrue(context['categories'])
self.assertEqual(list(context['categories']), [self.category_1])
def test_blog_archive_plugin(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
post1.publish = True
post1.save()
post2.publish = True
post2.save()
ph = page1.placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogArchivePlugin', language='en')
pages = self.get_pages()
posts = self.get_posts()
posts[0].publish = True
posts[0].save()
posts[1].publish = True
posts[1].save()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogArchivePlugin', language='en', app_config=self.app_config_1)
plugin_class = plugin.get_plugin_class_instance()
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
context = plugin_class.render(context, plugin, ph)
self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
self.assertEqual(context['dates'][0]['count'], 2)
post2.publish = False
post2.save()
posts[1].publish = False
posts[1].save()
context = plugin_class.render(context, plugin, ph)
self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
self.assertEqual(context['dates'][0]['count'], 1)
def test_templates(self):
posts = self.get_posts()
pages = self.get_pages()
ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
context = self.get_plugin_context(pages[0], 'en', plugin)
plugin_class = plugin.get_plugin_class_instance()
self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('djangocms_blog', plugin_class.base_render_template))
self.app_config_1.app_data.config.template_prefix = 'whatever'
self.app_config_1.save()
self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('whatever', plugin_class.base_render_template))
self.app_config_1.app_data.config.template_prefix = ''
self.app_config_1.save()

74
tests/test_setup.py Normal file
View file

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import sys
from cms.api import create_page, create_title
from cms.models import Page
from cms.utils import get_language_list
from django.utils.translation import override
from djangocms_blog.cms_appconfig import BlogConfig
from .base import BaseTest
class SetupTest(BaseTest):
@classmethod
def setUpClass(cls):
super(BaseTest, cls).setUpClass()
def test_setup_from_url(self):
# Tests starts with no page and no config
self.assertFalse(Page.objects.exists())
self.assertFalse(BlogConfig.objects.exists())
# importing urls triggers the auto setup
from djangocms_blog import urls # NOQA
# Home and blog, published and draft
self.assertEqual(Page.objects.count(), 4)
self.assertEqual(BlogConfig.objects.count(), 1)
def setUp(self):
self.reload_urlconf()
delete = [
'djangocms_blog',
'djangocms_blog.urls',
]
for module in delete:
del sys.modules[module]
def test_setup_filled(self):
# Tests starts with no page and no config
self.assertFalse(Page.objects.exists())
self.assertFalse(BlogConfig.objects.exists())
langs = get_language_list()
home = None
for lang in langs:
with override(lang):
if not home:
home = create_page(
'a new home', language=lang,
template='blog.html', in_navigation=True, published=True
)
else:
create_title(
language=lang, title='a new home', page=home
)
home.publish(lang)
# importing urls triggers the auto setup
from djangocms_blog import urls # NOQA
# Home and blog, published and draft
self.assertEqual(Page.objects.count(), 4)
self.assertEqual(BlogConfig.objects.count(), 1)
home = Page.objects.get_home()
for lang in langs:
self.assertEqual(home.get_title(lang), 'a new home')

View file

@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
from djangocms_blog.models import BLOG_CURRENT_POST_IDENTIFIER
from . import BaseTest
from .base import BaseTest
class ToolbarTest(BaseTest):
@ -16,13 +16,13 @@ class ToolbarTest(BaseTest):
Test that Blog toolbar is present and contains all items
"""
from cms.toolbar.toolbar import CMSToolbar
post = self._get_post(self.data['en'][0])
page1, page2 = self.get_pages()
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
setattr(request, BLOG_CURRENT_POST_IDENTIFIER, post)
posts = self.get_posts()
pages = self.get_pages()
request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True)
setattr(request, BLOG_CURRENT_POST_IDENTIFIER, posts[0])
toolbar = CMSToolbar(request)
toolbar.get_left_items()
blog_menu = toolbar.menus['djangocms_blog']
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_changelist'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_add'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(post.pk,)))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(posts[0].pk,)))), 1)

View file

@ -0,0 +1 @@
# -*- coding: utf-8 -*-

View file

@ -0,0 +1,35 @@
{% load cms_tags static menu_tags sekizai_tags %}
<!DOCTYPE html {% render_block 'html_extra' %}>
<html>
<head>
<title>{% block title %}{% page_attribute 'title' %}{% endblock title %}</title>
{% render_block "css" %}
{% include "meta_mixin/meta.html" %}
<style type="text/css">
.nav {
padding-left: 0;
}
.nav li {
display: inline;
list-style-type: none;
padding-right: 20px;
}
</style>
</head>
<body>
{% cms_toolbar %}
<div style="width: 940px; margin:0 auto">
<ul class="nav">
{% show_menu 0 100 100 100 %}
</ul>
{% block content %}
{% placeholder "content" %}
{% endblock content %}
</div>
{% render_block "js" %}
{% with_data "js-script" as jsset %}
{% for js in jsset %}<script type="text/javascript" src="{% static js %}"></script>{% endfor %}
{% end_with_data %}
{% render_block "js_end" %}
</body>
</html>

View file

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import sys
from cms.utils.conf import get_cms_setting
from django.conf import settings
from django.conf.urls import include, patterns, url
@ -22,9 +24,15 @@ urlpatterns = patterns(
urlpatterns += staticfiles_urlpatterns()
if 'server' not in sys.argv:
urlpatterns += i18n_patterns(
'',
url(r'^blog/', include(
'djangocms_blog.urls', namespace='djangocms_blog', app_name='djangocms_blog'
)),
)
urlpatterns += i18n_patterns(
'',
url(r'^admin/', include(admin.site.urls)),
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
url(r'^', include('cms.urls')),
)

View file

@ -1,71 +1,127 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
import os.path
from aldryn_apphooks_config.utils import get_app_instance
from cms.toolbar.items import ModalItem
from django.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.http import Http404
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from parler.tests.utils import override_parler_settings
from parler.utils.conf import add_default_language_settings
from parler.utils.context import smart_override, switch_language
from djangocms_blog.feeds import LatestEntriesFeed, TagFeed
from djangocms_blog.models import BLOG_CURRENT_NAMESPACE
from djangocms_blog.sitemaps import BlogSitemap
from djangocms_blog.views import (
AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView,
TaggedListView,
)
from . import BaseTest
from .base import BaseTest
class ViewTest(BaseTest):
def test_post_list_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
request = self.get_request(pages[1], 'en', AnonymousUser())
with smart_override('en'):
view_obj = PostListView()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE, None), None)
self.assertEqual(list(view_obj.get_queryset()), [post1])
self.assertEqual(list(view_obj.get_queryset()), [posts[0]])
self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE), self.app_config_1)
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=False)
request = self.get_page_request(pages[1], self.user, lang='en', edit=False)
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.request = request
view_obj.kwargs = {}
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(set(qs), set([post1]))
self.assertEqual(set(qs), set([posts[0]]))
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
request = self.get_page_request(pages[1], self.user, lang='en', edit=True)
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.request = request
self.assertEqual(set(view_obj.get_queryset()), set([post1, post2]))
self.assertEqual(set(view_obj.get_queryset()), set([posts[0], posts[1], posts[2]]))
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset()
view_obj.paginate_by = 1
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2])
self.assertEqual(context['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post')
self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Third post')
response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url())
self.assertEqual(getattr(request, BLOG_CURRENT_NAMESPACE), self.app_config_1)
with smart_override('it'):
request = self.get_page_request(page1, self.user, r'/it/blog/', lang='it', edit=True)
request = self.get_page_request(pages[1], self.user, lang='it', edit=True)
view_obj = PostListView()
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.request = request
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset()
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(context['post_list'][0].title, 'Secondo post')
self.assertEqual(context['post_list'][0].title, 'Terzo post')
response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url())
blog_menu = request.toolbar.get_or_create_menu('djangocms_blog', _('Blog'))
self.assertEqual(len(blog_menu.items), 3)
self.assertEqual(len(blog_menu.find_items(
ModalItem, url=reverse('admin:djangocms_blog_post_changelist')
)), 1)
self.assertEqual(len(blog_menu.find_items(
ModalItem, url=reverse('admin:djangocms_blog_post_add')
)), 1)
self.assertEqual(len(blog_menu.find_items(
ModalItem, url=reverse(
'admin:djangocms_blog_blogconfig_change', args=(self.app_config_1.pk,)
)
)), 1)
def test_get_view_url(self):
posts = self.get_posts()
pages = self.get_pages()
# Test the custom version of get_view_url against the different namespaces
request = self.get_request(pages[1], 'en', AnonymousUser())
view_obj_1 = PostListView()
view_obj_1.request = request
view_obj_1.args = ()
view_obj_1.kwargs = {}
view_obj_1.namespace, view_obj_1.config = get_app_instance(request)
self.assertEqual(view_obj_1.get_view_url(), pages[1].get_absolute_url())
request = self.get_request(pages[2], 'en', AnonymousUser())
view_obj_2 = PostListView()
view_obj_2.request = request
view_obj_2.args = ()
view_obj_2.kwargs = {}
view_obj_2.namespace, view_obj_2.config = get_app_instance(request)
self.assertEqual(view_obj_2.get_view_url(), pages[2].get_absolute_url())
view_obj_2.view_url_name = None
with self.assertRaises(ImproperlyConfigured):
view_obj_2.get_view_url()
def test_post_list_view_fallback(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
PARLER_FALLBACK = {
1: (
@ -81,89 +137,97 @@ class ViewTest(BaseTest):
with smart_override('fr'):
view_obj = PostListView()
request = self.get_page_request(page1, self.user, r'/fr/blog/', lang='fr', edit=True)
request = self.get_page_request(pages[1], self.user, lang='fr', edit=True)
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset()
view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(view_obj.get_queryset().count(), 2)
self.assertEqual(view_obj.get_queryset().count(), 3)
PARLER_FALLBACK = add_default_language_settings(PARLER_FALLBACK)
with override_parler_settings(PARLER_LANGUAGES=PARLER_FALLBACK):
view_obj = PostListView()
request = self.get_page_request(page1, self.user, r'/fr/blog/', lang='fr', edit=True)
request = self.get_page_request(pages[1], self.user, lang='fr', edit=True)
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset()
view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(view_obj.get_queryset().count(), 0)
def test_post_detail_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
with smart_override('en'):
with switch_language(post1, 'en'):
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
with switch_language(posts[0], 'en'):
request = self.get_page_request(pages[1], AnonymousUser(), lang='en', edit=False)
view_obj = PostDetailView()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
with self.assertRaises(Http404):
view_obj.kwargs = {'slug': 'not-existing'}
post_obj = view_obj.get_object()
view_obj.kwargs = {'slug': post1.slug}
view_obj.kwargs = {'slug': posts[0].slug}
post_obj = view_obj.get_object()
self.assertEqual(post_obj, post1)
self.assertEqual(post_obj, posts[0])
self.assertEqual(post_obj.language_code, 'en')
with smart_override('it'):
with switch_language(post1, 'it'):
request = self.get_page_request(page1, AnonymousUser(), r'/it/blog/', lang='it', edit=False)
with switch_language(posts[0], 'it'):
request = self.get_page_request(pages[1], AnonymousUser(), lang='it', edit=False)
view_obj = PostDetailView()
view_obj.request = request
view_obj.kwargs = {'slug': post1.slug}
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'slug': posts[0].slug}
post_obj = view_obj.get_object()
self.assertEqual(post_obj, post1)
self.assertEqual(post_obj, posts[0])
self.assertEqual(post_obj.language_code, 'it')
view_obj.object = post_obj
context = view_obj.get_context_data()
self.assertEqual(context['post'], post1)
self.assertEqual(context['post'], posts[0])
self.assertEqual(context['post'].language_code, 'it')
self.assertTrue(context['meta'])
def test_post_archive_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
with smart_override('en'):
request = self.get_page_request(page1, AnonymousUser(), r'/en/blog/', edit=False)
request = self.get_page_request(pages[1], AnonymousUser(), lang='en', edit=False)
view_obj = PostArchiveView()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'year': now().year, 'month': now().month}
# One post only, anonymous request
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1)
self.assertEqual(list(qs), [post1])
self.assertEqual(list(qs), [posts[0]])
view_obj.object_list = qs
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(context['archive_date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
def test_category_entries_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
with smart_override('en'):
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
request = self.get_page_request(pages[1], self.user, lang='en', edit=True)
view_obj = CategoryEntriesView()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'category': 'category-1'}
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2)
self.assertEqual(set(qs), set([post1, post2]))
self.assertEqual(qs.count(), 3)
self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]]))
view_obj.paginate_by = 1
view_obj.object_list = qs
@ -171,22 +235,28 @@ class ViewTest(BaseTest):
self.assertTrue(context['category'])
self.assertEqual(context['category'], self.category_1)
self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2])
self.assertEqual(context['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post')
self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Third post')
request = self.get_page_request(pages[1], self.user, edit=False)
view_obj.request = request
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1)
def test_author_entries_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
posts = self.get_posts()
pages = self.get_pages()
with smart_override('en'):
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
request = self.get_page_request(pages[1], self.user, lang='en', edit=True)
view_obj = AuthorEntriesView()
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.request = request
view_obj.kwargs = {'username': self.user.get_username()}
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2)
self.assertEqual(set(qs), set([post1, post2]))
self.assertEqual(qs.count(), 3)
self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]]))
view_obj.paginate_by = 1
view_obj.object_list = qs
@ -194,74 +264,107 @@ class ViewTest(BaseTest):
self.assertTrue(context['author'])
self.assertEqual(context['author'], self.user)
self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2])
self.assertEqual(context['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post')
self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Third post')
request = self.get_page_request(pages[1], self.user, edit=False)
view_obj.request = request
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1)
def test_taggedlist_view(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.save()
pages = self.get_pages()
posts = self.get_posts()
posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
posts[0].save()
posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
posts[1].save()
with smart_override('en'):
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True)
request = self.get_page_request(pages[1], self.user, lang='en', edit=True)
view_obj = TaggedListView()
view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'tag': 'tag-2'}
qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2)
self.assertEqual(set(qs), set([post1, post2]))
self.assertEqual(set(qs), set([posts[0], posts[1]]))
view_obj.paginate_by = 1
view_obj.object_list = qs
context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertTrue(context['tagged_entries'], 'tag-2')
self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2])
self.assertEqual(list(context['post_list']), [posts[1]])
self.assertEqual(context['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post')
def test_feed(self):
page1, page2 = self.get_pages()
post1, post2 = self.get_posts()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.save()
post1.set_current_language('en')
posts = self.get_posts()
pages = self.get_pages()
posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
posts[0].save()
posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
posts[1].save()
posts[0].set_current_language('en')
feed = LatestEntriesFeed()
self.assertEqual(list(feed.items()), [post1])
request = self.get_page_request(page1, self.user, r'/en/blog/', lang='en', edit=False)
xml = feed(request)
self.assertContains(xml, post1.get_absolute_url())
self.assertContains(xml, 'Blog articles on example.com')
with smart_override('en'):
with switch_language(posts[0], 'en'):
request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url())
feed = LatestEntriesFeed()
feed.namespace, feed.config = get_app_instance(request)
self.assertEqual(list(feed.items()), [posts[0]])
self.reload_urlconf()
xml = feed(request)
self.assertContains(xml, posts[0].get_absolute_url())
self.assertContains(xml, 'Blog articles on example.com')
with smart_override('it'):
with switch_language(post1, 'it'):
with switch_language(posts[0], 'it'):
feed = LatestEntriesFeed()
self.assertEqual(list(feed.items()), [post1])
request = self.get_page_request(page1, self.user, r'/it/blog/', lang='it', edit=False)
feed.namespace, feed.config = get_app_instance(request)
self.assertEqual(list(feed.items()), [posts[0]])
request = self.get_page_request(pages[1], self.user, path=posts[0].get_absolute_url())
xml = feed(request)
self.assertContains(xml, post1.get_absolute_url())
self.assertContains(xml, posts[0].get_absolute_url())
self.assertContains(xml, 'Articoli del blog su example.com')
feed = TagFeed()
self.assertEqual(list(feed.items('tag-2')), [post1])
feed.namespace = self.app_config_1.namespace
feed.config = self.app_config_1
self.assertEqual(list(feed.items('tag-2')), [posts[0]])
def test_sitemap(self):
post1, post2 = self.get_posts()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.publish = True
post2.save()
post1.set_current_language('en')
posts = self.get_posts()
posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
posts[0].save()
posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
posts[1].publish = True
posts[1].save()
posts[0].set_current_language('en')
sitemap = BlogSitemap()
self.assertEqual(sitemap.items().count(), 2)
self.assertEqual(sitemap.items().count(), 3)
for item in sitemap.items():
self.assertTrue(sitemap.lastmod(item).date(), now().today())
def test_templates(self):
posts = self.get_posts()
pages = self.get_pages()
with smart_override('en'):
request = self.get_page_request(pages[1], self.user, edit=True)
view_obj = PostListView()
view_obj.request = request
view_obj.namespace = self.app_config_1.namespace
view_obj.config = self.app_config_1
self.assertEqual(view_obj.get_template_names(), os.path.join('djangocms_blog', 'post_list.html'))
self.app_config_1.app_data.config.template_prefix = 'whatever'
self.app_config_1.save()
self.assertEqual(view_obj.get_template_names(), os.path.join('whatever', 'post_list.html'))
self.app_config_1.app_data.config.template_prefix = ''
self.app_config_1.save()

11
tox.ini
View file

@ -1,18 +1,25 @@
[tox]
envlist = py{26}-django16-cms{30,31},py{27,33,34}-django{16,17}-cms{30,31,32},py{27,33,34}-django{18}-cms{31,32},pep8,isort
envlist = pep8,isort,py{35,34,33,27}-django{19,18}-cms{32,31},py{34,33,27}-django{17,16}-cms{32,31,30},py{26}-django16-cms{31,30}
[testenv]
commands = {env:COMMAND:python} setup.py test
commands = {env:COMMAND:python} cms_helper.py test djangocms_blog --no-migrate
deps =
django16: Django>=1.6,<1.7
django17: Django>=1.7,<1.8
django18: Django>=1.7,<1.9
django18: https://github.com/stefanfoulis/django-filer/archive/develop.zip
django18: https://github.com/nephila/cmsplugin-filer/archive/fix/static_filer.zip
django19: Django==1.9a1
django19: https://github.com/stefanfoulis/django-filer/archive/develop.zip
django19: https://github.com/nephila/cmsplugin-filer/archive/fix/static_filer.zip
cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip
cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip
cms32: https://github.com/divio/django-cms/archive/develop.zip
https://github.com/nephila/django-meta-mixin/archive/master.zip
https://github.com/nephila/djangocms-helper/archive/develop.zip
py26: unittest2
django-parler<1.5
https://github.com/aldryn/aldryn-apphooks-config/archive/master.zip
-r{toxinidir}/requirements-test.txt
[testenv:isort]