Permalink
Browse files

REFACTORING: use get_urls() to add locking URLs instead of having out…

…-of-admin urls.py

We then have access to model, we know it's lockable, and is_staff checks are magically done.
  • Loading branch information...
1 parent 1fc7bc3 commit 2d0654c0aaa49b333548a088cd8b3ef5c8811afa @souen souen committed Jan 30, 2012
View
@@ -1,8 +1,17 @@
# coding=utf8
+import simplejson
from datetime import datetime
+from django.conf.urls.defaults import patterns, url
from django.contrib import admin
from django.conf import settings
from django.utils.translation import ugettext as _
+from django.contrib.admin.util import unquote
+from django.core.exceptions import PermissionDenied
+from django.http import HttpResponse
+from django.utils import formats
+
+from locking.models import ObjectLockedError
+from locking import views
class LockableAdmin(admin.ModelAdmin):
class Media():
@@ -13,6 +22,68 @@ class Media():
'locking/js/admin.locking.js',
'locking/js/jquery.url.packed.js',
)
+
+ def unlock_view(self, request, object_id, extra_context=None):
+ obj = self.get_object(request, unquote(object_id))
+
+ if not self.has_change_permission(request, obj):
+ raise PermissionDenied
+
+ # Users who don't have exclusive access to an object anymore may still
+ # request we unlock an object. This happens e.g. when a user navigates
+ # away from an edit screen that's been open for very long.
+ # When this happens, LockableModel.unlock_for will throw an exception,
+ # and we just ignore the request.
+ # That way, any new lock that may since have been put in place by another
+ # user won't get accidentally overwritten.
+ try:
+ obj.unlock_for(request.user)
+ obj._is_a_locking_request = True
+ return HttpResponse(status=200)
+ except ObjectLockedError:
+ return HttpResponse(status=403)
+
+ def refresh_lock_view(self, request, object_id, extra_context=None):
+ obj = self.get_object(request, unquote(object_id))
+
+ if not self.has_change_permission(request, obj):
+ raise PermissionDenied
+
+ try:
+ obj.lock_for(request.user)
+ except ObjectLockedError:
+ # The user tried to overwrite an existing lock by another user.
+ # No can do, pal!
+ return HttpResponse(status=409) # Conflict
+
+ # Format date like a DateTimeInput would have done
+ format = formats.get_format('DATETIME_INPUT_FORMATS')[0]
+ original_locked_at = obj.locked_at.strftime(format)
+ original_modified_at = obj.modified_at.strftime(format)
+
+ response = simplejson.dumps({
+ 'original_locked_at': original_locked_at,
+ 'original_modified_at': original_modified_at,
+ })
+
+ return HttpResponse(response, mimetype="application/json")
+
+
+ def get_urls(self):
+ """
+ Override get_urls() to add a locking URLs.
+ """
+ urls = super(LockableAdmin, self).get_urls()
+ info = self.model._meta.app_label, self.model._meta.module_name
+ locking_urls = patterns('',
+ url(r'^(.+)/unlock/$',
+ self.admin_site.admin_view(self.unlock_view),
+ name='unlock_%s_%s' % info),
+ url(r'^(.+)/refresh_lock/$',
+ self.admin_site.admin_view(self.refresh_lock_view),
+ name='refresh_lock_%s_%s' % info),
+ )
+ return locking_urls + urls
def changelist_view(self, request, extra_context=None):
# we need the request objects in a few places where it's usually not present,
View
@@ -1,42 +0,0 @@
-# encoding: utf-8
-
-from django.http import HttpResponse
-from django.contrib.contenttypes.models import ContentType
-
-from locking.models import LockableModelMethodsMixin
-from locking import logger
-
-def user_may_change_model(fn):
- def view(request, app, model, *vargs, **kwargs):
- may_change = '%s.change_%s' % (app, model)
- if not request.user.has_perm(may_change):
- return HttpResponse(status=401)
- else:
- return fn(request, app, model, *vargs, **kwargs)
-
- return view
-
-def is_lockable(fn):
- def view(request, app, model, *vargs, **kwargs):
- try:
- cls = ContentType.objects.get(app_label=app, model=model).model_class()
- if issubclass(cls, LockableModelMethodsMixin):
- lockable = True
- else:
- lockable = False
- except ContentType.DoesNotExist:
- lockable = False
-
- if lockable:
- return fn(request, app, model, *vargs, **kwargs)
- else:
- return HttpResponse(status=404)
- return view
-
-def log(view):
- def decorated_view(*vargs, **kwargs):
- response = view(*vargs, **kwargs)
- logger.debug("Sending a request: \n\t%s" % (response.content))
- return response
-
- return decorated_view
@@ -75,12 +75,13 @@ locking.admin = function() {
if (is_adding_content()) return;
// Get url parts.
+ var adminSite = $.url.segment(0)
var app = $.url.segment(1);
var model = $.url.segment(2);
var id = $.url.segment(3);
// Urls.
- var base_url = settings.base_url + "/" + [app, model, id].join("/");
+ var base_url = "/" + [adminSite, app, model, id].join("/");
var urls = {
unlock: base_url + "/unlock/",
refresh_lock: base_url + "/refresh_lock/"
@@ -16,7 +16,6 @@ def locking_variables(context):
locking_infos = {}
locking_error_when_saving = {}
locking_settings = {
- 'base_url': '/ajax/admin', # FIXME don't harcode base URL !
'time_until_expiration': settings.LOCKING['time_until_expiration'],
'time_until_warning': settings.LOCKING['time_until_warning'],
}
View
@@ -1,10 +1,5 @@
from django.conf.urls.defaults import *
-urlpatterns = patterns('locking.views',
- (r'(?P<app>[\w-]+)/(?P<model>[\w-]+)/(?P<id>\d+)/unlock/$', 'unlock'),
- (r'(?P<app>[\w-]+)/(?P<model>[\w-]+)/(?P<id>\d+)/refresh_lock/$', 'refresh_lock'),
- )
-
-urlpatterns += patterns('',
+urlpatterns = patterns('',
(r'jsi18n/$', 'django.views.i18n.javascript_catalog', {'packages': 'locking'}),
)
View
@@ -1,17 +0,0 @@
-# encoding: utf-8
-
-from django.contrib.contenttypes.models import ContentType
-from locking.models import LockableModelMethodsMixin
-
-def gather_lockable_models():
- lockable_models = dict()
- for contenttype in ContentType.objects.all():
- model = contenttype.model_class()
- # there might be a None value betwixt our content types
- if model:
- app = model._meta.app_label
- name = model._meta.module_name
- if issubclass(model, LockableModelMethodsMixin):
- lockable_models.setdefault(app, {})
- lockable_models[app][name] = model
- return lockable_models
View
@@ -1,56 +0,0 @@
-import simplejson
-from django.http import HttpResponse
-from django.conf import settings
-from django.utils import formats
-
-from locking.decorators import user_may_change_model, is_lockable, log
-from locking import utils, models
-
-"""
-These views are called from javascript to open and close assets (objects), in order
-to prevent concurrent editing.
-"""
-@log
-@user_may_change_model
-@is_lockable
-def refresh_lock(request, app, model, id):
- obj = utils.gather_lockable_models()[app][model].objects.get(pk=id)
-
- try:
- obj.lock_for(request.user)
- except models.ObjectLockedError:
- # The user tried to overwrite an existing lock by another user.
- # No can do, pal!
- return HttpResponse(status=409) # Conflict
-
- # Format date like a DateTimeInput would have done
- format = formats.get_format('DATETIME_INPUT_FORMATS')[0]
- original_locked_at = obj.locked_at.strftime(format)
- original_modified_at = obj.modified_at.strftime(format)
-
- response = simplejson.dumps({
- 'original_locked_at': original_locked_at,
- 'original_modified_at': original_modified_at,
- })
-
- return HttpResponse(response, mimetype="application/json")
-
-@log
-@user_may_change_model
-@is_lockable
-def unlock(request, app, model, id):
- obj = utils.gather_lockable_models()[app][model].objects.get(pk=id)
-
- # Users who don't have exclusive access to an object anymore may still
- # request we unlock an object. This happens e.g. when a user navigates
- # away from an edit screen that's been open for very long.
- # When this happens, LockableModel.unlock_for will throw an exception,
- # and we just ignore the request.
- # That way, any new lock that may since have been put in place by another
- # user won't get accidentally overwritten.
- try:
- obj.unlock_for(request.user)
- obj._is_a_locking_request = True
- return HttpResponse(status=200)
- except models.ObjectLockedError:
- return HttpResponse(status=403)

0 comments on commit 2d0654c

Please sign in to comment.