Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
807e066
rudimentary sane add-plugin
ojii Oct 8, 2014
475120c
removed old thing
ojii Oct 8, 2014
b2c1a51
moved "new-add-plugin" to CMSPluginBase, where it arguably belongs
ojii Oct 8, 2014
f18f3a9
fixed test
ojii Oct 8, 2014
c8ec72d
Factored out the request checking in add_view for TextPlugin
ojii Oct 13, 2014
256b691
frontend integration
ojii Oct 28, 2014
51d9424
tabs, not spaces
ojii Oct 28, 2014
8d7baac
removed comment
ojii Oct 28, 2014
aceeb6a
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Oct 29, 2014
1fabd32
fixed tests
ojii Oct 29, 2014
245f7e7
added selenium test for adding plugins via frontend
ojii Oct 30, 2014
f27dc81
removed method added that I never ended up using
ojii Oct 30, 2014
1e243c5
fixed two more failing tests
ojii Oct 30, 2014
5dafda3
added documentation
ojii Oct 30, 2014
c9e070f
minor edits to text
evildmp Oct 30, 2014
2b9984f
emphasize that the changes to add_plugin don't affect documented APIs
ojii Oct 30, 2014
b04df26
fixed tests on Django 1.7
ojii Oct 31, 2014
cd6833d
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Oct 31, 2014
0c62820
fixed security tests not working in ancient python versions
ojii Oct 31, 2014
412b2e8
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Nov 5, 2014
e7e0f10
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Nov 10, 2014
54275a2
Fixed some comments, drastically speed up CMSLiveTest._login, added test
ojii Dec 3, 2014
5106172
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Dec 3, 2014
b482760
save position on plugin addition in CMSPluginBase.save_model
ojii Dec 8, 2014
0a82783
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Dec 10, 2014
64136f5
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Dec 16, 2014
31a5b71
switched to new selenium apis
ojii Dec 17, 2014
c50c152
fixed broken tests
ojii Dec 17, 2014
2451896
switch to develop branch of djangocms-text-ckeditor
ojii Dec 27, 2014
ae20952
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Dec 27, 2014
820a51a
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Jan 15, 2015
25976c0
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Jan 15, 2015
5c6efbf
Fixed MTIPluginsTestCase.test_add_edit_plugin using old add-plugin API
ojii Jan 15, 2015
2de2289
switched travis account
ojii Jan 26, 2015
cd9f83f
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Jan 26, 2015
18fca52
retrying travis encrypt
ojii Jan 26, 2015
d1b4cd5
no saucelabs
ojii Jan 27, 2015
6fbaf4c
Merge remote-tracking branch 'origin/develop' into sane-add-plugin
ojii Jan 27, 2015
04d8e9c
fixed yml
ojii Jan 27, 2015
0551b2d
Revert "fixed yml"
ojii Jan 27, 2015
48b8342
Revert "no saucelabs"
ojii Jan 27, 2015
fd0c8a4
don't sauce if there's no sauce
ojii Jan 27, 2015
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ python:

sudo: false

addons:
sauce_connect: true

