From 8d69fb107ed5b5a1e283cbb2d7767cccbf3f47e6 Mon Sep 17 00:00:00 2001
From: Iacopo Spalletti <i.spalletti@nephila.it>
Date: Sun, 6 Sep 2015 13:40:20 +0200
Subject: [PATCH 1/3] Add customisable templates

---
 djangocms_blog/cms_appconfig.py |  4 ++++
 djangocms_blog/cms_plugins.py   | 20 ++++++++++++++------
 djangocms_blog/views.py         | 20 ++++++++++++++------
 tests/test_plugins.py           | 19 +++++++++++++++++++
 tests/test_views.py             | 20 ++++++++++++++++++++
 5 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py
index 5c4cdc8..dc55dab 100644
--- a/djangocms_blog/cms_appconfig.py
+++ b/djangocms_blog/cms_appconfig.py
@@ -42,4 +42,8 @@ class BlogConfigForm(AppDataForm):
                                      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')
+                                      )
 setup_config(BlogConfigForm, BlogConfig)
diff --git a/djangocms_blog/cms_plugins.py b/djangocms_blog/cms_plugins.py
index d115b78..aab204a 100644
--- a/djangocms_blog/cms_plugins.py
+++ b/djangocms_blog/cms_plugins.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import, print_function, unicode_literals
 
+import os.path
+
 from cms.plugin_base import CMSPluginBase
 from cms.plugin_pool import plugin_pool
 from django.utils.translation import ugettext_lazy as _
@@ -13,19 +15,25 @@ from .settings import get_setting
 class BlogPlugin(CMSPluginBase):
     module = 'Blog'
 
+    def get_render_template(self, context, instance, placeholder):
+        if instance.app_config.template_prefix:
+            return os.path.join(instance.app_config.template_prefix, self.base_render_template)
+        else:
+            return os.path.join('djangocms_blog', self.base_render_template)
+
 
 class BlogLatestEntriesPlugin(BlogPlugin):
     """
     Non cached plugin which returns the latest posts taking into account the
       user / toolbar state
     """
-    render_template = 'djangocms_blog/plugins/latest_entries.html'
     name = _('Latest Blog Articles')
     model = LatestPostsPlugin
     form = LatestEntriesForm
     filter_horizontal = ('categories',)
     fields = ('latest_posts', 'tags', 'categories')
     cache = False
+    base_render_template = 'plugins/latest_entries.html'
 
     def render(self, context, instance, placeholder):
         context = super(BlogLatestEntriesPlugin, self).render(context, instance, placeholder)
@@ -38,12 +46,12 @@ class BlogLatestEntriesPluginCached(BlogPlugin):
     """
     Cached plugin which returns the latest published posts
     """
-    render_template = 'djangocms_blog/plugins/latest_entries.html'
     name = _('Latest Blog Articles')
     model = LatestPostsPlugin
     form = LatestEntriesForm
     filter_horizontal = ('categories',)
     fields = ('latest_posts', 'tags', 'categories')
+    base_render_template = 'plugins/latest_entries.html'
 
     def render(self, context, instance, placeholder):
         context = super(BlogLatestEntriesPluginCached, self).render(context, instance, placeholder)
@@ -56,7 +64,7 @@ class BlogAuthorPostsPlugin(BlogPlugin):
     module = _('Blog')
     name = _('Author Blog Articles')
     model = AuthorEntriesPlugin
-    render_template = 'djangocms_blog/plugins/authors.html'
+    base_render_template = 'plugins/authors.html'
     filter_horizontal = ['authors']
 
     def render(self, context, instance, placeholder):
@@ -69,7 +77,7 @@ class BlogTagsPlugin(BlogPlugin):
     module = _('Blog')
     name = _('Tags')
     model = GenericBlogPlugin
-    render_template = 'djangocms_blog/plugins/tags.html'
+    base_render_template = 'plugins/tags.html'
 
     def render(self, context, instance, placeholder):
         context = super(BlogTagsPlugin, self).render(context, instance, placeholder)
