463 lines
18 KiB
Python
Executable File
463 lines
18 KiB
Python
Executable File
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import, print_function, unicode_literals
|
|
|
|
from copy import deepcopy
|
|
|
|
from aldryn_apphooks_config.admin import BaseAppHookConfig, ModelAppHookConfig
|
|
from cms.admin.placeholderadmin import FrontendEditableAdminMixin, PlaceholderAdminMixin
|
|
from cms.models import CMSPlugin, ValidationError
|
|
from django.apps import apps
|
|
from django.conf import settings
|
|
from django.conf.urls import url
|
|
from django.contrib import admin, messages
|
|
from django.contrib.sites.models import Site
|
|
from django.core.urlresolvers import reverse
|
|
from django.http import HttpResponseRedirect
|
|
from django.utils import timezone
|
|
from django.utils.six import callable, text_type
|
|
from django.utils.translation import get_language_from_request, ugettext_lazy as _, ungettext as __
|
|
from parler.admin import TranslatableAdmin
|
|
|
|
from .cms_appconfig import BlogConfig
|
|
from .forms import CategoryAdminForm, PostAdminForm
|
|
from .models import BlogCategory, Post
|
|
from .settings import get_setting
|
|
|
|
|
|
class SiteListFilter(admin.SimpleListFilter):
|
|
title = _('site')
|
|
parameter_name = 'sites'
|
|
|
|
def lookups(self, request, model_admin):
|
|
restricted_sites = model_admin.get_restricted_sites(request).values_list('id', flat=True)
|
|
|
|
qs = Site.objects.all()
|
|
if restricted_sites:
|
|
qs = qs.filter(id__in=restricted_sites)
|
|
|
|
return [(site.id, text_type(site.name)) for site in qs]
|
|
|
|
def queryset(self, request, queryset):
|
|
try:
|
|
if 'sites' in self.used_parameters:
|
|
return queryset.on_site(Site.objects.get(pk=self.used_parameters['sites']))
|
|
return queryset
|
|
except Site.DoesNotExist as e: # pragma: no cover
|
|
raise admin.options.IncorrectLookupParameters(e)
|
|
except ValidationError as e: # pragma: no cover
|
|
raise admin.options.IncorrectLookupParameters(e)
|
|
|
|
|
|
class BlogCategoryAdmin(ModelAppHookConfig, TranslatableAdmin):
|
|
form = CategoryAdminForm
|
|
list_display = [
|
|
'name', 'parent', 'app_config', 'all_languages_column',
|
|
]
|
|
|
|
def get_prepopulated_fields(self, request, obj=None):
|
|
app_config_default = self._app_config_select(request, obj)
|
|
if app_config_default is None and request.method == 'GET':
|
|
return {}
|
|
return {'slug': ('name',)}
|
|
|
|
class Media:
|
|
css = {
|
|
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
|
|
}
|
|
|
|
|
|
class PostAdmin(PlaceholderAdminMixin, FrontendEditableAdminMixin,
|
|
ModelAppHookConfig, TranslatableAdmin):
|
|
form = PostAdminForm
|
|
list_display = [
|
|
'title', 'author', 'date_published', 'app_config', 'all_languages_column',
|
|
'date_published_end'
|
|
]
|
|
search_fields = ('translations__title',)
|
|
date_hierarchy = 'date_published'
|
|
raw_id_fields = ['author']
|
|
frontend_editable_fields = ('title', 'abstract', 'post_text')
|
|
enhance_exclude = ('main_image', 'tags')
|
|
actions = [
|
|
'make_published',
|
|
'make_unpublished',
|
|
'enable_comments',
|
|
'disable_comments',
|
|
]
|
|
if apps.is_installed('djangocms_blog.liveblog'):
|
|
actions += ['enable_liveblog', 'disable_liveblog']
|
|
_fieldsets = [
|
|
(None, {
|
|
'fields': [['title', 'categories', 'publish', 'app_config']]
|
|
}),
|
|
(None, {
|
|
'fields': [[]]
|
|
}),
|
|
(_('Info'), {
|
|
'fields': [['slug', 'tags'],
|
|
['date_published', 'date_published_end', 'date_featured'],
|
|
['enable_comments']],
|
|
'classes': ('collapse',)
|
|
}),
|
|
(_('Images'), {
|
|
'fields': [['main_image', 'main_image_thumbnail', 'main_image_full']],
|
|
'classes': ('collapse',)
|
|
}),
|
|
(_('SEO'), {
|
|
'fields': [['meta_description', 'meta_title', 'meta_keywords']],
|
|
'classes': ('collapse',)
|
|
}),
|
|
]
|
|
|
|
app_config_values = {
|
|
'default_published': 'publish'
|
|
}
|
|
_sites = None
|
|
|
|
# Bulk actions for post admin
|
|
def make_published(self, request, queryset):
|
|
""" Bulk action to mark selected posts as published. If
|
|
the date_published field is empty the current time is
|
|
saved as date_published.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
cnt1 = queryset.filter(
|
|
date_published__isnull=True,
|
|
publish=False,
|
|
).update(date_published=timezone.now(), publish=True)
|
|
cnt2 = queryset.filter(
|
|
date_published__isnull=False,
|
|
publish=False,
|
|
).update(publish=True)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('%(updates)d entry published.',
|
|
'%(updates)d entries published.', cnt1+cnt2) % {
|
|
'updates': cnt1+cnt2, })
|
|
|
|
def make_unpublished(self, request, queryset):
|
|
""" Bulk action to mark selected posts as UNpublished.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
updates = queryset.filter(publish=True)\
|
|
.update(publish=False)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('%(updates)d entry unpublished.',
|
|
'%(updates)d entries unpublished.', updates) % {
|
|
'updates': updates, })
|
|
|
|
def enable_comments(self, request, queryset):
|
|
""" Bulk action to enable comments for selected posts.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
updates = queryset.filter(enable_comments=False)\
|
|
.update(enable_comments=True)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('Comments for %(updates)d entry enabled.',
|
|
'Comments for %(updates)d entries enabled', updates) % {
|
|
'updates': updates, })
|
|
|
|
def disable_comments(self, request, queryset):
|
|
""" Bulk action to disable comments for selected posts.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
updates = queryset.filter(enable_comments=True)\
|
|
.update(enable_comments=False)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('Comments for %(updates)d entry disabled.',
|
|
'Comments for %(updates)d entries disabled.', updates) % {
|
|
'updates': updates, })
|
|
|
|
def enable_liveblog(self, request, queryset):
|
|
""" Bulk action to enable comments for selected posts.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
updates = queryset.filter(enable_liveblog=False)\
|
|
.update(enable_liveblog=True)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('Liveblog for %(updates)d entry enabled.',
|
|
'Liveblog for %(updates)d entries enabled.', updates) % {
|
|
'updates': updates, })
|
|
|
|
def disable_liveblog(self, request, queryset):
|
|
""" Bulk action to disable comments for selected posts.
|
|
queryset must not be empty (ensured by DjangoCMS).
|
|
"""
|
|
updates = queryset.filter(enable_liveblog=True)\
|
|
.update(enable_liveblog=False)
|
|
messages.add_message(
|
|
request, messages.INFO,
|
|
__('Liveblog for %(updates)d entry enabled.',
|
|
'Liveblog for %(updates)d entries enabled.') % {
|
|
'updates': updates, })
|
|
|
|
# Make bulk action menu entries localizable
|
|
make_published.short_description = _("Publish selection")
|
|
make_unpublished.short_description = _("Unpublish selection")
|
|
enable_comments.short_description = _("Enable comments for selection")
|
|
disable_comments.short_description = _("Disable comments for selection ")
|
|
enable_liveblog.short_description = _("Enable liveblog for selection")
|
|
disable_liveblog.short_description = _("Disable liveblog for selection ")
|
|
|
|
def get_list_filter(self, request):
|
|
filters = ['app_config', 'publish', 'date_published']
|
|
if get_setting('MULTISITE'):
|
|
filters.append(SiteListFilter)
|
|
try:
|
|
from taggit_helpers.admin import TaggitListFilter
|
|
filters.append(TaggitListFilter)
|
|
except ImportError: # pragma: no cover
|
|
try:
|
|
from taggit_helpers import TaggitListFilter
|
|
filters.append(TaggitListFilter)
|
|
except ImportError:
|
|
pass
|
|
return filters
|
|
|
|
def get_urls(self):
|
|
"""
|
|
Customize the modeladmin urls
|
|
"""
|
|
urls = [
|
|
url(r'^publish/([0-9]+)/$', self.admin_site.admin_view(self.publish_post),
|
|
name='djangocms_blog_publish_article'),
|
|
]
|
|
urls.extend(super(PostAdmin, self).get_urls())
|
|
return urls
|
|
|
|
def post_add_plugin(self, request, obj1, obj2=None):
|
|
if isinstance(obj1, CMSPlugin):
|
|
plugin = obj1
|
|
elif isinstance(obj2, CMSPlugin):
|
|
plugin = obj2
|
|
if plugin.plugin_type in get_setting('LIVEBLOG_PLUGINS'):
|
|
plugin = plugin.move(plugin.get_siblings().first(), 'first-sibling')
|
|
if isinstance(obj1, CMSPlugin):
|
|
return super(PostAdmin, self).post_add_plugin(request, plugin)
|
|
elif isinstance(obj2, CMSPlugin):
|
|
return super(PostAdmin, self).post_add_plugin(request, obj1, plugin)
|
|
|
|
def publish_post(self, request, pk):
|
|
"""
|
|
Admin view to publish a single post
|
|
|
|
:param request: request
|
|
:param pk: primary key of the post to publish
|
|
:return: Redirect to the post itself (if found) or fallback urls
|
|
"""
|
|
language = get_language_from_request(request, check_path=True)
|
|
try:
|
|
post = Post.objects.get(pk=int(pk))
|
|
post.publish = True
|
|
post.save()
|
|
return HttpResponseRedirect(post.get_absolute_url(language))
|
|
except Exception:
|
|
try:
|
|
return HttpResponseRedirect(request.META['HTTP_REFERER'])
|
|
except KeyError:
|
|
return HttpResponseRedirect(reverse('djangocms_blog:posts-latest'))
|
|
|
|
def has_restricted_sites(self, request):
|
|
"""
|
|
Whether the current user has permission on one site only
|
|
|
|
:param request: current request
|
|
:return: boolean: user has permission on only one site
|
|
"""
|
|
sites = self.get_restricted_sites(request)
|
|
return sites and sites.count() == 1
|
|
|
|
def get_restricted_sites(self, request):
|
|
"""
|
|
The sites on which the user has permission on.
|
|
|
|
To return the permissions, the method check for the ``get_sites``
|
|
method on the user instance (e.g.: ``return request.user.get_sites()``)
|
|
which must return the queryset of enabled sites.
|
|
If the attribute does not exists, the user is considered enabled
|
|
for all the websites.
|
|
|
|
:param request: current request
|
|
:return: boolean or a queryset of available sites
|
|
"""
|
|
try:
|
|
return request.user.get_sites()
|
|
except AttributeError: # pragma: no cover
|
|
return Site.objects.none()
|
|
|
|
def _set_config_defaults(self, request, form, obj=None):
|
|
form = super(PostAdmin, self)._set_config_defaults(request, form, obj)
|
|
sites = self.get_restricted_sites(request)
|
|
if 'sites' in form.base_fields and sites.exists():
|
|
form.base_fields['sites'].queryset = self.get_restricted_sites(request).all()
|
|
return form
|
|
|
|
def get_fieldsets(self, request, obj=None):
|
|
"""
|
|
Customize the fieldsets according to the app settings
|
|
|
|
:param request: request
|
|
:param obj: post
|
|
:return: fieldsets configuration
|
|
"""
|
|
app_config_default = self._app_config_select(request, obj)
|
|
if app_config_default is None and request.method == 'GET':
|
|
return super(PostAdmin, self).get_fieldsets(request, obj)
|
|
if not obj:
|
|
config = app_config_default
|
|
else:
|
|
config = obj.app_config
|
|
|
|
fsets = deepcopy(self._fieldsets)
|
|
if config:
|
|
abstract = bool(config.use_abstract)
|
|
placeholder = bool(config.use_placeholder)
|
|
related = bool(config.use_related)
|
|
else:
|
|
abstract = get_setting('USE_ABSTRACT')
|
|
placeholder = get_setting('USE_PLACEHOLDER')
|
|
related = get_setting('USE_RELATED')
|
|
if abstract:
|
|
fsets[0][1]['fields'].append('abstract')
|
|
if not placeholder:
|
|
fsets[0][1]['fields'].append('post_text')
|
|
if get_setting('MULTISITE') and not self.has_restricted_sites(request):
|
|
fsets[1][1]['fields'][0].append('sites')
|
|
if request.user.is_superuser:
|
|
fsets[1][1]['fields'][0].append('author')
|
|
if apps.is_installed('djangocms_blog.liveblog'):
|
|
fsets[2][1]['fields'][2].append('enable_liveblog')
|
|
filter_function = get_setting('ADMIN_POST_FIELDSET_FILTER')
|
|
if related and Post.objects.namespace(config.namespace).active_translations().exists():
|
|
fsets[1][1]['fields'][0].append('related')
|
|
if callable(filter_function):
|
|
fsets = filter_function(fsets, request, obj=obj)
|
|
return fsets
|
|
|
|
def get_prepopulated_fields(self, request, obj=None):
|
|
return {'slug': ('title',)}
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
obj._set_default_author(request.user)
|
|
super(PostAdmin, self).save_model(request, obj, form, change)
|
|
|
|
def get_queryset(self, request):
|
|
qs = super(PostAdmin, self).get_queryset(request)
|
|
sites = self.get_restricted_sites(request)
|
|
if sites.exists():
|
|
pks = list(sites.all().values_list('pk', flat=True))
|
|
qs = qs.filter(sites__in=pks)
|
|
return qs.distinct()
|
|
|
|
def save_related(self, request, form, formsets, change):
|
|
if self.get_restricted_sites(request).exists():
|
|
if 'sites' in form.cleaned_data:
|
|
form_sites = form.cleaned_data.get('sites', [])
|
|
removed = set(
|
|
self.get_restricted_sites(request).all()
|
|
).difference(form_sites)
|
|
diff_original = set(
|
|
form.instance.sites.all()
|
|
).difference(removed).union(form_sites)
|
|
form.cleaned_data['sites'] = diff_original
|
|
else:
|
|
form.instance.sites.add(
|
|
*self.get_restricted_sites(request).all().values_list('pk', flat=True)
|
|
)
|
|
super(PostAdmin, self).save_related(request, form, formsets, change)
|
|
|
|
class Media:
|
|
css = {
|
|
'all': ('%sdjangocms_blog/css/%s' % (settings.STATIC_URL, 'djangocms_blog_admin.css'),)
|
|
}
|
|
|
|
|
|
class BlogConfigAdmin(BaseAppHookConfig, TranslatableAdmin):
|
|
@property
|
|
def declared_fieldsets(self):
|
|
return self.get_fieldsets(None)
|
|
|
|
def get_fieldsets(self, request, obj=None):
|
|
"""
|
|
Fieldsets configuration
|
|
"""
|
|
return [
|
|
(None, {
|
|
'fields': ('type', 'namespace', 'app_title', 'object_name')
|
|
}),
|
|
(_('Generic'), {
|
|
'fields': (
|
|
'config.default_published', 'config.use_placeholder', 'config.use_abstract',
|
|
'config.set_author', 'config.use_related',
|
|
)
|
|
}),
|
|
(_('Layout'), {
|
|
'fields': (
|
|
'config.paginate_by', 'config.url_patterns', 'config.template_prefix',
|
|
'config.menu_structure', 'config.menu_empty_categories',
|
|
('config.default_image_full', 'config.default_image_thumbnail'),
|
|
),
|
|
'classes': ('collapse',)
|
|
}),
|
|
(_('Notifications'), {
|
|
'fields': (
|
|
'config.send_knock_create', 'config.send_knock_update'
|
|
),
|
|
'classes': ('collapse',)
|
|
}),
|
|
(_('Sitemap'), {
|
|
'fields': (
|
|
'config.sitemap_changefreq', 'config.sitemap_priority',
|
|
),
|
|
'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'
|
|
)
|
|
}),
|
|
]
|
|
|
|
def save_model(self, request, obj, form, change):
|
|
"""
|
|
Clear menu cache when changing menu structure
|
|
"""
|
|
if 'config.menu_structure' in form.changed_data:
|
|
from menus.menu_pool import menu_pool
|
|
menu_pool.clear(all=True)
|
|
return super(BlogConfigAdmin, self).save_model(request, obj, form, change)
|
|
|
|
|
|
admin.site.register(BlogCategory, BlogCategoryAdmin)
|
|
admin.site.register(Post, PostAdmin)
|
|
admin.site.register(BlogConfig, BlogConfigAdmin)
|