diff --git a/HISTORY.rst b/HISTORY.rst
index 6a4d3f5..47d7dba 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -10,7 +10,11 @@ History
* Dropped support for Django<1.8, django CMS<3.2.
* Added liveblog application.
+* Refactored plugin filters: by default only data for current site are now shown.
+* Added global and per site posts count to BlogCategory.
+* Added option to hide empty categories from menu.
* Added standalone documentation at https://djangocms-blog.readthedocs.io.
+* Enabled cached version of BlogLatestEntriesPlugin.
******************
0.8.5 (2016-06-26)
diff --git a/cms_helper.py b/cms_helper.py
index 1643ff7..0d1c2d1 100755
--- a/cms_helper.py
+++ b/cms_helper.py
@@ -96,6 +96,11 @@ HELPER_SETTINGS = dict(
HAYSTACK_CONNECTIONS={
'default': {}
},
+ CACHES={
+ 'default': {
+ 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
+ }
+ }
)
try:
diff --git a/djangocms_blog/admin.py b/djangocms_blog/admin.py
index 7ea2e7c..b849c2f 100755
--- a/djangocms_blog/admin.py
+++ b/djangocms_blog/admin.py
@@ -262,7 +262,7 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
('Layout', {
'fields': (
'config.paginate_by', 'config.url_patterns', 'config.template_prefix',
- 'config.menu_structure',
+ 'config.menu_structure', 'config.menu_empty_categories',
),
'classes': ('collapse',)
}),
diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py
index e7e028d..0ac4542 100644
--- a/djangocms_blog/cms_appconfig.py
+++ b/djangocms_blog/cms_appconfig.py
@@ -66,6 +66,11 @@ class BlogConfigForm(AppDataForm):
choices=get_setting('MENU_TYPES'), initial=MENU_TYPE_COMPLETE,
help_text=_('Structure of the django CMS menu')
)
+ menu_empty_categories = forms.BooleanField(
+ label=_('Show empty categories in menu'), initial=get_setting('MENU_EMPTY_CATEGORIES'),
+ required=False,
+ help_text=_('Show categories with no post attached in the menu')
+ )
sitemap_changefreq = forms.ChoiceField(
label=_('Sitemap changefreq'), required=True,
choices=get_setting('SITEMAP_CHANGEFREQ'),
diff --git a/djangocms_blog/cms_menus.py b/djangocms_blog/cms_menus.py
index 426ac09..2b03fcd 100644
--- a/djangocms_blog/cms_menus.py
+++ b/djangocms_blog/cms_menus.py
@@ -48,33 +48,16 @@ class BlogCategoryMenu(CMSAttachMenu):
if config and config.menu_structure in (MENU_TYPE_COMPLETE, MENU_TYPE_POSTS):
posts_menu = True
- if categories_menu:
- categories = BlogCategory.objects
- if config:
- categories = categories.namespace(self.instance.application_namespace)
- categories = categories.active_translations(language).distinct()
- categories = categories.order_by('parent__id', 'translations__name')
- for category in categories:
- node = NavigationNode(
- category.name,
- category.get_absolute_url(),
- '{0}-{1}'.format(category.__class__.__name__, category.pk),
- (
- '{0}-{1}'.format(
- category.__class__.__name__, category.parent.id
- ) if category.parent else None
- )
- )
- nodes.append(node)
-
+ used_categories = []
if posts_menu:
posts = Post.objects
if hasattr(self, 'instance') and self.instance:
- posts = posts.namespace(self.instance.application_namespace)
+ posts = posts.namespace(self.instance.application_namespace).on_site()
posts = posts.active_translations(language).distinct()
for post in posts:
post_id = None
parent = None
+ used_categories.extend(post.categories.values_list('pk', flat=True))
if categories_menu:
category = post.categories.first()
if category:
@@ -91,6 +74,28 @@ class BlogCategoryMenu(CMSAttachMenu):
)
nodes.append(node)
+ if categories_menu:
+ categories = BlogCategory.objects
+ if config:
+ categories = categories.namespace(self.instance.application_namespace)
+ if config and not config.menu_empty_categories:
+ categories = categories.filter(pk__in=used_categories)
+ else:
+ categories = categories.active_translations(language).distinct()
+ categories = categories.order_by('parent__id', 'translations__name')
+ for category in categories:
+ node = NavigationNode(
+ category.name,
+ category.get_absolute_url(),
+ '{0}-{1}'.format(category.__class__.__name__, category.pk),
+ (
+ '{0}-{1}'.format(
+ category.__class__.__name__, category.parent.id
+ ) if category.parent else None
+ )
+ )
+ nodes.append(node)
+
return nodes
menu_pool.register_menu(BlogCategoryMenu)
diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py
index e7fc28c..70bb4b5 100644
--- a/djangocms_blog/cms_plugins.py
+++ b/djangocms_blog/cms_plugins.py
@@ -5,6 +5,8 @@ import os.path
from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool
+from django.contrib.sites.shortcuts import get_current_site
+from django.db import models
from .forms import LatestEntriesForm
from .models import AuthorEntriesPlugin, BlogCategory, GenericBlogPlugin, LatestPostsPlugin, Post
@@ -36,7 +38,7 @@ class BlogLatestEntriesPlugin(BlogPlugin):
def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPlugin, self).render(context, instance, placeholder)
- context['posts_list'] = instance.get_posts(context['request'])
+ context['posts_list'] = instance.get_posts(context['request'], published_only=False)
context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT')
return context
@@ -45,7 +47,7 @@ class BlogLatestEntriesPluginCached(BlogPlugin):
"""
Cached plugin which returns the latest published posts
"""
- name = get_setting('LATEST_ENTRIES_PLUGIN_NAME')
+ name = get_setting('LATEST_ENTRIES_PLUGIN_NAME_CACHED')
model = LatestPostsPlugin
form = LatestEntriesForm
filter_horizontal = ('categories',)
@@ -54,7 +56,7 @@ class BlogLatestEntriesPluginCached(BlogPlugin):
def render(self, context, instance, placeholder):
context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder)
- context['posts_list'] = instance.get_posts()
+ context['posts_list'] = instance.get_posts(context['request'])
context['TRUNCWORDS_COUNT'] = get_setting('POSTS_LIST_TRUNCWORDS_COUNT')
return context
@@ -80,11 +82,8 @@ class BlogTagsPlugin(BlogPlugin):
def render(self, context, instance, placeholder):
context = super(BlogTagsPlugin, self).render(context, instance, placeholder)
- 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())
+ qs = instance.post_queryset(context['request'])
+ context['tags'] = Post.objects.tag_cloud(queryset=qs.published())
return context
@@ -96,10 +95,15 @@ class BlogCategoryPlugin(BlogPlugin):
def render(self, context, instance, placeholder):
context = super(BlogCategoryPlugin, self).render(context, instance, placeholder)
- qs = BlogCategory._default_manager
+ qs = BlogCategory.objects.language().active_translations()
if instance.app_config:
qs = qs.namespace(instance.app_config.namespace)
- context['categories'] = qs
+ if instance.current_site:
+ site = get_current_site(context['request'])
+ qs = qs.filter(
+ models.Q(blog_posts__sites__isnull=True) | models.Q(blog_posts__sites=site.pk)
+ )
+ context['categories'] = qs.distinct()
return context
@@ -111,15 +115,13 @@ class BlogArchivePlugin(BlogPlugin):
def render(self, context, instance, placeholder):
context = super(BlogArchivePlugin, self).render(context, instance, placeholder)
- 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())
+ qs = instance.post_queryset(context['request'])
+ context['dates'] = Post.objects.get_months(queryset=qs.published())
return context
plugin_pool.register_plugin(BlogLatestEntriesPlugin)
+plugin_pool.register_plugin(BlogLatestEntriesPluginCached)
plugin_pool.register_plugin(BlogAuthorPostsPlugin)
plugin_pool.register_plugin(BlogTagsPlugin)
plugin_pool.register_plugin(BlogArchivePlugin)
diff --git a/djangocms_blog/liveblog/__init__.py b/djangocms_blog/liveblog/__init__.py
index f3a5710..d6e8ab9 100644
--- a/djangocms_blog/liveblog/__init__.py
+++ b/djangocms_blog/liveblog/__init__.py
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
+
default_app_config = 'djangocms_blog.liveblog.apps.LiveBlogAppConfig'
diff --git a/djangocms_blog/managers.py b/djangocms_blog/managers.py
index 004d383..8091c6a 100644
--- a/djangocms_blog/managers.py
+++ b/djangocms_blog/managers.py
@@ -56,8 +56,10 @@ class TaggedFilterItem(object):
queryset = self.tag_list(other_model, queryset)
return queryset.values('slug')
- def tag_cloud(self, other_model=None, queryset=None, published=True):
+ def tag_cloud(self, other_model=None, queryset=None, published=True, on_site=False):
from taggit.models import TaggedItem
+ if on_site:
+ queryset = queryset.on_site()
tag_ids = self._taglist(other_model, queryset)
kwargs = {}
if published:
@@ -80,20 +82,25 @@ class GenericDateQuerySet(AppHookConfigTranslatableQueryset):
end_date_field = 'date_published_end'
publish_field = 'publish'
- def on_site(self):
+ def on_site(self, site=None):
+ if not site:
+ site = Site.objects.get_current()
return self.filter(models.Q(sites__isnull=True) |
- models.Q(sites=Site.objects.get_current().pk))
+ models.Q(sites=site.pk))
- def published(self):
- queryset = self.published_future()
+ def published(self, current_site=True):
+ queryset = self.published_future(current_site)
if self.start_date_field:
return queryset.filter(
**{'%s__lte' % self.start_date_field: now()})
else:
return queryset
- def published_future(self):
- queryset = self.on_site()
+ def published_future(self, current_site=True):
+ if current_site:
+ queryset = self.on_site()
+ else:
+ queryset = self
if self.end_date_field:
qfilter = (
models.Q(**{'%s__gte' % self.end_date_field: now()}) |
@@ -102,21 +109,29 @@ class GenericDateQuerySet(AppHookConfigTranslatableQueryset):
queryset = queryset.filter(qfilter)
return queryset.filter(**{self.publish_field: True})
- def archived(self):
- queryset = self.on_site()
+ def archived(self, current_site=True):
+ if current_site:
+ queryset = self.on_site()
+ else:
+ queryset = self
if self.end_date_field:
qfilter = (
- models.Q(**{'%s__lte' % self.end_date_field: now()}) |
- models.Q(**{'%s__isnull' % self.end_date_field: False})
+ models.Q(**{'%s__lte' % self.end_date_field: now()})
)
queryset = queryset.filter(qfilter)
return queryset.filter(**{self.publish_field: True})
- def available(self):
- return self.on_site().filter(**{self.publish_field: True})
+ def available(self, current_site=True):
+ if current_site:
+ return self.on_site().filter(**{self.publish_field: True})
+ else:
+ return self.filter(**{self.publish_field: True})
- def filter_by_language(self, language):
- return self.active_translations(language_code=language).on_site()
+ def filter_by_language(self, language, current_site=True):
+ if current_site:
+ return self.active_translations(language_code=language).on_site()
+ else:
+ return self.active_translations(language_code=language)
class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManager):
@@ -127,29 +142,30 @@ class GenericDateTaggedManager(TaggedFilterItem, AppHookConfigTranslatableManage
def get_queryset(self, *args, **kwargs):
return super(GenericDateTaggedManager, self).get_queryset(*args, **kwargs)
- def published(self):
- return self.get_queryset().published()
+ def published(self, current_site=True):
+ return self.get_queryset().published(current_site)
- def available(self):
- return self.get_queryset().available()
+ def available(self, current_site=True):
+ return self.get_queryset().available(current_site)
- def archived(self):
- return self.get_queryset().archived()
+ def archived(self, current_site=True):
+ return self.get_queryset().archived(current_site)
- def published_future(self):
- return self.get_queryset().published_future()
+ def published_future(self, current_site=True):
+ return self.get_queryset().published_future(current_site)
- def filter_by_language(self, language):
- return self.get_queryset().filter_by_language(language)
+ def filter_by_language(self, language, current_site=True):
+ return self.get_queryset().filter_by_language(language, current_site)
- def get_months(self, queryset=None):
+ def get_months(self, queryset=None, current_site=True):
"""
Get months with aggregate count (how much posts is in the month).
Results are ordered by date.
"""
if queryset is None:
queryset = self.get_queryset()
- queryset = queryset.on_site()
+ if current_site:
+ queryset = queryset.on_site()
dates_qs = queryset.values_list(queryset.start_date_field, queryset.fallback_date_field)
dates = []
for blog_dates in dates_qs:
diff --git a/djangocms_blog/migrations/0023_auto_20160626_1539.py b/djangocms_blog/migrations/0023_auto_20160626_1539.py
new file mode 100644
index 0000000..5748fae
--- /dev/null
+++ b/djangocms_blog/migrations/0023_auto_20160626_1539.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.9.6 on 2016-06-26 13:39
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('djangocms_blog', '0022_auto_20160605_2305'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='authorentriesplugin',
+ name='current_site',
+ field=models.BooleanField(default=True, help_text='Select items from the current site only', verbose_name='current site'),
+ ),
+ migrations.AddField(
+ model_name='genericblogplugin',
+ name='current_site',
+ field=models.BooleanField(default=True, help_text='Select items from the current site only', verbose_name='current site'),
+ ),
+ migrations.AddField(
+ model_name='latestpostsplugin',
+ name='current_site',
+ field=models.BooleanField(default=True, help_text='Select items from the current site only', verbose_name='current site'),
+ ),
+ ]
diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py
index ba72459..91df778 100644
--- a/djangocms_blog/models.py
+++ b/djangocms_blog/models.py
@@ -8,6 +8,7 @@ from aldryn_apphooks_config.managers.parler import AppHookConfigTranslatableMana
from cms.models import CMSPlugin, PlaceholderField
from django.conf import settings as dj_settings
from django.contrib.auth import get_user_model
+from django.contrib.sites.shortcuts import get_current_site
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db import models
@@ -15,6 +16,7 @@ from django.db.models.signals import post_save, pre_delete
from django.dispatch import receiver
from django.utils import timezone
from django.utils.encoding import force_bytes, force_text, python_2_unicode_compatible
+from django.utils.functional import cached_property
from django.utils.html import escape, strip_tags
from django.utils.text import slugify
from django.utils.translation import get_language, ugettext_lazy as _
@@ -76,9 +78,17 @@ class BlogCategory(TranslatableModel):
verbose_name = _('blog category')
verbose_name_plural = _('blog categories')
- @property
+ @cached_property
+ def linked_posts(self):
+ return self.blog_posts.namespace(self.app_config.namespace)
+
+ @cached_property
def count(self):
- return self.blog_posts.namespace(self.app_config.namespace).published().count()
+ return self.linked_posts.published().count()
+
+ @cached_property
+ def count_all_sites(self):
+ return self.linked_posts.published(current_site=False).count()
def get_absolute_url(self, lang=None):
if not lang:
@@ -365,18 +375,24 @@ class BasePostPlugin(CMSPlugin):
app_config = AppHookConfigField(
BlogConfig, null=True, verbose_name=_('app. config'), blank=True
)
+ current_site = models.BooleanField(
+ _('current site'), default=True, help_text=_('Select items from the current site only')
+ )
class Meta:
abstract = True
- def post_queryset(self, request=None):
+ def post_queryset(self, request=None, published_only=True):
language = get_language()
- posts = Post._default_manager
+ posts = Post.objects
if self.app_config:
posts = posts.namespace(self.app_config.namespace)
+ if self.current_site:
+ posts = posts.on_site(get_current_site(request))
posts = posts.active_translations(language_code=language)
- if not request or not getattr(request, 'toolbar', False) or not request.toolbar.edit_mode:
- posts = posts.published()
+ if (published_only or not request or not getattr(request, 'toolbar', False) or
+ not request.toolbar.edit_mode):
+ posts = posts.published(current_site=self.current_site)
return posts.all()
@@ -384,14 +400,14 @@ class BasePostPlugin(CMSPlugin):
class LatestPostsPlugin(BasePostPlugin):
latest_posts = models.IntegerField(_('articles'), default=get_setting('LATEST_POSTS'),
help_text=_('The number of latests '
- u'articles to be displayed.'))
+ 'articles to be displayed.'))
tags = TaggableManager(_('filter by tag'), blank=True,
help_text=_('Show only the blog articles tagged with chosen tags.'),
related_name='djangocms_blog_latest_post')
categories = models.ManyToManyField('djangocms_blog.BlogCategory', blank=True,
verbose_name=_('filter by category'),
help_text=_('Show only the blog articles tagged '
- u'with chosen categories.'))
+ 'with chosen categories.'))
def __str__(self):
return force_text(_('%s latest articles by tag') % self.latest_posts)
@@ -402,8 +418,8 @@ class LatestPostsPlugin(BasePostPlugin):
for category in oldinstance.categories.all():
self.categories.add(category)
- def get_posts(self, request):
- posts = self.post_queryset(request)
+ def get_posts(self, request, published_only=True):
+ posts = self.post_queryset(request, published_only)
if self.tags.exists():
posts = posts.filter(tags__in=list(self.tags.all()))
if self.categories.exists():
@@ -428,8 +444,8 @@ class AuthorEntriesPlugin(BasePostPlugin):
def copy_relations(self, oldinstance):
self.authors = oldinstance.authors.all()
- def get_posts(self, request):
- posts = self.post_queryset(request)
+ def get_posts(self, request, published_only=True):
+ posts = self.post_queryset(request, published_only)
return posts[:self.latest_posts]
def get_authors(self):
@@ -439,7 +455,11 @@ class AuthorEntriesPlugin(BasePostPlugin):
qs = author.djangocms_blog_post_author
if self.app_config:
qs = qs.namespace(self.app_config.namespace)
- count = qs.filter(publish=True).count()
+ if self.current_site:
+ qs = qs.published()
+ else:
+ qs = qs.published(current_site=False)
+ count = qs.count()
if count:
author.count = count
return authors
diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py
index a1b220c..0199182 100644
--- a/djangocms_blog/settings.py
+++ b/djangocms_blog/settings.py
@@ -58,6 +58,7 @@ def get_setting(name):
settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
),
'BLOG_MENU_TYPES': MENU_TYPES,
+ 'BLOG_MENU_EMPTY_CATEGORIES': getattr(settings, 'MENU_EMPTY_CATEGORIES', True),
'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
'BLOG_TYPES': meta_settings.OBJECT_TYPES,
'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'),
@@ -113,6 +114,8 @@ def get_setting(name):
'BLOG_PLUGIN_MODULE_NAME': getattr(settings, 'BLOG_PLUGIN_MODULE_NAME', _('Blog')),
'BLOG_LATEST_ENTRIES_PLUGIN_NAME': getattr(
settings, 'BLOG_LATEST_ENTRIES_PLUGIN_NAME', _('Latest Blog Articles')),
+ 'BLOG_LATEST_ENTRIES_PLUGIN_NAME_CACHED': getattr(
+ settings, 'BLOG_LATEST_ENTRIES_PLUGIN_NAME_CACHED', _('Latest Blog Articles - Cache')),
'BLOG_AUTHOR_POSTS_PLUGIN_NAME': getattr(
settings, 'BLOG_AUTHOR_POSTS_PLUGIN_NAME', _('Author Blog Articles')),
'BLOG_TAGS_PLUGIN_NAME': getattr(
diff --git a/djangocms_blog/templates/djangocms_blog/plugins/categories.html b/djangocms_blog/templates/djangocms_blog/plugins/categories.html
index a6a2e72..135a67b 100644
--- a/djangocms_blog/templates/djangocms_blog/plugins/categories.html
+++ b/djangocms_blog/templates/djangocms_blog/plugins/categories.html
@@ -16,4 +16,4 @@
{% endfor %}
-{% endspaceless %}
\ No newline at end of file
+{% endspaceless %}
diff --git a/docs/settings.rst b/docs/settings.rst
index a4f4417..36d658a 100644
--- a/docs/settings.rst
+++ b/docs/settings.rst
@@ -11,6 +11,8 @@ Global Settings
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_MENU_EMPTY_CATEGORIES: Flag to show / hide categories without posts
+ attached from the menu; (default: ``True``)
* 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
@@ -119,6 +121,7 @@ be used as defaults.
* 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
+* Show empty categories in menu: Per-Apphook setting for BLOG_MENU_EMPTY_CATEGORIES
* Sitemap changefreq: Per-Apphook setting for BLOG_SITEMAP_CHANGEFREQ_DEFAULT
* Sitemap priority: Per-Apphook setting for BLOG_SITEMAP_PRIORITY_DEFAULT
* Object type: Per-Apphook setting for BLOG_TYPE
diff --git a/tests/base.py b/tests/base.py
index d88ea7b..0b79716 100644
--- a/tests/base.py
+++ b/tests/base.py
@@ -154,6 +154,8 @@ class BaseTest(BaseTestCase):
for post in Post.objects.all():
post.sites.clear()
post.tags.clear()
+ post.categories.clear()
+ cache.clear()
super(BaseTest, self).tearDown()
def _get_category(self, data, category=None, lang='en'):
diff --git a/tests/test_menu.py b/tests/test_menu.py
index 5400592..89482eb 100644
--- a/tests/test_menu.py
+++ b/tests/test_menu.py
@@ -70,6 +70,8 @@ class MenuTest(BaseTest):
posts = self.get_posts()
cats_url = {}
+ cats_with_post_url = {}
+ cats_without_post_url = {}
posts_url = {}
languages = ('en', 'it')
@@ -77,6 +79,8 @@ class MenuTest(BaseTest):
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)])
+ cats_with_post_url[lang] = set([cat.get_absolute_url() for cat in self.cats if cat.has_translation(lang) and cat.blog_posts.published().exists()])
+ cats_without_post_url[lang] = cats_url[lang].difference(cats_with_post_url[lang])
posts_url[lang] = set([post.get_absolute_url(lang) for post in posts if post.has_translation(lang) and post.app_config == self.app_config_1])
# No item in the menu
@@ -127,6 +131,27 @@ class MenuTest(BaseTest):
self.assertTrue(cats_url[lang].issubset(nodes_url))
self.assertTrue(posts_url[lang].issubset(nodes_url))
+ # Both types in the menu
+ self.app_config_1.app_data.config.menu_empty_categories = False
+ self.app_config_1.save()
+ self.app_config_2.app_data.config.menu_empty_categories = False
+ self.app_config_2.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)
+ nodes_url = set([node.url for node in nodes])
+ self.assertTrue(cats_with_post_url[lang].issubset(nodes_url))
+ self.assertFalse(cats_without_post_url[lang].intersection(nodes_url))
+ self.assertTrue(posts_url[lang].issubset(nodes_url))
+ # Both types in the menu
+ self.app_config_1.app_data.config.menu_empty_categories = True
+ self.app_config_1.save()
+ self.app_config_2.app_data.config.menu_empty_categories = True
+ self.app_config_2.save()
+ cache.clear()
+
def test_modifier(self):
"""
Tests if correct category is selected in the menu
diff --git a/tests/test_models.py b/tests/test_models.py
index befba6b..81e169d 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -438,6 +438,33 @@ class AdminTest(BaseTest):
class ModelsTest(BaseTest):
+ def test_category_attributes(self):
+ posts = self.get_posts()
+ posts[0].publish = True
+ posts[0].save()
+ posts[1].publish = True
+ posts[1].save()
+ posts[1].sites.add(self.site_2)
+ new_category = BlogCategory.objects.create(
+ name='category 2', app_config=self.app_config_1
+ )
+ posts[1].categories.add(new_category)
+
+ with self.settings(SITE_ID=2):
+ self.assertEqual(new_category.count, 1)
+ self.assertEqual(self.category_1.count, 2)
+ self.assertEqual(new_category.count_all_sites, 1)
+ self.assertEqual(self.category_1.count_all_sites, 2)
+
+ # needed to clear cached properties
+ new_category = self.reload_model(new_category)
+ self.category_1 = self.reload_model(self.category_1)
+ with self.settings(SITE_ID=1):
+ self.assertEqual(new_category.count, 0)
+ self.assertEqual(self.category_1.count, 1)
+ self.assertEqual(new_category.count_all_sites, 1)
+ self.assertEqual(self.category_1.count_all_sites, 2)
+
def test_model_attributes(self):
self.get_pages()
@@ -611,7 +638,10 @@ class ModelsTest(BaseTest):
# default queryset, published and unpublished posts
months = Post.objects.get_months()
for data in months:
- self.assertEqual(data['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
+ self.assertEqual(
+ data['date'].date(),
+ now().replace(year=now().year, month=now().month, day=1).date()
+ )
self.assertEqual(data['count'], 2)
# custom queryset, only published
@@ -619,9 +649,30 @@ class ModelsTest(BaseTest):
post1.save()
months = Post.objects.get_months(Post.objects.published())
for data in months:
- self.assertEqual(data['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
+ self.assertEqual(
+ data['date'].date(),
+ now().replace(year=now().year, month=now().month, day=1).date()
+ )
self.assertEqual(data['count'], 1)
+ # Move post to different site to filter it out
+ post2.sites.add(self.site_2)
+ months = Post.objects.get_months()
+ for data in months:
+ self.assertEqual(
+ data['date'].date(),
+ now().replace(year=now().year, month=now().month, day=1).date()
+ )
+ self.assertEqual(data['count'], 1)
+ months = Post.objects.get_months(current_site=False)
+ for data in months:
+ self.assertEqual(
+ data['date'].date(),
+ now().replace(year=now().year, month=now().month, day=1).date()
+ )
+ self.assertEqual(data['count'], 2)
+ post2.sites.clear()
+
self.assertEqual(len(Post.objects.available()), 1)
# If post is published but publishing date is in the future
@@ -632,6 +683,7 @@ class ModelsTest(BaseTest):
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(current_site=False)), 0)
# If post is published but end publishing date is in the past
post2.date_published = now().replace(year=now().year - 2, month=now().month, day=1)
@@ -640,10 +692,32 @@ class ModelsTest(BaseTest):
self.assertEqual(len(Post.objects.available()), 2)
self.assertEqual(len(Post.objects.published()), 1)
self.assertEqual(len(Post.objects.archived()), 1)
+ self.assertEqual(len(Post.objects.archived(current_site=False)), 1)
+
+ # Move post to different site to filter it out
+ post2.sites.add(self.site_2)
+ self.assertEqual(len(Post.objects.archived()), 0)
+ self.assertEqual(len(Post.objects.archived(current_site=False)), 1)
+ self.assertEqual(len(Post.objects.available()), 1)
+ self.assertEqual(len(Post.objects.available(current_site=False)), 2)
+ self.assertEqual(len(Post.objects.published()), 1)
+
+ # publish post
+ post2.date_published = now() - timedelta(days=1)
+ post2.date_published_end = now() + timedelta(days=10)
+ post2.save()
+ self.assertEqual(len(Post.objects.archived()), 0)
+ self.assertEqual(len(Post.objects.archived(current_site=False)), 0)
+ self.assertEqual(len(Post.objects.available()), 1)
+ self.assertEqual(len(Post.objects.available(current_site=False)), 2)
+ self.assertEqual(len(Post.objects.published()), 1)
+ self.assertEqual(len(Post.objects.published(current_site=False)), 2)
# counting with language fallback enabled
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')), 1)
+ self.assertEqual(len(Post.objects.filter_by_language('it', current_site=False)), 2)
+ post2.sites.clear()
# No fallback
parler.appsettings.PARLER_LANGUAGES['default']['hide_untranslated'] = True
@@ -706,7 +780,9 @@ class ModelsTest(BaseTest):
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False)
request_auth = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=False)
request_edit = self.get_page_request('/', self.user_staff, r'/en/blog/', edit=True)
- plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1
+ )
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
# unauthenticated users get no post
@@ -714,24 +790,33 @@ class ModelsTest(BaseTest):
# staff users not in edit mode get no post
self.assertEqual(len(plugin.get_posts(request_auth)), 0)
# staff users in edit mode get the post
- self.assertEqual(len(plugin.get_posts(request_edit)), 1)
+ self.assertEqual(len(plugin.get_posts(request_edit, published_only=False)), 1)
post1.publish = True
post1.save()
self.assertEqual(len(plugin.get_posts(request)), 1)
+
+class ModelsTest2(BaseTest):
+
def test_copy_plugin_latest(self):
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
tag1 = Tag.objects.create(name='tag 1')
tag2 = Tag.objects.create(name='tag 2')
- plugin = add_plugin(post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ post1.content, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1
+ )
plugin.tags.add(tag1)
plugin.tags.add(tag2)
if CMS_30:
- 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'
+ ))
else:
- plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('path', 'depth', 'position'))
+ plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by(
+ 'path', 'depth', 'position'
+ ))
copy_plugins_to(plugins, post2.content)
new = downcast_plugins(post2.content.cmsplugin_set.all())
self.assertEqual(set(new[0].tags.all()), set([tag1, tag2]))
@@ -741,7 +826,9 @@ class ModelsTest(BaseTest):
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
request = self.get_page_request('/', AnonymousUser(), r'/en/blog/', edit=False)
- plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1
+ )
plugin.authors.add(self.user)
self.assertEqual(len(plugin.get_posts(request)), 0)
self.assertEqual(plugin.get_authors()[0].count, 0)
@@ -759,12 +846,18 @@ class ModelsTest(BaseTest):
def test_copy_plugin_author(self):
post1 = self._get_post(self._post_data[0]['en'])
post2 = self._get_post(self._post_data[1]['en'])
- plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1
+ )
plugin.authors.add(self.user)
if CMS_30:
- plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('tree_id', 'level', 'position'))
+ plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by(
+ 'tree_id', 'level', 'position'
+ ))
else:
- plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by('path', 'depth', 'position'))
+ plugins = list(post1.content.cmsplugin_set.filter(language='en').order_by(
+ 'path', 'depth', 'position'
+ ))
copy_plugins_to(plugins, post2.content)
new = downcast_plugins(post2.content.cmsplugin_set.all())
self.assertEqual(set(new[0].authors.all()), set([self.user]))
@@ -797,13 +890,19 @@ class ModelsTest(BaseTest):
self.assertEqual(force_text(post1.categories.first()), 'category 1')
- plugin = add_plugin(post1.content, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_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)
+ 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)
+ plugin = add_plugin(
+ post1.content, 'BlogArchivePlugin', language='en', app_config=self.app_config_1
+ )
self.assertEqual(force_text(plugin.__str__()), 'generic blog plugin')
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index e433e86..e926fe4 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -5,6 +5,7 @@ import os.path
import re
from cms.api import add_plugin
+from django.contrib.sites.models import SITE_CACHE
from django.core.urlresolvers import reverse
from django.utils.timezone import now
from taggit.models import Tag
@@ -16,6 +17,43 @@ from .base import BaseTest
class PluginTest(BaseTest):
+ def test_plugin_latest_cached(self):
+ pages = self.get_pages()
+ posts = self.get_posts()
+ posts[0].tags.add('tag 1')
+ posts[0].publish = True
+ posts[0].save()
+ ph = pages[0].placeholders.get(slot='content')
+
+ plugin = add_plugin(
+ ph, 'BlogLatestEntriesPluginCached', language='en', app_config=self.app_config_1
+ )
+ context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
+ rendered = plugin.render_plugin(context, ph)
+ try:
+ self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1)
+ except AssertionError:
+ self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-1') > -1)
+ self.assertTrue(rendered.find('
first line
') > -1)
+ self.assertTrue(rendered.find(' -1)
+ self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)
+
+ plugin_nocache = add_plugin(
+ ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1
+ )
+ with self.assertNumQueries(53):
+ plugin_nocache.render_plugin(context, ph)
+
+ with self.assertNumQueries(17):
+ rendered = plugin.render_plugin(context, ph)
+ try:
+ self.assertTrue(rendered.find('cms_plugin-djangocms_blog-post-abstract-1') > -1)
+ except AssertionError:
+ self.assertTrue(rendered.find('cms-plugin-djangocms_blog-post-abstract-1') > -1)
+ self.assertTrue(rendered.find('first line
') > -1)
+ self.assertTrue(rendered.find(' -1)
+ self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)
+
def test_plugin_latest(self):
pages = self.get_pages()
posts = self.get_posts()
@@ -24,7 +62,9 @@ class PluginTest(BaseTest):
posts[0].save()
ph = pages[0].placeholders.get(slot='content')
- plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1
+ )
tag = Tag.objects.get(slug='tag-1')
plugin.tags.add(tag)
@@ -34,7 +74,9 @@ class PluginTest(BaseTest):
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('first line
') > -1)
self.assertTrue(rendered.find(' -1)
self.assertTrue(rendered.find(posts[0].get_absolute_url()) > -1)
@@ -45,7 +87,9 @@ class PluginTest(BaseTest):
category_2.save()
category_2.set_current_language('en')
posts[1].categories.add(category_2)
- plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
+ plugin = add_plugin(
+ ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1
+ )
plugin.categories.add(category_2)
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
@@ -54,7 +98,10 @@ class PluginTest(BaseTest):
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('second post first line
') > -1)
self.assertTrue(rendered.find(' -1)
self.assertTrue(rendered.find(posts[1].get_absolute_url()) > -1)
@@ -77,6 +124,16 @@ class PluginTest(BaseTest):
self.assertEqual(casted_categories.tags.count(), 0)
self.assertEqual(casted_categories.categories.count(), 1)
+ posts[1].sites.add(self.site_2)
+ context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
+ rendered = plugin.render_plugin(context, ph)
+ self.assertFalse(rendered.find('second post first line
') > -1)
+
+ posts[1].sites.remove(self.site_2)
+ context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
+ rendered = plugin.render_plugin(context, ph)
+ self.assertTrue(rendered.find('second post first line
') > -1)
+
def test_plugin_tags(self):
pages = self.get_pages()
posts = self.get_posts()
@@ -91,7 +148,9 @@ class PluginTest(BaseTest):
context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
rendered = plugin.render_plugin(context, ph)
for tag in Tag.objects.all():
- self.assertTrue(rendered.find(reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})) > -1)
+ self.assertTrue(rendered.find(
+ reverse('djangocms_blog:posts-tagged', kwargs={'tag': tag.slug})
+ ) > -1)
if tag.slug == 'test-tag':
rf = '\s+%s\s+\(\s+%s articles' % (tag.name, 2)
else:
@@ -99,21 +158,6 @@ class PluginTest(BaseTest):
rx = re.compile(rf)
self.assertEqual(len(rx.findall(rendered)), 1)
- def test_blog_category_plugin(self):
- pages = self.get_pages()
- posts = self.get_posts()
- posts[0].publish = True
- posts[0].save()
- posts[1].publish = True
- posts[1].save()
- ph = pages[0].placeholders.get(slot='content')
- plugin = add_plugin(ph, 'BlogCategoryPlugin', language='en', app_config=self.app_config_1)
- plugin_class = plugin.get_plugin_class_instance()
- context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
- context = plugin_class.render(context, plugin, ph)
- self.assertTrue(context['categories'])
- self.assertEqual(list(context['categories']), [self.category_1])
-
def test_blog_archive_plugin(self):
pages = self.get_pages()
posts = self.get_posts()
@@ -199,3 +243,44 @@ class PluginTest2(BaseTest):
casted_authors, __ = new[0].get_plugin_instance()
self.assertEqual(casted_authors.authors.count(), 3)
+
+ def test_blog_category_plugin(self):
+ pages = self.get_pages()
+ posts = self.get_posts()
+ self.category_1.set_current_language('en')
+ posts[0].publish = True
+ posts[0].save()
+ posts[1].publish = True
+ posts[1].save()
+ posts[1].sites.add(self.site_2)
+ new_category = BlogCategory.objects.create(
+ name='category 2', app_config=self.app_config_1
+ )
+ posts[1].categories.add(new_category)
+
+ ph = pages[0].placeholders.get(slot='content')
+ plugin = add_plugin(
+ ph, 'BlogCategoryPlugin', language='en', app_config=self.app_config_1
+ )
+ plugin_class = plugin.get_plugin_class_instance()
+ context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
+ context = plugin_class.render(context, plugin, ph)
+ self.assertTrue(context['categories'])
+ self.assertEqual(list(context['categories']), [self.category_1])
+
+ plugin.current_site = False
+ plugin.save()
+ context = plugin_class.render(context, plugin, ph)
+ self.assertEqual(list(context['categories']), [self.category_1, new_category])
+
+ plugin.current_site = True
+ plugin.save()
+ with self.settings(SITE_ID=2):
+ context = plugin_class.render(context, plugin, ph)
+ self.assertEqual(list(context['categories']), [self.category_1, new_category])
+
+ plugin.current_site = False
+ plugin.save()
+ with self.settings(SITE_ID=2):
+ context = plugin_class.render(context, plugin, ph)
+ self.assertEqual(list(context['categories']), [self.category_1, new_category])