Permalink
Browse files

- permission caching with cache backend

- publish on new object bug fix
- several minor fixes
  • Loading branch information...
1 parent 5146abb commit 2d809a5c6e238e5c7e89114bc9cbbfbecda673a1 @pcicman pcicman committed Jun 16, 2009
View
@@ -30,7 +30,7 @@
edit_plugin, remove_plugin, move_plugin, revert_plugins, change_moderation)
from cms.admin.widgets import PluginEditor
from cms.models import Page, Title, CMSPlugin, PagePermission,\
- PageModeratorState
+ PageModeratorState, EmptyTitle
from cms.plugin_pool import plugin_pool
from cms.utils import get_template_from_request, get_language_from_request
from cms.utils.permissions import has_page_add_permission,\
@@ -619,7 +619,24 @@ def change_view(self, request, object_id, extra_context=None):
'moderation_delete_request': moderation_delete_request,
}
+
return super(PageAdmin, self).change_view(request, object_id, extra_context)
+
+ def response_add(self, request, obj, post_url_continue='../%s/'):
+ """Called always when new object gets created, there may be some new
+ stuff, which should be published after all other objects on page are
+ collected. E.g. title, plugins, etc...
+ """
+ obj.save(commit=False)
+ return super(PageAdmin, self).response_add(request, obj, post_url_continue)
+
+ def response_change(self, request, obj):
+ """Called always when page gets changed, call save on page, there may be
+ some new stuff, which should be published after all other objects on page
+ are collected.
+ """
+ obj.save(commit=False)
+ return super(PageAdmin, self).response_change(request, obj)
def has_add_permission(self, request):
"""
View
No changes.
View
@@ -0,0 +1,32 @@
+from django.core.cache import cache
+
+# Time to live for cachce entry 2 minutes, so it gets cleaned if we don't catch
+# something - don't make higher; groups may be problematic because of no signals
+# when adding / removing from group
+TTL = 120
+
+permission_cache_keys = []
+
+get_cache_key = lambda user, key: "%s::%s" % (user.username, key)
+
+def get_permission_cache(user, key):
+ """Helper for reading values from cache
+ """
+ return cache.get(get_cache_key(user, key))
+
+def set_permission_cache(user, key, value):
+ """Helper method for storing values in cache. Stores used keys so
+ all of them can be cleaned when clean_permission_cache gets called.
+ """
+ # store this key, so we can clean it when required
+ if not key in permission_cache_keys:
+ permission_cache_keys.append(key)
+ cache.set(get_cache_key(user, key), value, TTL)
+
+
+def clear_permission_cache(user):
+ """Cleans permission cache for given user.
+ """
+ for key in permission_cache_keys:
+ cache.delete(get_cache_key(user, key))
+
View
@@ -0,0 +1,51 @@
+from django.db.models import signals
+from django.contrib.auth.models import User, Group
+from cms import settings
+from cms.models import PagePermission, GlobalPagePermission
+from cms.cache.permissions import clear_permission_cache
+
+def pre_save_user(instance, raw, **kwargs):
+ clear_permission_cache(instance)
+
+def pre_delete_user(instance, **kwargs):
+ clear_permission_cache(instance)
+
+def pre_save_group(instance, raw, **kwargs):
+ if instance.pk:
+ for user in instance.user_set.filter(is_staff=True):
+ clear_permission_cache(user)
+
+def pre_delete_group(instance, **kwargs):
+ for user in instance.user_set.filter(is_staff=True):
+ clear_permission_cache(user)
+
+def pre_save_pagepermission(instance, raw, **kwargs):
+ if instance.user:
+ clear_permission_cache(instance.user)
+
+def pre_delete_pagepermission(instance, **kwargs):
+ if instance.user:
+ clear_permission_cache(instance.user)
+
+def pre_save_globalpagepermission(instance, raw, **kwargs):
+ if instance.user:
+ clear_permission_cache(instance.user)
+
+def pre_delete_globalpagepermission(instance, **kwargs):
+ if instance.user:
+ clear_permission_cache(instance.user)
+
+
+if settings.CMS_PERMISSION:
+ # TODO: will this work also with PageUser and PageGroup??
+ signals.pre_save.connect(pre_save_user, sender=User)
+ signals.pre_delete.connect(pre_delete_user, sender=User)
+
+ signals.pre_save.connect(pre_save_group, sender=Group)
+ signals.pre_delete.connect(pre_delete_group, sender=Group)
+
+ signals.pre_save.connect(pre_save_pagepermission, sender=PagePermission)
+ signals.pre_delete.connect(pre_delete_pagepermission, sender=PagePermission)
+
+ signals.pre_save.connect(pre_save_globalpagepermission, sender=GlobalPagePermission)
+ signals.pre_delete.connect(pre_delete_globalpagepermission, sender=GlobalPagePermission)
@@ -13,7 +13,7 @@ $(document).ready(function() {
if($("#id_title")[0]._changed){
changed = true;
}
- if($("#id_status")[0]._changed){
+ if($("#id_published")[0]._changed){
changed = true;
}
if($('iframe').length){
@@ -48,7 +48,7 @@ $(document).ready(function() {
}
$("#id_slug").change(function() { this._changed = true; });
$('#id_title').change(function() {this._changed = true; })
- $('#id_status').change(function() {this._changed = true; })
+ $('#id_published').change(function() {this._changed = true; })
$("#id_title").keyup(function() {
var e = $("#id_slug")[0];
if (!e._changed && new_slug) {
@@ -123,6 +123,9 @@ $(document).ready(function() {
if (callback) callback(response);
var target = $(el).parents('div.cont:first');
var parent = target.parent();
+ if (response == "NotFound") {
+ return parent.remove();
+ }
target.replace(response);
parent.find('div.cont:first').yft();
});
View
@@ -215,7 +215,7 @@ def copy_page(self, target, site, position='first-child', copy_permissions=True,
if dif != 0:
tree.append(page)
- def save(self, no_signals=False, change_state=True):
+ def save(self, no_signals=False, change_state=True, commit=True):
# Published pages should always have a publication date
#print "save()", no_signals, change_state
publish_directly = False
@@ -247,10 +247,11 @@ def save(self, no_signals=False, change_state=True):
if self.reverse_id == "":
self.reverse_id = None
- if no_signals:# ugly hack because of mptt
- super(Page, self).save_base(cls=self.__class__)
- else:
- super(Page, self).save()
+ if commit:
+ if no_signals:# ugly hack because of mptt
+ super(Page, self).save_base(cls=self.__class__)
+ else:
+ super(Page, self).save()
if publish_directly or \
created and self.pk and not self.get_moderator_queryset().count():
@@ -495,10 +496,10 @@ def has_generic_permission(self, request, type):
Return true if the current user has permission on the page.
Return the string 'All' if the user has all rights.
"""
- if not request.user.is_authenticated() or not request.user.is_staff:
- return False
- if not settings.CMS_PERMISSION or request.user.is_superuser:
- return True
+ #if not request.user.is_authenticated() or not request.user.is_staff:
+ # return False
+ #if not settings.CMS_PERMISSION or request.user.is_superuser:
+ # return True
att_name = "permission_%s_cache" % type
if not hasattr(self, "permission_user_cache") or not hasattr(self, att_name) or request.user.pk != self.permission_user_cache.pk:
View
@@ -6,6 +6,7 @@
from cms.utils.urlutils import levelize_path
from cms.exceptions import NoPermissionsException
from sets import Set
+from cms.cache.permissions import get_permission_cache, set_permission_cache
class PageManager(models.Manager):
def on_site(self):
@@ -377,14 +378,21 @@ def __get_id_list(self, user, attr):
# TODO: result of this method should be cached per user, and cache should
# be cleaned after some change in permissions / globalpermission
+ if not user.is_authenticated() or not user.is_staff:
+ return []
+
if user.is_superuser or not settings.CMS_PERMISSION:
# got superuser, or permissions aren't enabled? just return grant
# all mark
return PagePermissionsPermissionManager.GRANT_ALL
+ # read from cache if posssible
+ cached = get_permission_cache(user, attr)
+ if cached is not None:
+ return cached
+
from cms.models import GlobalPagePermission, PagePermission, MASK_PAGE,\
MASK_CHILDREN, MASK_DESCENDANTS
-
# check global permissions
in_global_permissions = GlobalPagePermission.objects.with_user(user).filter(**{attr: True})
if in_global_permissions:
@@ -409,7 +417,10 @@ def __get_id_list(self, user, attr):
page_id_allow_list.extend(permission.page.get_children().values_list('id', flat=True))
elif permission.grant_on & MASK_DESCENDANTS:
page_id_allow_list.extend(permission.page.get_descendants().values_list('id', flat=True))
- #print "> perm u:", user, "attr:", attr, page_id_allow_list
+ print "> perm attr:", attr, "R:", page_id_allow_list
+
+ # store value in cache
+ set_permission_cache(user, attr, page_id_allow_list)
return page_id_allow_list
View
@@ -166,7 +166,7 @@ def post_save_user_group(instance, raw, created, **kwargs):
# regster signals to user related models
signals.post_save.connect(post_save_user, User)
signals.post_save.connect(post_save_user_group, Group)
-
+
def pre_save_page(instance, raw, **kwargs):
"""Helper pre save signal, assigns old_page attribute, so we can still
@@ -194,4 +194,4 @@ def post_save_page(instance, raw, created, **kwargs):
signals.post_save.connect(post_save_page, sender=Page)
-
+from cache import signals
View
@@ -6,6 +6,7 @@
from django.template.context import RequestContext
from cms.utils.permissions import has_add_page_on_same_level_permission,\
has_page_add_permission
+from django.http import HttpResponse, Http404
def get_admin_menu_item_context(request, page, filtered=False):
@@ -59,11 +60,16 @@ def get_admin_menu_item_context(request, page, filtered=False):
return context
+NOT_FOUND_RESPONSE = "NotFound"
+
def render_admin_menu_item(request, page):
"""Renders requested page item for the tree. This is used in case when item
must be reloaded over ajax.
"""
+ if not page.pk:
+ return HttpResponse(NOT_FOUND_RESPONSE) # Not found - tree will remove item
+
context = RequestContext(request, {
'has_add_permission': has_page_add_permission(request),
})
View
@@ -167,6 +167,7 @@ def has_add_page_on_same_level_permission(request, page):
or GlobalPagePermission.objects.with_user(request.user).filter(can_add=True).count():
return True
try:
+ # SQL: 7 queries lost
return page.parent.has_add_permission(request)
except AttributeError:
# if page doesnt have parent...

0 comments on commit 2d809a5

Please sign in to comment.