Merge pull request #158 from nephila/feature/sitemap_improvement

Improve sitemap
This commit is contained in:
Iacopo Spalletti 2015-10-31 09:38:55 +01:00
commit 72d70411ac
9 changed files with 187 additions and 10 deletions

View file

@ -3,6 +3,12 @@
History
-------
0.6.1 (2015-10-31)
++++++++++++++++++
* Improve toolbar: add all languages for each post
* Improve toolbar: add per-apphook configurable changefreq, priority
0.6.0 (2015-10-30)
++++++++++++++++++

View file

@ -63,8 +63,11 @@ Features
* Multisite support (posts can be visible in one or more Django sites on the
same project)
* Per-Apphook configuration
* Configurable permalinks
* Configurable django CMS menu support
* Per-Apphook templates set
* Auto Apphook setup
* Django sitemap framework support
* Support for django CMS 3.2+ Wizard
* Haystack index support
@ -163,7 +166,7 @@ suited for your deployment.
* 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; this step is not required when using
**Auto setup** (see below):
`Auto setup <auto_setup>`_:
* Create a new django CMS page
* Go to Advanced settings and select Blog from the Application selector and
@ -206,6 +209,16 @@ like the following in the project settings::
And change ``post/`` with the desired prefix.
Menu
++++
``djangocms_blog`` provides support for django CMS menu framework.
By default all the categories and posts are added to the menu, in a hierarcical structure.
Is it possibile to configure per Apphook, whether the menu includes post and categories
(the default), only categorie, only posts or no item.
Templates
+++++++++
@ -226,6 +239,8 @@ 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:
Auto setup
++++++++++
@ -249,6 +264,44 @@ The auto setup is execute once for each server start but it will skip any
action if a ``BlogConfig`` instance is found.
Sitemap
+++++++
``djangocms_blog`` provides a sitemap for improved SEO indexing.
Sitemap returns all the published posts in all the languages each post is available.
The changefreq and priority is configurable per-apphook (see ``BLOG_SITEMAP_*`` in
`Global settings <settings>`_).
To add the blog Sitemap, add the following code to the project ``urls.py``::
from cms.sitemaps import CMSSitemap
from djangocms_blog.sitemaps import BlogSitemap
urlpatterns = patterns(
'',
...
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap',
{'sitemaps': {
'cmspages': CMSSitemap, 'blog': BlogSitemap,
}
}),
)
django CMS 3.2+ Wizard
++++++++++++++++++++++
django CMS 3.2+ provides a content creation wizard that allows to quickly created supported
content types, such as blog posts.
For each configured Apphook, a content type is added to the wizard.
.. _settings:
Global Settings
---------------
* BLOG_IMAGE_THUMBNAIL_SIZE: Size of the main image when shown on the post
@ -292,8 +345,6 @@ Global Settings
* 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
@ -311,6 +362,19 @@ Global Settings
(default: ``Blog``)
* BLOG_AUTO_APP_TITLE: Title of the ``BlogConfig`` instance created by
**Auto setup**; (default: ``Blog``)
* BLOG_SITEMAP_PRIORITY_DEFAULT: Default priority for sitemap items; (default: ``0.5``)
* BLOG_SITEMAP_CHANGEFREQ: List for available changefreqs for sitemap items; (default: **always**,
**hourly**, **daily**, **weekly**, **monthly**, **yearly**, **never**)
* BLOG_SITEMAP_CHANGEFREQ_DEFAULT: Default changefreq for sitemap items; (default: ``monthly``)
Read-only settings
++++++++++++++++++
* BLOG_MENU_TYPES: Available structures of the Blog menu; (default list **Posts and Categories**,
**Categories only**, **Posts only**, **None**)
* BLOG_MENU_TYPE: Structure of the Blog menu;
(default: ``Posts and Categories``)
Per-Apphook settings
--------------------
@ -327,6 +391,8 @@ Per-Apphook settings
* 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
* 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
* Facebook type: Per-Apphook setting for BLOG_FB_TYPE
* Facebook application ID: Per-Apphook setting for BLOG_FB_APP_ID

View file