@@ -85,7 +93,7 @@ class BlogCategoryPlugin(BlogPlugin):
     module = _('Blog')
     name = _('Categories')
     model = GenericBlogPlugin
-    render_template = 'djangocms_blog/plugins/categories.html'
+    base_render_template = 'plugins/categories.html'
 
     def render(self, context, instance, placeholder):
         context = super(BlogCategoryPlugin, self).render(context, instance, placeholder)
@@ -100,7 +108,7 @@ class BlogArchivePlugin(BlogPlugin):
     module = _('Blog')
     name = _('Archive')
     model = GenericBlogPlugin
-    render_template = 'djangocms_blog/plugins/archive.html'
+    base_render_template = 'plugins/archive.html'
 
     def render(self, context, instance, placeholder):
         context = super(BlogArchivePlugin, self).render(context, instance, placeholder)
diff --git a/djangocms_blog/views.py b/djangocms_blog/views.py
index 87268d4..d0ec7b5 100644
--- a/djangocms_blog/views.py
+++ b/djangocms_blog/views.py
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import, print_function, unicode_literals
 
+import os.path
+
 from aldryn_apphooks_config.mixins import AppConfigMixin
 from django.contrib.auth import get_user_model
 from django.utils.timezone import now
@@ -27,11 +29,17 @@ class BaseBlogView(AppConfigMixin, ViewUrlMixin):
             queryset = queryset.published()
         return queryset
 
+    def get_template_names(self):
+        if self.config.template_prefix:
+            return os.path.join(self.config.template_prefix, self.base_template_name)
+        else:
+            return os.path.join('djangocms_blog', self.base_template_name)
+
 
 class PostListView(BaseBlogView, ListView):
     model = Post
     context_object_name = 'post_list'
-    template_name = 'djangocms_blog/post_list.html'
+    base_template_name = 'post_list.html'
     view_url_name = 'djangocms_blog:posts-latest'
 
     def get_context_data(self, **kwargs):
@@ -46,7 +54,7 @@ class PostListView(BaseBlogView, ListView):
 class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
     model = Post
     context_object_name = 'post'
-    template_name = 'djangocms_blog/post_detail.html'
+    base_template_name = 'post_detail.html'
     slug_field = 'slug'
     view_url_name = 'djangocms_blog:post-detail'
 
@@ -73,7 +81,7 @@ class PostDetailView(TranslatableSlugMixin, BaseBlogView, DetailView):
 class PostArchiveView(BaseBlogView, ListView):
     model = Post
     context_object_name = 'post_list'
-    template_name = 'djangocms_blog/post_list.html'
+    base_template_name = 'post_list.html'
     date_field = 'date_published'
     allow_empty = True
     allow_future = True
@@ -101,7 +109,7 @@ class PostArchiveView(BaseBlogView, ListView):
 class TaggedListView(BaseBlogView, ListView):
     model = Post
     context_object_name = 'post_list'
-    template_name = 'djangocms_blog/post_list.html'
+    base_template_name = 'post_list.html'
     paginate_by = get_setting('PAGINATION')
     view_url_name = 'djangocms_blog:posts-tagged'
 
@@ -120,7 +128,7 @@ class TaggedListView(BaseBlogView, ListView):
 class AuthorEntriesView(BaseBlogView, ListView):
     model = Post
     context_object_name = 'post_list'
-    template_name = 'djangocms_blog/post_list.html'
+    base_template_name = 'post_list.html'
     paginate_by = get_setting('PAGINATION')
     view_url_name = 'djangocms_blog:posts-authors'
 
@@ -140,7 +148,7 @@ class AuthorEntriesView(BaseBlogView, ListView):
 class CategoryEntriesView(BaseBlogView, ListView):
     model = Post
     context_object_name = 'post_list'
-    template_name = 'djangocms_blog/post_list.html'
+    base_template_name = 'post_list.html'
     _category = None
     paginate_by = get_setting('PAGINATION')
     view_url_name = 'djangocms_blog:posts-category'
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index 6e8b640..4b00943 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import, print_function, unicode_literals
 
