# HG changeset patch # User Sverre Rabbelier # Date 1233097141 0 # Node ID 6ad4fdb4884024f05bd5a42a41767bf94da12c94 # Parent 15a2f644725f958b103de6fa9f28b429eb3b9e15 Cache access checks and disable sidebar caching Patch by: Sverre Rabbelier diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/cache/base.py --- a/app/soc/cache/base.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/cache/base.py Tue Jan 27 22:59:01 2009 +0000 @@ -43,7 +43,7 @@ return result result = func(*args, **kwargs) - put(result) + put(result, *args, **kwargs) return result return wrapper diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/cache/sidebar.py --- a/app/soc/cache/sidebar.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/cache/sidebar.py Tue Jan 27 22:59:01 2009 +0000 @@ -28,34 +28,33 @@ import soc.cache.base -def key(user): +def key(id): """Returns the memcache key for the user's sidebar """ - return 'sidebar_for_%s' % repr(user) + return 'sidebar_for_%s' % repr(id) -def get(): +def get(id, user): """Retrieves the sidebar for the specified user from the memcache """ - user = users.get_current_user() - return memcache.get(key(user)) + memcache_key = key(id) + return memcache.get(memcache_key) -def put(sidebar): +def put(sidebar, id, user): """Sets the sidebar for the specified user in the memcache Args: sidebar: the sidebar to be cached """ - # Store sidebar for an hour since new programs might get added - # etc. etc. - retention = 60*60 + # Store sidebar for ten minutes since new programs might get added + retention = 10*60 - user = users.get_current_user() - memcache.add(key(user), sidebar, retention) + memcache_key = key(id) + memcache.add(memcache_key, sidebar, retention) def flush(user=None): @@ -68,7 +67,8 @@ if not user: user = users.get_current_user() - memcache.delete(key(user)) + memcache_key = key(user) + memcache.delete(memcache_key) # define the cache function diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/helper/access.py --- a/app/soc/views/helper/access.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/helper/access.py Tue Jan 27 22:59:01 2009 +0000 @@ -32,6 +32,7 @@ from google.appengine.api import users +from google.appengine.api import memcache from django.core import urlresolvers from django.utils.translation import ugettext @@ -117,6 +118,9 @@ base = params.get('rights') if params else None self.rights = base.rights if base else {} + self.id = None + self.user = None + self.cached_rights = {} def __setitem__(self, key, value): """Sets a value only if no old value exists. @@ -140,14 +144,81 @@ # Be nice an repack so that it is always a list with tuples if isinstance(i, tuple): name, arg = i - tmp = (getattr(self, name), (arg if isinstance(arg, list) else [arg])) + tmp = (name, (arg if isinstance(arg, list) else [arg])) result.append(tmp) else: - tmp = (getattr(self, i), []) + tmp = (i, []) result.append(tmp) return result + def key(self, checker_name): + """Returns the key for the specified checker for the current user. + """ + + return "%s.%s" % (self.id, checker_name) + + def put(self, checker_name, value): + """Puts the result for the specified checker in the cache. + """ + + retention = 30 + + memcache_key = self.key(checker_name) + memcache.add(memcache_key, value, retention) + + def get(self, checker_name): + """Retrieves the result for the specified checker from cache. + """ + + memcache_key = self.key(checker_name) + return memcache.get(memcache_key) + + def doCheck(self, checker_name, django_args, args): + """Runs the specified checker with the specified arguments. + """ + + checker = getattr(self, checker_name) + checker(django_args, *args) + + def doCachedCheck(self, checker_name, django_args, args): + """Retrieves from cache or runs the specified checker. + """ + + cached = self.get(checker_name) + + if cached is None: + try: + self.doCheck(checker_name, django_args, args) + self.put(checker_name, True) + return + except out_of_band.Error, e: + self.put(checker_name, e) + raise + + if cached is True: + return + + # re-raise the cached exception + raise cached + + def check(self, use_cache, checker_name, django_args, args): + """Runs the checker, optionally using the cache. + """ + + if use_cache: + self.doCachedCheck(checker_name, django_args, args) + else: + self.doCheck(checker_name, django_args, args) + + def setCurrentUser(self, id, user): + """Sets up everything for the current user. + """ + + self.id = id + self.user = user + self.cached_rights = {} + def checkAccess(self, access_type, django_args): """Runs all the defined checks for the specified type. @@ -167,20 +238,20 @@ value are called. """ - self.id = users.get_current_user() + use_cache = django_args.get('SIDEBAR_CALLING') # Call each access checker - for check, args in self['any_access']: - check(django_args, *args) + for checker_name, args in self['any_access']: + self.check(use_cache, checker_name, django_args, args) if access_type not in self.rights: - for check, args in self['unspecified']: - # No checks defined, so do the 'generic' checks and bail out - check(django_args, *args) + # No checks defined, so do the 'generic' checks and bail out + for checker_name, args in self['unspecified']: + self.check(use_cache, checker_name, django_args, args) return - for check, args in self[access_type]: - check(django_args, *args) + for checker_name, args in self[access_type]: + self.check(use_cache, checker_name, django_args, args) def allow(self, django_args): """Never raises an alternate HTTP response. (an access no-op, basically). @@ -253,12 +324,10 @@ self.checkIsLoggedIn(django_args) - user = user_logic.getForCurrentAccount() - - if not user: + if not self.user: raise out_of_band.LoginRequest(message_fmt=DEF_NO_USER_LOGIN_MSG_FMT) - if user_logic.agreesToSiteToS(user): + if user_logic.agreesToSiteToS(self.user): return # Would not reach this point of site-wide ToS did not exist, since diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/helper/decorators.py --- a/app/soc/views/helper/decorators.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/helper/decorators.py Tue Jan 27 22:59:01 2009 +0000 @@ -97,10 +97,16 @@ check_kwargs = kwargs.copy() context = responses.getUniversalContext(request) + id = context['account'] + user = context['user'] + check_kwargs['GET'] = request.GET check_kwargs['POST'] = request.POST check_kwargs['context'] = context + # reset and pre-fill the Checker's cache + rights.setCurrentUser(id, user) + # Do the access check dance try: rights.checkAccess(access_type, check_kwargs) diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/helper/responses.py --- a/app/soc/views/helper/responses.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/helper/responses.py Tue Jan 27 22:59:01 2009 +0000 @@ -96,21 +96,26 @@ """ account = users.get_current_user() + user = None + is_admin = False context = {} context['request'] = request if account: - context['account'] = account - context['user'] = soc.logic.models.user.logic.getForFields( + user = soc.logic.models.user.logic.getForFields( {'account': account}, unique=True) - context['is_admin'] = accounts.isDeveloper(account=account) + is_admin = accounts.isDeveloper(account=account) + + context['account'] = account + context['user'] = user + context['is_admin'] = is_admin context['is_debug'] = system.isDebug() context['sign_in'] = users.create_login_url(request.path) context['sign_out'] = users.create_logout_url(request.path) - context['sidebar_menu_items'] = sidebar.getSidebar() + context['sidebar_menu_items'] = sidebar.getSidebar(account, user) context['soc_release'] = release.RELEASE_TAG context['gae_version'] = system.getAppVersion() diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/models/base.py --- a/app/soc/views/models/base.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/models/base.py Tue Jan 27 22:59:01 2009 +0000 @@ -726,7 +726,7 @@ return self._params @decorators.merge_params - def getSidebarMenus(self, params=None): + def getSidebarMenus(self, id, user, params=None): """Returns an dictionary with one sidebar entry. Args: @@ -738,7 +738,7 @@ of _getSidebarItems on how it uses it. """ - return sitemap.sidebar.getSidebarMenus(params=params) + return sitemap.sidebar.getSidebarMenus(id, user, params=params) @decorators.merge_params def getDjangoURLPatterns(self, params=None): diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/models/group.py --- a/app/soc/views/models/group.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/models/group.py Tue Jan 27 22:59:01 2009 +0000 @@ -262,7 +262,7 @@ role_views = self._params['role_views'] role_views[role_name] = role_view - def getExtraMenus(self, params=None): + def getExtraMenus(self, id, user, params=None): """Returns the extra menu's for this view. A menu item is generated for each group that the user has an active @@ -276,11 +276,8 @@ params = dicts.merge(params, self._params) logic = params['logic'] - # get the current user - user_entity = user_logic.logic.getForCurrentAccount() - # set fields to match every active role this user has - fields = {'user': user_entity, + fields = {'user': user, 'state' : 'active'} # get the role views and start filling group_entities diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/models/program.py --- a/app/soc/views/models/program.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/models/program.py Tue Jan 27 22:59:01 2009 +0000 @@ -116,7 +116,7 @@ timeline = timeline_logic.updateOrCreateFromFields(properties, properties) return timeline - def getExtraMenus(self, params=None): + def getExtraMenus(self, id, user, params=None): """Returns the extra menu's for this view. A menu item is generated for each program that is currently diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/models/site.py --- a/app/soc/views/models/site.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/models/site.py Tue Jan 27 22:59:01 2009 +0000 @@ -87,13 +87,13 @@ super(View, self).__init__(params=params) - def getSidebarMenus(self, params=None): + def getSidebarMenus(self, id, user, params=None): """See base.View.getSidebarMenus. Returns a custom sidebar entry for the 'site' singleton. """ - entity = self._logic.getFromFields(link_id=self._logic.DEF_SITE_LINK_ID) + entity = self._logic.getSingleton() submenus = [] @@ -104,7 +104,7 @@ new_params['sidebar_additional'] = submenus params = dicts.merge(params, new_params) - return super(View, self).getSidebarMenus(params=params) + return super(View, self).getSidebarMenus(id, user, params=params) def mainPublic(self, request, page_name=None, **kwargs): """Displays the main site settings page. diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/models/user_self.py --- a/app/soc/views/models/user_self.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/models/user_self.py Tue Jan 27 22:59:01 2009 +0000 @@ -252,14 +252,12 @@ super(View, self)._editPost(request, entity, fields) - def getSidebarMenus(self, params=None): + def getSidebarMenus(self, id, user, params=None): """See base.View.getSidebarMenus(). """ link_title = ugettext('Notifications') - user = user_logic.getForCurrentAccount() - filter = { 'scope': user, 'unread': True, @@ -279,7 +277,7 @@ params = dicts.merge(params, new_params) - return super(View, self).getSidebarMenus(params=params) + return super(View, self).getSidebarMenus(id, user, params=params) view = View() diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/out_of_band.py --- a/app/soc/views/out_of_band.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/out_of_band.py Tue Jan 27 22:59:01 2009 +0000 @@ -32,7 +32,7 @@ TEMPLATE_NAME = 'error.html' DEF_TEMPLATE = 'soc/error.html' - def __init__(self, message_fmt, context=None, **response_args): + def __init__(self, message_fmt=None, context=None, **response_args): """Constructor used to set response message and HTTP response arguments. Args: @@ -46,6 +46,9 @@ set the HTTP status code for the response """ + if not message_fmt: + message_fmt = "" + self.message_fmt = message_fmt self.context = context self.response_args = response_args diff -r 15a2f644725f -r 6ad4fdb48840 app/soc/views/sitemap/sidebar.py --- a/app/soc/views/sitemap/sidebar.py Tue Jan 27 22:57:19 2009 +0000 +++ b/app/soc/views/sitemap/sidebar.py Tue Jan 27 22:59:01 2009 +0000 @@ -44,14 +44,14 @@ @soc.cache.sidebar.cache -def getSidebar(): +def getSidebar(id, user): """Constructs a sidebar for the current user. """ sidebar = [] for callback in SIDEBAR: - menus = callback() + menus = callback(id, user) for menu in (menus if menus else []): sidebar.append(menu) @@ -102,7 +102,7 @@ return result -def getSidebarMenu(items, params): +def getSidebarMenu(id, user, items, params): """Returns an dictionary with one sidebar entry. Items is expected to be a tuple with an url, a menu_text, and an @@ -138,6 +138,9 @@ args = SIDEBAR_ACCESS_ARGS kwargs = SIDEBAR_ACCESS_KWARGS + # reset and pre-fill the Checker's cache + rights.setCurrentUser(id, user) + for url, menu_text, access_type in items: try: rights.checkAccess(access_type, kwargs) @@ -148,7 +151,7 @@ return submenus -def getSidebarMenus(params=None): +def getSidebarMenus(id, user, params=None): """Constructs the default sidebar menu for a View. Calls getSidebarItems to retrieve the items that should be in the @@ -160,7 +163,7 @@ """ items = getSidebarItems(params) - submenus = getSidebarMenu(items, params) + submenus = getSidebarMenu(id, user, items, params) if not submenus: return