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

View file

@ -2,28 +2,34 @@
djangocms-blog djangocms-blog
============== ==============
.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg?style=flat-square
.. image:: https://img.shields.io/pypi/v/djangocms-blog.svg
:target: https://pypi.python.org/pypi/djangocms-blog :target: https://pypi.python.org/pypi/djangocms-blog
:alt: Latest PyPI version :alt: Latest PyPI version
.. image:: https://img.shields.io/travis/nephila/djangocms-blog.svg .. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg?style=flat-square
:target: https://travis-ci.org/nephila/djangocms-blog
:alt: Latest Travis CI build status
.. image:: https://img.shields.io/pypi/dm/djangocms-blog.svg
:target: https://pypi.python.org/pypi/djangocms-blog :target: https://pypi.python.org/pypi/djangocms-blog
:alt: Monthly downloads :alt: Monthly downloads
.. image:: https://coveralls.io/repos/nephila/djangocms-blog/badge.png .. image:: https://img.shields.io/pypi/pyversions/djangocms-blog.svg?style=flat-square
:target: https://coveralls.io/r/nephila/djangocms-blog :target: https://pypi.python.org/pypi/djangocms-blog
:alt: Python versions
.. 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://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 :alt: Test coverage
.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg .. image:: https://img.shields.io/codecov/c/github/nephila/djangocms-blog/master.svg?style=flat-square
:target: https://codecov.io/github/nephila/djangocms-blog
:alt: Test coverage
.. image:: https://codeclimate.com/github/nephila/djangocms-blog/badges/gpa.svg?style=flat-square
:target: https://codeclimate.com/github/nephila/djangocms-blog :target: https://codeclimate.com/github/nephila/djangocms-blog
:alt: Code Climate :alt: Code Climate
A djangoCMS 3 blog application. A djangoCMS 3 blog application.
Supported Django versions: Supported Django versions:
@ -46,6 +52,19 @@ Supported django CMS versions:
on South anymore; please install it separately if using this on South anymore; please install it separately if using this
application on Django 1.6. 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 Quickstart
---------- ----------
@ -85,6 +104,7 @@ Please, refer to each application documentation on details.
* django-filer: http://django-filer.readthedocs.org * django-filer: http://django-filer.readthedocs.org
* django-meta: https://github.com/nephila/django-meta#installation * 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-parler: http://django-parler.readthedocs.org/en/latest/quickstart.html#configuration
* django-taggit-autosuggest: https://bitbucket.org/fabian/django-taggit-autosuggest * django-taggit-autosuggest: https://bitbucket.org/fabian/django-taggit-autosuggest
@ -109,13 +129,14 @@ suited for your deployment.
META_SITE_PROTOCOL = 'http' META_SITE_PROTOCOL = 'http'
META_USE_SITES = True META_USE_SITES = True
* If you are using Django 1.7+, be aware than ``filer`` < 0.9.10, ``cmsplugin_filer`` * If you are using Django 1.7+, be aware than ``filer`` < 0.9.10,
and ``django-cms`` < 3.1 currently requires you to setup ``MIGRATION_MODULES`` in settings:: ``cmsplugin_filer`` and ``django-cms`` < 3.1 currently requires you to
setup ``MIGRATION_MODULES`` in settings::
MIGRATION_MODULES = { MIGRATION_MODULES = {
'cms': 'cms.migrations_django', # only for django CMS 3.0 'cms': 'cms.migrations_django', # only for django CMS 3.0
'menus': 'menus.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', 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django',
} }
@ -139,10 +160,12 @@ suited for your deployment.
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')), 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 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 * 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; * Eventually customise the Application instance name;
* Publish the page * Publish the page
* Restart the project instance to properly load blog urls. * Restart the project instance to properly load blog urls.
@ -154,26 +177,165 @@ suited for your deployment.
* Create a new blog entry in django admin backend or from the toolbar * 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 * Click on "view on site" button to view the post detail page
* Edit the post via djangocms frontend by adding / editing plugins * 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 Templates
+++++++++ +++++++++
To ease the template customisations a ``djangocms_blog/base.html`` template is used by all the blog templates; To ease the template customisations a ``djangocms_blog/base.html`` template is
the templates itself extends a ``base.html`` template; content is pulled in the ``content`` block. used by all the blog templates; the templates itself extends a ``base.html``
If you need to define a different base template, or if your base template does not defines a ``content`` block, template; content is pulled in the ``content`` block.
copy in your template directory ``djangocms_blog/base.html`` and customise it according to your If you need to define a different base template, or if your base template does
needs; the other application templates will use the newly created template and will ignore the bundled one. 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 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 https://pypi.python.org/pypi/the-real-django-wordpress and
this gist https://gist.github.com/yakky/11336204 as a base. this gist https://gist.github.com/yakky/11336204 as a base.
Settings
--------
* 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 Known djangocms-blog websites
+++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++
* http://nephila.co.uk/blog * http://nephila.co.uk/blog
* https://blog.ungleich.ch/ * 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', 'cmsplugin_filer_image',
'taggit', 'taggit',
'taggit_autosuggest', 'taggit_autosuggest',
'aldryn_apphooks_config',
'tests.test_utils',
], ],
LANGUAGE_CODE='en', LANGUAGE_CODE='en',
LANGUAGES=( LANGUAGES=(
@ -71,6 +73,9 @@ HELPER_SETTINGS = dict(
MIGRATION_MODULES={ MIGRATION_MODULES={
'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django', 'cmsplugin_filer_image': 'cmsplugin_filer_image.migrations_django',
}, },
CMS_TEMPLATES=(
('blog.html', 'Blog template'),
),
META_SITE_PROTOCOL='http', META_SITE_PROTOCOL='http',
META_SITE_DOMAIN='example.com', META_SITE_DOMAIN='example.com',
META_USE_OG_PROPERTIES=True, META_USE_OG_PROPERTIES=True,

View file

@ -1,5 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
__author__ = 'Iacopo Spalletti' __author__ = 'Iacopo Spalletti'
__email__ = 'i.spalletti@nephila.it' __email__ = 'i.spalletti@nephila.it'
__version__ = '0.5.1.dev1' __version__ = '0.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 copy import deepcopy
from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig
from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.utils.translation import ugettext_lazy as _
from parler.admin import TranslatableAdmin from parler.admin import TranslatableAdmin
from .cms_appconfig import BlogConfig
from .forms import PostAdminForm
from .models import BlogCategory, Post from .models import BlogCategory, Post
from .settings import get_setting from .settings import get_setting
@ -20,27 +24,33 @@ except ImportError:
pass pass
class BlogCategoryAdmin(EnhancedModelAdminMixin, TranslatableAdmin): class BlogCategoryAdmin(EnhancedModelAdminMixin, ModelAppHookConfig, TranslatableAdmin):
def get_prepopulated_fields(self, request, obj=None): def get_prepopulated_fields(self, request, obj=None):
app_config_default = self._app_config_select(request, obj)
if app_config_default is None and request.method == 'GET':
return {}
return {'slug': ('name',)} return {'slug': ('name',)}
class Media: class Media:
css = { css = {
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
'djangocms_blog_admin.css'),)
} }
class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin, class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
PlaceholderAdminMixin, TranslatableAdmin): ModelAppHookConfig, TranslatableAdmin):
list_display = ['title', 'author', 'date_published', 'date_published_end'] form = PostAdminForm
list_display = [
'title', 'author', 'date_published', 'app_config', 'languages', 'date_published_end'
]
list_filter = ('app_config',)
date_hierarchy = 'date_published' date_hierarchy = 'date_published'
raw_id_fields = ['author'] raw_id_fields = ['author']
frontend_editable_fields = ('title', 'abstract', 'post_text') frontend_editable_fields = ('title', 'abstract', 'post_text')
enhance_exclude = ('main_image', 'tags') enhance_exclude = ('main_image', 'tags')
_fieldsets = [ _fieldsets = [
(None, { (None, {
'fields': [('title', 'categories', 'publish')] 'fields': [('title', 'categories', 'publish', 'app_config')]
}), }),
('Info', { ('Info', {
'fields': (['slug', 'tags'], '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): def formfield_for_dbfield(self, db_field, **kwargs):
field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs) field = super(PostAdmin, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'meta_description': if db_field.name == 'meta_description':
@ -68,7 +85,21 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
return field return field
def get_fieldsets(self, request, obj=None): 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) fsets = deepcopy(self._fieldsets)
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'): if get_setting('USE_ABSTRACT'):
fsets[0][1]['fields'].append('abstract') fsets[0][1]['fields'].append('abstract')
if not get_setting('USE_PLACEHOLDER'): if not get_setting('USE_PLACEHOLDER'):
@ -83,7 +114,7 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
return {'slug': ('title',)} return {'slug': ('title',)}
def save_model(self, request, obj, form, change): 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: if get_setting('AUTHOR_DEFAULT') is True:
user = request.user user = request.user
else: else:
@ -93,10 +124,63 @@ class PostAdmin(EnhancedModelAdminMixin, FrontendEditableAdminMixin,
class Media: class Media:
css = { css = {
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
'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(BlogCategory, BlogCategoryAdmin)
admin.site.register(Post, PostAdmin) 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 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals 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 cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .cms_appconfig import BlogConfig
from .menu import BlogCategoryMenu from .menu import BlogCategoryMenu
class BlogApp(CMSApp): class BlogApp(CMSConfigApp):
name = _('Blog') name = _('Blog')
urls = ['djangocms_blog.urls'] urls = ['djangocms_blog.urls']
app_name = 'djangocms_blog' app_name = 'djangocms_blog'
app_config = BlogConfig
menus = [BlogCategoryMenu] menus = [BlogCategoryMenu]
apphook_pool.register(BlogApp) 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 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals 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_base import CMSPluginBase
from cms.plugin_pool import plugin_pool from cms.plugin_pool import plugin_pool
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from .forms import LatestEntriesForm from .forms import LatestEntriesForm
from .models import AuthorEntriesPlugin, BlogCategory, LatestPostsPlugin, Post from .models import AuthorEntriesPlugin, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, Post
from .settings import get_setting from .settings import get_setting
class BlogPlugin(CMSPluginBase): class BlogPlugin(CMSPluginBase):
module = 'Blog' 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): class BlogLatestEntriesPlugin(BlogPlugin):
""" """
Non cached plugin which returns the latest posts taking into account the Non cached plugin which returns the latest posts taking into account the
user / toolbar state user / toolbar state
""" """
render_template = 'djangocms_blog/plugins/latest_entries.html'
name = _('Latest Blog Articles') name = _('Latest Blog Articles')
model = LatestPostsPlugin model = LatestPostsPlugin
form = LatestEntriesForm form = LatestEntriesForm
filter_horizontal = ('categories',) filter_horizontal = ('categories',)
fields = ('latest_posts', 'tags', 'categories') fields = ('latest_posts', 'tags', 'categories')
cache = False cache = False
base_render_template = 'plugins/latest_entries.html'
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPlugin, self).render(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 Cached plugin which returns the latest published posts
""" """
render_template = 'djangocms_blog/plugins/latest_entries.html'
name = _('Latest Blog Articles') name = _('Latest Blog Articles')
model = LatestPostsPlugin model = LatestPostsPlugin
form = LatestEntriesForm form = LatestEntriesForm
filter_horizontal = ('categories',) filter_horizontal = ('categories',)
fields = ('latest_posts', 'tags', 'categories') fields = ('latest_posts', 'tags', 'categories')
base_render_template = 'plugins/latest_entries.html'
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder) context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder)
@ -57,8 +64,7 @@ class BlogAuthorPostsPlugin(BlogPlugin):
module = _('Blog') module = _('Blog')
name = _('Author Blog Articles') name = _('Author Blog Articles')
model = AuthorEntriesPlugin model = AuthorEntriesPlugin
form = LatestEntriesForm base_render_template = 'plugins/authors.html'
render_template = 'djangocms_blog/plugins/authors.html'
filter_horizontal = ['authors'] filter_horizontal = ['authors']
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
@ -70,36 +76,47 @@ class BlogAuthorPostsPlugin(BlogPlugin):
class BlogTagsPlugin(BlogPlugin): class BlogTagsPlugin(BlogPlugin):
module = _('Blog') module = _('Blog')
name = _('Tags') name = _('Tags')
model = CMSPlugin model = GenericBlogPlugin
render_template = 'djangocms_blog/plugins/tags.html' base_render_template = 'plugins/tags.html'
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
context = super(BlogTagsPlugin, self).render(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 return context
class BlogCategoryPlugin(BlogPlugin): class BlogCategoryPlugin(BlogPlugin):
module = _('Blog') module = _('Blog')
name = _('Categories') name = _('Categories')
model = CMSPlugin model = GenericBlogPlugin
render_template = 'djangocms_blog/plugins/categories.html' base_render_template = 'plugins/categories.html'
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
context = super(BlogCategoryPlugin, self).render(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 return context
class BlogArchivePlugin(BlogPlugin): class BlogArchivePlugin(BlogPlugin):
module = _('Blog') module = _('Blog')
name = _('Archive') name = _('Archive')
model = CMSPlugin model = GenericBlogPlugin
render_template = 'djangocms_blog/plugins/archive.html' base_render_template = 'plugins/archive.html'
def render(self, context, instance, placeholder): def render(self, context, instance, placeholder):
context = super(BlogArchivePlugin, self).render(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 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_base import CMSToolbar
from cms.toolbar_pool import toolbar_pool from cms.toolbar_pool import toolbar_pool
from django.core.urlresolvers import reverse 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 @toolbar_pool.register
@ -17,10 +17,15 @@ class BlogToolbar(CMSToolbar):
if not self.request.user.has_perm('djangocms_blog.add_post'): if not self.request.user.has_perm('djangocms_blog.add_post'):
return # pragma: no cover return # pragma: no cover
admin_menu = self.toolbar.get_or_create_menu('djangocms_blog', _('Blog')) admin_menu = self.toolbar.get_or_create_menu('djangocms_blog', _('Blog'))
with override(self.current_lang):
url = reverse('admin:djangocms_blog_post_changelist') url = reverse('admin:djangocms_blog_post_changelist')
admin_menu.add_modal_item(_('Post list'), url=url) admin_menu.add_modal_item(_('Post list'), url=url)
url = reverse('admin:djangocms_blog_post_add') url = reverse('admin:djangocms_blog_post_add')
admin_menu.add_modal_item(_('Add post'), url=url) 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) 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 if current_post and self.request.user.has_perm('djangocms_blog.change_post'): # pragma: no cover # NOQA

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
from aldryn_apphooks_config.utils import get_app_instance
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.contrib.syndication.views import Feed from django.contrib.syndication.views import Feed
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
@ -12,14 +13,18 @@ from .settings import get_setting
class LatestEntriesFeed(Feed): 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): def link(self):
return reverse('djangocms_blog:posts-latest') return reverse('%s:posts-latest' % self.namespace, current_app=self.namespace)
def title(self): def title(self):
return _('Blog articles on %(site_name)s') % {'site_name': Site.objects.get_current().name} return _('Blog articles on %(site_name)s') % {'site_name': Site.objects.get_current().name}
def items(self, obj=None): 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): def item_title(self, item):
return item.safe_translation_getter('title') 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 import forms
from django.conf import settings from django.conf import settings
from parler.forms import TranslatableModelForm
from taggit_autosuggest.widgets import TagAutoSuggest from taggit_autosuggest.widgets import TagAutoSuggest
from .models import BlogCategory, BlogConfig, Post
class LatestEntriesForm(forms.ModelForm): class LatestEntriesForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -16,3 +19,29 @@ class LatestEntriesForm(forms.ModelForm):
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL,
'djangocms_blog_admin.css'),) '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 "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \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" "PO-Revision-Date: 2014-03-05 18:09+0100\n"
"Last-Translator: Iacopo Spalletti\n" "Last-Translator: Iacopo Spalletti\n"
"Language-Team: Italian <i.spalletti@nephila.it>\n" "Language-Team: Italian <i.spalletti@nephila.it>\n"
@ -17,189 +17,361 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Lokalize 1.5\n" "X-Generator: Lokalize 1.5\n"
#: cms_app.py:8 cms_plugins.py:32 cms_plugins.py:45 cms_plugins.py:56 #: cms_app.py:13 cms_plugins.py:64 cms_plugins.py:77 cms_plugins.py:93
#: cms_plugins.py:67 cms_toolbar.py:18 #: cms_plugins.py:108 cms_toolbar.py:19
msgid "Blog" msgid "Blog"
msgstr "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" msgid "Latest Blog Articles"
msgstr "Latest Blog Articles" msgstr "Latest Blog Articles"
#: cms_plugins.py:33 #: cms_plugins.py:65
msgid "Author Blog Articles" msgid "Author Blog Articles"
msgstr "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" msgid "Tags"
msgstr "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" msgid "Categories"
msgstr "Categories" msgstr "Categories"
#: cms_plugins.py:68 templates/djangocms_blog/post_list.html:12 #: cms_plugins.py:109 templates/djangocms_blog/post_list.html:12
#: templates/djangocms_blog/plugins/archive.html:4 #: templates/djangocms_blog/plugins/archive.html:3
msgid "Archive" msgid "Archive"
msgstr "Archive" msgstr "Archive"
#: cms_toolbar.py:20 #: cms_toolbar.py:22
msgid "Post list" msgid "Post list"
msgstr "Post list" msgstr "Post list"
#: cms_toolbar.py:22 #: cms_toolbar.py:24
msgid "Add post" msgid "Add post"
msgstr "Add post" msgstr "Add post"
#: cms_toolbar.py:26 #: cms_toolbar.py:28
msgid "Edit configuration"
msgstr ""
#: cms_toolbar.py:32
msgid "Edit Post" msgid "Edit Post"
msgstr "" msgstr ""
#: feeds.py:16 #: feeds.py:24
#, python-format #, python-format
msgid "Blog articles on %(site_name)s" msgid "Blog articles on %(site_name)s"
msgstr "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" msgid "parent"
msgstr "parent" msgstr "parent"
#: models.py:33 #: models.py:35
msgid "created at" msgid "created at"
msgstr "created at" msgstr "created at"
#: models.py:34 #: models.py:36
msgid "modified at" msgid "modified at"
msgstr "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" msgid "name"
msgstr "name" msgstr "name"
#: models.py:38 models.py:105 #: models.py:41 models.py:124
msgid "slug" msgid "slug"
msgstr "slug" msgstr "slug"
#: models.py:45 #: models.py:48
msgid "blog category" msgid "blog category"
msgstr "blog category" msgstr "blog category"
#: models.py:46 #: models.py:49
msgid "blog categories" msgid "blog categories"
msgstr "blog categories" msgstr "blog categories"
#: models.py:70 #: models.py:89
msgid "Author" #, fuzzy
msgid "author"
msgstr "Author" msgstr "Author"
#: models.py:75 #: models.py:92
msgid "Published Since" #, 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" msgstr "Published Since"
#: models.py:77 #: models.py:96
msgid "Published Until" #, fuzzy
msgid "published until"
msgstr "Published Until" msgstr "Published Until"
#: models.py:79 #: models.py:98
msgid "Publish" #, fuzzy
msgid "publish"
msgstr "Publish" msgstr "Publish"
#: models.py:80 #: models.py:99
msgid "category" msgid "category"
msgstr "category" msgstr "category"
#: models.py:82 #: models.py:101
msgid "Main image" #, fuzzy
msgid "main image"
msgstr "Main image" msgstr "Main image"
#: models.py:85 #: models.py:105
msgid "Main image thumbnail" #, fuzzy
msgid "main image thumbnail"
msgstr "Main image thumbnail" msgstr "Main image thumbnail"
#: models.py:89 #: models.py:110
msgid "Main image full" #, fuzzy
msgid "main image full"
msgstr "Main image full" msgstr "Main image full"
#: models.py:93 #: models.py:114
msgid "Enable comments on post" msgid "enable comments on post"
msgstr "" msgstr ""
#: models.py:96 #: models.py:116
msgid "Site(s)" msgid "Site(s)"
msgstr "" msgstr ""
#: models.py:98 #: models.py:117
msgid "" msgid ""
"Select sites in which to show the post. If none is set it will be visible in " "Select sites in which to show the post. If none is set it will be visible in "
"all the configured sites." "all the configured sites."
msgstr "" msgstr ""
#: models.py:104 #: models.py:123
msgid "Title" #, fuzzy
msgid "title"
msgstr "Title" msgstr "Title"
#: models.py:106 #: models.py:125
msgid "Abstract" msgid "abstract"
msgstr "" msgstr ""
#: models.py:107 #: models.py:126
msgid "Post meta description"
msgstr "Post meta description"
#: models.py:109
#, fuzzy #, fuzzy
msgid "Post meta keywords" msgid "post meta description"
msgstr "Post meta description" msgstr "Post meta description"
#: models.py:111 #: models.py:128
#, fuzzy #, fuzzy
msgid "Post meta title" msgid "post meta keywords"
msgstr "Post meta description" 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" msgid "used in title tag and social sharing"
msgstr "" msgstr ""
#: models.py:115 #: models.py:134
msgid "Text" msgid "text"
msgstr "Text" msgstr ""
#: models.py:178 #: models.py:170
msgid "blog article" msgid "blog article"
msgstr "blog article" msgstr "blog article"
#: models.py:179 #: models.py:171
msgid "blog articles" msgid "blog articles"
msgstr "blog articles" msgstr "blog articles"
#: models.py:222 models.py:250 #: models.py:269
msgid "Articles" msgid "generic blog plugin"
msgstr "Articles" 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." msgid "The number of latests articles to be displayed."
msgstr "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." msgid "Show only the blog articles tagged with chosen tags."
msgstr "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." msgid "Show only the blog articles tagged with chosen categories."
msgstr "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 #: models.py:285
msgid "Authors" #, python-format
msgid "%s latest articles by tag"
msgstr ""
#: models.py:302
#, fuzzy
msgid "authors"
msgstr "Authors" msgstr "Authors"
#: models.py:251 #: models.py:307
msgid "The number of author articles to be displayed." msgid "The number of author articles to be displayed."
msgstr "The number of author articles to be displayed." msgstr "The number of author articles to be displayed."
#: templates/djangocms_blog/post_detail.html:18 #: models.py:311
#: templates/djangocms_blog/includes/blog_item.html:11 #, python-format
msgid "by" msgid "%s latest articles by author"
msgstr "by" 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 #: templates/djangocms_blog/post_list.html:11
msgid "Articles by" msgid "Articles by"
@ -209,16 +381,11 @@ msgstr "Articles by"
msgid "Tag" msgid "Tag"
msgstr "Tag" msgstr "Tag"
#: templates/djangocms_blog/post_list.html:14
msgid "Category"
msgstr "Category"
#: templates/djangocms_blog/post_list.html:21 #: 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/authors.html:15
#: templates/djangocms_blog/plugins/categories.html:16
#: templates/djangocms_blog/plugins/latest_entries.html:7 #: 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." msgid "No article found."
msgstr "No article found." msgstr "No article found."
@ -226,43 +393,59 @@ msgstr "No article found."
msgid "Back" msgid "Back"
msgstr "Back" msgstr "Back"
#: templates/djangocms_blog/post_list.html:30 #: templates/djangocms_blog/post_list.html:29
msgid "previous" msgid "previous"
msgstr "previous" msgstr "previous"
#: templates/djangocms_blog/post_list.html:33 #: templates/djangocms_blog/post_list.html:32
msgid "Page" msgid "Page"
msgstr "Page" msgstr "Page"
#: templates/djangocms_blog/post_list.html:33 #: templates/djangocms_blog/post_list.html:32
msgid "of" msgid "of"
msgstr "of" msgstr "of"
#: templates/djangocms_blog/post_list.html:36 #: templates/djangocms_blog/post_list.html:35
msgid "next" msgid "next"
msgstr "next" msgstr "next"
#: templates/djangocms_blog/includes/blog_item.html:46 #: templates/djangocms_blog/includes/blog_item.html:24
msgid "read more" msgid "read more"
msgstr "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/authors.html:10
#: templates/djangocms_blog/plugins/categories.html:11 #: templates/djangocms_blog/plugins/categories.html:10
#: templates/djangocms_blog/plugins/tags.html:11 #: templates/djangocms_blog/plugins/tags.html:10
#, python-format #, python-format
msgid "1 article" msgid "1 article"
msgid_plural "%(articles)s articles" msgid_plural "%(articles)s articles"
msgstr[0] "1 article" msgstr[0] "1 article"
msgstr[1] "%(articles)s articles" 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/authors.html:11
#: templates/djangocms_blog/plugins/categories.html:12 #: templates/djangocms_blog/plugins/categories.html:11
#: templates/djangocms_blog/plugins/tags.html:12 #: templates/djangocms_blog/plugins/tags.html:11
msgid "0 articles" msgid "0 articles"
msgstr "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" #~ msgid "blog post"
#~ msgstr "blog post" #~ msgstr "blog post"

View file

@ -2,10 +2,12 @@
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
import django import django
from aldryn_apphooks_config.managers.parler import (
AppHookConfigTranslatableManager, AppHookConfigTranslatableQueryset,
)
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.db import models from django.db import models
from django.utils.timezone import now from django.utils.timezone import now
from parler.managers import TranslatableQuerySet, TranslationManager
try: try:
from collections import Counter from collections import Counter
@ -21,7 +23,7 @@ class TaggedFilterItem(object):
o con gli stessi tag di un model o un queryset o con gli stessi tag di un model o un queryset
""" """
tags = self._taglist(other_model, 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): def _taglist(self, other_model=None, queryset=None):
""" """
@ -29,21 +31,21 @@ class TaggedFilterItem(object):
o queryset passati come argomento o queryset passati come argomento
""" """
from taggit.models import TaggedItem from taggit.models import TaggedItem
filtro = None filter = None
if queryset is not None: if queryset is not None:
filtro = set() filter = set()
for item in queryset.all(): for item in queryset.all():
filtro.update(item.tags.all()) filter.update(item.tags.all())
filtro = set([tag.id for tag in filtro]) filter = set([tag.id for tag in filter])
elif other_model is not None: elif other_model is not None:
filtro = set(TaggedItem.objects.filter( filter = set(TaggedItem.objects.filter(
content_type__model=other_model.__name__.lower() content_type__model=other_model.__name__.lower()
).values_list('tag_id', flat=True)) ).values_list('tag_id', flat=True))
tags = set(TaggedItem.objects.filter( tags = set(TaggedItem.objects.filter(
content_type__model=self.model.__name__.lower() content_type__model=self.model.__name__.lower()
).values_list('tag_id', flat=True)) ).values_list('tag_id', flat=True))
if filtro is not None: if filter is not None:
tags = tags.intersection(filtro) tags = tags.intersection(filter)
return list(tags) return list(tags)
def tag_list(self, other_model=None, queryset=None): def tag_list(self, other_model=None, queryset=None):
@ -76,7 +78,7 @@ class TaggedFilterItem(object):
return sorted(tags, key=lambda x: -x.count) return sorted(tags, key=lambda x: -x.count)
class GenericDateQuerySet(TranslatableQuerySet): class GenericDateQuerySet(AppHookConfigTranslatableQueryset):
start_date_field = 'date_published' start_date_field = 'date_published'
end_date_field = 'date_published_end' end_date_field = 'date_published_end'
publish_field = 'publish' publish_field = 'publish'
@ -120,7 +122,7 @@ class GenericDateQuerySet(TranslatableQuerySet):
return self.active_translations(language_code=language).on_site() return self.active_translations(language_code=language).on_site()
class GenericDateTaggedManager(TaggedFilterItem, TranslationManager): class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager):
use_for_related_fields = True use_for_related_fields = True
queryset_class = GenericDateQuerySet 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 cms.menu_bases import CMSAttachMenu
from django.db.models.signals import post_delete, post_save from django.db.models.signals import post_delete, post_save
from django.utils.translation import get_language, ugettext_lazy as _ from django.utils.translation import get_language_from_request, ugettext_lazy as _
from menus.base import Modifier, NavigationNode from menus.base import NavigationNode
from menus.menu_pool import menu_pool 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): class BlogCategoryMenu(CMSAttachMenu):
name = _('Blog Category menu') name = _('Blog menu')
def get_nodes(self, request): def get_nodes(self, request):
nodes = [] nodes = []
qs = BlogCategory.objects.translated(get_language())
qs = qs.order_by('parent__id', 'translations__name') language = get_language_from_request(request, check_path=True)
for category in qs:
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( node = NavigationNode(
category.name, category.name,
category.get_absolute_url(), category.get_absolute_url(),
category.pk, '%s-%s' % (category.__class__.__name__, category.pk),
category.parent_id ('%s-%s' % (category.__class__.__name__, category.parent.id) if category.parent
else None)
) )
nodes.append(node) nodes.append(node)
if posts_menu:
posts = Post.objects
if hasattr(self, 'instance') and self.instance:
posts = posts.namespace(self.instance.application_namespace)
posts = posts.active_translations(language).distinct()
for post in posts:
if categories_menu:
category = post.categories.first()
parent = '%s-%s' % (category.__class__.__name__, category.pk)
post_id = '%s-%s' % (post.__class__.__name__, post.pk),
else:
parent = None
post_id = '%s-%s' % (post.__class__.__name__, post.pk),
node = NavigationNode(
post.get_title(),
post.get_absolute_url(language),
post_id,
parent
)
nodes.append(node)
return nodes return nodes
menu_pool.register_menu(BlogCategoryMenu) menu_pool.register_menu(BlogCategoryMenu)
class BlogNavModifier(Modifier):
"""
This navigation modifier makes sure that when
a particular blog post is viewed,
a corresponding category is selected in menu
"""
def modify(self, request, nodes, namespace, root_id, post_cut, breadcrumb):
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): def clear_menu_cache(**kwargs):
menu_pool.clear(all=True) 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 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals 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 cms.models import CMSPlugin, PlaceholderField
from django.conf import settings as dj_settings from django.conf import settings as dj_settings
from django.core.urlresolvers import reverse 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 djangocms_text_ckeditor.fields import HTMLField
from filer.fields.image import FilerImageField from filer.fields.image import FilerImageField
from meta_mixin.models import ModelMeta from meta_mixin.models import ModelMeta
from parler.managers import TranslationManager
from parler.models import TranslatableModel, TranslatedFields from parler.models import TranslatableModel, TranslatedFields
from taggit_autosuggest.managers import TaggableManager from taggit_autosuggest.managers import TaggableManager
from .cms_appconfig import BlogConfig
from .managers import GenericDateTaggedManager from .managers import GenericDateTaggedManager
from .settings import get_setting from .settings import get_setting
BLOG_CURRENT_POST_IDENTIFIER = 'djangocms_post_current' BLOG_CURRENT_POST_IDENTIFIER = 'djangocms_post_current'
BLOG_CURRENT_NAMESPACE = 'djangocms_post_current_config'
@python_2_unicode_compatible @python_2_unicode_compatible
@ -28,10 +31,12 @@ class BlogCategory(TranslatableModel):
""" """
Blog category Blog category
""" """
parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, parent = models.ForeignKey('self', verbose_name=_('parent'), null=True, blank=True)
blank=True)
date_created = models.DateTimeField(_('created at'), auto_now_add=True) date_created = models.DateTimeField(_('created at'), auto_now_add=True)
date_modified = models.DateTimeField(_('modified at'), auto_now=True) date_modified = models.DateTimeField(_('modified at'), auto_now=True)
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config')
)
translations = TranslatedFields( translations = TranslatedFields(
name=models.CharField(_('name'), max_length=255), name=models.CharField(_('name'), max_length=255),
@ -39,7 +44,7 @@ class BlogCategory(TranslatableModel):
meta={'unique_together': (('language_code', 'slug'),)} meta={'unique_together': (('language_code', 'slug'),)}
) )
objects = TranslationManager() objects = AppHookConfigTranslatableManager()
class Meta: class Meta:
verbose_name = _('blog category') verbose_name = _('blog category')
@ -47,16 +52,23 @@ class BlogCategory(TranslatableModel):
@property @property
def count(self): 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): def get_absolute_url(self, lang=None):
if not lang:
lang = get_language() lang = get_language()
if self.has_translation(lang): if self.has_translation(lang, ):
slug = self.safe_translation_getter('slug', language_code=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 # in case category doesn't exist in this language, gracefully fallback
# to posts-latest # 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): def __str__(self):
return self.safe_translation_getter('name') return self.safe_translation_getter('name')
@ -81,9 +93,9 @@ class Post(ModelMeta, TranslatableModel):
date_created = models.DateTimeField(_('created'), auto_now_add=True) date_created = models.DateTimeField(_('created'), auto_now_add=True)
date_modified = models.DateTimeField(_('last modified'), auto_now=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) default=timezone.now)
date_published_end = models.DateTimeField(_('published Until'), null=True, date_published_end = models.DateTimeField(_('published until'), null=True,
blank=True) blank=True)
publish = models.BooleanField(_('publish'), default=False) publish = models.BooleanField(_('publish'), default=False)
categories = models.ManyToManyField('djangocms_blog.BlogCategory', verbose_name=_('category'), 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'), enable_comments = models.BooleanField(verbose_name=_('enable comments on post'),
default=get_setting('ENABLE_COMMENTS')) default=get_setting('ENABLE_COMMENTS'))
sites = models.ManyToManyField('sites.Site', verbose_name=_('Site(s)'), blank=True, sites = models.ManyToManyField('sites.Site', verbose_name=_('Site(s)'), blank=True,
null=True,
help_text=_('Select sites in which to show the post. ' help_text=_('Select sites in which to show the post. '
u'If none is set it will be ' 'If none is set it will be '
u'visible in all the configured sites.')) 'visible in all the configured sites.'))
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config')
)
translations = TranslatedFields( translations = TranslatedFields(
title=models.CharField(_('title'), max_length=255), title=models.CharField(_('title'), max_length=255),
@ -132,23 +146,24 @@ class Post(ModelMeta, TranslatableModel):
_metadata = { _metadata = {
'title': 'get_title', 'title': 'get_title',
'description': 'get_description', 'description': 'get_description',
'keywords': 'get_keywords',
'og_description': 'get_description', 'og_description': 'get_description',
'twitter_description': 'get_description', 'twitter_description': 'get_description',
'gplus_description': 'get_description', 'gplus_description': 'get_description',
'keywords': 'get_keywords', 'locale': 'get_locale',
'locale': None,
'image': 'get_image_full_url', 'image': 'get_image_full_url',
'object_type': get_setting('TYPE'), 'object_type': 'get_meta_attribute',
'og_type': get_setting('FB_TYPE'), 'og_type': 'get_meta_attribute',
'og_app_id': get_setting('FB_APPID'), 'og_app_id': 'get_meta_attribute',
'og_profile_id': get_setting('FB_PROFILE_ID'), 'og_profile_id': 'get_meta_attribute',
'og_publisher': get_setting('FB_PUBLISHER'), 'og_publisher': 'get_meta_attribute',
'og_author_url': get_setting('FB_AUTHOR_URL'), 'og_author_url': 'get_meta_attribute',
'twitter_type': get_setting('TWITTER_TYPE'), 'og_author': 'get_meta_attribute',
'twitter_site': get_setting('TWITTER_SITE'), 'twitter_type': 'get_meta_attribute',
'twitter_author': get_setting('TWITTER_AUTHOR'), 'twitter_site': 'get_meta_attribute',
'gplus_type': get_setting('GPLUS_TYPE'), 'twitter_author': 'get_meta_attribute',
'gplus_author': get_setting('GPLUS_AUTHOR'), 'gplus_type': 'get_meta_attribute',
'gplus_author': 'get_meta_attribute',
'published_time': 'date_published', 'published_time': 'date_published',
'modified_time': 'date_modified', 'modified_time': 'date_modified',
'expiration_time': 'date_published_end', 'expiration_time': 'date_published_end',
@ -165,19 +180,49 @@ class Post(ModelMeta, TranslatableModel):
def __str__(self): def __str__(self):
return self.safe_translation_getter('title') return self.safe_translation_getter('title')
def get_absolute_url(self): def get_absolute_url(self, lang=None):
kwargs = {'year': self.date_published.year, if not lang:
'month': '%02d' % self.date_published.month, lang = get_language()
'day': '%02d' % self.date_published.day, category = self.categories.first()
'slug': self.safe_translation_getter('slug', kwargs = {}
language_code=get_language(), urlconf = get_setting('PERMALINK_URLS')[self.app_config.url_patterns]
any_language=True)} if '<year>' in urlconf:
return reverse('djangocms_blog:post-detail', kwargs=kwargs) 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): def get_meta_attribute(self, param):
if not self.slug and self.title: """
self.slug = slugify(self.title) Retrieves django-meta attributes from apphook config instance
super(Post, self).save(*args, **kwargs) :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): def get_title(self):
title = self.safe_translation_getter('meta_title', any_language=True) title = self.safe_translation_getter('meta_title', any_language=True)
@ -188,6 +233,9 @@ class Post(ModelMeta, TranslatableModel):
def get_keywords(self): def get_keywords(self):
return self.safe_translation_getter('meta_keywords').strip().split(',') return self.safe_translation_getter('meta_keywords').strip().split(',')
def get_locale(self):
return self.get_current_language()
def get_description(self): def get_description(self):
description = self.safe_translation_getter('meta_description', any_language=True) description = self.safe_translation_getter('meta_description', any_language=True)
if not description: if not description:
@ -224,23 +272,28 @@ class Post(ModelMeta, TranslatableModel):
@python_2_unicode_compatible @python_2_unicode_compatible
class BasePostPlugin(CMSPlugin): class BasePostPlugin(CMSPlugin):
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config'), blank=True
)
class Meta: class Meta:
abstract = True abstract = True
def post_queryset(self, request=None): def post_queryset(self, request=None):
language = get_language() 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: if not request or not getattr(request, 'toolbar', False) or not request.toolbar.edit_mode:
posts = posts.published() posts = posts.published()
return posts return posts
def __str__(self): def __str__(self):
return force_text(self.latest_posts) return _('generic blog plugin')
class LatestPostsPlugin(BasePostPlugin): class LatestPostsPlugin(BasePostPlugin):
latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'), latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'),
help_text=_('The number of latests ' help_text=_('The number of latests '
u'articles to be displayed.')) u'articles to be displayed.'))
@ -292,6 +345,16 @@ class AuthorEntriesPlugin(BasePostPlugin):
authors = self.authors.all() authors = self.authors.all()
for author in authors: for author in authors:
author.count = 0 author.count = 0
if author.djangocms_blog_post_author.filter(publish=True).exists(): qs = author.djangocms_blog_post_author
author.count = author.djangocms_blog_post_author.filter(publish=True).count() if self.app_config:
qs = qs.namespace(self.app_config.namespace)
count = qs.filter(publish=True).count()
if count:
author.count = count
return authors return authors
class GenericBlogPlugin(BasePostPlugin):
class Meta:
abstract = False

View file

@ -1,11 +1,35 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
MENU_TYPE_COMPLETE = 'complete'
MENU_TYPE_CATEGORIES = 'categories'
MENU_TYPE_POSTS = 'posts'
MENU_TYPE_NONE = 'none'
def get_setting(name): def get_setting(name):
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from meta_mixin import settings as meta_settings 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 = { default = {
'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', { 'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
'size': '120x120', 'size': '120x120',
@ -19,38 +43,41 @@ def get_setting(name):
'upscale': False 'upscale': False
}), }),
'BLOG_TAGCLOUD_MIN': getattr(settings, 'BLOG_TAGCLOUD_MIN', 1),
'BLOG_TAGCLOUD_MAX': getattr(settings, 'BLOG_TAGCLOUD_MAX', 10),
'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10), 'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10),
'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5), 'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5),
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
100), ),
'BLOG_MENU_TYPE': MENU_TYPES,
'BLOG_MENU_TYPES': MENU_TYPES,
'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'), 'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
'BLOG_TYPES': meta_settings.OBJECT_TYPES,
'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'), 'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'),
'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', 'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', meta_settings.FB_TYPES),
meta_settings.FB_APPID), 'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID),
'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', 'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID),
meta_settings.FB_PROFILE_ID), 'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER),
'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', 'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'),
meta_settings.FB_PUBLISHER), 'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'summary'),
'get_author_url'), 'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', meta_settings.TWITTER_TYPES),
'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE),
'get_author_name'), 'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'), 'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_TYPE', 'Blog'),
'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', 'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', meta_settings.GPLUS_TYPES),
meta_settings.TWITTER_SITE), 'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
'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_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True), 'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True), 'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True), 'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True),
'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True), 'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True),
'BLOG_AUTHOR_DEFAULT': getattr(settings, 'BLOG_AUTHOR_DEFAULT', 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] 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> </div>
{% endif %} {% endif %}
{% endspaceless %} {% endspaceless %}
{% if use_placeholder %} {% if post.app_config.use_placeholder %}
<div class="blog-content">{% render_placeholder post.content %}</div> <div class="blog-content">{% render_placeholder post.content %}</div>
{% else %} {% else %}
<div class="blog-content">{% render_model post "post_text" "post_text" %}</div> <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 django.conf.urls import patterns, url
from .apps import BlogAppConfig
from .feeds import LatestEntriesFeed, TagFeed from .feeds import LatestEntriesFeed, TagFeed
from .settings import get_setting
from .views import ( from .views import (
AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView, AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView,
TaggedListView, 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( urlpatterns = patterns(
'', '',
url(r'^$', url(r'^$',
@ -19,8 +33,7 @@ urlpatterns = patterns(
PostArchiveView.as_view(), name='posts-archive'), PostArchiveView.as_view(), name='posts-archive'),
url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/$', url(r'^(?P<year>\d{4})/(?P<month>\d{1,2})/$',
PostArchiveView.as_view(), name='posts-archive'), 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]*)/$', ) + detail_urls + [
PostDetailView.as_view(), name='post-detail'),
url(r'^author/(?P<username>[\w\.@+-]+)/$', url(r'^author/(?P<username>[\w\.@+-]+)/$',
AuthorEntriesView.as_view(), name='posts-author'), AuthorEntriesView.as_view(), name='posts-author'),
url(r'^category/(?P<category>[\w\.@+-]+)/$', url(r'^category/(?P<category>[\w\.@+-]+)/$',
@ -29,4 +42,6 @@ urlpatterns = patterns(
TaggedListView.as_view(), name='posts-tagged'), TaggedListView.as_view(), name='posts-tagged'),
url(r'^tag/(?P<tag>[-\w]+)/feed/$', url(r'^tag/(?P<tag>[-\w]+)/feed/$',
TagFeed(), name='posts-tagged-feed'), TagFeed(), name='posts-tagged-feed'),
) ]
BlogAppConfig.setup()

View file

@ -1,38 +1,61 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals 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.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.timezone import now
from django.utils.translation import get_language from django.utils.translation import get_language
from django.views.generic import DetailView, ListView from django.views.generic import DetailView, ListView
from parler.views import TranslatableSlugMixin, ViewUrlMixin 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 from .settings import get_setting
User = get_user_model() 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): def get_queryset(self):
language = get_language() 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: if not getattr(self.request, 'toolbar', False) or not self.request.toolbar.edit_mode:
queryset = queryset.published() queryset = queryset.published()
setattr(self.request, BLOG_CURRENT_NAMESPACE, self.config)
return queryset return queryset
def render_to_response(self, context, **response_kwargs): def get_template_names(self):
response_kwargs['current_app'] = resolve(self.request.path).namespace if self.config.template_prefix:
return super(BaseBlogView, self).render_to_response(context, **response_kwargs) 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): class PostListView(BaseBlogView, ListView):
model = Post model = Post
context_object_name = 'post_list' 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-latest' view_url_name = 'djangocms_blog:posts-latest'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
@ -40,11 +63,17 @@ class PostListView(BaseBlogView, ListView):
context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT') context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT')
return context 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): class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
model = Post model = Post
context_object_name = 'post' context_object_name = 'post'
template_name = 'djangocms_blog/post_detail.html' base_template_name = 'post_detail.html'
slug_field = 'slug' slug_field = 'slug'
view_url_name = 'djangocms_blog:post-detail' view_url_name = 'djangocms_blog:post-detail'
@ -71,7 +100,7 @@ class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
class PostArchiveView(BaseBlogView, ListView): class PostArchiveView(BaseBlogView, ListView):
model = Post model = Post
context_object_name = 'post_list' context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html' base_template_name = 'post_list.html'
date_field = 'date_published' date_field = 'date_published'
allow_empty = True allow_empty = True
allow_future = True allow_future = True
@ -99,7 +128,7 @@ class PostArchiveView(BaseBlogView, ListView):
class TaggedListView(BaseBlogView, ListView): class TaggedListView(BaseBlogView, ListView):
model = Post model = Post
context_object_name = 'post_list' context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html' base_template_name = 'post_list.html'
paginate_by = get_setting('PAGINATION') paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-tagged' view_url_name = 'djangocms_blog:posts-tagged'
@ -118,7 +147,7 @@ class TaggedListView(BaseBlogView, ListView):
class AuthorEntriesView(BaseBlogView, ListView): class AuthorEntriesView(BaseBlogView, ListView):
model = Post model = Post
context_object_name = 'post_list' context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html' base_template_name = 'post_list.html'
paginate_by = get_setting('PAGINATION') paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-authors' view_url_name = 'djangocms_blog:posts-authors'
@ -138,7 +167,7 @@ class AuthorEntriesView(BaseBlogView, ListView):
class CategoryEntriesView(BaseBlogView, ListView): class CategoryEntriesView(BaseBlogView, ListView):
model = Post model = Post
context_object_name = 'post_list' context_object_name = 'post_list'
template_name = 'djangocms_blog/post_list.html' base_template_name = 'post_list.html'
_category = None _category = None
paginate_by = get_setting('PAGINATION') paginate_by = get_setting('PAGINATION')
view_url_name = 'djangocms_blog:posts-category' view_url_name = 'djangocms_blog:posts-category'

View file

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

View file

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

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
import re
from copy import deepcopy from copy import deepcopy
import parler import parler
@ -12,38 +13,110 @@ from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware from django.contrib.messages.middleware import MessageMiddleware
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse 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.timezone import now
from django.utils.translation import get_language, override from django.utils.translation import get_language, override
from djangocms_helper.utils import CMS_30 from djangocms_helper.utils import CMS_30
from taggit.models import Tag 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 djangocms_blog.settings import get_setting
from . import BaseTest from .base import BaseTest
class AdminTest(BaseTest): class AdminTest(BaseTest):
def test_admin_fieldsets(self): def setUp(self):
request = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False) super(AdminTest, self).setUp()
post_admin = admin.site._registry[Post] admin.autodiscover()
with self.settings(BLOG_USE_PLACEHOLDER=True): def test_admin_post_views(self):
post_admin = admin.site._registry[Post]
request = self.get_page_request('/', self.user, r'/en/blog/', edit=False)
post = self._get_post(self._post_data[0]['en'])
post = self._get_post(self._post_data[0]['it'], post, 'it')
# Add view only contains the apphook selection widget
response = post_admin.add_view(request)
self.assertNotContains(response, '<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)
# 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) fsets = post_admin.get_fieldsets(request)
self.assertFalse('post_text' in fsets[0][1]['fields']) self.assertFalse('post_text' in fsets[0][1]['fields'])
with self.settings(BLOG_USE_PLACEHOLDER=False): self.app_config_1.app_data.config.use_placeholder = False
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request) fsets = post_admin.get_fieldsets(request)
self.assertTrue('post_text' in fsets[0][1]['fields']) self.assertTrue('post_text' in fsets[0][1]['fields'])
with self.settings(BLOG_USE_ABSTRACT=True): 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) fsets = post_admin.get_fieldsets(request)
self.assertTrue('abstract' in fsets[0][1]['fields']) self.assertTrue('abstract' in fsets[0][1]['fields'])
with self.settings(BLOG_USE_ABSTRACT=False):
self.app_config_1.app_data.config.use_abstract = False
self.app_config_1.save()
fsets = post_admin.get_fieldsets(request) fsets = post_admin.get_fieldsets(request)
self.assertFalse('abstract' in fsets[0][1]['fields']) 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): with self.settings(BLOG_MULTISITE=True):
fsets = post_admin.get_fieldsets(request) fsets = post_admin.get_fieldsets(request)
self.assertTrue('sites' in fsets[1][1]['fields'][0]) self.assertTrue('sites' in fsets[1][1]['fields'][0])
@ -51,35 +124,48 @@ class AdminTest(BaseTest):
fsets = post_admin.get_fieldsets(request) fsets = post_admin.get_fieldsets(request)
self.assertFalse('sites' in fsets[1][1]['fields'][0]) 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) fsets = post_admin.get_fieldsets(request)
self.assertTrue('author' in fsets[1][1]['fields'][0]) 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): def test_admin_auto_author(self):
page1, page2 = self.get_pages() pages = self.get_pages()
data = deepcopy(self.data['en'][0]) data = deepcopy(self._post_data[0]['en'])
with self.login_user_context(self.user): with self.login_user_context(self.user):
with self.settings(BLOG_AUTHOR_DEFAULT=True): 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_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S') data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk 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 = MessageMiddleware()
msg_mid.process_request(request) msg_mid.process_request(request)
post_admin = admin.site._registry[Post] post_admin = admin.site._registry[Post]
response = post_admin.add_view(request) response = post_admin.add_view(request)
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertEqual(Post.objects.count(), 1) self.assertEqual(Post.objects.count(), 1)
self.assertEqual(Post.objects.get(translations__slug='first-post').author_id, self.assertEqual(Post.objects.get(translations__slug='first-post').author_id, request.user.pk)
request.user.pk)
with self.settings(BLOG_AUTHOR_DEFAULT=False): self.app_config_1.app_data.config.set_author = False
data = deepcopy(self.data['en'][1]) self.app_config_1.save()
data = deepcopy(self._post_data[1]['en'])
data['date_published_0'] = now().strftime('%Y-%m-%d') data['date_published_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S') data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk 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 = MessageMiddleware()
msg_mid.process_request(request) msg_mid.process_request(request)
post_admin = admin.site._registry[Post] post_admin = admin.site._registry[Post]
@ -89,11 +175,14 @@ class AdminTest(BaseTest):
self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None) self.assertEqual(Post.objects.get(translations__slug='second-post').author_id, None)
with self.settings(BLOG_AUTHOR_DEFAULT='staff'): 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_0'] = now().strftime('%Y-%m-%d')
data['date_published_1'] = now().strftime('%H:%M:%S') data['date_published_1'] = now().strftime('%H:%M:%S')
data['categories'] = self.category_1.pk 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 = MessageMiddleware()
msg_mid.process_request(request) msg_mid.process_request(request)
post_admin = admin.site._registry[Post] 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') self.assertEqual(Post.objects.get(translations__slug='third-post').author.username, 'staff')
def test_admin_post_text(self): def test_admin_post_text(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post = self._get_post(self.data['en'][0]) post = self._get_post(self._post_data[0]['en'])
with self.login_user_context(self.user): with self.login_user_context(self.user):
with self.settings(BLOG_USE_PLACEHOLDER=False): with self.settings(BLOG_USE_PLACEHOLDER=False):
data = {'post_text': 'ehi text'} data = {'post_text': 'ehi text', 'title': 'some title'}
request = self.post_request(page1, 'en', user=self.user, data=data, path='/en/?edit_fields=post_text') request = self.post_request(pages[0], 'en', user=self.user, data=data, path='/en/?edit_fields=post_text')
msg_mid = MessageMiddleware() msg_mid = MessageMiddleware()
msg_mid.process_request(request) msg_mid.process_request(request)
post_admin = admin.site._registry[Post] post_admin = admin.site._registry[Post]
@ -122,9 +211,14 @@ class AdminTest(BaseTest):
class ModelsTest(BaseTest): class ModelsTest(BaseTest):
def test_model_attributes(self): def test_model_attributes(self):
post = self._get_post(self.data['en'][0]) self.get_pages()
post = self._get_post(self.data['it'][0], post, 'it')
post.main_image = self.img 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.save()
post.set_current_language('en') post.set_current_language('en')
meta_en = post.as_meta() meta_en = post.as_meta()
@ -134,6 +228,14 @@ class ModelsTest(BaseTest):
self.assertEqual(meta_en.description, post.meta_description) self.assertEqual(meta_en.description, post.meta_description)
self.assertEqual(meta_en.keywords, post.meta_keywords.split(',')) self.assertEqual(meta_en.keywords, post.meta_keywords.split(','))
self.assertEqual(meta_en.published_time, post.date_published) 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') post.set_current_language('it')
meta_it = post.as_meta() meta_it = post.as_meta()
self.assertEqual(meta_it.title, post.title) self.assertEqual(meta_it.title, post.title)
@ -147,7 +249,11 @@ class ModelsTest(BaseTest):
'month': '%02d' % post.date_published.month, 'month': '%02d' % post.date_published.month,
'day': '%02d' % post.date_published.day, 'day': '%02d' % post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=get_language())} '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()) self.assertEqual(url_en, post.get_absolute_url())
with override('it'): with override('it'):
@ -156,7 +262,11 @@ class ModelsTest(BaseTest):
'month': '%02d' % post.date_published.month, 'month': '%02d' % post.date_published.month,
'day': '%02d' % post.date_published.day, 'day': '%02d' % post.date_published.day,
'slug': post.safe_translation_getter('slug', any_language=get_language())} '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.assertEqual(url_it, post.get_absolute_url())
self.assertNotEqual(url_it, url_en) self.assertNotEqual(url_it, url_en)
@ -185,9 +295,47 @@ class ModelsTest(BaseTest):
post.meta_title = 'meta title' post.meta_title = 'meta title'
self.assertEqual(post.get_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): def test_manager(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self.data['en'][1]) post2 = self._get_post(self._post_data[1]['en'])
# default queryset, published and unpublished posts # default queryset, published and unpublished posts
months = Post.objects.get_months() months = Post.objects.get_months()
@ -211,6 +359,7 @@ class ModelsTest(BaseTest):
post2.save() post2.save()
self.assertEqual(len(Post.objects.available()), 2) self.assertEqual(len(Post.objects.available()), 2)
self.assertEqual(len(Post.objects.published()), 1) self.assertEqual(len(Post.objects.published()), 1)
self.assertEqual(len(Post.objects.published_future()), 2)
self.assertEqual(len(Post.objects.archived()), 0) self.assertEqual(len(Post.objects.archived()), 0)
# If post is published but end publishing date is in the past # 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) self.assertEqual(len(Post.objects.archived()), 1)
# counting with language fallback enabled # 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) self.assertEqual(len(Post.objects.filter_by_language('it')), 2)
# No fallback # No fallback
@ -235,8 +384,8 @@ class ModelsTest(BaseTest):
parler.appsettings.PARLER_LANGUAGES[Site.objects.get_current().pk][index]['hide_untranslated'] = False parler.appsettings.PARLER_LANGUAGES[Site.objects.get_current().pk][index]['hide_untranslated'] = False
def test_tag_cloud(self): def test_tag_cloud(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self.data['en'][1]) post2 = self._get_post(self._post_data[1]['en'])
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save() post1.save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') 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()), set(tags_1))
self.assertEqual(set(Post.objects.tag_cloud(published=False)), set(tags)) 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): def test_plugin_latest(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
self._get_post(self.data['en'][1]) self._get_post(self._post_data[1]['en'])
post1.tags.add('tag 1') post1.tags.add('tag 1')
post1.save() post1.save()
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False) 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_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) 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') tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag) plugin.tags.add(tag)
# unauthenticated users get no post # unauthenticated users get no post
@ -292,11 +450,11 @@ class ModelsTest(BaseTest):
self.assertEqual(len(plugin.get_posts(request)), 1) self.assertEqual(len(plugin.get_posts(request)), 1)
def test_copy_plugin_latest(self): def test_copy_plugin_latest(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self.data['en'][1]) post2 = self._get_post(self._post_data[1]['en'])
tag1 = Tag.objects.create(name='tag 1') tag1 = Tag.objects.create(name='tag 1')
tag2 = Tag.objects.create(name='tag 2') 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(tag1)
plugin.tags.add(tag2) plugin.tags.add(tag2)
if CMS_30: if CMS_30:
@ -309,10 +467,10 @@ class ModelsTest(BaseTest):
self.assertEqual(set(new[0].tags.all()), set(plugin.tags.all())) self.assertEqual(set(new[0].tags.all()), set(plugin.tags.all()))
def test_plugin_author(self): def test_plugin_author(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self.data['en'][1]) post2 = self._get_post(self._post_data[1]['en'])
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False) 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) plugin.authors.add(self.user)
self.assertEqual(len(plugin.get_posts(request)), 0) self.assertEqual(len(plugin.get_posts(request)), 0)
self.assertEqual(plugin.get_authors()[0].count, 0) self.assertEqual(plugin.get_authors()[0].count, 0)
@ -328,9 +486,9 @@ class ModelsTest(BaseTest):
self.assertEqual(plugin.get_authors()[0].count, 2) self.assertEqual(plugin.get_authors()[0].count, 2)
def test_copy_plugin_author(self): def test_copy_plugin_author(self):
post1 = self._get_post(self.data['en'][0]) post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self.data['en'][1]) post2 = self._get_post(self._post_data[1]['en'])
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) plugin.authors.add(self.user)
if CMS_30: if CMS_30:
plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position')) 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): def test_multisite(self):
with override('en'): with override('en'):
post1 = self._get_post(self.data['en'][0], sites=(self.site_1,)) post1 = self._get_post(self._post_data[0]['en'], sites=(self.site_1,))
post2 = self._get_post(self.data['en'][1], sites=(self.site_2,)) post2 = self._get_post(self._post_data[1]['en'], sites=(self.site_2,))
post3 = self._get_post(self.data['en'][2], sites=(self.site_2, self.site_1)) post3 = self._get_post(self._post_data[2]['en'], sites=(self.site_2, self.site_1))
self.assertEqual(len(Post.objects.all()), 3) self.assertEqual(len(Post.objects.all()), 3)
with self.settings(**{'SITE_ID': self.site_1.pk}): 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}): with self.settings(**{'SITE_ID': self.site_2.pk}):
self.assertEqual(len(Post.objects.all().on_site()), 2) self.assertEqual(len(Post.objects.all().on_site()), 2)
self.assertEqual(set(list(Post.objects.all().on_site())), set([post2, post3])) 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 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
import os.path
import re import re
from cms.api import add_plugin from cms.api import add_plugin
@ -10,79 +11,80 @@ from taggit.models import Tag
from djangocms_blog.models import BlogCategory from djangocms_blog.models import BlogCategory
from . import BaseTest from .base import BaseTest
class PluginTest(BaseTest): class PluginTest(BaseTest):
def test_plugin_latest(self): def test_plugin_latest(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1 = self._get_post(self.data['en'][0]) posts = self.get_posts()
post2 = self._get_post(self.data['en'][1]) posts[0].tags.add('tag 1')
post1.tags.add('tag 1') posts[0].publish = True
post1.publish = True posts[0].save()
post1.save() ph = pages[0].placeholders.get(slot='content')
ph = page1.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') tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag) 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) rendered = plugin.render_plugin(context, ph)
try:
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1) 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(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
self.assertTrue(rendered.find('<p>first line</p>') > -1) self.assertTrue(rendered.find('<p>first line</p>') > -1)
self.assertTrue(rendered.find('<article id="post-first-post"') > -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.set_current_language('it', initialize=True)
category_2.name = u'categoria 2' category_2.name = 'categoria 2'
category_2.save() category_2.save()
category_2.set_current_language('en') category_2.set_current_language('en')
post2.categories.add(category_2) posts[1].categories.add(category_2)
plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en') plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
plugin.categories.add(category_2) 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) rendered = plugin.render_plugin(context, ph)
try:
self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-2') > -1) 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(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('<p>second post first line</p>') > -1)
self.assertTrue(rendered.find('<article id="post-second-post"') > -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): def test_plugin_authors(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1 = self._get_post(self.data['en'][0]) posts = self.get_posts()
post2 = self._get_post(self.data['en'][1]) posts[0].publish = True
post1.publish = True posts[0].save()
post1.save() posts[1].publish = True
post2.publish = True posts[1].save()
post2.save() ph = pages[0].placeholders.get(slot='content')
ph = page1.placeholders.get(slot='content') plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en')
plugin.authors.add(self.user)
context = self.get_plugin_context(page1, 'en', plugin, edit=True) context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph) 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('No article found') > -1)
self.assertTrue(rendered.find('2 articles') > -1)
def test_plugin_tags(self): def test_plugin_tags(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1 = self._get_post(self.data['en'][0]) posts = self.get_posts()
post2 = self._get_post(self.data['en'][1]) posts[0].tags.add('tag 1', 'tag 2', 'test tag')
post1.tags.add('tag 1', 'tag 2', 'test tag') posts[0].publish = True
post1.publish = True posts[0].save()
post1.save() posts[1].tags.add('test tag', 'another tag')
post2.tags.add('test tag', 'another tag') posts[1].publish = True
post2.publish = True posts[1].save()
post2.save() ph = pages[0].placeholders.get(slot='content')
ph = page1.placeholders.get(slot='content') plugin = add_plugin(ph, 'BlogTagsPlugin', language='en', app_config=self.app_config_1)
plugin = add_plugin(ph, 'BlogTagsPlugin', language='en') context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
context = self.get_plugin_context(page1, 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph) rendered = plugin.render_plugin(context, ph)
for tag in Tag.objects.all(): for tag in Tag.objects.all():
self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1) 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) self.assertEqual(len(rx.findall(rendered)), 1)
def test_blog_category_plugin(self): def test_blog_category_plugin(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1, post2 = self.get_posts() posts = self.get_posts()
post1.publish = True posts[0].publish = True
post1.save() posts[0].save()
post2.publish = True posts[1].publish = True
post2.save() posts[1].save()
ph = page1.placeholders.get(slot='content') ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en') plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en', app_config=self.app_config_1)
plugin_class = plugin.get_plugin_class_instance() 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) context = plugin_class.render(context, plugin, ph)
self.assertTrue(context['categories']) self.assertTrue(context['categories'])
self.assertEqual(list(context['categories']), [self.category_1]) self.assertEqual(list(context['categories']), [self.category_1])
def test_blog_archive_plugin(self): def test_blog_archive_plugin(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1, post2 = self.get_posts() posts = self.get_posts()
post1.publish = True posts[0].publish = True
post1.save() posts[0].save()
post2.publish = True posts[1].publish = True
post2.save() posts[1].save()
ph = page1.placeholders.get(slot='content') ph = pages[0].placeholders.get(slot='content')
plugin = add_plugin(ph, 'BlogArchivePlugin', language='en') plugin = add_plugin(ph, 'BlogArchivePlugin', language='en', app_config=self.app_config_1)
plugin_class = plugin.get_plugin_class_instance() 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) 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]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
self.assertEqual(context['dates'][0]['count'], 2) self.assertEqual(context['dates'][0]['count'], 2)
post2.publish = False posts[1].publish = False
post2.save() posts[1].save()
context = plugin_class.render(context, plugin, ph) 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]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
self.assertEqual(context['dates'][0]['count'], 1) 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 djangocms_blog.models import BLOG_CURRENT_POST_IDENTIFIER
from . import BaseTest from .base import BaseTest
class ToolbarTest(BaseTest): class ToolbarTest(BaseTest):
@ -16,13 +16,13 @@ class ToolbarTest(BaseTest):
Test that Blog toolbar is present and contains all items Test that Blog toolbar is present and contains all items
""" """
from cms.toolbar.toolbar import CMSToolbar from cms.toolbar.toolbar import CMSToolbar
post = self._get_post(self.data['en'][0]) posts = self.get_posts()
page1, page2 = self.get_pages() pages = self.get_pages()
request = self.get_page_request(page1, self.user, r'/en/blog/', edit=True) request = self.get_page_request(pages[0], self.user, r'/en/blog/', edit=True)
setattr(request, BLOG_CURRENT_POST_IDENTIFIER, post) setattr(request, BLOG_CURRENT_POST_IDENTIFIER, posts[0])
toolbar = CMSToolbar(request) toolbar = CMSToolbar(request)
toolbar.get_left_items() toolbar.get_left_items()
blog_menu = toolbar.menus['djangocms_blog'] blog_menu = toolbar.menus['djangocms_blog']
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_changelist'))), 1) self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_changelist'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_add'))), 1) self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_add'))), 1)
self.assertEqual(len(blog_menu.find_items(ModalItem, url=reverse('admin:djangocms_blog_post_change', args=(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 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals from __future__ import absolute_import, print_function, unicode_literals
import sys
from cms.utils.conf import get_cms_setting from cms.utils.conf import get_cms_setting
from django.conf import settings from django.conf import settings
from django.conf.urls import include, patterns, url from django.conf.urls import include, patterns, url
@ -22,9 +24,15 @@ urlpatterns = patterns(
urlpatterns += staticfiles_urlpatterns() 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( urlpatterns += i18n_patterns(
'', '',
url(r'^admin/', include(admin.site.urls)), url(r'^admin/', include(admin.site.urls)),
url(r'^blog/', include('djangocms_blog.urls', namespace='djangocms_blog')),
url(r'^', include('cms.urls')), url(r'^', include('cms.urls')),
) )

View file

@ -1,71 +1,127 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals 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.contrib.auth.models import AnonymousUser
from django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse
from django.http import Http404 from django.http import Http404
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _
from parler.tests.utils import override_parler_settings from parler.tests.utils import override_parler_settings
from parler.utils.conf import add_default_language_settings from parler.utils.conf import add_default_language_settings
from parler.utils.context import smart_override, switch_language from parler.utils.context import smart_override, switch_language
from djangocms_blog.feeds import LatestEntriesFeed, TagFeed from djangocms_blog.feeds import LatestEntriesFeed, TagFeed
from djangocms_blog.models import BLOG_CURRENT_NAMESPACE
from djangocms_blog.sitemaps import BlogSitemap from djangocms_blog.sitemaps import BlogSitemap
from djangocms_blog.views import ( from djangocms_blog.views import (
AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView, AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView,
TaggedListView, TaggedListView,
) )
from . import BaseTest from .base import BaseTest
class ViewTest(BaseTest): class ViewTest(BaseTest):
def test_post_list_view(self): def test_post_list_view(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = 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'): with smart_override('en'):
view_obj = PostListView() view_obj = PostListView()
view_obj.request = request 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.request = request
view_obj.kwargs = {} view_obj.kwargs = {}
qs = view_obj.get_queryset() qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1) 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 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.kwargs = {}
view_obj.object_list = view_obj.get_queryset() view_obj.object_list = view_obj.get_queryset()
view_obj.paginate_by = 1 view_obj.paginate_by = 1
context = view_obj.get_context_data(object_list=view_obj.object_list) context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertTrue(context['is_paginated']) self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2]) self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 2) self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Second post') self.assertEqual(context['post_list'][0].title, 'Third post')
response = view_obj.render_to_response(context) response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url()) 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'): 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.request = request
view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset() view_obj.object_list = view_obj.get_queryset()
context = view_obj.get_context_data(object_list=view_obj.object_list) 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) response = view_obj.render_to_response(context)
self.assertContains(response, context['post_list'][0].get_absolute_url()) 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): def test_post_list_view_fallback(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
PARLER_FALLBACK = { PARLER_FALLBACK = {
1: ( 1: (
@ -81,89 +137,97 @@ class ViewTest(BaseTest):
with smart_override('fr'): with smart_override('fr'):
view_obj = PostListView() 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.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {} view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset() view_obj.object_list = view_obj.get_queryset()
view_obj.get_context_data(object_list=view_obj.object_list) 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) PARLER_FALLBACK = add_default_language_settings(PARLER_FALLBACK)
with override_parler_settings(PARLER_LANGUAGES=PARLER_FALLBACK): with override_parler_settings(PARLER_LANGUAGES=PARLER_FALLBACK):
view_obj = PostListView() 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.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {} view_obj.kwargs = {}
view_obj.object_list = view_obj.get_queryset() view_obj.object_list = view_obj.get_queryset()
view_obj.get_context_data(object_list=view_obj.object_list) view_obj.get_context_data(object_list=view_obj.object_list)
self.assertEqual(view_obj.get_queryset().count(), 0) self.assertEqual(view_obj.get_queryset().count(), 0)
def test_post_detail_view(self): def test_post_detail_view(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
with smart_override('en'): with smart_override('en'):
with switch_language(post1, 'en'): with switch_language(posts[0], '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 = PostDetailView() view_obj = PostDetailView()
view_obj.request = request view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
with self.assertRaises(Http404): with self.assertRaises(Http404):
view_obj.kwargs = {'slug': 'not-existing'} view_obj.kwargs = {'slug': 'not-existing'}
post_obj = view_obj.get_object() 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() post_obj = view_obj.get_object()
self.assertEqual(post_obj, post1) self.assertEqual(post_obj, posts[0])
self.assertEqual(post_obj.language_code, 'en') self.assertEqual(post_obj.language_code, 'en')
with smart_override('it'): with smart_override('it'):
with switch_language(post1, 'it'): with switch_language(posts[0], 'it'):
request = self.get_page_request(page1, AnonymousUser(), r'/it/blog/', lang='it', edit=False) request = self.get_page_request(pages[1], AnonymousUser(), lang='it', edit=False)
view_obj = PostDetailView()
view_obj.request = request view_obj.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() post_obj = view_obj.get_object()
self.assertEqual(post_obj, post1) self.assertEqual(post_obj, posts[0])
self.assertEqual(post_obj.language_code, 'it') self.assertEqual(post_obj.language_code, 'it')
view_obj.object = post_obj view_obj.object = post_obj
context = view_obj.get_context_data() 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.assertEqual(context['post'].language_code, 'it')
self.assertTrue(context['meta']) self.assertTrue(context['meta'])
def test_post_archive_view(self): def test_post_archive_view(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
with smart_override('en'): 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 = PostArchiveView()
view_obj.request = request view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'year': now().year, 'month': now().month} view_obj.kwargs = {'year': now().year, 'month': now().month}
# One post only, anonymous request # One post only, anonymous request
qs = view_obj.get_queryset() qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 1) self.assertEqual(qs.count(), 1)
self.assertEqual(list(qs), [post1]) self.assertEqual(list(qs), [posts[0]])
view_obj.object_list = qs view_obj.object_list = qs
context = view_obj.get_context_data(object_list=view_obj.object_list) 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()) self.assertEqual(context['archive_date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
def test_category_entries_view(self): def test_category_entries_view(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
with smart_override('en'): 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 = CategoryEntriesView()
view_obj.request = request view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'category': 'category-1'} view_obj.kwargs = {'category': 'category-1'}
qs = view_obj.get_queryset() qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2) self.assertEqual(qs.count(), 3)
self.assertEqual(set(qs), set([post1, post2])) self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]]))
view_obj.paginate_by = 1 view_obj.paginate_by = 1
view_obj.object_list = qs view_obj.object_list = qs
@ -171,22 +235,28 @@ class ViewTest(BaseTest):
self.assertTrue(context['category']) self.assertTrue(context['category'])
self.assertEqual(context['category'], self.category_1) self.assertEqual(context['category'], self.category_1)
self.assertTrue(context['is_paginated']) self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2]) self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 2) self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Second post') 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): def test_author_entries_view(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
with smart_override('en'): 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 = AuthorEntriesView()
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.request = request view_obj.request = request
view_obj.kwargs = {'username': self.user.get_username()} view_obj.kwargs = {'username': self.user.get_username()}
qs = view_obj.get_queryset() qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2) self.assertEqual(qs.count(), 3)
self.assertEqual(set(qs), set([post1, post2])) self.assertEqual(set(qs), set([posts[0], posts[1], posts[2]]))
view_obj.paginate_by = 1 view_obj.paginate_by = 1
view_obj.object_list = qs view_obj.object_list = qs
@ -194,74 +264,107 @@ class ViewTest(BaseTest):
self.assertTrue(context['author']) self.assertTrue(context['author'])
self.assertEqual(context['author'], self.user) self.assertEqual(context['author'], self.user)
self.assertTrue(context['is_paginated']) self.assertTrue(context['is_paginated'])
self.assertEqual(list(context['post_list']), [post2]) self.assertEqual(list(context['post_list']), [posts[2]])
self.assertEqual(context['paginator'].count, 2) self.assertEqual(context['paginator'].count, 3)
self.assertEqual(context['post_list'][0].title, 'Second post') 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): def test_taggedlist_view(self):
page1, page2 = self.get_pages() pages = self.get_pages()
post1, post2 = self.get_posts() posts = self.get_posts()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save() posts[0].save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.save() posts[1].save()
with smart_override('en'): 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 = TaggedListView()
view_obj.request = request view_obj.request = request
view_obj.namespace, view_obj.config = get_app_instance(request)
view_obj.kwargs = {'tag': 'tag-2'} view_obj.kwargs = {'tag': 'tag-2'}
qs = view_obj.get_queryset() qs = view_obj.get_queryset()
self.assertEqual(qs.count(), 2) 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.paginate_by = 1
view_obj.object_list = qs view_obj.object_list = qs
context = view_obj.get_context_data(object_list=view_obj.object_list) context = view_obj.get_context_data(object_list=view_obj.object_list)
self.assertTrue(context['tagged_entries'], 'tag-2') self.assertTrue(context['tagged_entries'], 'tag-2')
self.assertTrue(context['is_paginated']) 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['paginator'].count, 2)
self.assertEqual(context['post_list'][0].title, 'Second post') self.assertEqual(context['post_list'][0].title, 'Second post')
def test_feed(self): def test_feed(self):
page1, page2 = self.get_pages() posts = self.get_posts()
post1, post2 = self.get_posts() pages = self.get_pages()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save() posts[0].save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.save() posts[1].save()
post1.set_current_language('en') posts[0].set_current_language('en')
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 = LatestEntriesFeed()
self.assertEqual(list(feed.items()), [post1]) feed.namespace, feed.config = get_app_instance(request)
request = self.get_page_request(page1, self.user, r'/en/blog/', lang='en', edit=False) self.assertEqual(list(feed.items()), [posts[0]])
self.reload_urlconf()
xml = feed(request) xml = feed(request)
self.assertContains(xml, post1.get_absolute_url()) self.assertContains(xml, posts[0].get_absolute_url())
self.assertContains(xml, 'Blog articles on example.com') self.assertContains(xml, 'Blog articles on example.com')
with smart_override('it'): with smart_override('it'):
with switch_language(post1, 'it'): with switch_language(posts[0], 'it'):
feed = LatestEntriesFeed() feed = LatestEntriesFeed()
self.assertEqual(list(feed.items()), [post1]) feed.namespace, feed.config = get_app_instance(request)
request = self.get_page_request(page1, self.user, r'/it/blog/', lang='it', edit=False) 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) 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') self.assertContains(xml, 'Articoli del blog su example.com')
feed = TagFeed() 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): def test_sitemap(self):
post1, post2 = self.get_posts() posts = self.get_posts()
post1.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
post1.save() posts[0].save()
post2.tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8') posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
post2.publish = True posts[1].publish = True
post2.save() posts[1].save()
post1.set_current_language('en') posts[0].set_current_language('en')
sitemap = BlogSitemap() sitemap = BlogSitemap()
self.assertEqual(sitemap.items().count(), 2) self.assertEqual(sitemap.items().count(), 3)
for item in sitemap.items(): for item in sitemap.items():
self.assertTrue(sitemap.lastmod(item).date(), now().today()) 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] [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] [testenv]
commands = {env:COMMAND:python} setup.py test commands = {env:COMMAND:python} cms_helper.py test djangocms_blog --no-migrate
deps = deps =
django16: Django>=1.6,<1.7 django16: Django>=1.6,<1.7
django17: Django>=1.7,<1.8 django17: Django>=1.7,<1.8
django18: Django>=1.7,<1.9 django18: Django>=1.7,<1.9
django18: https://github.com/stefanfoulis/django-filer/archive/develop.zip 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 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 cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip
cms32: https://github.com/divio/django-cms/archive/develop.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 https://github.com/nephila/djangocms-helper/archive/develop.zip
py26: unittest2 py26: unittest2
django-parler<1.5
https://github.com/aldryn/aldryn-apphooks-config/archive/master.zip
-r{toxinidir}/requirements-test.txt -r{toxinidir}/requirements-test.txt
[testenv:isort] [testenv:isort]