Skip to content
Browse files

Merged divio/master

  • Loading branch information...
2 parents b36a7c8 + 3fff03a commit 4ce53398d9ef374af29c4e2e5d2f3d80332d551d @CharString committed May 10, 2011
Showing with 4,271 additions and 6,606 deletions.
  1. +10 −0 .gitignore
  2. +156 −12 AUTHORS
  3. +18 −2 CHANGELOG.txt
  4. +2 −2 MANIFEST.in
  5. +0 −66 README.md
  6. +61 −0 README.rst
  7. +5 −4 cms/__init__.py
  8. +1 −0 cms/admin/__init__.py
  9. +19 −10 cms/admin/change_list.py
  10. +20 −9 cms/admin/dialog/forms.py
  11. +21 −6 cms/admin/dialog/views.py
  12. +61 −54 cms/admin/forms.py
  13. +1 −0 cms/admin/models.py
  14. +149 −97 cms/admin/pageadmin.py
  15. +2 −1 cms/admin/permissionadmin.py
  16. +126 −56 cms/admin/placeholderadmin.py
  17. +1 −0 cms/admin/useradmin.py
  18. +20 −22 cms/admin/views.py
  19. +1 −1 cms/app_base.py
  20. +16 −3 cms/apphook_pool.py
  21. +32 −20 cms/appresolver.py
  22. +1 −0 cms/cache/permissions.py
  23. +1 −0 cms/conf/__init__.py
  24. +1 −0 cms/conf/global_settings.py
  25. +7 −0 cms/conf/patch.py
  26. +1 −0 cms/context_processors.py
  27. +1 −0 cms/dbgettext_registration.py
  28. +0 −364 cms/docs/app_integration.txt
  29. +0 −447 cms/docs/configuration.txt
  30. +0 −90 cms/docs/contribution.txt
  31. +0 −224 cms/docs/custom_plugins.txt
  32. +0 −79 cms/docs/i18n.txt
  33. +0 −34 cms/docs/index.txt
  34. +0 −212 cms/docs/installation.txt
  35. +0 −172 cms/docs/navigation.txt
  36. +0 −45 cms/docs/placeholders.txt
  37. +0 −120 cms/docs/plugin_reference.txt
  38. +0 −215 cms/docs/plugins.txt
  39. +0 −41 cms/docs/sitemap.txt
  40. +0 −200 cms/docs/tutorial.txt
  41. +1 −0 cms/exceptions.py
  42. +2 −3 cms/forms/fields.py
  43. +1 −0 cms/forms/utils.py
  44. +11 −9 cms/forms/widgets.py
  45. BIN cms/locale/ar/LC_MESSAGES/django.mo
  46. +433 −468 cms/locale/ar/LC_MESSAGES/django.po
  47. BIN cms/locale/ar/LC_MESSAGES/djangojs.mo
  48. +16 −17 cms/locale/ar/LC_MESSAGES/djangojs.po
  49. BIN cms/locale/bg/LC_MESSAGES/django.mo
  50. +457 −569 cms/locale/bg/LC_MESSAGES/django.po
  51. BIN cms/locale/bg/LC_MESSAGES/djangojs.mo
  52. +20 −15 cms/locale/bg/LC_MESSAGES/djangojs.po
  53. BIN cms/locale/bn/LC_MESSAGES/django.mo
  54. +397 −422 cms/locale/bn/LC_MESSAGES/django.po
  55. BIN cms/locale/bn/LC_MESSAGES/djangojs.mo
  56. +14 −13 cms/locale/bn/LC_MESSAGES/djangojs.po
  57. BIN cms/locale/ca/LC_MESSAGES/django.mo
  58. +436 −489 cms/locale/ca/LC_MESSAGES/django.po
  59. BIN cms/locale/ca/LC_MESSAGES/djangojs.mo
  60. +23 −13 cms/locale/ca/LC_MESSAGES/djangojs.po
  61. BIN cms/locale/cs/LC_MESSAGES/django.mo
  62. +415 −450 cms/locale/cs/LC_MESSAGES/django.po
  63. BIN cms/locale/cs/LC_MESSAGES/djangojs.mo
  64. +17 −11 cms/locale/cs/LC_MESSAGES/djangojs.po
  65. BIN cms/locale/cy/LC_MESSAGES/django.mo
  66. +399 −422 cms/locale/cy/LC_MESSAGES/django.po
  67. BIN cms/locale/cy/LC_MESSAGES/djangojs.mo
  68. +14 −13 cms/locale/cy/LC_MESSAGES/djangojs.po
  69. BIN cms/locale/da/LC_MESSAGES/django.mo
  70. +397 −422 cms/locale/da/LC_MESSAGES/django.po
  71. BIN cms/locale/da/LC_MESSAGES/djangojs.mo
  72. +14 −13 cms/locale/da/LC_MESSAGES/djangojs.po
  73. BIN cms/locale/de/LC_MESSAGES/django.mo
  74. +448 −631 cms/locale/de/LC_MESSAGES/django.po
  75. BIN cms/locale/de/LC_MESSAGES/djangojs.mo
  76. +22 −18 cms/locale/de/LC_MESSAGES/djangojs.po
  77. BIN cms/locale/el/LC_MESSAGES/django.mo