env:
global:
- SAUCE_USERNAME=digi604
- secure: NWiSBCHFB6LbTMget2qLIdqZlx0zeu3j+Y7Lsqb8kuYXyT2IUBGFVedcGWuGv/9Mzypb80EQWtVTokA3/3QIbesqr29uG95pMPHiYWLdnTO6UHcLMcNXiSzhBGdRDZ40iHSVv2dDHs4GNwGOH5+UCA0z3j7SWmChuFbNXh+Vsqw=
- secure: "dyJE69B+QOE1AMV59MSV1nSsmucJY52ziEdwLFWzLQE6v9bK83jihGGzrxnEM1FEpS6hD8y9/TjnFUArYRnPnOGOnA7HGetpHj6fUcJbSBYYYZy2E5264tRwy1wM2sL45aSB9Hc9fXdGt4SvPDqE+bZQbUIP6yxaRc8iGIanxTU="
- secure: "VjvD6t2JfpZAExS3MBCyT3mOfhVxieIVllJajxiPNtQ020aF8eeyaqa804GYY8MJ/lNK/uclpKNZUURpexgHrQ+9vUJ0BHuFlwsDzWSyrr+ShL+O2C7cE0hKoop1TIrZvdHjSzpb31tIGItL3Gk16cVKYwcAULrmHh15QEOo4eg="
matrix:
- DJANGO=1.6 DATABASE_URL='sqlite://localhost/:memory:' SELENIUM=0
- DJANGO=1.6 DATABASE_URL='mysql://root@127.0.0.1/djangocms_test' SELENIUM=0
Expand Down
81 changes: 24 additions & 57 deletions cms/admin/placeholderadmin.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
from django.template import RequestContext
from django.template.defaultfilters import force_escape, escapejs
from django.utils.translation import ugettext as _
from django.conf import settings
from django.views.decorators.http import require_POST
from django.template.response import TemplateResponse

Expand All @@ -29,7 +28,7 @@
from django.db import router
from django.http import HttpResponseRedirect

from cms.utils import copy_plugins, permissions, get_language_from_request
from cms.utils import copy_plugins, permissions
from cms.utils.i18n import get_language_list
from cms.utils.transaction import wrap_transaction

Expand Down Expand Up @@ -206,67 +205,35 @@ def post_clear_placeholder(self, request, placeholder):
def get_placeholder_template(self, request, placeholder):
pass

@method_decorator(require_POST)
@xframe_options_sameorigin
def add_plugin(self, request):
"""
POST request should have the following data:
Shows the add plugin form and saves it on POST.

Requires the following GET parameters:

- placeholder_id
- plugin_type
- plugin_language
- plugin_parent (optional)
- placeholder_id
- plugin_type
- plugin_language
- plugin_parent (optional)
- plugin_position (optional)
"""
parent = None
plugin_type = request.POST['plugin_type']
placeholder_id = request.POST.get('placeholder_id', None)
placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
parent_id = request.POST.get('plugin_parent', None)
language = request.POST.get('plugin_language') or get_language_from_request(request)
if not self.has_add_plugin_permission(request, placeholder, plugin_type):
return HttpResponseForbidden(force_unicode(_('You do not have permission to add a plugin')))
for required in ['placeholder_id', 'plugin_type', 'plugin_language']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

placeholder_id may not be present (when inserting plugins in a text plugin)

if required not in request.GET:
return HttpResponseBadRequest(force_unicode(
_("Invalid request, missing '%s' parameter") % required
))
plugin_type = request.GET['plugin_type']
try:
has_reached_plugin_limit(placeholder, plugin_type, language,
template=self.get_placeholder_template(request, placeholder))
except PluginLimitReached as er:
return HttpResponseBadRequest(er)
# page add-plugin
if not parent_id:
position = request.POST.get('plugin_order',
CMSPlugin.objects.filter(language=language, placeholder=placeholder).count())
# in-plugin add-plugin
else:
parent = get_object_or_404(CMSPlugin, pk=parent_id)
placeholder = parent.placeholder
position = request.POST.get('plugin_order',
CMSPlugin.objects.filter(language=language, parent=parent).count())
# placeholder (non-page) add-plugin

# Sanity check to make sure we're not getting bogus values from JavaScript:
if settings.USE_I18N:
if not language or not language in [lang[0] for lang in settings.LANGUAGES]:
return HttpResponseBadRequest(force_unicode(_("Language must be set to a supported language!")))
if parent and parent.language != language:
return HttpResponseBadRequest(force_unicode(_("Parent plugin language must be same as language!")))
else:
language = settings.LANGUAGE_CODE
plugin = CMSPlugin(language=language, plugin_type=plugin_type, position=position, placeholder=placeholder)