+import os.path
 import re
 
 from cms.api import add_plugin
@@ -132,3 +133,21 @@ class PluginTest(BaseTest):
         context = plugin_class.render(context, plugin, ph)
         self.assertEqual(context['dates'][0]['date'].date(), now().replace(year=now().year, month=now().month, day=1).date())
         self.assertEqual(context['dates'][0]['count'], 1)
+
+    def test_templates(self):
+        posts = self.get_posts()
+        pages = self.get_pages()
+
+        ph = pages[0].placeholders.get(slot='content')
+        plugin = add_plugin(ph, 'BlogLatestEntriesPlugin', language='en', app_config=self.app_config_1)
+
+        context = self.get_plugin_context(pages[0], 'en', plugin)
+        plugin_class = plugin.get_plugin_class_instance()
+        self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('djangocms_blog', plugin_class.base_render_template))
+
+        self.app_config_1.app_data.config.template_prefix = 'whatever'
+        self.app_config_1.save()
+        self.assertEqual(plugin_class.get_render_template(context, plugin, ph), os.path.join('whatever', plugin_class.base_render_template))
+        self.app_config_1.app_data.config.template_prefix = ''
+        self.app_config_1.save()
+
diff --git a/tests/test_views.py b/tests/test_views.py
index adc716b..ae921c0 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -2,6 +2,8 @@
 from __future__ import absolute_import, print_function, unicode_literals
 from aldryn_apphooks_config.utils import get_app_instance
 
+import os.path
+
 from django.contrib.auth.models import AnonymousUser
 from django.http import Http404
 from django.utils.timezone import now
@@ -299,3 +301,21 @@ class ViewTest(BaseTest):
         self.assertEqual(sitemap.items().count(), 3)
         for item in sitemap.items():
             self.assertTrue(sitemap.lastmod(item).date(), now().today())
+
+    def test_templates(self):
+        posts = self.get_posts()
+        pages = self.get_pages()
+
+        with smart_override('en'):
+            request = self.get_page_request(pages[1], self.user, edit=True)
+            view_obj = PostListView()
+            view_obj.request = request
+            view_obj.namespace = self.app_config_1.namespace
+            view_obj.config = self.app_config_1
+            self.assertEqual(view_obj.get_template_names(), os.path.join('djangocms_blog', 'post_list.html'))
+
+            self.app_config_1.app_data.config.template_prefix = 'whatever'
+            self.app_config_1.save()
+            self.assertEqual(view_obj.get_template_names(), os.path.join('whatever', 'post_list.html'))
+            self.app_config_1.app_data.config.template_prefix = ''
+            self.app_config_1.save()

From 63adbb1131528d6e64b635f7329766c606bf4653 Mon Sep 17 00:00:00 2001
From: Iacopo Spalletti <i.spalletti@nephila.it>
Date: Thu, 24 Sep 2015 21:51:54 +0200
Subject: [PATCH 2/3] Add meta attributes

---
 djangocms_blog/cms_appconfig.py | 82 ++++++++++++++++++++++++---------
 djangocms_blog/models.py        | 30 +++++++-----
 djangocms_blog/settings.py      | 42 ++++++++---------
 3 files changed, 100 insertions(+), 54 deletions(-)

diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py
index dc55dab..f10ba0e 100644
--- a/djangocms_blog/cms_appconfig.py
+++ b/djangocms_blog/cms_appconfig.py
@@ -25,25 +25,65 @@ class BlogConfig(TranslatableModel, AppHookConfig):
 
 
 class BlogConfigForm(AppDataForm):
