Permalink
Browse files

Merge branch 'develop' of github.com:divio/django-cms into feature/se…

…kizai

Conflicts:
	cms/tests/__init__.py
	cms/tests/plugins.py
	setup.py
  • Loading branch information...
2 parents 0ea5d84 + 03a3322 commit b246699b3c24a956284654f0b017191518c2d6e3 Jonas Obrist committed Feb 23, 2011
Showing with 2,116 additions and 5,113 deletions.
  1. +1 −0 AUTHORS
  2. +8 −1 CHANGELOG.txt
  3. +1 −1 cms/__init__.py
  4. +14 −5 cms/admin/pageadmin.py
  5. +100 −37 cms/admin/placeholderadmin.py
  6. +36 −119 cms/locale/ar/LC_MESSAGES/django.po
  7. +2 −10 cms/locale/ar/LC_MESSAGES/djangojs.po
  8. +34 −196 cms/locale/bg/LC_MESSAGES/django.po
  9. +6 −20 cms/locale/bg/LC_MESSAGES/djangojs.po
  10. +28 −83 cms/locale/bn/LC_MESSAGES/django.po
  11. +3 −11 cms/locale/bn/LC_MESSAGES/djangojs.po
  12. +51 −146 cms/locale/ca/LC_MESSAGES/django.po
  13. +5 −11 cms/locale/ca/LC_MESSAGES/djangojs.po
  14. +36 −112 cms/locale/cs/LC_MESSAGES/django.po
  15. +3 −7 cms/locale/cs/LC_MESSAGES/djangojs.po
  16. +28 −83 cms/locale/cy/LC_MESSAGES/django.po
  17. +3 −11 cms/locale/cy/LC_MESSAGES/djangojs.po
  18. +28 −83 cms/locale/da/LC_MESSAGES/django.po
  19. +3 −11 cms/locale/da/LC_MESSAGES/djangojs.po
  20. +59 −190 cms/locale/de/LC_MESSAGES/django.po
  21. +8 −26 cms/locale/de/LC_MESSAGES/djangojs.po
  22. +27 −111 cms/locale/el/LC_MESSAGES/django.po
  23. +7 −20 cms/locale/el/LC_MESSAGES/djangojs.po
  24. +52 −222 cms/locale/es/LC_MESSAGES/django.po
  25. +4 −18 cms/locale/es/LC_MESSAGES/djangojs.po
  26. +46 −122 cms/locale/es_AR/LC_MESSAGES/django.po
  27. +3 −11 cms/locale/es_AR/LC_MESSAGES/djangojs.po
  28. +33 −93 cms/locale/et/LC_MESSAGES/django.po
  29. +3 −11 cms/locale/et/LC_MESSAGES/djangojs.po
  30. +28 −83 cms/locale/eu/LC_MESSAGES/django.po
  31. +3 −11 cms/locale/eu/LC_MESSAGES/djangojs.po
  32. +44 −128 cms/locale/fa/LC_MESSAGES/django.po
  33. +3 −7 cms/locale/fa/LC_MESSAGES/djangojs.po
  34. +52 −135 cms/locale/fi/LC_MESSAGES/django.po
  35. +4 −14 cms/locale/fi/LC_MESSAGES/djangojs.po
  36. +54 −172 cms/locale/fr/LC_MESSAGES/django.po
  37. +6 −15 cms/locale/fr/LC_MESSAGES/djangojs.po
  38. +28 −83 cms/locale/gu/LC_MESSAGES/django.po
  39. +2 −10 cms/locale/gu/LC_MESSAGES/djangojs.po
  40. +28 −83 cms/locale/he/LC_MESSAGES/django.po
  41. +3 −11 cms/locale/he/LC_MESSAGES/djangojs.po
  42. +49 −128 cms/locale/hi/LC_MESSAGES/django.po
  43. +7 −11 cms/locale/hi/LC_MESSAGES/djangojs.po
  44. +28 −83 cms/locale/hu/LC_MESSAGES/django.po
  45. +3 −11 cms/locale/hu/LC_MESSAGES/djangojs.po
  46. +64 −212 cms/locale/it/LC_MESSAGES/django.po
  47. +6 −20 cms/locale/it/LC_MESSAGES/djangojs.po
  48. +25 −96 cms/locale/ja/LC_MESSAGES/django.po
  49. +3 −7 cms/locale/ja/LC_MESSAGES/djangojs.po
  50. +73 −188 cms/locale/nl/LC_MESSAGES/django.po
  51. +5 −19 cms/locale/nl/LC_MESSAGES/djangojs.po
  52. +51 −141 cms/locale/no/LC_MESSAGES/django.po
  53. +3 −8 cms/locale/no/LC_MESSAGES/djangojs.po
  54. +52 −151 cms/locale/pl/LC_MESSAGES/django.po
  55. +3 −13 cms/locale/pl/LC_MESSAGES/djangojs.po
  56. +61 −167 cms/locale/pt/LC_MESSAGES/django.po
  57. +3 −17 cms/locale/pt/LC_MESSAGES/djangojs.po
  58. +58 −168 cms/locale/pt_BR/LC_MESSAGES/django.po
  59. +7 −11 cms/locale/pt_BR/LC_MESSAGES/djangojs.po
  60. +58 −273 cms/locale/ru/LC_MESSAGES/django.po
  61. +7 −15 cms/locale/ru/LC_MESSAGES/djangojs.po
  62. +50 −140 cms/locale/sk/LC_MESSAGES/django.po
  63. +6 −19 cms/locale/sk/LC_MESSAGES/djangojs.po
  64. +38 −195 cms/locale/sv/LC_MESSAGES/django.po
  65. +3 −8 cms/locale/sv/LC_MESSAGES/djangojs.po
  66. +46 −113 cms/locale/tr/LC_MESSAGES/django.po
  67. +3 −17 cms/locale/tr/LC_MESSAGES/djangojs.po
  68. +32 −211 cms/locale/zh_CN/LC_MESSAGES/django.po
  69. +3 −16 cms/locale/zh_CN/LC_MESSAGES/djangojs.po
  70. +27 −82 cms/locale/zh_TW/LC_MESSAGES/django.po
  71. +3 −11 cms/locale/zh_TW/LC_MESSAGES/djangojs.po
  72. +5 −6 cms/models/__init__.py
  73. +48 −6 cms/models/placeholdermodel.py
  74. +9 −0 cms/test_utils/testcases.py
  75. +16 −12 cms/test_utils/util/context_managers.py
  76. +6 −3 cms/tests/__init__.py
  77. +25 −0 cms/tests/menu.py
  78. +13 −0 cms/tests/multilingual.py
  79. +66 −0 cms/tests/placeholder.py
  80. +18 −10 cms/tests/plugins.py
  81. +58 −0 cms/tests/po.py
  82. +175 −0 cms/tests/security.py
  83. +4 −0 docs/extending_cms/app_integration.rst
  84. +8 −1 menus/templatetags/menu_tags.py
  85. +0 −1 setup.py
  86. +1 −0 tests/project/fixtures/menus-sub.json