Sorry, we could not display the entire diff because too many files (498) changed.
View
10 .gitignore
@@ -6,11 +6,14 @@ cms/django
*.DS_Store
*.svn
.*
+*.xml
+cms.sqlite
cms/media/cms_page_media/
cms/docs/build
example/run
example/local_settings.py
reversion/
+htmlcov
build
dist
.ropeproject
@@ -24,3 +27,10 @@ eggs
parts
bin
/dist
+*.rst~
+/docs/*.html
+distribute-*.tar.gz
+include/
+lib/
+man/
+share/
View
168 AUTHORS
@@ -1,17 +1,161 @@
-Django CMS
-==========
+Current or previous core committers:
+
+* Chris Glass
+* Eric Robitaille
+* Jonas Obrist
* Patrick Lauber
-* Jason Zylks
-* Simon Meers
-* Peter Cicman
-* limpbrains :P
+* Peter Ciciman
+* Stefan Foulis
+* Øyvind Saltvik
-Django Page CMS
-===============
-* Batiste Bieler
-* Jannis Leidel
+Contributors (in alphabetical order):
+
+* A. Bram Neijt
+* aaloy
+* Aaron Renner
+* aball
+* Adi Sieker
+* Alberto Paro
+* Alessandro Ronchi
+* Angelo Dini
+* angular_circle
* Antoni Aloy López
+* Arne Gellhaus
+* Artem Skoretskiy
+* Arthur Debert
+* Batiste Bieler
+* behrooz
+* Behrooz Nobakht
* Benjamin Wohlwend
-* poweredbypenguins
+* Bernd Zeimetz
+* beshrkayali
+* Bob Karreman
+* brightwhitefox
+* Chanita Siridechkun
+* Charpentier Johan
+* Cheng-Chia Tseng
+* Chris Adams
+* Chris Hughes
+* Christof Hagedorn
+* daniele
+* DaNmarner
+* Darryl Woods
+* David Jean Louis
+* David Thompson
+* Diógenes Augusto Fernandes Hermínio
+* dstufft
+* DZ
+* eged
+* Egor V. Nazarkin
+* Ekrem Seren
+* Evandro Miquelito
+* f4nt
+* fcurella
+* Filip Kazimierczak
+* GaretJax
+* George Marshall
+* Gerard Świderski
* homebrew79
-
+* hysia
+* Iacopo Spalletti
+* Ian Lewis
+* indexofire
+* Ionel Maries Cristian
+* Ivan Vershigora
+* izi
+* Jameel Al-Aziz
+* James Richards
+* Jannis Leidel
+* Janusz Harkot
+* Jason Davies
+* Jason Jenkins
+* Jason Zylks
+* Javi Nievas
+* Johannes Bornhold
+* John-Scott Atlakson
+* Jonathan Stoppani
+* jordanjambazov
+* kar1m
+* kochin
+* Krzysztof Bandurski
+* kunitoki
+* Lars Smit
+* Lars van de Kerkhof
+* limpbrains :P
+* Lucas Vogelsang
+* Lucio Asnaghi
+* Luke Plant
+* m000
+* Maik Lustenberger
+* Manolis Stamatogiannakis
+* Manuel Schmidt
+* Marco Rimoldi
+* Mark Rogers
+* Martin Bommeli
+* Martin Kosír
+* martinkosir
+* mathijs
+* Maxim Bodyansky
+* Maxime Haineault
+* mbouchar
+* mcosta
+* meers
+* MerLex
+* Mokys
+* mrlundis
+* MW
+* neoprolog
+* Orne Brocaar
+* padelt
+* Patrick Toal
+* Paul van der Linden
+* pbgc
+* Pedro Gracia
+* Pete Loggie
+* Peter-Paul van Gemerden
+* Petteri
+* Philipp Bosch
+* philomat
+* phuihock
+* Pigletto
+* pigletto
+* Piotr Kilczuk
+* poweredbypenguins
+* Remco Wendt
+* Restless Being
+* Robert Pogorzelski
+* Rodolfo Carvalho
+* rtpm
+* Samuel Lüscher
+* sealibora
+* Sean Bleier
+* Seyhun Akyurek
+* Shatalov Vadim
+* shed
+* Shinya Okano
+* Simon Hedberg
+* Simon Meers
+* sleytr
+* spookylukey
+* ssteinerX
+* Stavros Korokithakis
+* Steve R. Jones
+* Steve Steiner
+* Tanel Külaots
+* Thomas Parslow
+* timesong
+* Tino de Bruijn
+* tiret
+* Ulrich Petri
+* wangJunjie
+* Wayne Moore
+* wid
+* wildermesser
+* Yann Malet
+* Yann Malet
+* yedpodtrzitko
+* yohanboniface
+* Yosuke Ikeda
+* Yuri van der Meer
+* zundoya
+* Антон Евжаков
View
20 CHANGELOG.txt
@@ -12,7 +12,7 @@
- testsuite working again
- changelog file added
-==== 2.1.0 (2010-XX-XX) ====
+==== 2.1.0 (2011-01-26) ====
- language namespaces for apphooks (reverse("de:myview"), reverse("en:myview"))
- video plugin switch to http://github.com/FlashJunior/OSFlashVideoPlayer
@@ -33,5 +33,21 @@
- a lot of bugfixes
- the cms now depends on the cms.middleware.media.PlaceholderMediaMiddleware middleware
- templatetags refactored: see cms/docs/templatetags.txt for new signatures.
-- placeholder has new option: or and a endbpalceholder templatetag
+- placeholder has new option: or and a endpalceholder templatetag
+==== 2.1.1 (2011-02-09) ====
+
+- Fixed CMS AJAX requests not being CSRF protected, thus not working in Django 1.2.5
+- Fixed toolbar CSS issues in Chrome/Firefox
+
+==== 2.1.2 (2011-02-16) ====
+
+- Fixed issues with the CSRF fix from 2.1.1.
+- 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
4 MANIFEST.in
@@ -1,11 +1,11 @@
include AUTHORS
include LICENSE
-include README.md
+include README.rst
include CHANGELOG.txt
recursive-include cms/locale *
recursive-include cms/templates *
recursive-include cms/media *
-recursive-include cms/docs *.txt *.png
recursive-include cms/plugins *
recursive-include menus/templates *
recursive-include mptt/tests/fixtures *.json
+recursive-include docs *
View
66 README.md
@@ -1,66 +0,0 @@
-Django CMS 2.0
-==============
-
-A Django app for managing hierarchical pages of content in multiple languages, on different sites.
-
-Django CMS handles the navigation rendering for you in multiple languages with internationalization (i18n) slugs,
-and the navigation can be extended by your own models.
-
-Pages are rendered with a template that has placeholders which get filled via plugins.
-Plugins included at the moment include the following:
-
-* File
-* Flash
-* Google Map
-* Link
-* Picture
-* HTML Snippet
-* Teaser
-* Text
-* Video
-* Twitter
-
-
-Many more are in the works. Plugins are very easy to write and integrate with your own models.
-For a list of 3rd party plugins have a look [here](http://www.django-cms.org/en/extensions/).
-
-Tour & Screenshots
-------------------
-
-Can be found [here](http://www.django-cms.org/en/tour/).
-Some Sites done with django-cms can be found [here](http://www.django-cms.org/en/sites/)
-
-
-
-Documentation
--------------
-
-Can be found [here](http://www.django-cms.org/en/documentation/).
-
-Installation instructions can be found [here](http://www.django-cms.org/en/documentation/2.0/installation/).
-
-
-Sourcecode
-----------
-
-Can be found [here](http://github.com/digi604/django-cms-2.0/) on github.
-
-Help
-----
-
-There is a [google group mailinglist](http://groups.google.com/group/django-cms)
-You can also visit the project website at [django-cms.org](http://www.django-cms.org/)
-or #django-cms on freenet IRC for more info.
-
-For a feature comparison of all the CMS apps available for django see
-[CMSComparison](http://code.djangoproject.com/wiki/CMSAppsComparison).
-
-Kudos
------
-
-- This is a fork of django-page-cms.
-- Some icons are from [http://www.famfamfam.com](http://www.famfamfam.com/)
-- Video plugin uses [OSFlashVideoPlayer](http://github.com/FlashJunior/OSFlashVideoPlayer)
-- Includes [Wymeditor](http://www.wymeditor.org/)
-- Tree Component from [jstree.com](http://www.jstree.com/)
-
View
61 README.rst
@@ -0,0 +1,61 @@
+##########
+django CMS
+##########
+
+A Django application for managing hierarchical pages of content, possibly in
+multiple languages and/or on multiple sites.
+
+Django CMS handles the navigation rendering for you with clean, slug based URLs,
+and this navigation can be extended by custom Django applications
+
+You can define editable areas, called placeholders, in your templates which you
+can fill with CMS plugins, by default we ship the following plugins:
+
+* File
+* Flash
+* Google Map
+* Link
+* Picture
+* HTML Snippet
+* Teaser
+* Text
+* Video
+* Twitter
+
+However this is not the end of the list, there are many
+`3rd party plugins <http://www.djangopackages.com/grids/g/django-cms/>`_
+already available for you to use. Should you be unable to find a suitable plugin
+for you needs, writing your own is very simple.
+
+More information on `our website <http://www.django-cms.org>`_.
+
+*************
+Documentation
+*************
+
+Please head over to our `documentation <http://docs.django-cms.org/>`_ for all
+the details on how to install, extend and use the django CMS.
+
+************
+Getting Help
+************
+
+Please head over to our IRC channel, #django-cms, on irc.freenode.net or write
+to our `mailing list <https://groups.google.com/forum/#!forum/django-cms>`_.
+
+*******
+Credits
+*******
+
+* This is a fork of the
+ `django-page-cms <https://github.com/batiste/django-page-cms>`_.
+* Includes icons from `FamFamFam <http://www.famfamfam.com>`_.
+* Video plugin uses the
+ `OSFlashVideoPlayer <https://github.com/FlashJunior/OSFlashVideoPlayer/>`_.
+* Includes the `Wymeditor <http://www.wymeditor.org/>`_.
+* Python tree engine powered by
+ `django-mptt <https://github.com/django-mptt/django-mptt>`_.
+* Javascript tree in admin uses `jsTree <http://www.jstree.com>`_.
+* Many thanks to the
+ `over 150 contributors <https://github.com/divio/django-cms/blob/master/AUTHORS>`_
+ to the django CMS!
View
9 cms/__init__.py
@@ -1,7 +1,8 @@
-VERSION = (2, 1, 0, 'beta3')
-if VERSION[-1] != "final":
+# -*- coding: utf-8 -*-
+VERSION = (2, 1, 3, 'final')
+if VERSION[-1] != "final": # pragma: no cover
__version__ = '.'.join(map(str, VERSION))
-else:
+else: # pragma: no cover
__version__ = '.'.join(map(str, VERSION[:-1]))
@@ -11,7 +12,7 @@
if 'cms' in settings.INSTALLED_APPS:
from conf import patch_settings
patch_settings()
-except ImportError:
+except ImportError: # pragma: no cover
"""
This exception means that either the application is being built, or is
otherwise installed improperly. Both make running patch_settings
View
1 cms/admin/__init__.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
import pageadmin
import useradmin
import permissionadmin
View
29 cms/admin/change_list.py
@@ -1,20 +1,26 @@
-from django.conf import settings
-from django.contrib.sites.models import Site
-from django.contrib.admin.views.main import ChangeList, ALL_VAR, IS_POPUP_VAR,\
- ORDER_TYPE_VAR, ORDER_VAR, SEARCH_VAR
-from cms.models import Title, PagePermission, Page, PageModerator
-from cms.utils import get_language_from_request
-from cms.utils.permissions import get_user_sites_queryset
+# -*- coding: utf-8 -*-
from cms.exceptions import NoHomeFound
-from cms.models.moderatormodels import MASK_PAGE, MASK_CHILDREN,\
+from cms.models import Title, Page, PageModerator
+from cms.models.moderatormodels import MASK_PAGE, MASK_CHILDREN, \
MASK_DESCENDANTS, PageModeratorState
+from cms.utils.permissions import get_user_sites_queryset
+from django.conf import settings
+from django.contrib.admin.views.main import ChangeList, ALL_VAR, IS_POPUP_VAR, \
+ ORDER_TYPE_VAR, ORDER_VAR, SEARCH_VAR
+from django.contrib.sites.models import Site
from menus.utils import find_children
COPY_VAR = "copy"
class CMSChangeList(ChangeList):
+ '''
+ Renders a Changelist - In our case it looks like a tree - it's the list of
+ *instances* in the Admin.
+ It is usually responsible for pagination (not here though, we have a
+ treeview)
+ '''
real_queryset = False
def __init__(self, request, *args, **kwargs):
@@ -66,15 +72,18 @@ def get_results(self, request):
self.full_result_count = self.root_query_set.count()
def set_items(self, request):
- lang = get_language_from_request(request)
site = self._current_site
+ # Get all the pages, ordered by tree ID (it's convenient to build the
+ # tree using a stack now)
pages = self.get_query_set(request).drafts().order_by('tree_id', 'parent', 'lft').select_related()
+ # Get lists of page IDs for which the current user has
+ # "permission to..." on the current site.
perm_edit_ids = Page.permissions.get_change_id_list(request.user, site)
perm_publish_ids = Page.permissions.get_publish_id_list(request.user, site)
perm_advanced_settings_ids = Page.permissions.get_advanced_settings_id_list(request.user, site)
perm_change_list_ids = Page.permissions.get_change_id_list(request.user, site)
-
+
if perm_edit_ids and perm_edit_ids != Page.permissions.GRANT_ALL:
pages = pages.filter(pk__in=perm_edit_ids)
#pages = pages.filter(pk__in=perm_change_list_ids)
View
29 cms/admin/dialog/forms.py
@@ -1,13 +1,24 @@
+# -*- coding: utf-8 -*-
from django import forms
from django.utils.translation import ugettext_lazy as _
-from django.conf import settings
-def get_copy_dialog_form(request):
- fields = {}
- if settings.CMS_PERMISSION:
- fields['copy_permissions'] = forms.BooleanField(label=_('Copy permissions'), required=False, initial=True)
+class PermissionForm(forms.Form):
+ '''
+ Holds the specific field for permissions
+ '''
+ copy_permissions = forms.BooleanField(label=_('Copy permissions'),
+ required=False, initial=True)
- if settings.CMS_MODERATOR:
- fields['copy_moderation'] = forms.BooleanField(label=_('Copy moderation'), required=False, initial=True)
- Form = type('CopyDialogForm', (forms.BaseForm,), { 'base_fields': fields })
- return Form
+class ModeratorForm(forms.Form):
+ '''
+ Holds the specific field for moderator
+ '''
+ copy_moderation = forms.BooleanField(label=_('Copy moderation'),
+ required=False, initial=True)
+
+class PermissionAndModeratorForm(PermissionForm, ModeratorForm):
+ '''
+ Subclass of both ModeratorForm AND PermissionForm, thus it inherits both
+ fields
+ '''
+ pass
View
0 cms/admin/dialog/utils.py
Sorry, we could not display the changes to this file because there were too many other changes to display.
View
27 cms/admin/dialog/views.py
@@ -1,9 +1,24 @@
-from cms.admin.dialog.forms import get_copy_dialog_form
-from django.shortcuts import render_to_response, get_object_or_404
+# -*- coding: utf-8 -*-
+from cms.admin.dialog.forms import PermissionAndModeratorForm, PermissionForm, ModeratorForm
+from cms.models import Page
+from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.http import Http404, HttpResponse
-from django.conf import settings
-from cms.models import Page
+from django.shortcuts import render_to_response, get_object_or_404
+
+def _form_class_selector():
+ '''
+ This replaces the magic that used to happen in forms, where a dynamic
+ class was generated at runtime. Now it's a bit cleaner...
+ '''
+ form_class = None
+ if settings.CMS_PERMISSION and settings.CMS_MODERATOR:
+ form_class = PermissionAndModeratorForm
+ elif settings.CMS_PERMISSION:
+ form_class = PermissionForm
+ elif settings.CMS_MODERATOR:
+ form_class = ModeratorForm
+ return form_class
@staff_member_required
def get_copy_dialog(request, page_id):
@@ -14,12 +29,12 @@ def get_copy_dialog(request, page_id):
target = get_object_or_404(Page, pk=request.REQUEST['target'])
if not page.has_change_permission(request) or \
- not target.has_add_permission(request):
+ not target.has_add_permission(request): # pragma: no cover
raise Http404
context = {
'dialog_id': 'dialog-copy',
- 'form': get_copy_dialog_form(request)(),
+ 'form': _form_class_selector()(), # class needs to be instanciated
'callback': request.REQUEST['callback'],
}
return render_to_response("admin/cms/page/dialog/copy.html", context)
View
115 cms/admin/forms.py
@@ -1,27 +1,51 @@
-from django.conf import settings
+# -*- coding: utf-8 -*-
+from cms.apphook_pool import apphook_pool
+from cms.forms.widgets import UserSelectAdminWidget
+from cms.models import Page, PagePermission, PageUser, ACCESS_PAGE, \
+ PageUserGroup
+from cms.utils.page import is_valid_page_slug
+from cms.utils.permissions import get_current_user, get_subordinate_users, \
+ get_subordinate_groups, mail_page_user_change
+from cms.utils.urlutils import any_path_re
from django import forms
-from django.template.defaultfilters import slugify
-from django.utils.translation import ugettext_lazy as _, get_language
-from django.forms.util import ErrorList
+from django.conf import settings
from django.contrib.auth.forms import UserCreationForm
-from django.contrib.auth.models import User, Permission, Group
+from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
-from django.db.models.fields import BooleanField
-
-
-from cms.models import Page, Title, PagePermission, PageUser, ACCESS_PAGE,\
- PageUserGroup
-from cms.utils.urlutils import any_path_re
-from cms.utils.permissions import get_current_user, get_subordinate_users,\
- get_subordinate_groups, mail_page_user_change
-from cms.forms.widgets import UserSelectAdminWidget
-from cms.utils.page import is_valid_page_slug
-from django.forms.widgets import HiddenInput
from django.contrib.sites.models import Site
from django.core.exceptions import ValidationError
+from django.db.models.fields import BooleanField
+from django.forms.util import ErrorList
+from django.forms.widgets import HiddenInput
+from django.template.defaultfilters import slugify
+from django.utils.translation import ugettext_lazy as _, get_language
from menus.menu_pool import menu_pool
-from django.utils.functional import lazy
-from cms.apphook_pool import apphook_pool
+
+def get_permission_acessor(obj):
+ if isinstance(obj, (PageUser, User,)):
+ rel_name = 'user_permissions'
+ else:
+ rel_name = 'permissions'
+ return getattr(obj, rel_name)
+
+def save_permissions(data, obj):
+ models = ((Page, 'page'), (PageUser, 'pageuser'), (PageUserGroup, 'pageuser'), (PagePermission, 'pagepermission'))
+
+ if not obj.pk:
+ # save obj, otherwise we can't assign permissions to him
+ obj.save()
+ permission_acessor = get_permission_acessor(obj)
+
+ for model, name in models:
+ content_type = ContentType.objects.get_for_model(model)
+ for t in ('add', 'change', 'delete'):
+ # add permission `t` to model `model`
+ codename = getattr(model._meta, 'get_%s_permission' % t)()
+ permission = Permission.objects.get(content_type=content_type, codename=codename)
+ if data.get('can_%s_%s' % (t, name), None):
+ permission_acessor.add(permission)
+ else:
+ permission_acessor.remove(permission)
class PageAddForm(forms.ModelForm):
@@ -67,7 +91,10 @@ def clean(self):
slug = ""
page = self.instance
- lang = cleaned_data['language']
+ lang = cleaned_data.get('language', None)
+ # No language, can not go further, but validation failed already
+ if not lang:
+ return cleaned_data
if 'parent' not in cleaned_data:
cleaned_data['parent'] = None
@@ -78,6 +105,7 @@ def clean(self):
except Site.DoesNotExist:
site = None
raise ValidationError("No site found for current settings.")
+
if site and not is_valid_page_slug(page, parent, lang, slug, site):
self._errors['slug'] = ErrorList([_('Another page with this slug already exists')])
del cleaned_data['slug']
@@ -127,18 +155,20 @@ def __init__(self, *args, **kwargs):
def clean(self):
cleaned_data = super(PageForm, self).clean()
- id = cleaned_data['reverse_id']
- site_id = cleaned_data['site']
- if id:
- if Page.objects.filter(reverse_id=id, site=site_id, publisher_is_draft=True).exclude(pk=self.instance.pk).count():
- raise forms.ValidationError(_('A page with this reverse URL id exists already.'))
+ if 'reverse_id' in self.fields:
+ id = cleaned_data['reverse_id']
+ site_id = cleaned_data['site']
+ if id:
+ if Page.objects.filter(reverse_id=id, site=site_id, publisher_is_draft=True).exclude(pk=self.instance.pk).count():
+ raise forms.ValidationError(_('A page with this reverse URL id exists already.'))
return cleaned_data
def clean_overwrite_url(self):
- url = self.cleaned_data['overwrite_url']
- if url:
- if not any_path_re.match(url):
- raise forms.ValidationError(_('Invalid URL, use /my/url format.'))
+ if 'overwrite_url' in self.fields:
+ url = self.cleaned_data['overwrite_url']
+ if url:
+ if not any_path_re.match(url):
+ raise forms.ValidationError(_('Invalid URL, use /my/url format.'))
return url
@@ -247,7 +277,7 @@ def populate_initials(self, obj):
initials['can_%s_%s' % (t, name)] = obj.has_perm('%s.%s' % (model._meta.app_label, codename))
return initials
"""
- permission_acessor = self.permission_acessor(obj)
+ permission_acessor = get_permission_acessor(obj)
for model in models:
name = model.__name__.lower()
content_type = ContentType.objects.get_for_model(model)
@@ -257,31 +287,8 @@ def populate_initials(self, obj):
initials['can_%s_%s' % (t, name)] = codename in permissions
return initials
- def permission_acessor(self, obj):
- if isinstance(obj, PageUser):
- rel_name = 'user_permissions'
- else:
- rel_name = 'permissions'
- return getattr(obj, rel_name)
-
def save_permissions(self, obj):
- models = ((Page, 'page'), (PageUser, 'pageuser'), (PageUserGroup, 'pageuser'), (PagePermission, 'pagepermission'))
-
- if not obj.pk:
- # save obj, otherwise we can't assign permissions to him
- obj.save()
- permission_acessor = self.permission_acessor(obj)
-
- for model, name in models:
- content_type = ContentType.objects.get_for_model(model)
- for t in ('add', 'change', 'delete'):
- # add permission `t` to model `model`
- codename = getattr(model._meta, 'get_%s_permission' % t)()
- permission = Permission.objects.get(content_type=content_type, codename=codename)
- if self.cleaned_data.get('can_%s_%s' % (t, name), None):
- permission_acessor.add(permission)
- else:
- permission_acessor.remove(permission)
+ save_permissions(self.cleaned_data, obj)
class PageUserForm(UserCreationForm, GenericCmsPermissionForm):
@@ -352,7 +359,7 @@ def save(self, commit=True):
if commit:
user.save()
- self.save_permissions(user)
+ save_permissions(self.cleaned_data, user)
if self.cleaned_data['notify_user']:
mail_page_user_change(user, created, self.cleaned_data['password1'])
View
1 cms/admin/models.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.forms.models import BaseInlineFormSet
class BaseInlineFormSetWithQuerySet(BaseInlineFormSet):
View
246 cms/admin/pageadmin.py
@@ -1,11 +1,13 @@
+# -*- coding: utf-8 -*-
from cms.admin.change_list import CMSChangeList
from cms.admin.dialog.views import get_copy_dialog
from cms.admin.forms import PageForm, PageAddForm
from cms.admin.permissionadmin import PAGE_ADMIN_INLINES, \
PagePermissionInlineAdmin
-from cms.admin.views import save_all_plugins, revert_plugins
-from cms.forms.widgets import PluginEditor
+from cms.admin.views import revert_plugins
+from cms.apphook_pool import apphook_pool
from cms.exceptions import NoPermissionsException
+from cms.forms.widgets import PluginEditor
from cms.models import Page, Title, CMSPlugin, PagePermission, \
PageModeratorState, EmptyTitle, GlobalPagePermission
from cms.models.managers import PagePermissionsPermissionManager
@@ -15,18 +17,17 @@
from cms.plugin_pool import plugin_pool
from cms.utils import get_template_from_request, get_language_from_request
from cms.utils.admin import render_admin_menu_item
+from cms.utils.copy_plugins import copy_plugins_to
+from cms.utils.helpers import make_revision_with_plugins
from cms.utils.moderator import update_moderation_message, \
get_test_moderation_level, moderator_should_approve, approve_page, \
will_require_moderation
from cms.utils.permissions import has_page_add_permission, \
has_page_change_permission, get_user_permission_level, \
has_global_change_permissions_permission
-from cms.utils.plugins import get_placeholders, get_page_from_plugin_or_404
from cms.utils.placeholder import get_page_from_placeholder_if_exists
-from cms.apphook_pool import apphook_pool
-
-from menus.menu_pool import menu_pool
-
+from cms.utils.plugins import get_placeholders, get_page_from_plugin_or_404
+from copy import deepcopy
from django import template
from django.conf import settings
from django.contrib import admin
@@ -35,21 +36,17 @@
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist
from django.core.urlresolvers import reverse
-from django.db import transaction
+from django.db import transaction, models
from django.forms import Widget, Textarea, CharField
from django.http import HttpResponseRedirect, HttpResponse, Http404, \
HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed
from django.shortcuts import render_to_response, get_object_or_404
from django.template.context import RequestContext
from django.template.defaultfilters import title, escape, force_escape, escapejs
from django.utils.encoding import force_unicode
-from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _
-
+from menus.menu_pool import menu_pool
import os
-from copy import deepcopy
-
-
model_admin = admin.ModelAdmin
create_on_success = lambda x: x
@@ -192,6 +189,7 @@ def get_urls(self):
pat(r'^([0-9]+)/moderation-states/$', self.get_moderation_states),
pat(r'^([0-9]+)/change-moderation/$', self.change_moderation),
pat(r'^([0-9]+)/approve/$', self.approve_page), # approve page
+ pat(r'^([0-9]+)/publish/$', self.publish_page), # publish page
pat(r'^([0-9]+)/remove-delete-state/$', self.remove_delete_state),
pat(r'^([0-9]+)/dialog/copy/$', get_copy_dialog), # copy dialog
pat(r'^([0-9]+)/preview/$', self.preview_page), # copy dialog
@@ -227,8 +225,9 @@ def save_model(self, request, obj, form, change):
obj.pk = pk
obj.save(no_signals=True)
obj.save()
+
else:
- if 'revert' in request.path:
+ if 'history' in request.path:
old_obj = Page.objects.get(pk=obj.pk)
obj.level = old_obj.level
obj.parent_id = old_obj.parent_id
@@ -239,6 +238,14 @@ def save_model(self, request, obj, form, change):
will_require_moderation(target, position)
obj.save(force_with_moderation=force_with_moderation)
+
+ if 'recover' in request.path or 'history' in request.path:
+ obj.pagemoderatorstate_set.all().delete()
+ if settings.CMS_MODERATOR:
+ from cms.utils.moderator import page_changed
+ page_changed(obj, force_moderation_action=PageModeratorState.ACTION_CHANGED)
+ revert_plugins(request, obj.version.pk, obj)
+
language = form.cleaned_data['language']
if target is not None and position is not None:
@@ -249,26 +256,20 @@ def save_model(self, request, obj, form, change):
else:
obj.move_to(target, position)
-
-
Title.objects.set_or_create(
+ request,
obj,
+ form,
language,
- slug=form.cleaned_data['slug'],
- title=form.cleaned_data['title'],
- application_urls=form.cleaned_data.get('application_urls', None),
- overwrite_url=form.cleaned_data.get('overwrite_url', None),
- redirect=form.cleaned_data.get('redirect', None),
- meta_description=form.cleaned_data.get('meta_description', None),
- meta_keywords=form.cleaned_data.get('meta_keywords', None),
- page_title=form.cleaned_data.get('page_title', None),
- menu_title=form.cleaned_data.get('menu_title', None),
)
# is there any moderation message? save/update state
if settings.CMS_MODERATOR and 'moderator_message' in form.cleaned_data and \
form.cleaned_data['moderator_message']:
update_moderation_message(obj, form.cleaned_data['moderator_message'])
+
+ if obj and "reversion" in settings.INSTALLED_APPS:
+ make_revision_with_plugins(obj)
@create_on_success
def change_template(self, request, object_id):
@@ -278,6 +279,8 @@ def change_template(self, request, object_id):
if to_template in dict(settings.CMS_TEMPLATES):
page.template = to_template
page.save()
+ if "reversion" in settings.INSTALLED_APPS:
+ make_revision_with_plugins(page)
return HttpResponse(str("ok"))
else:
return HttpResponseBadRequest("template not valid")
@@ -308,12 +311,14 @@ def get_fieldsets(self, request, obj=None):
l = list(given_fieldsets[0][1]['fields'][2])
l.remove('published')
given_fieldsets[0][1]['fields'][2] = tuple(l)
- for placeholder_name in get_placeholders(placeholders_template):
+ for placeholder_name in sorted(get_placeholders(placeholders_template)):
name = settings.CMS_PLACEHOLDER_CONF.get("%s %s" % (obj.template, placeholder_name), {}).get("name", None)
if not name:
name = settings.CMS_PLACEHOLDER_CONF.get(placeholder_name, {}).get("name", None)
if not name:
name = placeholder_name
+ else:
+ name = _(name)
given_fieldsets += [(title(name), {'fields':[placeholder_name], 'classes':['plugin-holder']})]
advanced = given_fieldsets.pop(3)
if obj.has_advanced_settings_permission(request):
@@ -359,6 +364,8 @@ def get_form(self, request, obj=None, **kwargs):
title_obj = obj.get_title_obj(language=language, fallback=False, version_id=version_id, force_reload=True)
except:
title_obj = EmptyTitle()
+ if form.base_fields['site'].initial is None:
+ form.base_fields['site'].initial = obj.site
for name in ['slug',
'title',
'application_urls',
@@ -377,25 +384,38 @@ def get_form(self, request, obj=None, **kwargs):
template_choices = list(settings.CMS_TEMPLATES)
form.base_fields['template'].choices = template_choices
form.base_fields['template'].initial = force_unicode(selected_template)
-
+
placeholders = get_placeholders(selected_template)
for placeholder_name in placeholders:
- placeholder, created = obj.placeholders.get_or_create(slot=placeholder_name)
- installed_plugins = plugin_pool.get_all_plugins(placeholder_name, obj)
plugin_list = []
show_copy = False
copy_languages = {}
if versioned:
from reversion.models import Version
version = get_object_or_404(Version, pk=version_id)
- revs = [related_version.object_version for related_version in version.revision.version_set.all()]
+ installed_plugins = plugin_pool.get_all_plugins()
plugin_list = []
plugins = []
bases = {}
+ revs = []
+ for related_version in version.revision.version_set.all():
+ try:
+ rev = related_version.object_version
+ except models.FieldDoesNotExist:
+ # in case the model has changed in the meantime
+ continue
+ else:
+ revs.append(rev)
+ for rev in revs:
+ pobj = rev.object
+ if pobj.__class__ == Placeholder:
+ if pobj.slot == placeholder_name:
+ placeholder = pobj
+ break
for rev in revs:
pobj = rev.object
if pobj.__class__ == CMSPlugin:
- if pobj.language == language and pobj.placeholder == placeholder and not pobj.parent_id:
+ if pobj.language == language and pobj.placeholder_id == placeholder.id and not pobj.parent_id:
if pobj.get_plugin_class() == CMSPlugin:
plugin_list.append(pobj)
else:
@@ -404,15 +424,19 @@ def get_form(self, request, obj=None, **kwargs):
plugins.append(pobj)
for plugin in plugins:
if int(plugin.cmsplugin_ptr_id) in bases:
+ bases[int(plugin.cmsplugin_ptr_id)].placeholder = placeholder
bases[int(plugin.cmsplugin_ptr_id)].set_base_attr(plugin)
plugin_list.append(plugin)
else:
+ placeholder, created = obj.placeholders.get_or_create(slot=placeholder_name)
+ installed_plugins = plugin_pool.get_all_plugins(placeholder_name, obj)
plugin_list = CMSPlugin.objects.filter(language=language, placeholder=placeholder, parent=None).order_by('position')
other_plugins = CMSPlugin.objects.filter(placeholder=placeholder, parent=None).exclude(language=language)
dict_cms_languages = dict(settings.CMS_LANGUAGES)
for plugin in other_plugins:
if (not plugin.language in copy_languages) and (plugin.language in dict_cms_languages):
copy_languages[plugin.language] = dict_cms_languages[plugin.language]
+
language = get_language_from_request(request, obj)
if copy_languages and not settings.CMS_DBGETTEXT and len(settings.CMS_LANGUAGES) > 1:
show_copy = True
@@ -431,6 +455,9 @@ def get_form(self, request, obj=None, **kwargs):
form.base_fields['parent'].initial = request.GET.get('target', None)
form.base_fields['site'].initial = request.session.get('cms_admin_site', None)
form.base_fields['template'].initial = settings.CMS_TEMPLATES[0][0]
+ if obj and not obj.has_advanced_settings_permission(request):
+ for field in self.advanced_fields:
+ del form.base_fields[field]
return form
# remove permission inlines, if user isn't allowed to change them
@@ -450,14 +477,6 @@ def get_formsets(self, request, obj=None):
yield inline.get_formset(request, obj)
- def save_form(self, request, form, change):
- """
- Given a ModelForm return an unsaved instance. ``change`` is True if
- the object is being changed, and False if it's being added.
- """
- instance = super(PageAdmin, self).save_form(request, form, change)
- return instance
-
def get_widget(self, request, page, lang, name):
"""
Given the request and name of a placeholder return a PluginEditor Widget
@@ -724,17 +743,10 @@ def render_revision_form(self, request, obj, version, context, revert=False, rec
obj.parent = None
obj.parent_id = None
version.field_dict['parent'] = None
+
+ obj.version = version
- response = super(PageAdmin, self).render_revision_form(request, obj, version, context, revert, recover)
- if request.method == "POST" \
- and ('history' in request.path or 'recover' in request.path) \
- and response.status_code == 302:
- obj.pagemoderatorstate_set.all().delete()
- if settings.CMS_MODERATOR:
- from cms.utils.moderator import page_changed
- page_changed(obj, force_moderation_action=PageModeratorState.ACTION_CHANGED)
- revert_plugins(request, version.pk, obj)
- return response
+ return super(PageAdmin, self).render_revision_form(request, obj, version, context, revert, recover)
def list_pages(self, request, template_name=None, extra_context=None):
"""
@@ -777,6 +789,10 @@ def move_page(self, request, page_id, extra_context=None):
# move page
page.move_page(target, position)
+
+ if "reversion" in settings.INSTALLED_APPS:
+ make_revision_with_plugins(page)
+
return render_admin_menu_item(request, page)
def get_permissions(self, request, page_id):
@@ -832,7 +848,7 @@ def copy_page(self, request, page_id, extra_context=None):
return HttpResponse("error")
#context.update({'error': _('Page could not been moved.')})
else:
- kwargs ={
+ kwargs = {
'copy_permissions': request.REQUEST.get('copy_permissions', False),
'copy_moderation': request.REQUEST.get('copy_moderation', False)
}
@@ -861,7 +877,6 @@ def approve_page(self, request, page_id):
"""Approve changes on current page by user from request.
"""
#TODO: change to POST method !! get is not safe
-
page = get_object_or_404(Page, id=page_id)
if not page.has_moderate_permission(request):
raise Http404()
@@ -875,7 +890,25 @@ def approve_page(self, request, page_id):
if 'node' in request.REQUEST:
# if request comes from tree..
return render_admin_menu_item(request, page)
- return HttpResponseRedirect('../../')
+ referer = request.META.get('HTTP_REFERER', reverse('admin:cms_page_changelist'))
+ path = '../../'
+ if 'admin' not in referer:
+ path = '%s?edit-off' % referer.split('?')[0]
+ return HttpResponseRedirect( path )
+
+
+ @transaction.commit_on_success
+ def publish_page(self, request, page_id):
+ page = get_object_or_404(Page, id=page_id)
+ # ensure user has permissions to publish this page
+ if not page.has_moderate_permission(request):
+ return HttpResponseForbidden("Denied")
+ page.publish()
+ referer = request.META['HTTP_REFERER']
+ path = '../../'
+ if 'admin' not in referer:
+ path = '%s?edit-off' % referer.split('?')[0]
+ return HttpResponseRedirect( path )
def delete_view(self, request, object_id, *args, **kwargs):
@@ -898,9 +931,6 @@ def delete_view(self, request, object_id, *args, **kwargs):
return HttpResponseRedirect("../../")
response = super(PageAdmin, self).delete_view(request, object_id, *args, **kwargs)
- public = page.publisher_public
- if request.method == 'POST' and response.status_code == 302 and public:
- public.delete()
return response
@create_on_success
@@ -952,15 +982,13 @@ def delete_translation(self, request, object_id, extra_context=None):
for p in plugins:
p.delete()
- if 'reversion' in settings.INSTALLED_APPS:
- obj.save()
- for placeholder in obj.placeholders.all():
- save_all_plugins(request, obj, placeholder)
-
public = obj.publisher_public
if public:
public.save()
-
+
+ if "reversion" in settings.INSTALLED_APPS:
+ make_revision_with_plugins(obj)
+
if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../")
return HttpResponseRedirect("../../")
@@ -1023,10 +1051,10 @@ def change_status(self, request, page_id):
page = get_object_or_404(Page, pk=page_id)
if page.has_publish_permission(request):
page.published = not page.published
- page.save(force_state=Page.MODERATOR_NEED_APPROVEMENT)
+ page.save()
return render_admin_menu_item(request, page)
else:
- return HttpResponseForbidden(_("You do not have permission to publish this page"))
+ return HttpResponseForbidden(unicode(_("You do not have permission to publish this page")))
def change_innavigation(self, request, page_id):
"""
@@ -1038,16 +1066,17 @@ def change_innavigation(self, request, page_id):
if page.has_change_permission(request):
if page.in_navigation:
page.in_navigation = False
- val = 0
else:
page.in_navigation = True
- val = 1
page.save(force_state=Page.MODERATOR_NEED_APPROVEMENT)
return render_admin_menu_item(request, page)
return HttpResponseForbidden(_("You do not have permission to change this page's in_navigation status"))
@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":
@@ -1082,30 +1111,37 @@ 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(_("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 ]:
- return HttpResponseBadRequest(_("Language must be set to a supported language!"))
+ return HttpResponseBadRequest(unicode(_("Language must be set to a supported language!")))
plugin = CMSPlugin(language=language, plugin_type=plugin_type, position=position, placeholder=placeholder)
if parent:
plugin.parent = parent
plugin.save()
+
if 'reversion' in settings.INSTALLED_APPS and page:
- page.save()
- save_all_plugins(request, page, placeholder)
+ make_revision_with_plugins(page)
reversion.revision.user = request.user
plugin_name = unicode(plugin_pool.get_plugin(plugin_type).name)
- reversion.revision.comment = _(u"%(plugin_name)s plugin added to %(placeholder)s") % {'plugin_name':plugin_name, 'placeholder':placeholder}
+ reversion.revision.comment = unicode(_(u"%(plugin_name)s plugin added to %(placeholder)s") % {'plugin_name':plugin_name, 'placeholder':placeholder})
+
return HttpResponse(str(plugin.pk))
raise Http404
@@ -1128,14 +1164,14 @@ def copy_plugins(self, request):
if language == copy_from:
return HttpResponseBadRequest(_("Language must be different than the copied language!"))
plugins = list(placeholder.cmsplugin_set.filter(language=copy_from).order_by('tree_id', '-rght'))
- ptree = []
- for p in plugins:
- p.copy_plugin(placeholder, language, ptree)
- if 'reversion' in settings.INSTALLED_APPS:
- page.save()
- save_all_plugins(request, page, placeholder)
+
+ copy_plugins_to(plugins, placeholder, language)
+
+ if page and "reversion" in settings.INSTALLED_APPS:
+ make_revision_with_plugins(page)
reversion.revision.user = request.user
reversion.revision.comment = _(u"Copied %(language)s plugins to %(placeholder)s") % {'language':dict(settings.LANGUAGES)[language], 'placeholder':placeholder}
+
plugin_list = CMSPlugin.objects.filter(language=language, placeholder=placeholder, parent=None).order_by('position')
return render_to_response('admin/cms/page/widgets/plugin_item.html', {'plugin_list':plugin_list}, RequestContext(request))
raise Http404
@@ -1148,39 +1184,45 @@ 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
pre_edit = request.path.split("/edit-plugin/")[0]
version_id = pre_edit.split("/")[-1]
Version.objects.get(pk=version_id)
version = get_object_or_404(Version, pk=version_id)
- revs = [related_version.object_version for related_version in version.revision.version_set.all()]
+ rev_objs = []
+ for related_version in version.revision.version_set.all():
+ try:
+ rev = related_version.object_version
+ except models.FieldDoesNotExist:
+ continue
+ else:
+ rev_objs.append(rev.object)
# TODO: check permissions
- for rev in revs:
- obj = rev.object
+ for obj in rev_objs:
if obj.__class__ == CMSPlugin and obj.pk == plugin_id:
cms_plugin = obj
break
- page = get_page_from_plugin_or_404(cms_plugin)
inst, plugin_admin = cms_plugin.get_plugin_instance(self.admin_site)
instance = None
if cms_plugin.get_plugin_class().model == CMSPlugin:
instance = cms_plugin
else:
- for rev in revs:
- obj = rev.object
+ for obj in rev_objs:
if hasattr(obj, "cmsplugin_ptr_id") and int(obj.cmsplugin_ptr_id) == int(cms_plugin.pk):
instance = obj
break
if not instance:
raise Http404("This plugin is not saved in a revision")
plugin_admin.cms_plugin_instance = cms_plugin
- plugin_admin.placeholder = cms_plugin.placeholder # TODO: what for reversion..? should it be inst ...?
- plugin_admin.page = page
+ try:
+ plugin_admin.placeholder = cms_plugin.placeholder # TODO: what for reversion..? should it be inst ...?
+ except Placeholder.DoesNotExist:
+ pass
if request.method == "POST":
# set the continue flag, otherwise will plugin_admin make redirect to list
# view, which actually does'nt exists
@@ -1203,11 +1245,10 @@ def edit_plugin(self, request, plugin_id):
# 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:
+
# if reversion is installed, save version of the page plugins
if 'reversion' in settings.INSTALLED_APPS and page:
- # perform this only if object was successfully changed
- page.save()
- save_all_plugins(request, page, cms_plugin.placeholder, [cms_plugin.pk])
+ make_revision_with_plugins(page)
reversion.revision.user = request.user
plugin_name = unicode(plugin_pool.get_plugin(cms_plugin.plugin_type).name)
reversion.revision.comment = _(u"%(plugin_name)s plugin edited at position %(position)s in %(placeholder)s") % {'plugin_name':plugin_name, 'position':cms_plugin.position, 'placeholder': cms_plugin.placeholder.slot}
@@ -1250,8 +1291,8 @@ def move_plugin(self, request):
plugin.save()
success = True
if 'ids' in request.POST:
- for id in request.POST['ids'].split("_"):
- plugin = CMSPlugin.objects.get(pk=id)
+ for plugin_id in request.POST['ids'].split("_"):
+ plugin = CMSPlugin.objects.get(pk=plugin_id)
page = get_page_from_placeholder_if_exists(plugin.placeholder)
if page and not page.has_change_permission(request):
@@ -1264,11 +1305,12 @@ def move_plugin(self, request):
success = True
if not success:
HttpResponse(str("error"))
+
if page and 'reversion' in settings.INSTALLED_APPS:
- page.save()
- save_all_plugins(request, page, plugin.placeholder)
+ make_revision_with_plugins(page)
reversion.revision.user = request.user
reversion.revision.comment = unicode(_(u"Plugins where moved"))
+
return HttpResponse(str("ok"))
else:
return HttpResponse(str("error"))
@@ -1285,17 +1327,22 @@ def remove_plugin(self, request):
raise Http404
if page and settings.CMS_MODERATOR and page.is_under_moderation():
+ # delete the draft version of the plugin
plugin.delete()
+ # set the page to require approval and save
+ page.moderator_state = Page.MODERATOR_NEED_APPROVEMENT
+ page.save()
else:
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}
+
if page and 'reversion' in settings.INSTALLED_APPS:
- save_all_plugins(request, page, placeholder)
- page.save()
+ make_revision_with_plugins(page)
reversion.revision.user = request.user
reversion.revision.comment = comment
+
return HttpResponse("%s,%s" % (plugin_id, comment))
raise Http404
@@ -1328,5 +1375,10 @@ def change_moderation(self, request, page_id):
page_moderator.save()
return render_admin_menu_item(request, page)
raise Http404
+
+ def lookup_allowed(self, key, *args, **kwargs):
+ if key == 'site__exact':
+ return True
+ return super(PageAdmin, self).lookup_allowed(key)
admin.site.register(Page, PageAdmin)
View
3 cms/admin/permissionadmin.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.conf import settings
from cms.admin.forms import GlobalPagePermissionAdminForm, \
PagePermissionInlineAdminForm
@@ -84,7 +85,7 @@ class GlobalPagePermissionAdmin(admin.ModelAdmin):
form = GlobalPagePermissionAdminForm
- search_fields = ('user__username', 'user__firstname', 'user__lastname', 'group__name')
+ search_fields = ('user__username', 'user__first_name', 'user__last_name', 'group__name')
exclude = []
View
182 cms/admin/placeholderadmin.py
@@ -1,21 +1,21 @@
-from django.contrib.admin import ModelAdmin
-from django.http import (HttpResponseRedirect, HttpResponse, Http404,
- HttpResponseBadRequest, HttpResponseForbidden, HttpResponseNotAllowed)
-from django.shortcuts import render_to_response, get_object_or_404
-from django.db import transaction
-from django.conf import settings
-from django.template.defaultfilters import title, escape, force_escape, escapejs
-from django.template import RequestContext
-from django.utils.encoding import force_unicode
-from django.utils.translation import ugettext_lazy as _
+# -*- 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.models.fields import PlaceholderField
-from cms.forms.fields import PlaceholderFormField
from cms.plugin_pool import plugin_pool
from cms.utils import get_language_from_request
+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_lazy as _
import os
+
class PlaceholderAdmin(ModelAdmin):
class Media:
@@ -30,6 +30,7 @@ class Media:
}
js = [os.path.join(settings.CMS_MEDIA_URL, path) for path in (
'js/lib/jquery.js',
+ 'js/csrf.js',
'js/lib/jquery.query.js',
'js/lib/ui.core.js',
'js/lib/ui.dialog.js',
@@ -43,24 +44,28 @@ def get_fieldsets(self, request, obj=None):
placeholder_fields = self._get_placeholder_fields(form)
if self.declared_fieldsets:
# check those declared fieldsets
- found = []
- fieldsets = tuple(self.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']):
placeholder_fields.remove(field)
else:
fields.remove(field)
- fieldset['fields'] = fields
+ if fields:
+ fieldset['fields'] = fields
+ else:
+ # no fields in the fieldset anymore, delete the fieldset
+ fieldsets.remove((label, fieldset))
for placeholder in placeholder_fields:
- fieldsets += (self.get_label_for_placeholder(placeholder), {
+ 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]}))
@@ -69,7 +74,9 @@ def get_fieldsets(self, request, obj=None):
'fields': (placeholder,),
'classes': ('plugin-holder', 'plugin-holder-nopage',),
}))
- fieldsets.append((None, {'fields': list(self.get_readonly_fields(request, obj))}))
+ 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):
@@ -82,8 +89,8 @@ def formfield_for_dbfield(self, db_field, **kwargs):
If kwargs are given, they're passed to the form Field's constructor.
"""
- request = kwargs.pop("request", None)
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)
@@ -115,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
@@ -178,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'],
View
1 cms/admin/useradmin.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.conf import settings
from cms.admin.forms import PageUserForm, PageUserGroupForm
from cms.admin.permissionadmin import GenericCmsPermissionAdmin
View
42 cms/admin/views.py
@@ -1,23 +1,14 @@
-from django.shortcuts import get_object_or_404, render_to_response
-from django.http import HttpResponse, Http404, HttpResponseForbidden, HttpResponseBadRequest
-from django.contrib.admin.views.decorators import staff_member_required
-from django.utils.translation import ugettext, ugettext_lazy as _
-from django.template.context import RequestContext
-from django.conf import settings
-from django.template.defaultfilters import escapejs, force_escape
-from django.views.decorators.http import require_POST
-
-from cms.models import Page, Title, CMSPlugin, MASK_CHILDREN, MASK_DESCENDANTS,\
- MASK_PAGE
-from cms.plugin_pool import plugin_pool
-from cms.utils.admin import render_admin_menu_item
-from django.core.exceptions import ObjectDoesNotExist, PermissionDenied
+# -*- coding: utf-8 -*-
+from cms.models import Page, Title, CMSPlugin, Placeholder
from cms.utils import get_language_from_request
+from django.http import Http404
+from django.shortcuts import get_object_or_404
def save_all_plugins(request, page, placeholder, excludes=None):
+
if not page.has_change_permission(request):
raise Http404
-
+
for plugin in CMSPlugin.objects.filter(placeholder=placeholder):
if excludes:
if plugin.pk in excludes:
@@ -33,14 +24,16 @@ def revert_plugins(request, version_id, obj):
version = get_object_or_404(Version, pk=version_id)
revs = [related_version.object_version for related_version in version.revision.version_set.all()]
cms_plugin_list = []
+ placeholders = {}
plugin_list = []
titles = []
others = []
page = obj
lang = get_language_from_request(request)
for rev in revs:
obj = rev.object
-
+ if obj.__class__ == Placeholder:
+ placeholders[obj.pk] = obj
if obj.__class__ == CMSPlugin:
cms_plugin_list.append(obj)
elif hasattr(obj, 'cmsplugin_ptr_id'):
@@ -49,15 +42,22 @@ def revert_plugins(request, version_id, obj):
pass
#page = obj #Page.objects.get(pk=obj.pk)
elif obj.__class__ == Title:
- if not obj.language == lang:
- titles.append(obj)
+ titles.append(obj)
else:
others.append(rev)
if not page.has_change_permission(request):
raise Http404
current_plugins = list(CMSPlugin.objects.filter(placeholder__page=page))
+ for pk, placeholder in placeholders.items():
+ # admin has already created the placeholders/ get them instead
+ try:
+ placeholders[pk] = page.placeholders.get(slot=placeholder.slot)
+ except Placeholder.DoesNotExist:
+ placeholders[pk].save()
+ page.placeholders.add(placeholders[pk])
for plugin in cms_plugin_list:
- plugin.page = page
+ # connect plugins to the correct placeholder
+ plugin.placeholder = placeholders[plugin.placeholder_id]
plugin.save(no_signals=True)
for plugin in cms_plugin_list:
plugin.save()
@@ -67,7 +67,6 @@ def revert_plugins(request, version_id, obj):
p.save()
for old in current_plugins:
if old.pk == plugin.pk:
- plugin.publisher_public = old.publisher_public
plugin.save()
current_plugins.remove(old)
for title in titles:
@@ -80,5 +79,4 @@ def revert_plugins(request, version_id, obj):
for other in others:
other.object.save()
for plugin in current_plugins:
- plugin.delete()
-
+ plugin.delete()
View
2 cms/app_base.py
@@ -1,4 +1,4 @@
-
+# -*- coding: utf-8 -*-
class CMSApp(object):
name = None
urls = None
View
19 cms/apphook_pool.py
@@ -1,6 +1,8 @@
-from django.conf import settings
+# -*- coding: utf-8 -*-
from cms.exceptions import AppAllreadyRegistered
+from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
+from django.utils.importlib import import_module
class ApphookPool(object):
def __init__(self):
@@ -18,12 +20,21 @@ def discover_apps(self):
self.block_register = True
path = ".".join(app.split(".")[:-1])
class_name = app.split(".")[-1]
- cls = __import__(path, {}, {}, [class_name])
+ module = import_module(path)
+ cls = getattr(module, class_name, None)
+ if cls is None:
+ raise ImproperlyConfigured(
+ "Cannot find class %s" % app
+ )
self.block_register = False
self.register(cls)
else:
for app in settings.INSTALLED_APPS:
- __import__(app, {}, {}, ['cms_app'])
+ cms_app = '%s.cms_app' % app
+ try:
+ import_module(cms_app)
+ except ImportError:
+ pass
self.discovered = True
def clear(self):
@@ -45,6 +56,8 @@ def get_apphooks(self):
for app_name in self.apps.keys():
app = self.apps[app_name]
hooks.append((app_name, app.name))
+ # Unfortunately, we loose the ordering since we now have a list of tuples. Let's reorder by app_name:
+ hooks = sorted(hooks, key=lambda hook: hook[1])
return hooks
def get_apphook(self, app_name):
View
52 cms/appresolver.py
@@ -1,34 +1,43 @@
-from django.conf import settings
-from django.core.urlresolvers import RegexURLResolver, Resolver404, reverse, RegexURLPattern
-from django.conf.urls.defaults import *
-from django.utils.importlib import import_module
-from django.core.exceptions import ImproperlyConfigured
-from django.contrib.sites.models import Site
-from cms.exceptions import NoHomeFound
-import re
+# -*- coding: utf-8 -*-
from cms.apphook_pool import apphook_pool
+from cms.exceptions import NoHomeFound
+from cms.utils.moderator import get_page_queryset
+
+from django.conf import settings
+from django.conf.urls.defaults import patterns
+from django.contrib.sites.models import Site
+from django.core.exceptions import ImproperlyConfigured
+from django.core.urlresolvers import RegexURLResolver, Resolver404, reverse, \
+ RegexURLPattern
+from django.utils.importlib import import_module
APP_RESOLVERS = []
+def clear_app_resolvers():
+ global APP_RESOLVERS
+ APP_RESOLVERS = []
+
def applications_page_check(request, current_page=None, path=None):
"""Tries to find if given path was resolved over application.
Applications have higher priority than other cms pages.
"""
- from cms.utils.moderator import get_page_queryset
if current_page:
return current_page
if path is None:
+ # We should get in this branch only if an apphook is active on /
+ # This removes the non-CMS part of the URL.
path = request.path.replace(reverse('pages-root'), '', 1)
# check if application resolver can resolve this
for resolver in APP_RESOLVERS:
try:
- page_id = resolver.resolve_page_id(path+"/")
+ page_id = resolver.resolve_page_id(path)
# yes, it is application page
page = get_page_queryset(request).get(id=page_id)
# If current page was matched, then we have some override for content
# from cms, but keep current page. Otherwise return page to which was application assigned.
return page
except Resolver404:
+ # Raised if the page is not managed by an apphook
pass
return None
@@ -94,23 +103,26 @@ def _flatten_patterns(patterns):
flat.append(pattern)
return flat
+def get_app_urls(urls):
+ for urlconf in urls:
+ if isinstance(urlconf, basestring):
+ mod = import_module(urlconf)
+ if not hasattr(mod, 'urlpatterns'):
+ raise ImproperlyConfigured(
+ "URLConf `%s` has no urlpatterns attribute" % urlconf)
+ yield getattr(mod, 'urlpatterns')
+ else:
+ yield urlconf
+
+
def get_patterns_for_title(path, title):
"""
Resolve the urlconf module for a path+title combination
Returns a list of url objects.
"""
app = apphook_pool.get_apphook(title.application_urls)
patterns = []
- for urlconf in app.urls:
- pattern_list = None
- if isinstance(urlconf, (str)):
- mod = import_module(urlconf)
- if not hasattr(mod, 'urlpatterns'):
- raise ImproperlyConfigured("URLConf `%s` has no urlpatterns attribute"
- % urlconf)
- pattern_list = getattr(mod, 'urlpatterns')
- else:
- pattern_list = urlconf
+ for pattern_list in get_app_urls(app.urls):
if not path.endswith('/'):
path += '/'
page_id = title.page.id
View
1 cms/cache/permissions.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.core.cache import cache
# Time to live for cache entry 10 minutes, so it gets cleaned if we don't catch
View
1 cms/conf/__init__.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.conf import settings
from patch import pre_patch, post_patch, post_patch_check
View
1 cms/conf/global_settings.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
"""
Global cms settings, are applied if there isn't value defined in project
settings. All available settings are listed here. Please don't put any
View
7 cms/conf/patch.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ImproperlyConfigured
@@ -33,6 +34,12 @@ def post_patch():
else:
# dummy translation
settings.dbgettext = lambda x: x
+ if settings.CMS_DBGETTEXT_SLUGS:
+ warn(
+ "CMS_DBGETTEXT_SLUGS (and general support for django-dbggettext "
+ "for CMS contents) will be deprecated in django CMS 2.2.",
+ DeprecationWarning
+ )
def post_patch_check():
View
1 cms/context_processors.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from django.conf import settings
def media(request):
View
1 cms/dbgettext_registration.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
from dbgettext.registry import registry, Options
from models import Page
from django.conf import settings
View
0 cms/docs/Makefile
Sorry, we could not display the changes to this file because there were too many other changes to display.
View
364 cms/docs/app_integration.txt
@@ -1,364 +0,0 @@
-App Integration
-===============
-
-It is pretty easy to integrate your own django applications with django-cms.
-You have 5 ways of integrating your app:
-
-1. Menus
-
- Static extend the menu entries
-