-    default_published = forms.BooleanField(label=_('Post published by default'), required=False,
-                                           initial=get_setting('DEFAULT_PUBLISHED'))
-    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')
-                                      )
+    default_published = forms.BooleanField(
+        label=_('Post published by default'), required=False,
+        initial=get_setting('DEFAULT_PUBLISHED')
+    )
+    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')
+    )
+    object_type = forms.ChoiceField(
+        label=_('Object type'), required=False,
+        choices=get_setting('TYPES_GENERIC'), initial=get_setting('TYPE')
+    )
+    og_type = forms.ChoiceField(
+        label=_('Facebook type'), required=False,
+        choices=get_setting('TYPES_FACEBOOK'), initial=get_setting('TYPES_FACEBOOK')[0][0]
+    )
+    og_app_id = forms.CharField(
+        max_length=200, label=_('Facebook application ID'), required=False,
+    )
+    og_profile_id = forms.CharField(
+        max_length=200, label=_('Facebook profile ID'), required=False,
+    )
+    og_publisher = forms.CharField(
+        max_length=200, label=_('Facebook page URL'), required=False
+    )
+    og_author_url = forms.CharField(
+        max_length=200, label=_('Facebook author URL'), required=False
+    )
+    twitter_type = forms.ChoiceField(
+        label=_('Twitter type'), required=False,
+        choices=get_setting('TYPES_TWITTER'), initial=get_setting('TYPES_TWITTER')[0][0]
+    )
+    twitter_site = forms.CharField(
+        max_length=200, label=_('Twitter site handle'), required=False
+    )
+    twitter_author = forms.CharField(
+        max_length=200, label=_('Twitter author handle'), required=False
+    )
+    gplus_type = forms.CharField(
+        max_length=200, label=_('Google+ type'), required=False,
+        choices=get_setting('TYPES_GPLUS'), initial=get_setting('TYPES_GPLUS')[0][0]
+    )
+    gplus_author = forms.CharField(
+        max_length=200, label=_('Google+ author name'), required=False
+    )
 setup_config(BlogConfigForm, BlogConfig)
diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py
index 54aa6cf..95be636 100644
--- a/djangocms_blog/models.py
+++ b/djangocms_blog/models.py
@@ -144,20 +144,19 @@ class Post(ModelMeta, TranslatableModel):
         'og_description': 'get_description',
         'twitter_description': 'get_description',
         'gplus_description': 'get_description',
-        'keywords': 'get_keywords',
         'locale': None,
         'image': 'get_image_full_url',
-        'object_type': get_setting('TYPE'),
-        'og_type': get_setting('FB_TYPE'),
-        'og_app_id': get_setting('FB_APPID'),
-        'og_profile_id': get_setting('FB_PROFILE_ID'),
-        'og_publisher': get_setting('FB_PUBLISHER'),
-        'og_author_url': get_setting('FB_AUTHOR_URL'),
-        'twitter_type': get_setting('TWITTER_TYPE'),
-        'twitter_site': get_setting('TWITTER_SITE'),
-        'twitter_author': get_setting('TWITTER_AUTHOR'),
-        'gplus_type': get_setting('GPLUS_TYPE'),
-        'gplus_author': get_setting('GPLUS_AUTHOR'),
+        'object_type': 'get_meta_attribute',
+        'og_type': 'get_meta_attribute',
+        'og_app_id': 'get_meta_attribute',
+        'og_profile_id': 'get_meta_attribute',
+        'og_publisher': 'get_meta_attribute',
+        'og_author_url': 'get_meta_attribute',
+        'twitter_type': 'get_meta_attribute',
+        'twitter_site': 'get_meta_attribute',
+        'twitter_author': 'get_meta_attribute',
+        'gplus_type': 'get_meta_attribute',
+        'gplus_author': 'get_meta_attribute',
         'published_time': 'date_published',
         'modified_time': 'date_modified',
         'expiration_time': 'date_published_end',
@@ -185,6 +184,13 @@ class Post(ModelMeta, TranslatableModel):
                                                        any_language=True)}
         return reverse('%s:post-detail' % self.app_config.namespace, kwargs=kwargs)
 
+    def get_meta_attribute(self, param):
+        """
+        Retrieves django-meta attributes from apphook config instance
+        :param param: django-meta attribute passed as key
+        """
+        return getattr(self.app_config, param)
+
     def save_translation(self, translation, *args, **kwargs):
         if not translation.slug and translation.title:
             translation.slug = slugify(translation.title)
diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py
index 40b9dbe..a7564ac 100644
--- a/djangocms_blog/settings.py
+++ b/djangocms_blog/settings.py
@@ -6,6 +6,11 @@ def get_setting(name):
     from django.conf import settings
     from meta_mixin import settings as meta_settings
 
+    OBJECT_TYPES = {
+        'Article': _('Article'),
+        'Website': _('Website'),
+    }
+
     default = {
         'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
             'size': '120x120',
@@ -23,30 +28,25 @@ def get_setting(name):
         'BLOG_TAGCLOUD_MAX': getattr(settings, 'BLOG_TAGCLOUD_MAX', 10),
         'BLOG_PAGINATION': getattr(settings, 'BLOG_PAGINATION', 10),
         'BLOG_LATEST_POSTS': getattr(settings, 'BLOG_LATEST_POSTS', 5),
-        'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(settings,
-                                                    'BLOG_POSTS_LIST_TRUNCWORDS_COUNT',
-                                                    100),
+        'BLOG_POSTS_LIST_TRUNCWORDS_COUNT': getattr(
+            settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
+        ),
         'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
+        'BLOG_TYPES': getattr(settings, 'BLOG_TYPES', ),
         'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'),
-        'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID',
-                                 meta_settings.FB_APPID),
-        'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID',
-                                      meta_settings.FB_PROFILE_ID),
-        'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER',
-                                     meta_settings.FB_PUBLISHER),
-        'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL',
-                                      'get_author_url'),
-        'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR',
-                                  'get_author_name'),
+        'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', OBJECT_TYPES.items()),
+        'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID),
+        'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID),
+        'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER),
+        'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'),
+        'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
         'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'),
-        'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE',
-                                     meta_settings.TWITTER_SITE),
-        'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR',
-                                       'get_author_twitter'),
-        'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY',
-                                   'Blog'),
-        'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR',
-                                     'get_author_gplus'),
+        'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', OBJECT_TYPES.items()),
+        'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE),
+        'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
+        'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'),
+        'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', OBJECT_TYPES.items()),
+        'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
         'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
         'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
         'BLOG_USE_PLACEHOLDER': getattr(settings, 'BLOG_USE_PLACEHOLDER', True),

From 29df04208c77d06635c02b85e1376845f293c411 Mon Sep 17 00:00:00 2001
From: Iacopo Spalletti <i.spalletti@nephila.it>
Date: Sat, 26 Sep 2015 18:01:22 +0200
Subject: [PATCH 3/3] Scattered fixes

---
 djangocms_blog/cms_appconfig.py | 12 ++++++------
 djangocms_blog/models.py        |  1 +
 djangocms_blog/settings.py      | 18 ++++++++++--------
 tests/__init__.py               |  7 +++++++
 tests/test_plugins.py           |  6 ++----
 tests/test_views.py             |  2 +-
 tox.ini                         |  1 +
 7 files changed, 28 insertions(+), 19 deletions(-)