View
@@ -57,6 +57,7 @@ Contributors (in alphabetical order):
* Gerard Świderski
* homebrew79
* hysia
+* Iacopo Spalletti
* Ian Lewis
* indexofire
* Ionel Cristian Maries
View
@@ -43,4 +43,11 @@
==== 2.1.2 (2011-02-16) ====
- Fixed issues with the CSRF fix from 2.1.1.
-- Updated translation files from transifex.
+- Updated translation files from transifex.
+
+==== 2.1.3 (2011-02-22) ====
+
+- Fixed a serious security issue in PlaceholderAdmin
+- Fixed bug with submenus showing pages that are not 'in_navigation' (#716, thanks to Iacopo Spalletti for the patch)
+- Fixed PlaceholderField not respecting limits in CMS_PLACEHOLDER_CONF (thanks to Ben Hockey for reporting this)
+- Fixed the double-monkeypatch check for url reversing (thanks to Benjamin Wohlwend for the patch)
View
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
-VERSION = (2, 1, 2, 'final')
+VERSION = (2, 1, 3, 'final')
if VERSION[-1] != "final": # pragma: no cover
__version__ = '.'.join(map(str, VERSION))
else: # pragma: no cover
@@ -1081,6 +1081,9 @@ def change_innavigation(self, request, page_id):
@create_on_success
def add_plugin(self, request):
+ '''
+ Could be either a page or a parent - if it's a parent we get the page via parent.
+ '''
if 'history' in request.path or 'recover' in request.path:
return HttpResponse(str("error"))
if request.method == "POST":
@@ -1113,14 +1116,20 @@ def add_plugin(self, request):
parent = get_object_or_404(CMSPlugin, pk=parent_id)
placeholder = parent.placeholder
page = get_page_from_placeholder_if_exists(placeholder)
+ if not page: # Make sure we do have a page
+ raise Http404
language = parent.language
position = None
# placeholder (non-page) add-plugin
else:
- position = None
- language = request.POST['language'] or get_language_from_request(request)
- if page and not page.has_change_permission(request):
- return HttpResponseForbidden(unicode(_("You do not have permission to change this page")))
+ # do NOT allow non-page placeholders to use this method, they
+ # should use their respective admin!
+ raise Http404
+
+ if not page.has_change_permission(request):
+ # we raise a 404 instead of 403 for a slightly improved security
+ # and to be consistent with placeholder admin
+ raise Http404
# Sanity check to make sure we're not getting bogus values from JavaScript:
if not language or not language in [ l[0] for l in settings.LANGUAGES ]:
@@ -1180,7 +1189,7 @@ def edit_plugin(self, request, plugin_id):
page = get_page_from_placeholder_if_exists(cms_plugin.placeholder)
instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
if page and not page.has_change_permission(request):
- raise PermissionDenied
+ raise Http404
else:
# history view with reversion
from reversion.models import Version
@@ -122,29 +122,66 @@ def get_urls(self):
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']
placeholder_id = request.POST.get('placeholder', None)
position = None
language = get_language_from_request(request)
- if not placeholder_id:
+ 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 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)
- plugin = CMSPlugin(language=language, plugin_type=plugin_type,
- position=position, parent=parent, placeholder=parent.placeholder)
- else:
- placeholder = get_object_or_404(Placeholder, pk=placeholder_id)
- plugin = CMSPlugin(language=language, plugin_type=plugin_type,
- position=position, placeholder=placeholder)
+ placeholder = parent.placeholder
+
+ # check add permissions on placeholder
+ if not placeholder.has_add_permission(request):
+ raise Http404
+
+ # 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
+ ).count()
+ 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)
plugin.save()
+
+ # returns it's ID as response
return HttpResponse(str(plugin.pk))
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)
+
+ # check that the user has permission to change this plugin
+ if not cms_plugin.placeholder.has_change_permission(request):
+ raise Http404
+
instance, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
plugin_admin.cms_plugin_instance = cms_plugin
@@ -185,46 +222,72 @@ def edit_plugin(self, request, plugin_id):
return response
def move_plugin(self, request):
- if request.method == "POST":
- pos = 0
- if 'ids' in request.POST:
- for id in request.POST['ids'].split("_"):
- plugin = CMSPlugin.objects.get(pk=id)
- if plugin.position != pos:
- plugin.position = pos
- plugin.save()
- pos += 1
- elif 'plugin_id' in request.POST:
- plugin = CMSPlugin.objects.get(pk=int(request.POST['plugin_id']))
- placeholder = plugin.placeholder
- # plugin positions are 0 based, so just using count here should give us 'last_position + 1'
- position = CMSPlugin.objects.filter(placeholder=placeholder).count()
- plugin.position = position
- plugin.save()
- else:
- HttpResponse(str("error"))
- return HttpResponse(str("ok"))
- else:
+ # only allow POST
+ if request.method != "POST":
return HttpResponse(str("error"))
+ pos = 0
+ if 'ids' in request.POST: # multiple plugins
+ 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 plugin.placeholder.pk not in whitelisted_placeholders:
+ if plugin.placeholder.has_change_permission(request):
+ whitelisted_placeholders.append(plugin.placeholder.pk)
+ else:
+ raise Http404
+
+ # actually do the moving
+ if plugin.position != pos:
+ plugin.position = pos
+ plugin.save()
+ pos += 1
+ elif 'plugin_id' in request.POST: # single plugin moving
+ plugin = CMSPlugin.objects.get(pk=int(request.POST['plugin_id']))
- def remove_plugin(self, request):
- if request.method == "POST":
- plugin_id = request.POST['plugin_id']
- plugin = get_object_or_404(CMSPlugin, pk=plugin_id)
+ # check permissions
+ if not plugin.placeholder.has_change_permission(request):
+ raise Http404
+
placeholder = plugin.placeholder
- plugin.delete_with_public()
- 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))
- 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.position = position
+ plugin.save()
+ else:
+ HttpResponse(str("error"))
+ 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):
+ raise Http404
+
+ plugin.delete_with_public()
+ 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(
target_placeholder=placeholder,
source_language=request.POST['copy_from'],
Oops, something went wrong.

0 comments on commit b246699

Please sign in to comment.