Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fixes #1615

adds a new settings and keeps the DB small for reversions
  • Loading branch information...
commit 8d364b75306d92264288f05a1b64e0bbbdb669a2 1 parent 54e9023
@digi604 digi604 authored
View
3  CHANGELOG.txt
@@ -124,6 +124,8 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
- Compatibility with Django 1.4 and 1.5 (1.3 support dropped)
- Support for Python 2.5 dropped
+- CMS_MAX_PAGE_PUBLISH_REVERSIONS has been added
+- Reversion integration has changed to limit DB size
- CMS_LANGUAGE setting has changed
- CMS_HIDE_UNTRANSLATED setting removed
- CMS_LANGUAGE_FALLBACK setting removed
@@ -133,5 +135,6 @@ Please see Install/2.4 release notes *before* attempting to upgrade to version 2
- MultilingualMiddleware has been removed
- CMS_FLAT_URLS has been removed
+
==== NEXT ====
- Fixed #1543: Nasty empty line in cms/plugins/link.html
View
261 cms/admin/pageadmin.py
@@ -3,19 +3,20 @@
from distutils.version import LooseVersion
from urllib2 import unquote
from cms.utils.conf import get_cms_setting
+from cms.utils.helpers import find_placeholder_relation
import django
from django.conf import settings
from django.contrib import admin, messages
from django.contrib.admin.options import IncorrectLookupParameters
from django.contrib.admin.util import get_deleted_objects
+from django.contrib.contenttypes.models import ContentType
from django.contrib.sites.models import Site
from django.core.exceptions import PermissionDenied, ObjectDoesNotExist, ValidationError
from django.core.urlresolvers import reverse
from django.db import router, transaction, models
from django.forms import CharField
-from django.http import (HttpResponseRedirect, HttpResponse, Http404,
- HttpResponseBadRequest, HttpResponseForbidden)
+from django.http import (HttpResponseRedirect, HttpResponse, Http404, HttpResponseBadRequest, HttpResponseForbidden)
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)
@@ -27,21 +28,19 @@
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, ViewRestrictionInlineAdmin)
+from cms.admin.permissionadmin import (PAGE_ADMIN_INLINES, PagePermissionInlineAdmin, ViewRestrictionInlineAdmin)
from cms.admin.views import revert_plugins
from cms.apphook_pool import apphook_pool
-from cms.exceptions import NoPermissionsException, PluginLimitReached
+from cms.exceptions import PluginLimitReached
from cms.forms.widgets import PluginEditor
-from cms.models import (Page, Title, CMSPlugin, PagePermission,
- PageModeratorState, EmptyTitle, GlobalPagePermission, titlemodels)
+from cms.models import (Page, Title, CMSPlugin, PagePermission, PageModeratorState, EmptyTitle, GlobalPagePermission,
+ titlemodels)
from cms.models.managers import PagePermissionsPermissionManager
from cms.models.placeholdermodel import Placeholder
from cms.plugin_pool import plugin_pool
from cms.templatetags.cms_admin import admin_static_url
-from cms.utils import (copy_plugins, helpers, moderator, permissions, plugins,
- get_template_from_request, get_language_from_request,
- placeholder as placeholder_utils, admin as admin_utils, cms_static_url)
+from cms.utils import (copy_plugins, helpers, moderator, permissions, plugins, get_template_from_request,
+ get_language_from_request, placeholder as placeholder_utils, admin as admin_utils, cms_static_url)
from cms.utils.i18n import get_language_dict, get_language_list, get_language_tuple, get_language_object
from cms.utils.page_resolver import is_valid_url
from cms.utils.admin import jsonify_request
@@ -55,14 +54,15 @@
require_POST = method_decorator(require_POST)
if 'reversion' in settings.INSTALLED_APPS:
- import reversion
- import reversion.models
from reversion.admin import VersionAdmin as ModelAdmin
from reversion import create_revision
else: # pragma: no cover
from django.contrib.admin import ModelAdmin
+
create_revision = lambda: lambda x: x
+PUBLISH_COMMENT = "Publish"
+
def contribute_fieldsets(cls):
if get_cms_setting('MENU_TITLE_OVERWRITE'):
@@ -116,9 +116,9 @@ def contribute_fieldsets(cls):
if get_cms_setting('SEO_FIELDS'):
fieldsets.append((_("SEO Settings"), {
- 'fields': seo_fields,
- 'classes': ('collapse',),
- }))
+ 'fields': seo_fields,
+ 'classes': ('collapse',),
+ }))
setattr(cls, 'fieldsets', fieldsets)
setattr(cls, 'advanced_fields', advanced_fields)
setattr(cls, 'hidden_fields', hidden_fields)
@@ -143,7 +143,8 @@ class PageAdmin(ModelAdmin):
recover_form_template = "admin/cms/page/recover_form.html"
exclude = []
- mandatory_placeholders = ('title', 'slug', 'parent', 'site', 'meta_description', 'meta_keywords', 'page_title', 'menu_title')
+ mandatory_placeholders = (
+ 'title', 'slug', 'parent', 'site', 'meta_description', 'meta_keywords', 'page_title', 'menu_title')
add_general_fields = ['title', 'slug', 'language', 'template']
change_list_template = "admin/cms/page/change_list.html"
@@ -172,43 +173,44 @@ class Media:
)]
}
js = ['%sjs/jquery.min.js' % admin_static_url()] + [cms_static_url(path) for path in [
- 'js/plugins/admincompat.js',
- 'js/libs/jquery.query.js',
- 'js/libs/jquery.ui.core.js',
- 'js/libs/jquery.ui.dialog.js',
- ]
+ 'js/plugins/admincompat.js',
+ 'js/libs/jquery.query.js',
+ 'js/libs/jquery.ui.core.js',
+ 'js/libs/jquery.ui.dialog.js',
+ ]
]
def get_urls(self):
"""Get the admin urls
"""
- from django.conf.urls.defaults import patterns, url
+ from django.conf.urls import patterns, url
+
info = "%s_%s" % (self.model._meta.app_label, self.model._meta.module_name)
pat = lambda regex, fn: url(regex, self.admin_site.admin_view(fn), name='%s_%s' % (info, fn.__name__))
url_patterns = patterns('',
- pat(r'copy-plugins/$', self.copy_plugins),
- pat(r'add-plugin/$', self.add_plugin),
- pat(r'edit-plugin/([0-9]+)/$', self.edit_plugin),
- pat(r'remove-plugin/$', self.remove_plugin),
- pat(r'move-plugin/$', self.move_plugin),
- pat(r'^([0-9]+)/delete-translation/$', self.delete_translation),
- pat(r'^([0-9]+)/move-page/$', self.move_page),
- pat(r'^([0-9]+)/copy-page/$', self.copy_page),
- pat(r'^([0-9]+)/change-status/$', self.change_status),
- pat(r'^([0-9]+)/change-navigation/$', self.change_innavigation),
- pat(r'^([0-9]+)/jsi18n/$', self.redirect_jsi18n),
- pat(r'^([0-9]+)/permissions/$', self.get_permissions),
- pat(r'^([0-9]+)/moderation-states/$', self.get_moderation_states),
- pat(r'^([0-9]+)/publish/$', self.publish_page), # publish page
- pat(r'^([0-9]+)/revert/$', self.revert_page), # publish page
- pat(r'^([0-9]+)/confirm-delete/$', self.confirm_delete),
- 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
- pat(r'^([0-9]+)/descendants/$', self.descendants), # menu html for page descendants
- pat(r'^(?P<object_id>\d+)/change_template/$', self.change_template), # copy dialog
+ pat(r'copy-plugins/$', self.copy_plugins),
+ pat(r'add-plugin/$', self.add_plugin),
+ pat(r'edit-plugin/([0-9]+)/$', self.edit_plugin),
+ pat(r'remove-plugin/$', self.remove_plugin),
+ pat(r'move-plugin/$', self.move_plugin),
+ pat(r'^([0-9]+)/delete-translation/$', self.delete_translation),
+ pat(r'^([0-9]+)/move-page/$', self.move_page),
+ pat(r'^([0-9]+)/copy-page/$', self.copy_page),
+ pat(r'^([0-9]+)/change-status/$', self.change_status),
+ pat(r'^([0-9]+)/change-navigation/$', self.change_innavigation),
+ pat(r'^([0-9]+)/jsi18n/$', self.redirect_jsi18n),
+ pat(r'^([0-9]+)/permissions/$', self.get_permissions),
+ pat(r'^([0-9]+)/moderation-states/$', self.get_moderation_states),
+ pat(r'^([0-9]+)/publish/$', self.publish_page), # publish page
+ pat(r'^([0-9]+)/revert/$', self.revert_page), # publish page
+ pat(r'^([0-9]+)/confirm-delete/$', self.confirm_delete),
+ 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
+ pat(r'^([0-9]+)/descendants/$', self.descendants), # menu html for page descendants
+ pat(r'^(?P<object_id>\d+)/change_template/$', self.change_template), # copy dialog
)
url_patterns += super(PageAdmin, self).get_urls()
@@ -217,6 +219,18 @@ def get_urls(self):
def redirect_jsi18n(self, request):
return HttpResponseRedirect(reverse('admin:jsi18n'))
+ def get_revision_instances(self, request, object):
+ """Returns all the instances to be used in the object's revision."""
+ placeholder_relation = find_placeholder_relation(object)
+ data = [object]
+ filters = {'placeholder__%s' % placeholder_relation: object}
+ for plugin in CMSPlugin.objects.filter(**filters):
+ plugin_instance, admin = plugin.get_plugin_instance()
+ if plugin_instance:
+ data.append(plugin_instance)
+ data.append(plugin)
+ return data
+
def save_model(self, request, obj, form, change):
"""
Move the page in the tree if necessary and save every placeholder
@@ -250,7 +264,7 @@ def save_model(self, request, obj, form, change):
obj.tree_id = old_obj.tree_id
obj.save()
-
+
if 'recover' in request.path or 'history' in request.path:
obj.pagemoderatorstate_set.all().delete()
moderator.page_changed(obj, force_moderation_action=PageModeratorState.ACTION_CHANGED)
@@ -273,8 +287,8 @@ def save_model(self, request, obj, form, change):
language,
)
- if obj and "reversion" in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(obj)
+ #if obj and "reversion" in settings.INSTALLED_APPS:
+ # helpers.make_revision_with_plugins(obj, request.user)
def get_fieldsets(self, request, obj=None):
"""
@@ -291,7 +305,7 @@ def get_fieldsets(self, request, obj=None):
for placeholder_name in self.get_fieldset_placeholders(placeholders_template):
name = placeholder_utils.get_placeholder_conf("name", placeholder_name, obj.template, placeholder_name)
name = _(name)
- given_fieldsets += [(title(name), {'fields':[placeholder_name], 'classes':['plugin-holder']})]
+ given_fieldsets += [(title(name), {'fields': [placeholder_name], 'classes': ['plugin-holder']})]
advanced = given_fieldsets.pop(3)
if obj.has_advanced_settings_permission(request):
given_fieldsets.append(advanced)
@@ -332,19 +346,20 @@ def get_form(self, request, obj=None, **kwargs):
version_id = request.path.split("/")[-2]
try:
- title_obj = obj.get_title_obj(language=language, fallback=False, version_id=version_id, force_reload=True)
+ title_obj = obj.get_title_obj(language=language, fallback=False, version_id=version_id,
+ force_reload=True)
except titlemodels.Title.DoesNotExist:
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',
- 'redirect',
- 'meta_description',
- 'meta_keywords',
- 'menu_title',
- 'page_title']:
+ 'title',
+ 'application_urls',
+ 'redirect',
+ 'meta_description',
+ 'meta_keywords',
+ 'menu_title',
+ 'page_title']:
form.base_fields[name].initial = getattr(title_obj, name)
if title_obj.overwrite_url:
form.base_fields['overwrite_url'].initial = title_obj.path
@@ -363,6 +378,7 @@ def get_form(self, request, obj=None, **kwargs):
copy_languages = {}
if versioned:
from reversion.models import Version
+
version = get_object_or_404(Version, pk=version_id)
installed_plugins = plugin_pool.get_all_plugins()
plugin_list = []
@@ -401,8 +417,10 @@ def get_form(self, request, obj=None, **kwargs):
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)
+ 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 = get_language_dict()
for plugin in other_plugins:
if (not plugin.language in copy_languages) and (plugin.language in dict_cms_languages):
@@ -427,7 +445,7 @@ def get_form(self, request, obj=None, **kwargs):
else:
self.inlines = []
form = PageAddForm
- for name in ['slug','title']:
+ for name in ['slug', 'title']:
form.base_fields[name].initial = u''
form.base_fields['parent'].initial = request.GET.get('target', None)
form.base_fields['site'].initial = request.session.get('cms_admin_site', None)
@@ -446,7 +464,7 @@ def get_inline_instances(self, request, obj=None):
filtered_inlines = []
for inline in inlines:
if (isinstance(inline, PagePermissionInlineAdmin)
- and not isinstance(inline, ViewRestrictionInlineAdmin)):
+ and not isinstance(inline, ViewRestrictionInlineAdmin)):
if "recover" in request.path or "history" in request.path:
# do not display permissions in recover mode
continue
@@ -467,7 +485,7 @@ def get_unihandecode_context(self, language):
uhd_urls = [
'%sunihandecode-%s.core.min.js' % (uhd_host, uhd_version),
'%sunihandecode-%s.%s.min.js' % (uhd_host, uhd_version, uhd_lang),
- ]
+ ]
else:
uhd_urls = []
return {'unihandecode_lang': uhd_lang, 'unihandecode_urls': uhd_urls}
@@ -523,7 +541,7 @@ def change_view(self, request, object_id, extra_context=None):
if DJANGO_1_4:
self._current_page = obj
response = super(PageAdmin, self).change_view(request, object_id, extra_context=extra_context)
- if tab_language and response.status_code == 302 and response._headers['location'][1] == request.path :
+ if tab_language and response.status_code == 302 and response._headers['location'][1] == request.path:
location = response._headers['location']
response._headers['location'] = (location[0], "%s?language=%s" % (location[1], tab_language))
return response
@@ -616,13 +634,15 @@ def has_recover_permission(self, request):
def changelist_view(self, request, extra_context=None):
"The 'change list' admin view for this model."
from django.contrib.admin.views.main import ERROR_FLAG
+
opts = self.model._meta
app_label = opts.app_label
if not self.has_change_permission(request, None):
return HttpResponseForbidden(_("You do not have permission to change pages."))
try:
cl = CMSChangeList(request, self.model, self.list_display, self.list_display_links, self.list_filter,
- self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page, self.list_max_show_all, self.list_editable, self)
+ self.date_hierarchy, self.search_fields, self.list_select_related, self.list_per_page,
+ self.list_max_show_all, self.list_editable, self)
except IncorrectLookupParameters:
# Wacky lookup parameters were given, so redirect to the main
# changelist page, without parameters, and pass an 'invalid=1'
@@ -654,7 +674,7 @@ def changelist_view(self, request, extra_context=None):
'title': cl.title,
'is_popup': cl.is_popup,
'cl': cl,
- 'opts':opts,
+ 'opts': opts,
'has_add_permission': self.has_add_permission(request),
'root_path': reverse('admin:index'),
'app_label': app_label,
@@ -732,7 +752,8 @@ def change_template(self, request, object_id):
page.template = to_template
page.save()
if "reversion" in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(page)
+ message = _("Template changed to %s") % dict(get_cms_setting('TEMPLATES')[to_template])
+ helpers.make_revision_with_plugins(page, request.user, message)
return HttpResponse(_("The template was successfully changed"))
@transaction.commit_on_success
@@ -753,17 +774,19 @@ def move_page(self, request, page_id, extra_context=None):
# does he haves permissions to do this...?
if not page.has_move_page_permission(request) or \
- not target.has_add_permission(request):
- return jsonify_request(HttpResponseForbidden(_("Error! You don't have permissions to move this page. Please reload the page")))
+ not target.has_add_permission(request):
+ return jsonify_request(
+ HttpResponseForbidden(_("Error! You don't have permissions to move this page. Please reload the page")))
if page.delete_requested():
- return jsonify_request(HttpResponseBadRequest(_('The page "%s" has a delete request. Delete or confirm the request first.') % page))
+ return jsonify_request(HttpResponseBadRequest(
+ _('The page "%s" has a delete request. Delete or confirm the request first.') % page))
# move page
page.move_page(target, position)
if "reversion" in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(page)
+ helpers.make_revision_with_plugins(page, request.user, _("Page moved"))
return jsonify_request(HttpResponse(admin_utils.render_admin_menu_item(request, page).content))
@@ -844,28 +867,46 @@ def get_moderation_states(self, request, page_id):
#TODO: Make the change form buttons use POST
#@require_POST
@transaction.commit_on_success
+ @create_revision()
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_publish_permission(request):
return HttpResponseForbidden(_("You do not have permission to publish this page"))
-
if page.delete_requested():
- messages.error(request, _('The page "%s" has a delete request. Delete or confirm the request first.') % page)
+ messages.error(request,
+ _('The page "%s" has a delete request. Delete or confirm the request first.') % page)
else:
page.publish()
-
messages.info(request, _('The page "%s" was successfully published.') % page)
-
+ if "reversion" in settings.INSTALLED_APPS:
+ # delete revisions that are not publish revisions
+ from reversion.models import Version
+
+ content_type = ContentType.objects.get_for_model(Page)
+ versions = Version.objects.filter(type=1, content_type=content_type, object_id_int=page.pk).exclude(
+ revision__comment__exact=PUBLISH_COMMENT)
+ for version in versions:
+ revision = version.revision
+ revision.delete()
+ # delete all publish revisions that are more then MAX_PAGE_PUBLISH_REVERSIONS
+ limit = get_cms_setting("MAX_PAGE_PUBLISH_REVERSIONS")
+ if limit:
+ versions = Version.objects.filter(type=1, content_type=content_type, object_id_int=page.pk,
+ revision__comment__exact=PUBLISH_COMMENT).order_by('-revision__pk')
+ for version in versions[limit - 1:]:
+ revision = version.revision
+ revision.delete()
+ # create a new publish reversion
+ helpers.make_revision_with_plugins(page, request.user, PUBLISH_COMMENT)
if 'node' in request.REQUEST:
# if request comes from tree..
return admin_utils.render_admin_menu_item(request, page)
-
- referer = request.META.get('HTTP_REFERER', '')
+ referrer = request.META.get('HTTP_REFERER', '')
path = '../../'
# TODO: use admin base here!
- if 'admin' not in referer:
- path = '%s?edit-off' % referer.split('?')[0]
+ if 'admin' not in referrer:
+ path = '%s?edit-off' % referrer.split('?')[0]
return HttpResponseRedirect(path)
#TODO: Make the change form buttons use POST
@@ -1014,7 +1055,7 @@ def delete_translation(self, request, object_id, extra_context=None):
public.save()
if "reversion" in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(obj)
+ helpers.make_revision_with_plugins(obj, request.user, message)
if not self.has_change_permission(request, None):
return HttpResponseRedirect("../../../../")
@@ -1058,7 +1099,7 @@ def preview_page(self, request, object_id):
if not site == page.site:
url = "http%s://%s%s" % ('s' if request.is_secure() else '',
- page.site.domain, url)
+ page.site.domain, url)
return HttpResponseRedirect(url)
@require_POST
@@ -1071,7 +1112,7 @@ def change_status(self, request, page_id):
return HttpResponseForbidden(_("You do not have permission to publish this page"))
try:
- if page.published or is_valid_url(page.get_absolute_url(),page,False):
+ if page.published or is_valid_url(page.get_absolute_url(), page, False):
published = page.published
method = page.publish if not published else page.unpublish
try:
@@ -1109,7 +1150,7 @@ def descendants(self, request, page_id):
"""
page = get_object_or_404(Page, pk=page_id)
return admin_utils.render_admin_menu_item(request, page,
- template="admin/cms/page/lazy_menu.html")
+ template="admin/cms/page/lazy_menu.html")
@require_POST
@create_revision()
@@ -1161,7 +1202,7 @@ def add_plugin(self, request):
raise Http404()
# Sanity check to make sure we're not getting bogus values from JavaScript:
- if not language or not language in [ lang[0] for lang in settings.LANGUAGES ]:
+ if not language or not language in [lang[0] for lang in settings.LANGUAGES]:
return HttpResponseBadRequest(_("Language must be set to a supported language!"))
plugin = CMSPlugin(language=language, plugin_type=plugin_type, position=position, placeholder=placeholder)
@@ -1172,12 +1213,10 @@ def add_plugin(self, request):
plugin.save()
if 'reversion' in settings.INSTALLED_APPS and page:
- helpers.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") % {
+ message = _(u"%(plugin_name)s plugin added to %(placeholder)s") % {
'plugin_name': plugin_name, 'placeholder': placeholder}
-
+ helpers.make_revision_with_plugins(page, request.user, message)
return HttpResponse(str(plugin.pk), content_type='text/plain')
@require_POST
@@ -1208,13 +1247,14 @@ def copy_plugins(self, request):
copy_plugins.copy_plugins_to(plugins, placeholder, language)
if page and "reversion" in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(page)
- reversion.revision.user = request.user
- reversion.revision.comment = _(u"Copied %(language)s plugins to %(placeholder)s") % {
+ message = _(u"Copied %(language)s plugins to %(placeholder)s") % {
'language': _(dict(settings.LANGUAGES)[language]), 'placeholder': placeholder}
+ helpers.make_revision_with_plugins(page, request.user, message)
- 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))
+ 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))
@create_revision()
def edit_plugin(self, request, plugin_id):
@@ -1228,6 +1268,7 @@ def edit_plugin(self, request, plugin_id):
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 = get_object_or_404(Version, pk=version_id)
@@ -1239,7 +1280,7 @@ def edit_plugin(self, request, plugin_id):
continue
else:
rev_objs.append(rev.object)
- # TODO: check permissions
+ # TODO: check permissions
for obj in rev_objs:
if obj.__class__ == CMSPlugin and obj.pk == plugin_id:
@@ -1273,7 +1314,8 @@ def edit_plugin(self, request, plugin_id):
if 'reversion' in settings.INSTALLED_APPS and ('history' in request.path or 'recover' in request.path):
# in case of looking to history just render the plugin content
context = RequestContext(request)
- return render_to_response(plugin_admin.render_template, plugin_admin.render(context, instance, plugin_admin.placeholder))
+ return render_to_response(plugin_admin.render_template,
+ plugin_admin.render(context, instance, plugin_admin.placeholder))
if request.POST.get("_cancel", False):
# cancel button was clicked
@@ -1311,19 +1353,18 @@ def edit_plugin(self, request, plugin_id):
response = plugin_admin.change_view(request, str(plugin_id))
if request.method == "POST" and plugin_admin.object_successfully_changed:
moderator.page_changed(page,
- force_moderation_action=PageModeratorState.ACTION_CHANGED)
+ force_moderation_action=PageModeratorState.ACTION_CHANGED)
# if reversion is installed, save version of the page plugins
if 'reversion' in settings.INSTALLED_APPS and page:
- helpers.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
- }
- # read the saved object from plugin_admin - ugly but works
+ message = _(
+ 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
+ }
+ helpers.make_revision_with_plugins(page, request.user, message)
saved_object = plugin_admin.saved_object
context = {
@@ -1366,7 +1407,7 @@ def move_plugin(self, request):
has_reached_plugin_limit(placeholder, plugin.plugin_type, plugin.language, template=page.get_template())
except PluginLimitReached, e:
return HttpResponseBadRequest(str(e))
- # plugin positions are 0 based, so just using count here should give us 'last_position + 1'
+ # plugin positions are 0 based, so just using count here should give us 'last_position + 1'
position = CMSPlugin.objects.filter(placeholder=placeholder).count()
plugin.placeholder = placeholder
plugin.position = position
@@ -1374,7 +1415,7 @@ def move_plugin(self, request):
for child in plugin.get_descendants():
child.placeholder = placeholder
child.save()
- # make sure the plugin has no parent
+ # make sure the plugin has no parent
plugin.parent = None
plugin.save()
success = True
@@ -1398,13 +1439,10 @@ def move_plugin(self, request):
return HttpResponse(str("error"))
moderator.page_changed(page,
- force_moderation_action=PageModeratorState.ACTION_CHANGED)
+ force_moderation_action=PageModeratorState.ACTION_CHANGED)
if page and 'reversion' in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(page)
- reversion.revision.user = request.user
- reversion.revision.comment = _(u"Plugins were moved")
-
+ helpers.make_revision_with_plugins(page, request.user, _(u"Plugins were moved"))
return HttpResponse(str("ok"))
@require_POST
@@ -1442,12 +1480,10 @@ def remove_plugin(self, request):
}
moderator.page_changed(page,
- force_moderation_action=PageModeratorState.ACTION_CHANGED)
+ force_moderation_action=PageModeratorState.ACTION_CHANGED)
if page and 'reversion' in settings.INSTALLED_APPS:
- helpers.make_revision_with_plugins(page)
- reversion.revision.user = request.user
- reversion.revision.comment = comment
+ helpers.make_revision_with_plugins(page, request.user, comment)
return HttpResponse("%s,%s" % (plugin_id, comment))
@@ -1456,6 +1492,7 @@ def lookup_allowed(self, key, *args, **kwargs):
return True
return super(PageAdmin, self).lookup_allowed(key, *args, **kwargs)
+
contribute_fieldsets(PageAdmin)
contribute_list_filter(PageAdmin)
View
82 cms/models/pagemodel.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
+from datetime import timedelta
+
from cms import constants
from cms.utils.conf import get_cms_setting
from django.core.exceptions import PermissionDenied
@@ -8,10 +10,9 @@
from cms.models.placeholdermodel import Placeholder
from cms.models.pluginmodel import CMSPlugin
from cms.publisher.errors import MpttPublisherCantPublish
-from cms.utils import i18n, urlutils, page as page_utils
+from cms.utils import i18n, page as page_utils
from cms.utils.copy_plugins import copy_plugins_to
from cms.utils.helpers import reversion_register
-from django.conf import settings
from django.contrib.sites.models import Site
from django.core.urlresolvers import reverse
from django.db import models
@@ -22,8 +23,6 @@
from menus.menu_pool import menu_pool
from mptt.models import MPTTModel
from os.path import join
-from datetime import timedelta
-import copy
class Page(MPTTModel):
@@ -49,19 +48,27 @@ class Page(MPTTModel):
creation_date = models.DateTimeField(auto_now_add=True)
changed_date = models.DateTimeField(auto_now=True)
- publication_date = models.DateTimeField(_("publication date"), null=True, blank=True, help_text=_('When the page should go live. Status must be "Published" for page to go live.'), db_index=True)
- publication_end_date = models.DateTimeField(_("publication end date"), null=True, blank=True, help_text=_('When to expire the page. Leave empty to never expire.'), db_index=True)
+ publication_date = models.DateTimeField(_("publication date"), null=True, blank=True, help_text=_(
+ 'When the page should go live. Status must be "Published" for page to go live.'), db_index=True)
+ publication_end_date = models.DateTimeField(_("publication end date"), null=True, blank=True,
+ help_text=_('When to expire the page. Leave empty to never expire.'),
+ db_index=True)
in_navigation = models.BooleanField(_("in navigation"), default=True, db_index=True)
- soft_root = models.BooleanField(_("soft root"), db_index=True, default=False, help_text=_("All ancestors will not be displayed in the navigation"))
- reverse_id = models.CharField(_("id"), max_length=40, db_index=True, blank=True, null=True, help_text=_("An unique identifier that is used with the page_url templatetag for linking to this page"))
+ soft_root = models.BooleanField(_("soft root"), db_index=True, default=False,
+ help_text=_("All ancestors will not be displayed in the navigation"))
+ reverse_id = models.CharField(_("id"), max_length=40, db_index=True, blank=True, null=True, help_text=_(
+ "An unique identifier that is used with the page_url templatetag for linking to this page"))
navigation_extenders = models.CharField(_("attached menu"), max_length=80, db_index=True, blank=True, null=True)
published = models.BooleanField(_("is published"), blank=True)
- template = models.CharField(_("template"), max_length=100, choices=template_choices, help_text=_('The template used to render the content.'))
+ template = models.CharField(_("template"), max_length=100, choices=template_choices,
+ help_text=_('The template used to render the content.'))
site = models.ForeignKey(Site, help_text=_('The site the page is accessible at.'), verbose_name=_("site"))
login_required = models.BooleanField(_("login required"), default=False)
- limit_visibility_in_menu = models.SmallIntegerField(_("menu visibility"), default=None, null=True, blank=True, choices=LIMIT_VISIBILITY_IN_MENU_CHOICES, db_index=True, help_text=_("limit when this page is visible in the menu"))
+ limit_visibility_in_menu = models.SmallIntegerField(_("menu visibility"), default=None, null=True, blank=True,
+ choices=LIMIT_VISIBILITY_IN_MENU_CHOICES, db_index=True,
+ help_text=_("limit when this page is visible in the menu"))
level = models.PositiveIntegerField(db_index=True, editable=False)
lft = models.PositiveIntegerField(db_index=True, editable=False)
@@ -94,9 +101,9 @@ class Meta:
class PublisherMeta:
exclude_fields_append = ['id', 'publisher_is_draft', 'publisher_public',
- 'publisher_state', 'moderator_state',
- 'placeholders', 'lft', 'rght', 'tree_id',
- 'parent']
+ 'publisher_state', 'moderator_state',
+ 'placeholders', 'lft', 'rght', 'tree_id',
+ 'parent']
def __unicode__(self):
title = self.get_menu_title(fallback=True)
@@ -131,8 +138,8 @@ def move_page(self, target, position='first-child'):
# make sure move_page does not break when using INHERIT template
# and moving to a top level position
if (position in ('left', 'right')
- and not target.parent
- and self.template == constants.TEMPLATE_INHERITANCE_MAGIC):
+ and not target.parent
+ and self.template == constants.TEMPLATE_INHERITANCE_MAGIC):
self.template = self.get_template()
self.move_to(target, position)
@@ -140,6 +147,7 @@ def move_page(self, target, position='first-child'):
from cms.models.moderatormodels import PageModeratorState
from cms.utils import moderator
import cms.signals as cms_signals
+
cms_signals.page_moved.send(sender=Page, instance=self) # titles get saved before moderation
self.save() # always save the page after move, because of publisher
moderator.page_changed(self, force_moderation_action=PageModeratorState.ACTION_MOVE)
@@ -159,6 +167,7 @@ def _copy_titles(self, target):
title.save()
if old_titles:
from titlemodels import Title
+
Title.objects.filter(id__in=old_titles.values()).delete()
def _copy_contents(self, target):
@@ -278,6 +287,7 @@ def copy_page(self, target, site, position='first-child',
# copy permissions if necessary
if get_cms_setting('PERMISSION') and copy_permissions:
from cms.models.permissionmodels import PagePermission
+
for permission in PagePermission.objects.filter(page__id=origin_id):
permission.pk = None
permission.page = page
@@ -332,6 +342,7 @@ def save(self, no_signals=False, commit=True, **kwargs):
self.reverse_id = None
from cms.utils.permissions import _thread_locals
+
user = getattr(_thread_locals, "user", None)
if user:
self.changed_by = user.username
@@ -459,6 +470,7 @@ def publish(self):
# fire signal after publishing is done
import cms.signals as cms_signals
+
cms_signals.post_publish.send(sender=Page, instance=self)
return published
@@ -507,7 +519,7 @@ def revert(self):
public = self.publisher_public
public._copy_titles(self)
if self.parent != (self.publisher_public.parent_id and
- self.publisher_public.parent.publisher_draft):
+ self.publisher_public.parent.publisher_draft):
# We don't send the signals here
self.move_to(public.parent.publisher_draft)
public._copy_contents(self)
@@ -615,6 +627,7 @@ def get_title_obj(self, language=None, fallback=True, version_id=None, force_rel
if language in self.title_cache:
return self.title_cache[language]
from cms.models.titlemodels import EmptyTitle
+
return EmptyTitle()
def get_title_obj_attribute(self, attrname, language=None, fallback=True, version_id=None, force_reload=False):
@@ -622,7 +635,7 @@ def get_title_obj_attribute(self, attrname, language=None, fallback=True, versio
"""
try:
attribute = getattr(self.get_title_obj(
- language, fallback, version_id, force_reload), attrname)
+ language, fallback, version_id, force_reload), attrname)
return attribute
except AttributeError:
return None
@@ -703,8 +716,10 @@ def _get_title_cache(self, language, fallback, version_id, force_reload):
load = True
if load:
from cms.models.titlemodels import Title
+
if version_id:
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()]
for rev in revs:
@@ -758,7 +773,7 @@ def has_view_permission(self, request):
if not self.publisher_is_draft:
return self.publisher_draft.has_view_permission(request)
- # does any restriction exist?
+ # does any restriction exist?
# inherited and direct
is_restricted = PagePermission.objects.for_page(page=self).filter(can_view=True).exists()
if request.user.is_authenticated():
@@ -776,7 +791,7 @@ def has_view_permission(self, request):
elif not is_restricted:
if ((get_cms_setting('PUBLIC_FOR') == 'all') or
(get_cms_setting('PUBLIC_FOR') == 'staff' and
- request.user.is_staff)):
+ request.user.is_staff)):
return True
# a restricted page and an authenticated user
@@ -795,7 +810,7 @@ def has_view_permission(self, request):
else:
# anonymous user, no restriction saved in database
return True
- # Authenticated user
+ # Authenticated user
# Django wide auth perms "can_view" or cms auth perms "can_view"
opts = self._meta
codename = '%s.view_%s' % (opts.app_label, opts.object_name.lower())
@@ -807,21 +822,22 @@ def has_change_permission(self, request):
if request.user.is_superuser:
return True
return request.user.has_perm(opts.app_label + '.' + opts.get_change_permission()) and \
- self.has_generic_permission(request, "change")
+ self.has_generic_permission(request, "change")
def has_delete_permission(self, request):
opts = self._meta
if request.user.is_superuser:
return True
return request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()) and \
- self.has_generic_permission(request, "delete")
+ self.has_generic_permission(request, "delete")
def has_publish_permission(self, request):
if request.user.is_superuser:
return True
opts = self._meta
return request.user.has_perm(opts.app_label + '.' + "publish_page") and \
- self.has_generic_permission(request, "publish")
+ self.has_generic_permission(request, "publish")
+
has_moderate_permission = has_publish_permission
def has_advanced_settings_permission(self, request):
@@ -851,11 +867,12 @@ def has_generic_permission(self, request, perm_type):
"""
att_name = "permission_%s_cache" % perm_type
if not hasattr(self, "permission_user_cache") or not hasattr(self, att_name) \
- or request.user.pk != self.permission_user_cache.pk:
+ or request.user.pk != self.permission_user_cache.pk:
from cms.utils.permissions import has_generic_permission
+
self.permission_user_cache = request.user
setattr(self, att_name, has_generic_permission(
- self.id, request.user, perm_type, self.site_id))
+ self.id, request.user, perm_type, self.site_id))
if getattr(self, att_name):
self.permission_edit_cache = True
return getattr(self, att_name)
@@ -880,6 +897,7 @@ def set_home_pk_cache(self, value):
attr = "%s_home_pk_cache_%s" % (self.publisher_is_draft and "draft" or "public", self.site_id)
setattr(self, attr, value)
+
home_pk_cache = property(get_home_pk_cache, set_home_pk_cache)
def clear_home_pk_cache(self):
@@ -913,6 +931,7 @@ def delete_requested(self):
Uses the same cache as last_page_states to minimize DB requests
"""
from cms.models import PageModeratorState
+
result = getattr(self, '_moderator_state_cache', None)
if result is None:
return self.pagemoderatorstate_set.get_delete_actions().exists()
@@ -927,7 +946,7 @@ def is_public_published(self):
if hasattr(self, '_public_published_cache'):
# if it was cached in change list, return cached value
return self._public_published_cache
- # If we have a public version it will be published as well.
+ # If we have a public version it will be published as well.
# If it isn't published, it should be deleted.
return self.published and self.publisher_public_id and self.publisher_public.published
@@ -966,7 +985,7 @@ def get_next_filtered_sibling(self, **filters):
})
else:
filters.update({
- opts.parent_attr: getattr(self, '%s_id' % opts.parent_attr),
+ opts.parent_attr: getattr(self, '%s_id' % opts.parent_attr),
'%s__gt' % opts.left_attr: getattr(self, opts.right_attr),
})
@@ -1000,7 +1019,7 @@ def get_previous_filtered_sibling(self, **filters):
order_by = '-%s' % opts.tree_id_attr
else:
filters.update({
- opts.parent_attr: getattr(self, '%s_id' % opts.parent_attr),
+ opts.parent_attr: getattr(self, '%s_id' % opts.parent_attr),
'%s__lt' % opts.right_attr: getattr(self, opts.left_attr),
})
order_by = '-%s' % opts.right_attr
@@ -1049,8 +1068,8 @@ def _publisher_save_public(self, obj):
# check if object was moved / structural tree change
prev_public_sibling = obj.get_previous_filtered_sibling()
if self.level != obj.level or \
- public_parent != obj.parent or \
- public_prev_sib != prev_public_sibling:
+ public_parent != obj.parent or \
+ public_prev_sib != prev_public_sibling:
if public_prev_sib:
obj.move_to(public_prev_sib, position="right")
elif public_parent:
@@ -1070,6 +1089,7 @@ def rescan_placeholders(self):
"""
# inline import to prevent circular imports
from cms.utils.plugins import get_placeholders
+
placeholders = get_placeholders(self.get_template())
found = {}
for placeholder in self.placeholders.all():
@@ -1090,4 +1110,6 @@ def _reversion():
follow=["title_set", "placeholders", "pagepermission_set"],
exclude_fields=exclude_fields
)
+
+
_reversion()
View
71 cms/tests/reversion_tests.py
@@ -1,25 +1,46 @@
# -*- coding: utf-8 -*-
from __future__ import with_statement
+import shutil
+
from cms.models import Page, Title
from cms.models.pluginmodel import CMSPlugin
from cms.plugins.text.models import Text
from cms.test_utils.project.fileapp.models import FileModel
-from cms.test_utils.testcases import (CMSTestCase, URL_CMS_PAGE,
+from cms.test_utils.testcases import (CMSTestCase, URL_CMS_PAGE,
URL_CMS_PAGE_CHANGE, URL_CMS_PAGE_ADD, URL_CMS_PLUGIN_ADD, URL_CMS_PLUGIN_EDIT)
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.files.uploadedfile import SimpleUploadedFile
from os.path import join
-from cms.test_utils.project.fileapp.models import FileModel
import reversion
from reversion.models import Revision, Version, VERSION_CHANGE
-import shutil
+
+
+class BasicReversionTestCase(CMSTestCase):
+ def setUp(self):
+ u = User(username="test", is_staff=True, is_active=True,
+ is_superuser=True)
+ u.set_password("test")
+ u.save()
+ self.user = u
+
+ def test_number_revisions(self):
+ with self.login_user_context(self.user):
+ self.assertEquals(Revision.objects.all().count(), 0)
+ self.page_data = self.get_new_page_data()
+
+ response = self.client.post(URL_CMS_PAGE_ADD, self.page_data)
+
+ self.assertRedirects(response, URL_CMS_PAGE)
+ self.assertEquals(Page.objects.all().count(), 1)
+ self.assertEquals(Revision.objects.all().count(), 1)
+
class ReversionTestCase(CMSTestCase):
def setUp(self):
u = User(username="test", is_staff=True, is_active=True,
- is_superuser=True)
+ is_superuser=True)
u.set_password("test")
u.save()
@@ -32,27 +53,27 @@ def setUp(self):
page = Page.objects.all()[0]
placeholderpk = page.placeholders.get(slot="body").pk
plugin_data = {
- 'plugin_type':"TextPlugin",
- 'page_id':page.pk,
- 'language':settings.LANGUAGES[0][0],
- 'placeholder':placeholderpk,
+ 'plugin_type': "TextPlugin",
+ 'page_id': page.pk,
+ 'language': settings.LANGUAGES[0][0],
+ 'placeholder': placeholderpk,
}
response = self.client.post(URL_CMS_PLUGIN_ADD, plugin_data)
self.assertEquals(response.status_code, 200)
self.assertEquals(int(response.content),
- CMSPlugin.objects.all()[0].pk)
+ CMSPlugin.objects.all()[0].pk)
# now edit the plugin
edit_url = URL_CMS_PLUGIN_EDIT + response.content + "/"
response = self.client.get(edit_url)
self.assertEquals(response.status_code, 200)
- response = self.client.post(edit_url, {"body":"Hello World"})
+ response = self.client.post(edit_url, {"body": "Hello World"})
self.assertEquals(response.status_code, 200)
txt = Text.objects.all()[0]
self.assertEquals("Hello World", txt.body)
-
+ self.txt = txt
# change the content
- response = self.client.post(edit_url, {"body":"Bye Bye World"})
+ response = self.client.post(edit_url, {"body": "Bye Bye World"})
self.assertEquals(response.status_code, 200)
txt = Text.objects.all()[0]
self.assertEquals("Bye Bye World", txt.body)
@@ -69,7 +90,7 @@ def test_revert(self):
self.assertEquals(Page.objects.all().count(), 2)
self.assertEquals(Title.objects.all().count(), 2)
self.assertEquals(CMSPlugin.objects.all().count(), 2)
- self.assertEquals(Revision.objects.all().count(), 7)
+ self.assertEquals(Revision.objects.all().count(), 5)
ctype = ContentType.objects.get_for_model(Page)
revision = Revision.objects.all()[2]
@@ -95,14 +116,15 @@ def test_revert(self):
# test that CMSPlugin subclasses are reverted
self.assertEquals(Text.objects.all().count(), 2)
- self.assertEquals(Revision.objects.all().count(), 9)
+ self.assertEquals(Text.objects.get(pk=self.txt.pk).body, "Hello World")
+ self.assertEquals(Revision.objects.all().count(), 6)
def test_recover(self):
"""
Test that you can recover a page
"""
with self.login_user_context(User.objects.get(username="test")):
- self.assertEquals(Revision.objects.all().count(), 7)
+ self.assertEquals(Revision.objects.all().count(), 5)
ctype = ContentType.objects.get_for_model(Page)
revision = Revision.objects.all()[4]
version = Version.objects.get(content_type=ctype, revision=revision)
@@ -134,6 +156,17 @@ def test_recover(self):
# test that CMSPlugin subclasses are recovered
self.assertEquals(Text.objects.all().count(), 1)
+ def test_publish(self):
+ with self.login_user_context(User.objects.get(username="test")):
+ page = Page.objects.all()[0]
+ page_pk = page.pk
+ self.assertEquals(Revision.objects.all().count(), 5)
+ publish_url = URL_CMS_PAGE + "%s/publish/" % page_pk
+ response = self.client.get(publish_url)
+ self.assertEquals(response.status_code, 302)
+ self.assertEquals(Revision.objects.all().count(), 2)
+
+
class ReversionFileFieldTests(CMSTestCase):
def tearDown(self):
shutil.rmtree(join(settings.MEDIA_ROOT, 'fileapp'))
@@ -142,16 +175,14 @@ def test_file_persistence(self):
with reversion.create_revision():
# add a file instance
file1 = FileModel()
- file1.test_file.save('file1.txt',
- SimpleUploadedFile('file1.txt', 'content1'), False)
+ file1.test_file.save('file1.txt', SimpleUploadedFile('file1.txt', 'content1'), False)
file1.save()
# manually add a revision because we use the explicit way
# django-cms uses too.
adapter = reversion.get_adapter(FileModel)
reversion.revision_context_manager.add_to_context(
- reversion.default_revision_manager, file1,
- adapter.get_version_data(file1, VERSION_CHANGE))
-
+ reversion.default_revision_manager, file1,
+ adapter.get_version_data(file1, VERSION_CHANGE))
# reload the instance from db
file2 = FileModel.objects.all()[0]
# delete the instance.
View
1  cms/utils/conf.py
@@ -53,6 +53,7 @@ def wrapper():
'UNIHANDECODE_VERSION': None,
'UNIHANDECODE_DECODERS': ['ja', 'zh', 'kr', 'vn', 'diacritic'],
'UNIHANDECODE_DEFAULT_DECODER': 'diacritic',
+ 'MAX_PAGE_PUBLISH_REVERSIONS': 25,
}
def get_cache_durations():
View
6 cms/utils/helpers.py
@@ -51,7 +51,11 @@ def make_revision_with_plugins(obj, user=None, message=None):
placeholder_relation = find_placeholder_relation(obj)
- if revision_context.is_active():
+ if revision_context.is_active():
+ if user:
+ revision_context.set_user(user)
+ if message:
+ revision_context.set_comment(message)
# add toplevel object to the revision
adapter = revision_manager.get_adapter(obj.__class__)
revision_context.add_to_context(revision_manager, obj, adapter.get_version_data(obj, VERSION_CHANGE))
View
15 docs/getting_started/configuration.rst
@@ -634,4 +634,19 @@ Example::
:ref:`cache key prefixing <django:cache_key_prefixing>`
+.. setting::CMS_MAX_PAGE_PUBLISH_REVERSIONS
+
+CMS_MAX_PAGE_PUBLISH_REVERSIONS
+===============================
+
+Default: 25
+
+If `django-reversion`_ is installed everything you do with a page and all plugin changes will be saved in a revision.
+In the page admin there is a history button to revert to previous version of a page. In the past we had the problem
+with huge databases from the revision tables after some time. As a mitigation when you publish a page all revisions
+that are not publish revision will be deleted. This setting however declares how many publish revisions are saved in the
+database. By default the newest 25 publish revisions are kept and all other are deleted when you publish a page.
+If you set this to 0 all publish revisions are kept but you are responsible to keep the revision table small.
+
+.. _django-reversion: https://github.com/etianen/django-reversion
.. _unihandecode.js: https://github.com/ojii/unihandecode.js
View
5 docs/getting_started/installation.rst
@@ -46,6 +46,11 @@ Recommended
django-reversion wiki in order to make sure that the package versions are
compatible.)
+ .. note::
+
+ Since django CMS 2.4 we only save the latest 25 publish revisions anymore.
+ You can change this behavior with :setting:`CMS_MAX_PAGE_PUBLISH_REVERSIONS`.
+
.. _django-filer: https://github.com/stefanfoulis/django-filer
.. _django CMS plugins: https://github.com/stefanfoulis/cmsplugin-filer
.. _django-reversion: https://github.com/etianen/django-reversion
View
50 docs/upgrade/2.4.rst
@@ -13,7 +13,7 @@ What's new in 2.4
2.4 introduces some changes that **require** action if you are upgrading
from a previous version.
-
+
You will need to read the sections :ref:`migrations-upgrade` and
:ref:`cms-moderator-upgrade` below.
@@ -73,7 +73,7 @@ Added a management command for deleting orphaned plugins from the database.
The command can be run with::
- manage.py cms delete_orphaned_plugins
+ manage.py cms delete_orphaned_plugins
Please read :ref:`cms-delete-orphaned-plugins-command` before using.
@@ -114,8 +114,8 @@ Added a management command for fixing MPTT tree data.
The command can be run with::
- manage.py cms fix-mptt
-
+ manage.py cms fix-mptt
+
Removed the MultilingualMiddleware
==================================
@@ -256,14 +256,14 @@ The column plugin can be found here:
At the moment the limitation is that plugins in plugins is only editable in the frontend.
Here is the MultiColumn Plugin as an example::
-
- class MultiColumnPlugin(CMSPluginBase):
- model = MultiColumns
- name = _("Multi Columns")
- render_template = "cms/plugins/multi_column.html"
- allow_children = True
- child_classes = ["ColumnPlugin"]
-
+
+ class MultiColumnPlugin(CMSPluginBase):
+ model = MultiColumns
+ name = _("Multi Columns")
+ render_template = "cms/plugins/multi_column.html"
+ allow_children = True
+ child_classes = ["ColumnPlugin"]
+
There are 2 new properties for plugins:
**allow_children**
@@ -282,13 +282,13 @@ How to render your child plugins in the template
We introduce a new templatetag in the cms_tags called ``{% render_plugin %}``
Here is an example of how the MultiColumn plugin uses it::
-
- {% load cms_tags %}
- <div class="multicolumn">
- {% for plugin in instance.child_plugins %}
- {% render_plugin plugin %}
- {% endfor %}
- </div>
+
+ {% load cms_tags %}
+ <div class="multicolumn">
+ {% for plugin in instance.child_plugins %}
+ {% render_plugin plugin %}
+ {% endfor %}
+ </div>
As you can see the children are accessible via the plugins children attribute.
@@ -327,6 +327,16 @@ which used to live in ``cms.conf.global_settings`` but was moved to the new
``cms.constants`` module in the settings overhaul mentioned above.
+django-reversion integration changes
+====================================
+
+`django-reversion`_ integration has changed. Because of huge databases after some time we introduce some changes
+to the way revisions are handled for pages.
+
+ 1. Only publish revisions are saved. All other revisions are deleted when you publish a page.
+ 2. By default only the latest 25 publish revisions are kept. You can change this behavior with the new
+ :setting:`CMS_MAX_PAGE_PUBLISH_REVERSIONS` setting.
+
******************************
Backwards incompatible changes
******************************
@@ -341,5 +351,5 @@ New minimum requirements for dependencies
Pending deprecations
********************
-* ``simple_language_changer`` will be removed in version 2.5. A bugfix makes
+* ``simple_language_changer`` will be removed in version 3.0. A bugfix makes
this redundant as every non managed url will behave like this.
View
2  setup.py
@@ -36,7 +36,7 @@
'django-sekizai>=0.7',
],
tests_require=[
- 'django-reversion>=1.6',
+ 'django-reversion>=1.6.6',
'Pillow==1.7.7',
'Sphinx==1.1.3',
'Jinja2==2.6',
Please sign in to comment.
Something went wrong with that request. Please try again.