Implement ACL support for tags

This commit is contained in:
Roberto Rosario
2012-01-02 16:54:19 -04:00
parent cc7d55933a
commit 8478c33898
8 changed files with 145 additions and 163 deletions

View File

@@ -7,6 +7,7 @@ from navigation.api import (register_links, register_top_menu,
from common.utils import encapsulate
from documents.models import Document
from acls.models import class_permissions
from acls.permissions import ACLS_VIEW_ACL
from taggit.models import Tag
from taggit.managers import TaggableManager
@@ -17,8 +18,8 @@ from .permissions import (PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH,
PERMISSION_TAG_VIEW)
tag_list = {'text': _(u'tag list'), 'view': 'tag_list', 'famfam': 'tag_blue'}
tag_create = {'text': _(u'create new tag'), 'view': 'tag_create', 'famfam': 'tag_blue_add'}
tag_add_attach = {'text': _(u'attach tag'), 'view': 'tag_add_attach', 'args': 'object.pk', 'famfam': 'tag_blue_add', 'permission': [PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH]}
tag_create = {'text': _(u'create new tag'), 'view': 'tag_create', 'famfam': 'tag_blue_add', 'permission': [PERMISSION_TAG_CREATE]}
tag_attach = {'text': _(u'attach tag'), 'view': 'tag_attach', 'args': 'object.pk', 'famfam': 'tag_blue_add', 'permission': [PERMISSION_TAG_ATTACH]}
tag_document_remove = {'text': _(u'remove'), 'view': 'tag_remove', 'args': ['object.id', 'document.id'], 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_REMOVE]}
tag_document_remove_multiple = {'text': _(u'remove'), 'view': 'tag_multiple_remove', 'args': 'document.id', 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_REMOVE]}
tag_document_list = {'text': _(u'tags'), 'view': 'document_tags', 'args': 'object.pk', 'famfam': 'tag_blue', 'permissions': [PERMISSION_TAG_REMOVE], 'children_view_regex': ['tag']}
@@ -26,6 +27,8 @@ tag_delete = {'text': _(u'delete'), 'view': 'tag_delete', 'args': 'object.id', '
tag_edit = {'text': _(u'edit'), 'view': 'tag_edit', 'args': 'object.id', 'famfam': 'tag_blue_edit', 'permissions': [PERMISSION_TAG_EDIT]}
tag_tagged_item_list = {'text': _(u'tagged documents'), 'view': 'tag_tagged_item_list', 'args': 'object.id', 'famfam': 'page'}
tag_multiple_delete = {'text': _(u'delete'), 'view': 'tag_multiple_delete', 'famfam': 'tag_blue_delete', 'permissions': [PERMISSION_TAG_DELETE]}
tag_acl_list = {'text': _(u'ACLs'), 'view': 'tag_acl_list', 'args': 'object.pk', 'famfam': 'lock', 'permissions': [ACLS_VIEW_ACL]}
tag_new_holder = {'text': _(u'New holder'), 'view': 'tag_new_holder', 'args': 'object.pk', 'famfam': 'user', 'permissions': [ACLS_VIEW_ACL]}
register_model_list_columns(Tag, [
{
@@ -35,22 +38,21 @@ register_model_list_columns(Tag, [
{
'name': _(u'color name'),
'attribute': encapsulate(lambda x: x.tagproperties_set.get().get_color_display()),
},
{
'name': _(u'tagged items'),
'attribute': encapsulate(lambda x: x.taggit_taggeditem_items.count())
}
])
register_links(Tag, [tag_tagged_item_list, tag_edit, tag_delete])
register_links(Tag, [tag_tagged_item_list, tag_edit, tag_delete, tag_acl_list])
register_multi_item_links(['tag_list'], [tag_multiple_delete])
register_links(['tag_list', 'tag_delete', 'tag_edit', 'tag_tagged_item_list', 'tag_multiple_delete', 'tag_create'], [tag_list, tag_create], menu_name='secondary_menu')
#register_sidebar_template(['document_tags'], 'tags_sidebar_template.html')
register_top_menu('tags', link={'text': _(u'tags'), 'view': 'tag_list', 'famfam': 'tag_blue'}, children_path_regex=[r'^tags/[^d]'])
register_links([Tag, 'tag_list', 'tag_create'], [tag_list, tag_create], menu_name='secondary_menu')
register_top_menu('tags', link={'text': _(u'tags'), 'view': 'tag_list', 'famfam': 'tag_blue'}, children_path_regex=[r'^tags/[^d]'])#TODO: change to children view regex or list
register_links(['tag_acl_list', 'tag_new_holder'], [tag_new_holder], menu_name='sidebar')
register_links(Document, [tag_document_list], menu_name='form_header')
register_links(['document_tags', 'tag_add_attach', 'tag_remove', 'tag_multiple_remove'], [tag_add_attach], menu_name='sidebar')
register_links(['document_tags', 'tag_add_attach', 'tag_remove', 'tag_multiple_remove'], [tag_attach], menu_name='sidebar')
register_multi_item_links(['document_tags'], [tag_document_remove_multiple])
class_permissions(Document, [

View File

@@ -4,5 +4,4 @@ from django.contrib import admin
from .models import TagProperties
admin.site.register(TagProperties)

View File

@@ -1,26 +1,41 @@
from __future__ import absolute_import
import logging
from django import forms
from django.utils.translation import ugettext_lazy as _
from taggit.models import Tag
from acls.models import AccessEntry
from permissions.models import Permission
from .models import COLOR_CHOICES
from .permissions import PERMISSION_TAG_VIEW
class AddTagForm(forms.Form):
"""
Form to be displayed in the sidebar of a document and allow adding
new or existing tags
"""
new_tag = forms.CharField(required=False, label=_(u'New tag'))
color = forms.ChoiceField(choices=COLOR_CHOICES, required=False, label=_(u'Color'))
existing_tags = forms.ModelChoiceField(required=False, queryset=Tag.objects.all(), label=_(u'Existing tags'))
logger = logging.getLogger(__name__)
class TagForm(forms.Form):
"""
'''
Form to edit an existing tag's properties
"""
'''
name = forms.CharField(label=_(u'Name'))
color = forms.ChoiceField(choices=COLOR_CHOICES, label=_(u'Color'))
class TagListForm(forms.Form):
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
logger.debug('user: %s' % user)
super(TagListForm, self).__init__(*args, **kwargs)
queryset = Tag.objects.all()
try:
Permission.objects.check_permissions(user, [PERMISSION_TAG_VIEW])
except PermissionDenied:
queryset = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_VIEW, user, queryset)
self.fields['tag'] = forms.ModelChoiceField(
queryset=queryset,
label=_(u'Tags'))

View File

@@ -1,44 +1,11 @@
from __future__ import absolute_import
from django.db import models
from django.utils.translation import ugettext_lazy as _
from taggit.models import Tag
COLOR_RED = u'red'
COLOR_BLUE = u'blu'
COLOR_MAGENTA = u'mag'
COLOR_CYAN = u'cya'
COLOR_YELLOW = u'yel'
COLOR_GREENYELLOW = u'gry'
COLOR_CORAL = u'crl'
COLOR_KHAKI = u'kki'
COLOR_LIGHTGREY = u'lig'
COLOR_ORANGE = u'org'
COLOR_CHOICES = (
(COLOR_BLUE, _(u'Blue')),
(COLOR_CYAN, _(u'Cyan')),
(COLOR_CORAL, _(u'Coral')),
(COLOR_GREENYELLOW, _(u'Green-Yellow')),
(COLOR_KHAKI, _(u'Khaki')),
(COLOR_LIGHTGREY, _(u'LightGrey')),
(COLOR_MAGENTA, _(u'Magenta')),
(COLOR_RED, _(u'Red')),
(COLOR_ORANGE, _(u'Orange')),
(COLOR_YELLOW, _(u'Yellow'))
)
COLOR_CODES = (
(COLOR_RED, u'red'),
(COLOR_BLUE, u'blue'),
(COLOR_MAGENTA, u'magenta'),
(COLOR_CYAN, u'cyan'),
(COLOR_YELLOW, u'yellow'),
(COLOR_GREENYELLOW, u'greenyellow '),
(COLOR_CORAL, u'coral'),
(COLOR_KHAKI, u'khaki'),
(COLOR_ORANGE, u'orange'),
(COLOR_LIGHTGREY, u'lightgrey'),
)
from .literals import COLOR_CHOICES, COLOR_CODES
class TagProperties(models.Model):

View File

@@ -1,6 +1,7 @@
from django.utils import unittest
from .models import Tag, TagProperties, COLOR_RED
from .models import Tag, TagProperties
from .literals import COLOR_RED
class TagTestCase(unittest.TestCase):

View File

@@ -10,7 +10,9 @@ urlpatterns = patterns('tags.views',
url(r'^(?P<tag_id>\d+)/remove_from_document/(?P<document_id>\d+)/$', 'tag_remove', (), 'tag_remove'),
url(r'^multiple/remove_from_document/(?P<document_id>\d+)/$', 'tag_multiple_remove', (), 'tag_multiple_remove'),
url(r'^document/(?P<document_id>\d+)/add/$', 'tag_add_attach', (), 'tag_add_attach'),
url(r'^document/(?P<document_id>\d+)/add/from_sidebar/$', 'tag_add_sidebar', (), 'tag_add_sidebar'),
url(r'^document/(?P<document_id>\d+)/add/$', 'tag_attach', (), 'tag_attach'),
url(r'^document/(?P<document_id>\d+)/list/$', 'document_tags', (), 'document_tags'),
url(r'^(?P<tag_pk>\d+)/acl/list/$', 'tag_acl_list', (), 'tag_acl_list'),
url(r'^(?P<tag_pk>\d+)/acl/holder/new/$', 'tag_new_holder', (), 'tag_new_holder'),
)

View File

@@ -1,5 +1,7 @@
from __future__ import absolute_import
import logging
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
@@ -7,22 +9,26 @@ from django.contrib import messages
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from permissions.models import Permission
from permissions import Permission
from taggit.models import Tag
from documents.models import Document
from documents.views import document_list
from documents.permissions import PERMISSION_DOCUMENT_VIEW
from common.utils import encapsulate
from acls.models import AccessEntry, PermissionDenied
from acls.views import acl_list_for, acl_new_holder_for
from .forms import AddTagForm, TagForm
from .forms import TagListForm, TagForm
from .models import TagProperties
from .permissions import (PERMISSION_TAG_CREATE, PERMISSION_TAG_ATTACH,
PERMISSION_TAG_REMOVE, PERMISSION_TAG_DELETE, PERMISSION_TAG_EDIT,
PERMISSION_TAG_VIEW)
from . import tag_tagged_item_list as tag_tagged_item_list_link
logger = logging.getLogger(__name__)
def tag_create(request):
#Permission.objects.check_permissions(request.user, [PERMISSION_TAG_EDIT])
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_CREATE])
redirect_url = reverse('tag_list')
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', redirect_url)))
@@ -51,85 +57,30 @@ def tag_create(request):
context_instance=RequestContext(request))
def tag_add_sidebar(request, document_id):
def tag_attach(request, document_id):
document = get_object_or_404(Document, pk=document_id)
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse('tag_list'))))
if request.method == 'POST':
previous = request.META.get('HTTP_REFERER', '/')
form = AddTagForm(request.POST)
if form.is_valid():
if form.cleaned_data['new_tag']:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_CREATE])
tag_name = form.cleaned_data['new_tag']
if Tag.objects.filter(name=tag_name):
is_new = False
else:
is_new = True
elif form.cleaned_data['existing_tags']:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_ATTACH])
tag_name = form.cleaned_data['existing_tags']
is_new = False
else:
messages.error(request, _(u'Must choose either a new tag or an existing one.'))
return HttpResponseRedirect(previous)
if tag_name in document.tags.values_list('name', flat=True):
messages.warning(request, _(u'Document is already tagged as "%s"') % tag_name)
return HttpResponseRedirect(previous)
document.tags.add(tag_name)
if is_new:
tag = Tag.objects.get(name=tag_name)
TagProperties(tag=tag, color=form.cleaned_data['color']).save()
messages.success(request, _(u'Tag "%s" added successfully.') % tag_name)
return HttpResponseRedirect(previous)
def tag_add_attach(request, document_id):
# TODO: merge with tag_add_sidebar
document = get_object_or_404(Document, pk=document_id)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_ATTACH])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_TAG_ATTACH, request.user, document)
next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', reverse('document_tags', args=[document.pk]))))
if request.method == 'POST':
form = AddTagForm(request.POST)
form = TagListForm(request.POST, user=request.user)
if form.is_valid():
if form.cleaned_data['new_tag']:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_CREATE])
tag_name = form.cleaned_data['new_tag']
if Tag.objects.filter(name=tag_name):
is_new = False
else:
is_new = True
elif form.cleaned_data['existing_tags']:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_ATTACH])
tag_name = form.cleaned_data['existing_tags']
is_new = False
else:
messages.error(request, _(u'Must choose either a new tag or an existing one.'))
tag = form.cleaned_data['tag']
if tag in document.tags.all():
messages.warning(request, _(u'Document is already tagged as "%s"') % tag)
return HttpResponseRedirect(next)
if tag_name in document.tags.values_list('name', flat=True):
messages.warning(request, _(u'Document is already tagged as "%s"') % tag_name)
return HttpResponseRedirect(next)
document.tags.add(tag_name)
if is_new:
tag = Tag.objects.get(name=tag_name)
TagProperties(tag=tag, color=form.cleaned_data['color']).save()
messages.success(request, _(u'Tag "%s" added and attached successfully.') % tag_name)
else:
messages.success(request, _(u'Tag "%s" attached successfully.') % tag_name)
document.tags.add(tag)
messages.success(request, _(u'Tag "%s" attached successfully.') % tag)
return HttpResponseRedirect(next)
else:
form = AddTagForm()
form = TagListForm(user=request.user)
return render_to_response('generic_form.html', {
'title': _(u'attach tag to: %s') % document,
@@ -140,23 +91,31 @@ def tag_add_attach(request, document_id):
context_instance=RequestContext(request))
def tag_list(request):
return render_to_response('generic_list.html', {
'object_list': Tag.objects.all(),
def tag_list(request, queryset=None, extra_context=None):
context = {
'title': _(u'tags'),
'hide_link': True,
'multi_select_as_buttons': True,
'extra_columns': [
{
'name': _(u'tagged items'),
'attribute': encapsulate(lambda x: x.taggit_taggeditem_items.count())
}
]
}, context_instance=RequestContext(request))
}
if extra_context:
context.update(extra_context)
queryset = queryset if not (queryset is None) else Tag.objects.all()
try:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_VIEW])
except PermissionDenied:
queryset = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_VIEW, request.user, queryset)
context['object_list'] = queryset
return render_to_response('generic_list.html',
context,
context_instance=RequestContext(request)
)
def tag_delete(request, tag_id=None, tag_id_list=None):
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_DELETE])
post_action_redirect = None
if tag_id:
@@ -167,6 +126,11 @@ def tag_delete(request, tag_id=None, tag_id_list=None):
else:
messages.error(request, _(u'Must provide at least one tag.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
try:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_DELETE])
except PermissionDenied:
tags = AccessEntry.objects.filter_objects_by_access(PERMISSION_TAG_DELETE, request.user, tags)
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
@@ -209,9 +173,13 @@ def tag_multiple_delete(request):
def tag_edit(request, tag_id):
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_EDIT])
tag = get_object_or_404(Tag, pk=tag_id)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_EDIT])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_TAG_EDIT, request.user, tag)
if request.method == 'POST':
form = TagForm(request.POST)
if form.is_valid():
@@ -253,24 +221,29 @@ def tag_tagged_item_list(request, tag_id):
def document_tags(request, document_id):
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_VIEW])
document = get_object_or_404(Document, pk=document_id)
return render_to_response('generic_list.html', {
'title': _(u'tags for: %s') % document,
'object_list': document.tags.all(),
'hide_link': True,
'navigation_object_links': [tag_tagged_item_list_link],
try:
Permission.objects.check_permissions(request.user, [PERMISSION_DOCUMENT_VIEW])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_DOCUMENT_VIEW, request.user, document)
context = {
'object': document,
'document': document,
'disable_auto_focus': True,
'multi_select_as_buttons': True,
},
context_instance=RequestContext(request))
'title': _(u'tags for: %s') % document,
}
return tag_list(request, queryset=document.tags.all(), extra_context=context)
def tag_remove(request, document_id, tag_id=None, tag_id_list=None):
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_REMOVE])
document = get_object_or_404(Document, pk=document_id)
try:
Permission.objects.check_permissions(request.user, [PERMISSION_TAG_REMOVE])
except PermissionDenied:
AccessEntry.objects.check_access(PERMISSION_TAG_REMOVE, request.user, document)
post_action_redirect = None
@@ -282,8 +255,6 @@ def tag_remove(request, document_id, tag_id=None, tag_id_list=None):
messages.error(request, _(u'Must provide at least one tag.'))
return HttpResponseRedirect(request.META.get('HTTP_REFERER', '/'))
document = get_object_or_404(Document, pk=document_id)
previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', '/')))
next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', '/')))
@@ -317,3 +288,28 @@ def tag_remove(request, document_id, tag_id=None, tag_id_list=None):
def tag_multiple_remove(request, document_id):
return tag_remove(request, document_id=document_id, tag_id_list=request.GET.get('id_list', []))
def tag_acl_list(request, tag_pk):
tag = get_object_or_404(Tag, pk=tag_pk)
logger.debug('tag: %s' % tag)
return acl_list_for(
request,
tag,
extra_context={
'object': tag,
}
)
def tag_new_holder(request, tag_pk):
tag = get_object_or_404(Tag, pk=tag_pk)
return acl_new_holder_for(
request,
tag,
extra_context={
#'tag': tag,
'object': tag,
}
)

View File

@@ -3,9 +3,9 @@ from django.utils.safestring import mark_safe
def get_tags_inline_widget(document):
"""
'''
A tag widget that includes the total tag count for a given document
"""
'''
tags_template = []
tag_count = document.tags.count()
if tag_count:
@@ -20,10 +20,10 @@ def get_tags_inline_widget(document):
def get_tags_inline_widget_simple(document):
"""
'''
A tag widget that only displayes the rectangular colored boxes for a
given document
"""
'''
tags_template = []
tag_count = document.tags.count()