diff options
| -rw-r--r-- | django/contrib/admin/media/js/row_level_permission.js | 160 | ||||
| -rw-r--r-- | django/contrib/admin/row_level_perm_manipulator.py | 138 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/change_form.html | 8 | ||||
| -rw-r--r-- | django/contrib/admin/templates/admin/row_level_permission.html | 79 | ||||
| -rw-r--r-- | django/contrib/admin/templatetags/row_level_permission.py | 37 | ||||
| -rw-r--r-- | django/contrib/admin/urls.py | 6 | ||||
| -rw-r--r-- | django/contrib/admin/utils.py | 11 | ||||
| -rw-r--r-- | django/contrib/admin/views/main.py | 19 | ||||
| -rw-r--r-- | django/contrib/admin/views/row_level_permissions.py | 105 |
9 files changed, 562 insertions, 1 deletions
diff --git a/django/contrib/admin/media/js/row_level_permission.js b/django/contrib/admin/media/js/row_level_permission.js new file mode 100644 index 0000000000..66e9d395be --- /dev/null +++ b/django/contrib/admin/media/js/row_level_permission.js @@ -0,0 +1,160 @@ +/** + * @author clong + */ + //dojo.require("dojo.io.*"); + //dojo.require("dojo.event.*"); + //dojo.require("dojo.lfx.*"); + //dojo.require("dojo.widget.*"); + //dojo.require("dojo.json"); + +function addButtonPressed(obj_ct, obj_id) +{ + return; + dojo.io.bind({ + url: "/rlp/add/"+obj_ct+"/"+obj_id+"/ajax/", + handler: addCallback, + formNode: dojo.byId('addRLPForm') + }); +} + +function addCallback(type, data, evt) +{ + if (type == 'error') + alert('Error when retrieving data from the server!'); + else + { + dictData = dojo.json.evalJson(data); + if(dictData.result == false) + { + //outputError(dictData.text); + return; + } + alert("Success!"); + //outputMsg(dictData.text); + //dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [184, 204, 228], 1000).play(); + } +} + +function applyButtonPressed(id) +{ +return; + strArray = id.split("/"); + dojo.io.bind({ + url: "/rlp/change/"+id+'/ajax/', + handler: editCallback, + formNode: dojo.byId('editRLPForm-'+strArray[1]) + }); +} + +function editCallback(type, data, evt) +{ + if (type == 'error') + alert('Error when retrieving data from the server!'); + else + { + dictData = dojo.json.evalJson(data); + if(dictData.result == false) + { + outputError(dictData.text); + dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [255, 0, 0], 1000).play(); + alert("Error"); + return; + } + outputMessage(dictData.text); + dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [184, 204, 228], 1000).play(); + } +} + +function deleteRLP(id) +{ +return; + var confirm_ans = confirm("Are you sure?"); + if(confirm_ans) + { + dojo.io.bind({ + url: '/rlp/delete/'+id+'/ajax', + handler: deleteCallback, + mimetype: 'text/json' + }); + } +} + +function deleteCallback(type, data, evt) +{ + if (type == 'error') + alert('Error when retrieving data from the server!'); + else + { + dictData = dojo.json.evalJson(data); + if(dictData.result == false) + { + //outputError(dictData.text); + dojo.lfx.html.highlight(dojo.byId("editRLP-"+dictData.id), [255, 0, 0], 1000).play(); + return; + } + outputMessage(dictData.text); + var row = dojo.byId('editRLP-'+dictData.id); + var fadeOut = dojo.lfx.fadeOut(row, 1000, null, function(n) { + var table = dojo.byId('rlpTable'); + table.deleteRow(row.rowIndex); + }); + dojo.lfx.html.highlight(row, [255, 0, 0], 500).play(1500); + fadeOut.play(); + } +} + +function copyToNew(id) +{ + var newForm = document.addRLPForm; + var form = dojo.byId("editRLPForm-"+id); + newForm.owner.selectedIndex = form.owner.selectedIndex; + newForm.perm.selectedIndex = form.perm.selectedIndex; + newForm.negative.checked = form.negative.checked; +} + +function outputErrors(errs) +{ + var output = genOutput('errors', errs); + dojo.lfx.html.highlight(output, [240, 0, 0], 3000).play(); +} + +function outputMessage(messages) +{ + var output = genOutput('messages', messages); + dojo.lfx.html.highlight(output, [184, 204, 228], 3000).play(); +} + +function genOutput(id, str) +{ + var list = document.createElement("ul"); + list.id = id; + var txt = document.createTextNode(str); + var ele = document.createElement("li"); + ele.appendChild(txt); + list.appendChild(ele); + var output = dojo.byId('output'); + removeChildrenFromNode(output); + output.appendChild(list); + return output; +} + + +function removeChildrenFromNode(node) +{ + + while (node.hasChildNodes()) + { + node.removeChild(node.firstChild); + } +} + + +function init() +{ + for(var i=0; i<document.forms.length; i++) + { + document.forms[i].reset(); + } +} + +//dojo.addOnLoad(init);
\ No newline at end of file diff --git a/django/contrib/admin/row_level_perm_manipulator.py b/django/contrib/admin/row_level_perm_manipulator.py new file mode 100644 index 0000000000..8939238472 --- /dev/null +++ b/django/contrib/admin/row_level_perm_manipulator.py @@ -0,0 +1,138 @@ +from django import forms +from django.contrib.contenttypes.models import ContentType +from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.contrib.auth.models import User, Group, Permission, RowLevelPermission +from django.db.models import manipulators +from auth import utils + +class ChangeRLPManipulator(forms.Manipulator): + def __init__(self, ct=None): + perm_list = [] + if ct: + self.ct = ct + perm_list = [(o.id, o.name) for o in self.ct.permission_set.order_by("name")] + + obj_list = [('user', User.objects.order_by("username"))] + obj_list.extend([('group', Group.objects.order_by("name"))]) + + self.fields = ( + MultipleObjSelectField(field_name="owner", obj_list=obj_list), + forms.SelectField(field_name="perm", choices=perm_list), + forms.CheckboxField(field_name="negative"), + ) + + def save(self, new_data): + + rlp = RowLevelPermission.objects.get(pk=new_data['id']) + + self.original_object = rlp + self.manager = rlp._default_manager + self.opts = rlp._meta + + owner = MultipleObjSelectField.returnObject(new_data['owner']) + owner_ct = ContentType.objects.get_for_model(owner) + + model_ct = rlp.type_ct + model = model_ct.get_object_for_this_type (pk=rlp.type_id) + + perm = Permission.objects.get(pk=new_data['perm']) + + + field_name_list = ('owner_ct', 'owner_id', 'type_ct', 'type_id', 'permission') + field_data = owner_ct.id + all_data = {'owner_id':owner.id, 'type_ct_id':model_ct.id, 'type_id':model.id, 'permission_id':perm.id} + manipulators.manipulator_validator_unique_together(field_name_list, self.opts, self, field_data, all_data) + + rlp.owner = owner + rlp.permission = perm + rlp.negative = new_data['negative'] + rlp.save() + return rlp + +class AddRLPManipulator(ChangeRLPManipulator): + def __init__(self, obj_instance, ct): + self.ct = ct + self.obj_instance = obj_instance + obj_list = [('user', User.objects.order_by("username"))] + obj_list.extend([('group', Group.objects.order_by("name"))]) + perm_list = [(o.id, o.name) for o in self.ct.permission_set.order_by("name")] + self.fields = ( + MultipleObjSelectField(field_name="owner", obj_list=obj_list), + forms.SelectMultipleField(field_name="perm", choices=perm_list, size=3), + forms.CheckboxField(field_name="negative"), + ) + + def save(self, new_data): + + owner = MultipleObjSelectField.returnObject(new_data['owner']) + + self.manager = RowLevelPermission._default_manager + self.opts = RowLevelPermission._meta + + ct = ContentType.objects.get_for_model(owner) + rlp_list = [] + for i in new_data.getlist('perm'): + perm = Permission.objects.get(pk=i) + + field_name_list = ('owner_ct', 'owner_id', 'type_ct', 'type_id', 'permission') + field_data = ct.id + all_data = {'owner_id':owner.id, 'type_ct_id':self.ct.id, 'type_id':self.obj_instance.id, 'permission_id':perm.id} + manipulators.manipulator_validator_unique_together(field_name_list, self.opts, self, field_data, all_data) + + rlp = RowLevelPermission.objects.create_row_level_permission(self.obj_instance, owner, perm, negative=new_data['negative']) + rlp_list.append(rlp) + + return rlp_list + +#def validate_unique_together(orig_obj, field_name_list, all_data): + #field_list = [opts.get_field(field_name) for field_name in field_name_list] + #kwargs = {} + #for f in field_list: + #field_val = all_data.get(f.attname, None) + #if field_val is None: + #return + #if isinstance(f.rel, ManyToOneRel): + #kwargs['%s__pk' % f.name] = field_val + #else: + #kwargs['%s__iexact' % f.name] = field_val + #try: + #old_obj = self.manager.get(**kwargs) + #except ObjectDoesNotExist: + #return + #if hasattr(self, 'original_object') and self.original_object._get_pk_val() == old_obj._get_pk_val(): + #pass + #else: + #raise validators.ValidationError, _("%(object)s with this %(type)s already exists for the given %(field)s.") % \ + #{'object': capfirst(opts.verbose_name), 'type': field_list[0].verbose_name, 'field': get_text_list(field_name_list[1:], 'and')} + + +class MultipleObjSelectField(forms.SelectField): + def __init__(self, field_name, obj_list=None, + default_text=None, size=1, is_required=False, validator_list=None, + member_name=None): + if default_text is None: + default_text = "Select an option" + choice_list = [('', default_text)] + for obj, obj_choices in obj_list: + ct = ContentType.objects.get(model__exact=obj) + choice_list.extend([(MultipleObjSelectField.returnKey(o), str(o)+" ("+ct.name.title()+")") for o in obj_choices]) + + super(MultipleObjSelectField, self).__init__(field_name, choices=choice_list, + size=size, is_required=is_required, + validator_list=validator_list, + member_name=member_name) + def returnObject(data): + data = data.split('-') + ct = ContentType.objects.get(model__exact=data[0]) + obj = ct.get_object_for_this_type(pk=data[1]) + return obj + + def returnKey(obj): + ct = ContentType.objects.get_for_model(obj.__class__) + return ct.model+"-"+str(obj.id) + + returnObject = staticmethod(returnObject) + returnKey = staticmethod(returnKey) + + +
\ No newline at end of file diff --git a/django/contrib/admin/templates/admin/change_form.html b/django/contrib/admin/templates/admin/change_form.html index e61eb5513b..5dddfa26ae 100644 --- a/django/contrib/admin/templates/admin/change_form.html +++ b/django/contrib/admin/templates/admin/change_form.html @@ -64,5 +64,11 @@ </script> {% endif %} </div> -</form></div> +</form> + +{% if rlp_form_list %} +{% include "admin/row_level_permission.html" %} +{% endif %} + +</div> {% endblock %} diff --git a/django/contrib/admin/templates/admin/row_level_permission.html b/django/contrib/admin/templates/admin/row_level_permission.html new file mode 100644 index 0000000000..bb62c78a52 --- /dev/null +++ b/django/contrib/admin/templates/admin/row_level_permission.html @@ -0,0 +1,79 @@ +{% load i18n admin_modify %} +{% include_admin_script "js/row_level_permission.js" %} +<table id="rlpTable"> +<tr class="header"> +<th> +{% trans "Owner" %} +</th> +<th> +{% trans "Permission" %} +</th> +<th> +{% trans "Negative" %} +</th> +<th> +{% trans "Options" %} +</th> +</tr> +<form id="addRLPForm" method="POST" name="addRLPForm" action="../../../auth/row_level_permission/add/{{ content_type_id }}/{{ object_id }}/" onSubmit="addButtonPressed({{ obj_ct }}, {{ obj_id }}); return false;"> +<tr> +<th colspan=4> +{% trans "Add Permissions" %} +</th> +</tr> +<TBODY> +<tr> +<td> +<p> + {{ new_rlp_form.owner }} +</p> +</td> +<td> +<p> + {{ new_rlp_form.perm }} +</p> +</td> +<td> +<p> + {{ new_rlp_form.negative }} +</p> +</td> +<td> +<input id="addButton" type="submit" value="{% trans 'Add' %}" /> +</td> +</tr> +</form> +<tr> +<th colspan=4> +{% trans "Current Permissions" %} +</th> +</tr> +{% if rlp_form_list %} +{% load row_level_permission %} +{% for o in rlp_form_list %} +<tr id="editRLP-{{ o.rlp.id }}"> +<form id="editRLPForm-{{ o.rlp.id }}" method="POST" name="editRLPForm-{{ rlp.id }}" action="../../../auth/row_level_permission/{% objref o.rlp %}/change/" onSubmit="applyButtonPressed('{% objref o.rlp %}'); return false;"> +<td> +{{ o.form.owner }} +</td> +<td> +{{ o.form.perm }} +</td> +<td> +{{ o.form.negative }} +</td> +<td> +<input id="applyButton-{{ o.rlp.id }}" type="submit" value="{% trans 'Apply' %}" /> +<input id="cancelButton-{{ o.rlp.id }}" type="reset" value="{% trans 'Reset' %}"/> +<br/> +<a href="../../../auth/row_level_permission/{% objref o.rlp %}/delete/" onClick="deleteRLP('{% objref o.rlp %}'); return false;" class="deleteLink">{% trans 'Delete' %}</a> | +<a href="" onClick="copyToNew({{ o.rlp.id }}); return false;" class="copyToNewLink">{% trans 'Copy to New' %}</a> +</td> +</form> +</tr> +{% endfor %} +{% else %} +<tr><td>No row level permissions</td></tr> +{% endif %} +</TBODY> +</table>
\ No newline at end of file diff --git a/django/contrib/admin/templatetags/row_level_permission.py b/django/contrib/admin/templatetags/row_level_permission.py new file mode 100644 index 0000000000..edbd6e09a0 --- /dev/null +++ b/django/contrib/admin/templatetags/row_level_permission.py @@ -0,0 +1,37 @@ +import sha +from django.contrib.contenttypes.models import ContentType +from django import template +from django.conf import settings + +register = template.Library() + +#Based off work by Ian Holsman +#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/templatetags/media.py +class objref_class(template.Node): + """ return a object reference to a given object """ + def __init__(self, obj): + self.object_name= obj + + def render(self, context): + object_id = template.resolve_variable( self.object_name, context ) + c_obj = ContentType.objects.get_for_model( object_id ).id + #c_obj = get_content_type_id( object_id ) + return "%s/%d/%s" % ( c_obj, object_id.id, sha.new("%s/%d" % (c_obj, object_id.id) + settings.SECRET_KEY).hexdigest()) + +#Based off work by Ian Holsman +#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/templatetags/media.py +def objref(parser, token): + """ + {% objref <object> %} + """ + bits = token.contents.split() + tok="" + if len(bits) > 2: + raise template.TemplateSyntaxError, "'objref' statements must be 'objref <object>': %s" % token.contents + if len(bits) == 2: + tok = bits[1] + else: + tok = "object" + return objref_class(tok) + +register.tag('objref', objref)
\ No newline at end of file diff --git a/django/contrib/admin/urls.py b/django/contrib/admin/urls.py index bd894d8de1..bbdfedf087 100644 --- a/django/contrib/admin/urls.py +++ b/django/contrib/admin/urls.py @@ -28,6 +28,11 @@ urlpatterns = patterns('', # ('^doc/templates/$', 'django.views.admin.doc.template_index'), ('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'), + #Row level permissions + ('^auth/row_level_permission/(?P<ct_id>\d+)/(?P<rlp_id>\d+)/(?P<hash>\w+)/delete/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.delete_row_level_permission'), + ('^auth/row_level_permission/(?P<ct_id>\d+)/(?P<rlp_id>\d+)/(?P<hash>\w+)/change/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.change_row_level_permission'), + ('^auth/row_level_permission/add/(?P<ct_id>\d+)/(?P<obj_id>\d+)/(?P<ajax>.*)$', 'django.contrib.admin.views.row_level_permissions.add_row_level_permission'), + # Add/change/delete/history ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'), ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'), @@ -36,4 +41,5 @@ urlpatterns = patterns('', ('^([^/]+)/([^/]+)/(.+)/$', 'django.contrib.admin.views.main.change_stage'), ) + del i18n_view diff --git a/django/contrib/admin/utils.py b/django/contrib/admin/utils.py index 9adf09b6a5..e1d408b275 100644 --- a/django/contrib/admin/utils.py +++ b/django/contrib/admin/utils.py @@ -100,3 +100,14 @@ if docutils_is_available: for name, urlbase in ROLES.items(): create_reference_role(name, urlbase) + +#Based off work by Ian Holsman +#http://svn.zyons.python-hosting.com/trunk/zilbo/common/utils/misc.py +def verify_objref_hash( content_type_id, object_id, hash ): + import sha + from django.conf import settings + hash_match = sha.new("%s/%s" % (content_type_id, object_id) + settings.SECRET_KEY).hexdigest() + if hash == hash_match: + return True + else: + return False diff --git a/django/contrib/admin/views/main.py b/django/contrib/admin/views/main.py index 705dfad6c8..168199a54d 100644 --- a/django/contrib/admin/views/main.py +++ b/django/contrib/admin/views/main.py @@ -391,7 +391,26 @@ def change_stage(request, app_label, model_name, object_id): 'object_id': object_id, 'original': manipulator.original_object, 'is_popup': request.REQUEST.has_key('_popup'), + 'is_row_level_perm': model._meta.row_level_permissions, }) + + if model._meta.row_level_permissions: + from django.contrib.admin.row_level_perm_manipulator import AddRLPManipulator, ChangeRLPManipulator + model_instance = manipulator.original_object + model_ct = ContentType.objects.get_for_model(model) + rlp_list = model_instance.row_level_permissions.order_by('owner_ct', 'owner_id') + rlp_errors = rlp_new_data = {} + add_rlp_manip = AddRLPManipulator(model_instance, model_ct) + edit_rlp_manip = ChangeRLPManipulator(model_ct) + new_rlp_form = forms.FormWrapper(add_rlp_manip, rlp_new_data, rlp_errors) + rlp_form_list = [] + for r in rlp_list: + owner_val = str(r.owner_ct)+"-"+str(r.owner_id) + data = {'id':r.id, 'owner':owner_val, 'perm':r.permission.id, 'negative':r.negative} + rlp_form_list.append({'form':forms.FormWrapper(edit_rlp_manip, data, rlp_errors), 'rlp':r}) + rlp_context = {'new_rlp_form':new_rlp_form, 'rlp_form_list':rlp_form_list,} + c.update(rlp_context) + return render_change_form(model, manipulator, c, change=True) change_stage = staff_member_required(never_cache(change_stage)) diff --git a/django/contrib/admin/views/row_level_permissions.py b/django/contrib/admin/views/row_level_permissions.py new file mode 100644 index 0000000000..607c6904cf --- /dev/null +++ b/django/contrib/admin/views/row_level_permissions.py @@ -0,0 +1,105 @@ +from django.contrib.admin import utils +from django.shortcuts import render_to_response, get_object_or_404 +from django.http import Http404, HttpResponse, HttpResponseRedirect +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import RowLevelPermission +from django.contrib.admin.row_level_perm_manipulator import AddRLPManipulator, ChangeRLPManipulator +import simplejson + +def delete_row_level_permission(request, ct_id, rlp_id, hash, ajax=None): + msg = {} + if utils.verify_objref_hash(ct_id, rlp_id, hash): + rlp = get_object_or_404(RowLevelPermission, pk=rlp_id) + ct = rlp.type_ct + obj = rlp.type + opts = rlp._meta + if not request.user.has_perm(opts.app_label + '.' + opts.get_delete_permission()): + raise PermissionDenied + if not request.user.has_perm(obj._meta.app_label + '.' + obj._meta.get_delete_permission()): + raise PermissionDenied + rlp.delete() + msg = {"result":True, "text":_("Row level permission was successful deleted"), "id":rlp_id} + else: + msg = { 'result':False, 'text': _("row level permission not found (bad hash)" )} + if ajax: + return HttpResponse(simplejson.dumps(msg), 'text/javascript') + request.user.message_set.create(message=msg['text']) + #return HttpResponseRedirect("/edit/%s/%s" % (ct.model, obj.id)) + return HttpResponseRedirect("../../../../../../%s/%s/%s" % (obj._meta.app_label, obj._meta.module_name , str(obj.id))) + +def add_row_level_permission(request, ct_id, obj_id, ajax=None): + msg = {} + if not request.POST: + msg = { 'result':False, 'text': _("Only POSTs are allowed" )} + if ajax: + return HttpResponse(simplejson.dumps(msg), 'text/javascript') + request.user.message_set.create(message=msg['text']) + return HttpResponseRedirect("/edit/%s/%s" % (obj_type, obj_id)) + + ct = get_object_or_404(ContentType, pk=ct_id) + obj = get_object_or_404(ct.model_class(), pk=obj_id) + + if not request.user.has_perm(obj._meta.app_label + '.' + obj._meta.get_add_permission()): + raise PermissionDenied + + manip = AddRLPManipulator(obj, ct) + + new_data = request.POST.copy() + manip.do_html2python(new_data) + + rlp_list = [] + from django.core import validators + try: + rlp_list = manip.save(new_data) + except validators.ValidationError: + msg = {"result":False, "text":_("A row level permission already exists with the specified values.")} + else: + msg = {"result":True, "text":_("Row level permission has successful been added.")} + if not ajax: + request.user.message_set.create(message=msg['text']) + return HttpResponseRedirect("../../../../../%s/%s/%s" % (obj._meta.app_label, obj._meta.module_name , str(obj.id))) + + + resp_list = [] + for rlp in rlp_list: + resp_list.append({"id":rlp.id, "permission":rlp.permission.id}) + msg["results"]=resp_list + return HttpResponse(simplejson.dumps(msg), 'text/javascript') + +def change_row_level_permission(request, ct_id, rlp_id, hash, ajax=None): + msg = {} + if not request.POST: + msg = { 'result':False, 'text': _("Only POSTs are allowed" )} + + if not utils.verify_objref_hash(ct_id, rlp_id, hash): + msg = { 'result':False, 'text': _("row level permission not found (bad hash)" )} + + if msg.has_key("result"): + if ajax: + return HttpResponse(simplejson.dumps(msg), 'text/javascript') + request.user.message_set.create(message=msg['text']) + return HttpResponseRedirect("/edit/%s/%s" % (obj_type, obj_id)) + + obj = get_object_or_404(RowLevelPermission, pk=rlp_id) + opts = obj._meta + + #if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): + #if not request.user.has_perm(opts.get_change_permission()): + #raise PermissionDenied + + manip = ChangeRLPManipulator() + new_data = request.POST.copy() + + new_data["id"] = rlp_id + manip.do_html2python(new_data) + from django.core import validators + try: + new_rlp = manip.save(new_data) + except validators.ValidationError: + msg = {"result":False, "text":_("A row level permission already exists with the specified values")} + else: + msg = {"result":True, "text":_("Row level permission has successfully been changed"), "id":rlp_id} + if ajax: + return HttpResponse(simplejson.dumps(msg), 'text/javascript') + request.user.message_set.create(message=msg['text']) + return HttpResponseRedirect("../../../../../../%s/%s/%s" % (new_rlp.type._meta.app_label, new_rlp.type._meta.module_name , str(rlp.type_id)))
\ No newline at end of file |