@ -14,6 +14,7 @@ HELPER_SETTINGS = dict(
'meta',
'meta_mixin',
'easy_thumbnails',
'django.contrib.sitemaps',
'djangocms_text_ckeditor',
'cmsplugin_filer_image',
'taggit',

View file

@ -149,6 +149,12 @@ class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
),
'classes': ('collapse',)
}),
('Sitemap', {
'fields': (
'config.sitemap_changefreq', 'config.sitemap_priority',
),
'classes': ('collapse',)
}),
('Meta', {
'fields': (
'config.object_type',

View file

@ -62,6 +62,17 @@ class BlogConfigForm(AppDataForm):
choices=get_setting('MENU_TYPES'), initial=MENU_TYPE_COMPLETE,
help_text=_('Structure of the django CMS menu')
)
sitemap_changefreq = forms.ChoiceField(
label=_('Sitemap changefreq'), required=True,
choices=get_setting('SITEMAP_CHANGEFREQ'),
initial=get_setting('SITEMAP_CHANGEFREQ_DEFAULT'),
help_text=_('Changefreq attribute for sidebar items')
)
sitemap_priority = forms.CharField(
label=_('Sitemap priority'), required=True,
initial=get_setting('SITEMAP_PRIORITY_DEFAULT'),
help_text=_('Priority attribute for sidebar items')
)
object_type = forms.ChoiceField(
label=_('Object type'), required=False,
choices=get_setting('TYPES'), initial=get_setting('TYPE')

View file

@ -30,6 +30,15 @@ def get_setting(name):
(MENU_TYPE_POSTS, _('Posts only')),
(MENU_TYPE_NONE, _('None')),
)
SITEMAP_CHANGEFREQ_LIST = (
('always', _('always')),
('hourly', _('hourly')),
('daily', _('daily')),
('weekly', _('weekly')),
('monthly', _('monthly')),
('yearly', _('yearly')),
('never', _('never')),
)
default = {
'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
'size': '120x120',
@ -48,7 +57,6 @@ def get_setting(name):
'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
),
'BLOG_MENU_TYPE': MENU_TYPES,
'BLOG_MENU_TYPES': MENU_TYPES,
'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
'BLOG_TYPES': meta_settings.OBJECT_TYPES,
@ -72,7 +80,9 @@ def get_setting(name):
'BLOG_MULTISITE': getattr(settings, 'BLOG_MULTISITE', True),
'BLOG_AUTHOR_DEFAULT': getattr(settings, 'BLOG_AUTHOR_DEFAULT', True),
'BLOG_DEFAULT_PUBLISHED': getattr(settings, 'BLOG_DEFAULT_PUBLISHED', False),
'BLOG_AVAILABLE_PERMALINK_STYLES': getattr(settings, 'BLOG_AVAILABLE_PERMALINK_STYLES', PERMALINKS), # NOQA
'BLOG_AVAILABLE_PERMALINK_STYLES': getattr(
settings, 'BLOG_AVAILABLE_PERMALINK_STYLES', PERMALINKS
),
'BLOG_PERMALINK_URLS': getattr(settings, 'BLOG_PERMALINK_URLS', PERMALINKS_URLS),
'BLOG_DEFAULT_OBJECT_NAME': getattr(settings, 'BLOG_DEFAULT_OBJECT_NAME', 'Article'),
@ -82,6 +92,14 @@ def get_setting(name):
'BLOG_AUTO_APP_TITLE': getattr(settings, 'BLOG_AUTO_APP_TITLE', 'Blog'),
'BLOG_AUTO_NAMESPACE': getattr(settings, 'BLOG_AUTO_NAMESPACE', 'Blog'),
'BLOG_SITEMAP_PRIORITY_DEFAULT': getattr(settings, 'BLOG_SITEMAP_PRIORITY_DEFAULT', '0.5'),
'BLOG_SITEMAP_CHANGEFREQ': getattr(
settings, 'BLOG_SITEMAP_CHANGEFREQ', SITEMAP_CHANGEFREQ_LIST
),
'BLOG_SITEMAP_CHANGEFREQ_DEFAULT': getattr(
settings, 'BLOG_SITEMAP_CHANGEFREQ_DEFAULT', 'monthly'
),
'BLOG_ENABLE_SEARCH': getattr(settings, 'BLOG_ENABLE_SEARCH', True),
}
return default['BLOG_%s' % name]

View file

@ -1,17 +1,35 @@
# -*- coding: utf-8 -*-
from __future__ import absolute_import, print_function, unicode_literals
from cms.utils import get_language_list
from django.contrib.sitemaps import Sitemap
from parler.utils.context import smart_override
from ..models import Post
from ..settings import get_setting
class BlogSitemap(Sitemap):
changefreq = 'never'
priority = 0.5
def priority(self, obj):
if obj and obj.app_config:
return obj.app_config.sitemap_priority
return get_setting('SITEMAP_PRIORITY_DEFAULT')
def changefreq(self, obj):
if obj and obj.app_config:
return obj.app_config.sitemap_changefreq
return get_setting('SITEMAP_CHANGEFREQ_DEFAULT')
def location(self, obj):
with smart_override(obj.get_current_language()):
return obj.get_absolute_url(obj.get_current_language())
def items(self):
return Post.objects.published()
items = []
for lang in get_language_list():
items.extend(Post.objects.translated(lang).language(lang).published())
return items
def lastmod(self, obj):
return obj.date_modified

View file

@ -3,6 +3,7 @@ from __future__ import absolute_import, print_function, unicode_literals
import sys
from cms.sitemaps import CMSSitemap
from cms.utils.conf import get_cms_setting
from django.conf import settings
from django.conf.urls import include, patterns, url
@ -10,6 +11,8 @@ from django.conf.urls.i18n import i18n_patterns
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from djangocms_blog.sitemaps import BlogSitemap
admin.autodiscover()
urlpatterns = patterns(
@ -20,6 +23,11 @@ urlpatterns = patterns(
{'document_root': get_cms_setting('MEDIA_ROOT'), 'show_indexes': True}),
url(r'^jsi18n/(?P<packages>\S+?)/$', 'django.views.i18n.javascript_catalog'),
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
url(r'^sitemap\.xml$', 'django.contrib.sitemaps.views.sitemap',
{'sitemaps': {
'cmspages': CMSSitemap, 'blog': BlogSitemap,
}
}),
)
urlpatterns += staticfiles_urlpatterns()

View file

@ -17,6 +17,7 @@ from parler.utils.context import smart_override, switch_language
from djangocms_blog.feeds import LatestEntriesFeed, TagFeed
from djangocms_blog.models import BLOG_CURRENT_NAMESPACE
from djangocms_blog.settings import get_setting
from djangocms_blog.sitemaps import BlogSitemap
from djangocms_blog.views import (
AuthorEntriesView, CategoryEntriesView, PostArchiveView, PostDetailView, PostListView,
@ -339,6 +340,7 @@ class ViewTest(BaseTest):
def test_sitemap(self):
posts = self.get_posts()
self.get_pages()
posts[0].tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4')
posts[0].save()
posts[1].tags.add('tag 6', 'tag 2', 'tag 5', 'tag 8')
@ -347,9 +349,50 @@ class ViewTest(BaseTest):
posts[0].set_current_language('en')
sitemap = BlogSitemap()
self.assertEqual(sitemap.items().count(), 3)
self.assertEqual(len(sitemap.items()), 6)
for item in sitemap.items():
self.assertTrue(sitemap.lastmod(item).date(), now().today())
self.assertEqual(sitemap.lastmod(item).date(), now().date())
self.assertEqual(
sitemap.priority(item), get_setting('SITEMAP_PRIORITY_DEFAULT')
)
self.assertEqual(
sitemap.changefreq(item), get_setting('SITEMAP_CHANGEFREQ_DEFAULT')
)
with smart_override(item.get_current_language()):
self.assertEqual(
sitemap.location(item), item.get_absolute_url()
)
def test_sitemap_config(self):
posts = self.get_posts()
self.app_config_1.app_data.config.sitemap_changefreq = 'daily'
self.app_config_1.app_data.config.sitemap_priority = '0.2'
self.app_config_1.save()
sitemap = BlogSitemap()
self.assertEqual(len(sitemap.items()), 4)
for item in sitemap.items():
self.assertEqual(sitemap.lastmod(item).date(), now().date())
if item.app_config == self.app_config_1:
self.assertEqual(
sitemap.priority(item), '0.2'
)
self.assertEqual(
sitemap.changefreq(item), 'daily'
)
else:
self.assertEqual(
sitemap.priority(item), get_setting('SITEMAP_PRIORITY_DEFAULT')
)
self.assertEqual(
sitemap.changefreq(item), get_setting('SITEMAP_CHANGEFREQ_DEFAULT')
)
self.assertEqual(
sitemap.priority(None), get_setting('SITEMAP_PRIORITY_DEFAULT')
)
self.assertEqual(
sitemap.changefreq(None), get_setting('SITEMAP_CHANGEFREQ_DEFAULT')
)
def test_templates(self):
posts = self.get_posts()