django-cms-mycmsproject-toporojo / cms / admin /

# -*- 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
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 = ['%sjs/jquery.min.js' % admin_static_url()] + [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 = settings.CMS_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 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': settings.CMS_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")