diff --git a/djangocms_blog/cms_appconfig.py b/djangocms_blog/cms_appconfig.py
index f10ba0e..2407968 100644
--- a/djangocms_blog/cms_appconfig.py
+++ b/djangocms_blog/cms_appconfig.py
@@ -51,11 +51,11 @@ class BlogConfigForm(AppDataForm):
     )
     object_type = forms.ChoiceField(
         label=_('Object type'), required=False,
-        choices=get_setting('TYPES_GENERIC'), initial=get_setting('TYPE')
+        choices=get_setting('TYPES'), initial=get_setting('TYPE')
     )
     og_type = forms.ChoiceField(
         label=_('Facebook type'), required=False,
-        choices=get_setting('TYPES_FACEBOOK'), initial=get_setting('TYPES_FACEBOOK')[0][0]
+        choices=get_setting('FB_TYPES'), initial=get_setting('FB_TYPES')[0][0]
     )
     og_app_id = forms.CharField(
         max_length=200, label=_('Facebook application ID'), required=False,
@@ -71,7 +71,7 @@ class BlogConfigForm(AppDataForm):
     )
     twitter_type = forms.ChoiceField(
         label=_('Twitter type'), required=False,
-        choices=get_setting('TYPES_TWITTER'), initial=get_setting('TYPES_TWITTER')[0][0]
+        choices=get_setting('TWITTER_TYPES'), initial=get_setting('TWITTER_TYPES')[0][0]
     )
     twitter_site = forms.CharField(
         max_length=200, label=_('Twitter site handle'), required=False
@@ -79,9 +79,9 @@ class BlogConfigForm(AppDataForm):
     twitter_author = forms.CharField(
         max_length=200, label=_('Twitter author handle'), required=False
     )
-    gplus_type = forms.CharField(
-        max_length=200, label=_('Google+ type'), required=False,
-        choices=get_setting('TYPES_GPLUS'), initial=get_setting('TYPES_GPLUS')[0][0]
+    gplus_type = forms.ChoiceField(
+        label=_('Google+ type'), required=False,
+        choices=get_setting('GPLUS_TYPES'), initial=get_setting('GPLUS_TYPES')[0][0]
     )
     gplus_author = forms.CharField(
         max_length=200, label=_('Google+ author name'), required=False
diff --git a/djangocms_blog/models.py b/djangocms_blog/models.py
index 95be636..d3dcd8d 100644
--- a/djangocms_blog/models.py
+++ b/djangocms_blog/models.py
@@ -141,6 +141,7 @@ class Post(ModelMeta, TranslatableModel):
     _metadata = {
         'title': 'get_title',
         'description': 'get_description',
+        'keywords': 'get_keywords',
         'og_description': 'get_description',
         'twitter_description': 'get_description',
         'gplus_description': 'get_description',
diff --git a/djangocms_blog/settings.py b/djangocms_blog/settings.py
index a7564ac..3b25659 100644
--- a/djangocms_blog/settings.py
+++ b/djangocms_blog/settings.py
@@ -4,12 +4,14 @@ from __future__ import absolute_import, print_function, unicode_literals
 
 def get_setting(name):
     from django.conf import settings
+    from django.utils.translation import ugettext_lazy as _
     from meta_mixin import settings as meta_settings
 
-    OBJECT_TYPES = {
-        'Article': _('Article'),
-        'Website': _('Website'),
-    }
+    OBJECT_TYPES = (
+        ('Article', _('Article')),
+        ('Website', _('Website')),
+    )
+    BLOG_TYPES = getattr(settings, 'BLOG_TYPES', OBJECT_TYPES)
 
     default = {
         'BLOG_IMAGE_THUMBNAIL_SIZE': getattr(settings, 'BLOG_IMAGE_THUMBNAIL_SIZE', {
@@ -32,20 +34,20 @@ def get_setting(name):
             settings, 'BLOG_POSTS_LIST_TRUNCWORDS_COUNT', 100
         ),
         'BLOG_TYPE': getattr(settings, 'BLOG_TYPE', 'Article'),
-        'BLOG_TYPES': getattr(settings, 'BLOG_TYPES', ),
+        'BLOG_TYPES': BLOG_TYPES,
         'BLOG_FB_TYPE': getattr(settings, 'BLOG_FB_TYPE', 'Article'),
-        'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', OBJECT_TYPES.items()),
+        'BLOG_FB_TYPES': getattr(settings, 'BLOG_FB_TYPES', BLOG_TYPES),
         'BLOG_FB_APPID': getattr(settings, 'BLOG_FB_APPID', meta_settings.FB_APPID),
         'BLOG_FB_PROFILE_ID': getattr(settings, 'BLOG_FB_PROFILE_ID', meta_settings.FB_PROFILE_ID),
         'BLOG_FB_PUBLISHER': getattr(settings, 'BLOG_FB_PUBLISHER', meta_settings.FB_PUBLISHER),
         'BLOG_FB_AUTHOR_URL': getattr(settings, 'BLOG_FB_AUTHOR_URL', 'get_author_url'),
         'BLOG_FB_AUTHOR': getattr(settings, 'BLOG_FB_AUTHOR', 'get_author_name'),
         'BLOG_TWITTER_TYPE': getattr(settings, 'BLOG_TWITTER_TYPE', 'Summary'),
-        'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', OBJECT_TYPES.items()),
+        'BLOG_TWITTER_TYPES': getattr(settings, 'BLOG_TWITTER_TYPES', BLOG_TYPES),
         'BLOG_TWITTER_SITE': getattr(settings, 'BLOG_TWITTER_SITE', meta_settings.TWITTER_SITE),
         'BLOG_TWITTER_AUTHOR': getattr(settings, 'BLOG_TWITTER_AUTHOR', 'get_author_twitter'),
         'BLOG_GPLUS_TYPE': getattr(settings, 'BLOG_GPLUS_SCOPE_CATEGORY', 'Blog'),
-        'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', OBJECT_TYPES.items()),
+        'BLOG_GPLUS_TYPES': getattr(settings, 'BLOG_GPLUS_TYPES', BLOG_TYPES),
         'BLOG_GPLUS_AUTHOR': getattr(settings, 'BLOG_GPLUS_AUTHOR', 'get_author_gplus'),
         'BLOG_ENABLE_COMMENTS': getattr(settings, 'BLOG_ENABLE_COMMENTS', True),
         'BLOG_USE_ABSTRACT': getattr(settings, 'BLOG_USE_ABSTRACT', True),
diff --git a/tests/__init__.py b/tests/__init__.py
index fe9cbfe..6fad4a6 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -127,6 +127,13 @@ class BaseTest(BaseTestCase):
         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():
diff --git a/tests/test_plugins.py b/tests/test_plugins.py
index 4b00943..73b0e2a 100644
--- a/tests/test_plugins.py
+++ b/tests/test_plugins.py
@@ -7,6 +7,7 @@ import re
 from cms.api import add_plugin
 from django.core.urlresolvers import reverse
 from django.utils.timezone import now
+from djangocms_helper.utils import get_user_model
 from taggit.models import Tag
 
 from djangocms_blog.models import BlogCategory
@@ -68,12 +69,10 @@ class PluginTest(BaseTest):
         posts[1].save()
         ph = pages[0].placeholders.get(slot='content')
         plugin = add_plugin(ph, 'BlogAuthorPostsPlugin', language='en', app_config=self.app_config_1)
-        plugin.authors.add(self.user)
 
         context = self.get_plugin_context(pages[0], 'en', plugin, edit=True)
         rendered = plugin.render_plugin(context, ph)
-        self.assertTrue(rendered.find(reverse('djangocms_blog:posts-author', kwargs={'username': self.user.get_username()})) > -1)
-        self.assertTrue(rendered.find('2 articles') > -1)
+        self.assertTrue(rendered.find('No article found') > -1)
 
     def test_plugin_tags(self):
         pages = self.get_pages()
@@ -150,4 +149,3 @@ class PluginTest(BaseTest):
         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()
-
diff --git a/tests/test_views.py b/tests/test_views.py
index ae921c0..818484a 100644
--- a/tests/test_views.py
+++ b/tests/test_views.py
@@ -1,9 +1,9 @@
 # -*- coding: utf-8 -*-
 from __future__ import absolute_import, print_function, unicode_literals
-from aldryn_apphooks_config.utils import get_app_instance
 
 import os.path
 
+from aldryn_apphooks_config.utils import get_app_instance
 from django.contrib.auth.models import AnonymousUser
 from django.http import Http404
 from django.utils.timezone import now
diff --git a/tox.ini b/tox.ini
index 2dbc0d7..2887060 100644
--- a/tox.ini
+++ b/tox.ini
@@ -15,6 +15,7 @@ deps =
     cms30: https://github.com/divio/django-cms/archive/support/3.0.x.zip
     cms31: https://github.com/divio/django-cms/archive/support/3.1.x.zip
     cms32: https://github.com/divio/django-cms/archive/develop.zip
+    https://github.com/nephila/django-meta-mixin/archive/master.zip
     https://github.com/nephila/djangocms-helper/archive/develop.zip
     py26: unittest2
     django-parler<1.5