if parent:
plugin.position = CMSPlugin.objects.filter(parent=parent).count()
plugin.parent_id = parent.pk
plugin.save()
self.post_add_plugin(request, placeholder, plugin)
response = {
'url': force_unicode(
admin_reverse("%s_%s_edit_plugin" % (self.model._meta.app_label, self.model._meta.model_name),
args=[plugin.pk])),
'delete': force_unicode(
admin_reverse("%s_%s_delete_plugin" % (self.model._meta.app_label, self.model._meta.model_name),
args=[plugin.pk])),
'breadcrumb': plugin.get_breadcrumb(),
}
return HttpResponse(json.dumps(response), content_type='application/json')
plugin_class = plugin_pool.get_plugin(plugin_type)
except KeyError:
return HttpResponseBadRequest(force_unicode(
_("Invalid plugin type '%s'") % plugin_type
))

plugin_admin = plugin_class(admin_site=self.admin_site)

return plugin_admin.add_view(request)

@method_decorator(require_POST)
@xframe_options_sameorigin
Expand Down
143 changes: 126 additions & 17 deletions cms/plugin_base.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
# -*- coding: utf-8 -*-
import json
import re
import warnings

from django.http import HttpResponse
from django.http import HttpResponseBadRequest
from django.http import HttpResponseForbidden
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
from django.forms.models import ModelForm
from django.utils.encoding import smart_str
from django.utils.translation import ugettext_lazy as _

try:
from django.contrib.admin.options import (RenameBaseModelAdminMethods as
ModelAdminMetaClass)
except ImportError:
from django.forms.widgets import (MediaDefiningClass as ModelAdminMetaClass)
import re

from cms.constants import PLUGIN_MOVE_ACTION, PLUGIN_COPY_ACTION
from cms.utils import get_cms_setting
from cms.utils import get_cms_setting, get_language_list
from cms.utils.compat.metaclasses import with_metaclass
from cms.utils.placeholder import get_placeholder_conf
from cms.utils.urlutils import admin_reverse
from cms.utils.compat.dj import force_unicode, python_2_unicode_compatible
from cms.exceptions import SubClassNeededError, Deprecated
from cms.models import CMSPlugin
from django.core.urlresolvers import reverse
from django.contrib import admin
from django.core.exceptions import ImproperlyConfigured
from django.forms.models import ModelForm
from django.utils.encoding import smart_str
from django.utils.translation import ugettext_lazy as _
from cms.exceptions import SubClassNeededError, Deprecated, PluginLimitReached
from cms.models import CMSPlugin, Placeholder


