Skip to content
Fetching contributors…
Cannot retrieve contributors at this time
343 lines (299 sloc) 15 KB
# -*- coding: utf-8 -*-
from cms.forms.fields import PlaceholderFormField
from cms.models.fields import PlaceholderField
from cms.models.placeholdermodel import Placeholder
from cms.models.pluginmodel import CMSPlugin
from cms.plugin_pool import plugin_pool
from cms.utils import get_language_from_request, cms_static_url, get_cms_setting
from cms.utils.permissions import has_plugin_permission
from copy import deepcopy
from django.conf import settings
from django.contrib.admin import ModelAdmin
from django.http import (HttpResponse, Http404, HttpResponseBadRequest,
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
from django.template.defaultfilters import force_escape, escapejs
from django.utils.translation import ugettext as _
from cms.templatetags.cms_admin import admin_static_url
class PlaceholderAdmin(ModelAdmin):
class Media:
css = {
'all': [cms_static_url(path) for path in (
js = [cms_static_url(path) for path in [
def get_fieldsets(self, request, obj=None):
Get fieldsets to enforce correct fieldsetting of placeholder fields
form = self.get_form(request, obj)
placeholder_fields = self._get_placeholder_fields(form)
if self.declared_fieldsets:
# check those declared fieldsets
fieldsets = list(deepcopy(self.declared_fieldsets))
for label, fieldset in fieldsets:
fields = list(fieldset['fields'])
for field in fieldset['fields']:
if field in placeholder_fields:
if (len(fieldset['fields']) == 1 and
'classes' in fieldset and
'plugin-holder' in fieldset['classes'] and
'plugin-holder-nopage' in fieldset['classes']):
if fields:
fieldset['fields'] = fields
# no fields in the fieldset anymore, delete the fieldset
fieldsets.remove((label, fieldset))
for placeholder in placeholder_fields:
fieldsets.append((self.get_label_for_placeholder(placeholder), {
'fields': (placeholder,),
'classes': ('plugin-holder', 'plugin-holder-nopage',),
return fieldsets
fieldsets = []
fieldsets.append((None, {'fields': [f for f in form.base_fields.keys() if not f in placeholder_fields]}))
for placeholder in placeholder_fields:
fieldsets.append((self.get_label_for_placeholder(placeholder), {
'fields': (placeholder,),
'classes': ('plugin-holder', 'plugin-holder-nopage',),
readonly_fields = self.get_readonly_fields(request, obj)
if readonly_fields:
fieldsets.append((None, {'fields': list(readonly_fields)}))
return fieldsets
def get_label_for_placeholder(self, placeholder):
return ' '.join([x.capitalize() for x in self.model._meta.get_field_by_name(placeholder)[0].verbose_name.split(' ')])
def formfield_for_dbfield(self, db_field, **kwargs):
Hook for specifying the form Field instance for a given database Field
If kwargs are given, they're passed to the form Field's constructor.
if isinstance(db_field, PlaceholderField):
request = kwargs.pop("request", None)
return db_field.formfield_for_admin(request, self.placeholder_plugin_filter, **kwargs)
return super(PlaceholderAdmin, self).formfield_for_dbfield(db_field, **kwargs)
def placeholder_plugin_filter(self, request, queryset):
return queryset
def _get_placeholder_fields(self, form):
placeholder_fields = []
for key, value in form.base_fields.items():
if isinstance(value, PlaceholderFormField):
return placeholder_fields
def get_urls(self):
Register the plugin specific urls (add/edit/copy/remove/move)
from django.conf.urls.defaults import patterns, url
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.module_name)
pat = lambda regex, fn: url(regex, self.admin_site.admin_view(fn), name='%s_%s' % (info, fn.__name__))
url_patterns = patterns('',
pat(r'add-plugin/$', self.add_plugin),
pat(r'edit-plugin/([0-9]+)/$', self.edit_plugin),
pat(r'remove-plugin/$', self.remove_plugin),
pat(r'move-plugin/$', self.move_plugin),
pat(r'copy-plugins/$', self.copy_plugins),
return url_patterns + super(PlaceholderAdmin, self).get_urls()
def add_plugin(self, request):
# only allow POST
if request.method != "POST":
raise Http404
plugin_type = request.POST['plugin_type']
if not has_plugin_permission(request.user, plugin_type, "add"):
return HttpResponseForbidden("You don't have permission to add plugins")
placeholder_id = request.POST.get('placeholder', None)
position = None
language = get_language_from_request(request)
parent = None
# check if we got a placeholder (id)
if placeholder_id:
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
else: # else get the parent_id
parent_id = request.POST.get('parent_id', None)
if not parent_id: # if we get neither a placeholder nor a parent, bail out
raise Http404
parent = get_object_or_404(CMSPlugin, pk=parent_id)
placeholder = parent.placeholder
# check add permissions on placeholder
if not placeholder.has_add_permission(request):
return HttpResponseForbidden(_("You don't have permission to add content here."))
# check the limits defined in CMS_PLACEHOLDER_CONF for this placeholder
limits = get_cms_setting('PLACEHOLDER_CONF').get(placeholder.slot, {}).get('limits', None)
if limits:
count = placeholder.cmsplugin_set.count()
global_limit = limits.get("global", None)
type_limit = limits.get(plugin_type, None)
# check the global limit first
if global_limit and count >= global_limit:
return HttpResponseBadRequest(
"This placeholder already has the maximum number of plugins."
elif type_limit: # then check the type specific limit
type_count = CMSPlugin.objects.filter(
language=language, placeholder=placeholder, plugin_type=plugin_type
if type_count >= type_limit:
return HttpResponseBadRequest(
"This placeholder already has the maximum number (%s) "
"of %s plugins." % (type_limit, plugin_type)
# actually add the plugin
plugin = CMSPlugin(language=language, plugin_type=plugin_type,
position=position, placeholder=placeholder, parent=parent)
# returns it's ID as response
return HttpResponse(str(
def edit_plugin(self, request, plugin_id):
plugin_id = int(plugin_id)
# get the plugin to edit of bail out
cms_plugin = get_object_or_404(CMSPlugin, pk=plugin_id)
if not has_plugin_permission(request.user, cms_plugin.plugin_type, "change"):
return HttpResponseForbidden(_("You don't have permission to add plugins"))
# check that the user has permission to change this plugin
if not cms_plugin.placeholder.has_change_permission(request):
return HttpResponseForbidden(_("You don't have permission to add content here."))
instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
plugin_admin.cms_plugin_instance = cms_plugin
plugin_admin.placeholder = cms_plugin.placeholder
if request.method == "POST":
# set the continue flag, otherwise will plugin_admin make redirect to list
# view, which actually does'nt exists
post_request = request.POST.copy()
post_request['_continue'] = True
request.POST = post_request
if request.POST.get("_cancel", False):
# cancel button was clicked
context = {
'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
'plugin': cms_plugin,
'is_popup': True,
'name': unicode(cms_plugin),
"type": cms_plugin.get_plugin_name(),
'plugin_id': plugin_id,
'icon': force_escape(escapejs(cms_plugin.get_instance_icon_src())),
'alt': force_escape(escapejs(cms_plugin.get_instance_icon_alt())),
'cancel': True,
instance = cms_plugin.get_plugin_instance()[0]
if not instance:
# cancelled before any content was added to plugin
return render_to_response('admin/cms/page/plugin_forms_ok.html', context, RequestContext(request))
if not instance:
# instance doesn't exist, call add view
response = plugin_admin.add_view(request)
# already saved before, call change view
# we actually have the instance here, but since i won't override
# change_view method, is better if it will be loaded again, so
# just pass id to plugin_admin
response = plugin_admin.change_view(request, str(plugin_id))
if request.method == "POST" and plugin_admin.object_successfully_changed:
# read the saved object from plugin_admin - ugly but works
saved_object = plugin_admin.saved_object
context = {
'CMS_MEDIA_URL': get_cms_setting('MEDIA_URL'),
'plugin': saved_object,
'is_popup': True,
'name': unicode(saved_object),
"type": saved_object.get_plugin_name(),
'plugin_id': plugin_id,
'icon': force_escape(saved_object.get_instance_icon_src()),
'alt': force_escape(escapejs(saved_object.get_instance_icon_alt())),
return render_to_response('admin/cms/page/plugin_forms_ok.html', context, RequestContext(request))
return response
def move_plugin(self, request):
# only allow POST
if request.method != "POST":
return HttpResponse(str("error"))
if 'plugin_id' in request.POST: # single plugin moving
plugin = CMSPlugin.objects.get(pk=int(request.POST['plugin_id']))
if 'placeholder_id' in request.POST:
placeholder = Placeholder.objects.get(pk=int(request.POST['placeholder_id']))
placeholder = plugin.placeholder
# check permissions
if not placeholder.has_change_permission(request):
raise Http404
# plugin positions are 0 based, so just using count here should give us 'last_position + 1'
position = CMSPlugin.objects.filter(placeholder=placeholder).count()
plugin.placeholder = placeholder
plugin.position = position
pos = 0
if 'ids' in request.POST: # multiple plugins/ reordering
whitelisted_placeholders = []
for id in request.POST['ids'].split("_"):
plugin = CMSPlugin.objects.get(pk=id)
# check the permissions for *each* plugin, but cache them locally
# per placeholder
if not in whitelisted_placeholders:
if plugin.placeholder.has_change_permission(request):
raise Http404
# actually do the moving
if plugin.position != pos:
plugin.position = pos
pos += 1
return HttpResponse(str("ok"))
def remove_plugin(self, request):
if request.method != "POST": # only allow POST
raise Http404
plugin_id = request.POST['plugin_id']
plugin = get_object_or_404(CMSPlugin, pk=plugin_id)
# check the permissions!
if not plugin.placeholder.has_delete_permission(request):
return HttpResponseForbidden(_("You don't have permission to delete a plugin"))
plugin_name = unicode(plugin_pool.get_plugin(plugin.plugin_type).name)
comment = _(u"%(plugin_name)s plugin at position %(position)s in %(placeholder)s was deleted.") % {'plugin_name':plugin_name, 'position':plugin.position, 'placeholder':plugin.placeholder}
return HttpResponse("%s,%s" % (plugin_id, comment))
def copy_plugins(self, request):
# only allow POST
if request.method != "POST":
raise Http404
placeholder_id = request.POST['placeholder']
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
# check permissions
if not placeholder.has_add_permission(request):
raise Http404
# the placeholder actions are responsible for copying, they should return
# a list of plugins if successful.
plugins = placeholder.actions.copy(
if plugins:
return render_to_response('admin/cms/page/widgets/plugin_item.html',
{'plugin_list': list(plugins)}, RequestContext(request))
return HttpResponseBadRequest("Error during copy")
Something went wrong with that request. Please try again.