class CMSPluginBaseMetaclass(ModelAdminMetaClass):
Expand Down Expand Up @@ -186,15 +193,107 @@ def render_change_form(self, request, context, add=False, change=False, form_url

return super(CMSPluginBase, self).render_change_form(request, context, add, change, form_url, obj)

def has_add_permission(self, request, *args, **kwargs):
"""Permission handling change - if user is allowed to change the page
he must be also allowed to add/change/delete plugins..
def has_add_permission(self, request):
"""
By default requires the user to have permission to add the plugin
instance and add permission of the object to which the plugin is
added (eg a page).
"""
if 'placeholder_id' not in request.GET:
return False
if not super(CMSPluginBase, self).has_add_permission(request):
return False
placeholder = Placeholder.objects.get(pk=request.GET['placeholder_id'])
return placeholder.has_add_permission(request)

def has_change_permission(self, request, obj=None):
"""
By default requires the user to have permission to change the plugin
instance and the object, to which the plugin is attached (eg a page).
"""
if obj:
return obj.has_change_permission(request)
else:
return self.has_add_permission(request)
has_delete_permission = has_change_permission

def get_form(self, request, obj=None, **kwargs):
form_class = super(CMSPluginBase, self).get_form(request, obj, **kwargs)

if obj:
return form_class

plugin_type = self.__class__.__name__

class Form(form_class):
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
self.instance.language = request.GET['plugin_language']
self.instance.placeholder_id = request.GET['placeholder_id']
self.instance.parent_id = request.GET.get(
'plugin_parent', None
)
self.instance.plugin_type = plugin_type

return Form

def add_view_check_request(self, request):
from cms.utils.plugins import has_reached_plugin_limit
plugin_type = self.__class__.__name__

placeholder = get_object_or_404(
Placeholder, pk=request.GET['placeholder_id']
)

Not sure if there will be plugin permission requirement in future, but
if, then this must be changed.
language = request.GET['plugin_language']
if language not in get_language_list():
return HttpResponseBadRequest(force_unicode(
_("Language must be set to a supported language!")
))

if request.GET.get('plugin_parent', None):
get_object_or_404(
CMSPlugin, pk=request.GET['plugin_parent']
)

if not self.has_add_permission(request):
return HttpResponseForbidden(force_unicode(
_('You do not have permission to add a plugin')
))

if placeholder.page:
template = placeholder.page.get_template()
else:
template = None
try:
has_reached_plugin_limit(
placeholder,
plugin_type,
language,
template=template
)
except PluginLimitReached as er:
return HttpResponseBadRequest(er)

return True

def add_view(self, request, form_url='', extra_context=None):
result = self.add_view_check_request(request)

if isinstance(result, HttpResponse):
return result

return super(CMSPluginBase, self).add_view(
request, form_url, extra_context
)

def response_post_save_add(self, request, obj):
"""
Always redirect to index. Usually CMS Plugins aren't registred with
an admin site directly and the add_view is accessed via frontend
editing.
"""
return self.cms_plugin_instance.has_change_permission(request)
has_delete_permission = has_change_permission = has_add_permission
return HttpResponseRedirect(admin_reverse('index'))

def save_model(self, request, obj, form, change):
"""
Expand All @@ -213,6 +312,16 @@ def save_model(self, request, obj, form, change):
# subclassing cms_plugin_instance (one to one relation)
value = getattr(self.cms_plugin_instance, field.name)
setattr(obj, field.name, value)
# When adding an object, it won't have a position
if not obj.position:
if obj.parent_id:
obj.position = CMSPlugin.objects.filter(
parent_id=obj.parent_id
).count()
else:
obj.position = CMSPlugin.objects.filter(
placeholder_id=obj.placeholder_id
).count()

# remember the saved object
self.saved_object = obj
Expand Down
37 changes: 12 additions & 25 deletions cms/static/cms/js/modules/cms.plugins.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,34 +221,21 @@ $(document).ready(function () {
// public methods
addPlugin: function (type, name, parent) {
// cancel request if already in progress
if(CMS.API.locked) return false;
CMS.API.locked = true;

var that = this;
var data = {
var params = {
'placeholder_id': this.options.placeholder_id,
'plugin_type': type,
'plugin_parent': parent || '',
'plugin_language': this.options.plugin_language,
'csrfmiddlewaretoken': this.csrf
'plugin_language': this.options.plugin_language
};

$.ajax({
'type': 'POST',
'url': this.options.urls.add_plugin,
'data': data,
'success': function (data) {
CMS.API.locked = false;
that.newPlugin = data;
that.editPlugin(data.url, name, data.breadcrumb);
},
'error': function (jqXHR) {
CMS.API.locked = false;
var msg = CMS.config.lang.error;
// trigger error
that._showError(msg + jqXHR.responseText || jqXHR.status + ' ' + jqXHR.statusText);
}
if (parent){
params['plugin_parent'] = parent;
}
var url = this.options.urls.add_plugin + '?' + CMS.$.param(params);
var modal = new CMS.Modal({
'newPlugin': this.newPlugin || false,
'onClose': this.options.onClose || false,
'redirectOnClose': this.options.redirectOnClose || false
});
modal.open(url, name);
},

editPlugin: function (url, name, breadcrumb) {
Expand Down Expand Up @@ -796,4 +783,4 @@ $(document).ready(function () {
});

});
})(CMS.$);
})(CMS.$);
Loading