Update Link class interface

Remove Link class support for multiple permissions. Accept
only one permission for each link. Remove support for the
permission related field.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-25 01:11:51 -04:00
parent 75fd7647d4
commit daf79983aa
2 changed files with 195 additions and 195 deletions

View File

@@ -31,49 +31,163 @@ from .utils import get_current_view_name
logger = logging.getLogger(__name__)
class ResolvedLink(object):
def __init__(self, link, current_view_name):
self.current_view_name = current_view_name
self.disabled = False
self.link = link
self.url = '#'
self.context = None
self.request = None
class Link(object):
_registry = {}
@property
def active(self):
return self.link.view == self.current_view_name
@classmethod
def get(cls, name):
return cls._registry[name]
@property
def description(self):
return self.link.description
@classmethod
def remove(cls, name):
del cls._registry[name]
@property
def html_data(self):
return self.link.html_data
def __init__(self, text, view=None, args=None, condition=None,
conditional_disable=None, description=None, html_data=None,
html_extra_classes=None, icon=None, icon_class=None,
keep_query=False, kwargs=None, name=None, permission=None,
remove_from_query=None, tags=None, url=None):
@property
def html_extra_classes(self):
return self.link.html_extra_classes
self.args = args or []
self.condition = condition
self.conditional_disable = conditional_disable
self.description = description
self.html_data = html_data
self.html_extra_classes = html_extra_classes
self.icon = icon
self.icon_class = icon_class
self.keep_query = keep_query
self.kwargs = kwargs or {}
self.name = name
self.permission = permission
self.remove_from_query = remove_from_query or []
self.tags = tags
self.text = text
self.view = view
self.url = url
@property
def icon(self):
return self.link.icon
if name:
self.__class__._registry[name] = self
@property
def icon_class(self):
return self.link.icon_class
def resolve(self, context, resolved_object=None):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
@property
def tags(self):
return self.link.tags
@property
def text(self):
# Try to get the request object the faster way and fallback to the
# slower method.
try:
return self.link.text(context=self.context)
except TypeError:
return self.link.text
request = context.request
except AttributeError:
request = Variable('request').resolve(context)
current_path = request.META['PATH_INFO']
current_view_name = resolve(current_path).view_name
# ACL is tested agains the resolved_object or just {{ object }} if not
if not resolved_object:
try:
resolved_object = Variable('object').resolve(context=context)
except VariableDoesNotExist:
pass
# If this link has a required permission check that the user has it
# too
if self.permission:
if resolved_object:
try:
AccessControlList.objects.check_access(
obj=resolved_object, permission=self.permission,
user=request.user
)
except PermissionDenied:
return None
else:
try:
Permission.check_user_permission(
permission=self.permission, user=request.user
)
except PermissionDenied:
return None
# Check to see if link has conditional display function and only
# display it if the result of the conditional display function is
# True
if self.condition:
if not self.condition(context):
return None
resolved_link = ResolvedLink(
current_view_name=current_view_name, link=self
)
if self.view:
view_name = Variable('"{}"'.format(self.view))
if isinstance(self.args, list) or isinstance(self.args, tuple):
# TODO: Don't check for instance check for iterable in try/except
# block. This update required changing all 'args' argument in
# links.py files to be iterables and not just strings.
args = [Variable(arg) for arg in self.args]
else:
args = [Variable(self.args)]
# If we were passed an instance of the view context object we are
# resolving, inject it into the context. This help resolve links for
# object lists.
if resolved_object:
context['resolved_object'] = resolved_object
try:
kwargs = self.kwargs(context)
except TypeError:
# Is not a callable
kwargs = self.kwargs
kwargs = {key: Variable(value) for key, value in kwargs.items()}
# Use Django's exact {% url %} code to resolve the link
node = URLNode(
view_name=view_name, args=args, kwargs=kwargs, asvar=None
)
try:
resolved_link.url = node.render(context)
except Exception as exception:
logger.error(
'Error resolving link "%s" URL; %s', self.text, exception
)
elif self.url:
resolved_link.url = self.url
# This is for links that should be displayed but that are not clickable
if self.conditional_disable:
resolved_link.disabled = self.conditional_disable(context)
else:
resolved_link.disabled = False
# Lets a new link keep the same URL query string of the current URL
if self.keep_query:
# Sometimes we are required to remove a key from the URL QS
parsed_url = furl(
force_str(
request.get_full_path() or request.META.get(
'HTTP_REFERER', resolve_url(settings.LOGIN_REDIRECT_URL)
)
)
)
for key in self.remove_from_query:
try:
parsed_url.query.remove(key)
except KeyError:
pass
# Use the link's URL but with the previous URL querystring
new_url = furl(resolved_link.url)
new_url.args = parsed_url.querystr
resolved_link.url = new_url.url
resolved_link.context = context
return resolved_link
class Menu(object):
@@ -295,165 +409,49 @@ class Menu(object):
)
class Link(object):
_registry = {}
class ResolvedLink(object):
def __init__(self, link, current_view_name):
self.current_view_name = current_view_name
self.disabled = False
self.link = link
self.url = '#'
self.context = None
self.request = None
@classmethod
def get(cls, name):
return cls._registry[name]
@property
def active(self):
return self.link.view == self.current_view_name
@classmethod
def remove(cls, name):
del cls._registry[name]
@property
def description(self):
return self.link.description
def __init__(self, text, view=None, args=None, condition=None,
conditional_disable=None, description=None, html_data=None,
html_extra_classes=None, icon=None, icon_class=None,
keep_query=False, kwargs=None, name=None, permissions=None,
permissions_related=None, remove_from_query=None, tags=None,
url=None):
@property
def html_data(self):
return self.link.html_data
self.args = args or []
self.condition = condition
self.conditional_disable = conditional_disable
self.description = description
self.html_data = html_data
self.html_extra_classes = html_extra_classes
self.icon = icon
self.icon_class = icon_class
self.keep_query = keep_query
self.kwargs = kwargs or {}
self.name = name
self.permissions = permissions or []
self.permissions_related = permissions_related
self.remove_from_query = remove_from_query or []
self.tags = tags
self.text = text
self.view = view
self.url = url
@property
def html_extra_classes(self):
return self.link.html_extra_classes
if name:
self.__class__._registry[name] = self
@property
def icon(self):
return self.link.icon
def resolve(self, context, resolved_object=None):
AccessControlList = apps.get_model(
app_label='acls', model_name='AccessControlList'
)
@property
def icon_class(self):
return self.link.icon_class
# Try to get the request object the faster way and fallback to the
# slower method.
@property
def tags(self):
return self.link.tags
@property
def text(self):
try:
request = context.request
except AttributeError:
request = Variable('request').resolve(context)
current_path = request.META['PATH_INFO']
current_view_name = resolve(current_path).view_name
# ACL is tested agains the resolved_object or just {{ object }} if not
if not resolved_object:
try:
resolved_object = Variable('object').resolve(context=context)
except VariableDoesNotExist:
pass
# If this link has a required permission check that the user has it
# too
if self.permissions:
if resolved_object:
try:
AccessControlList.objects.check_access(
permissions=self.permissions, user=request.user,
obj=resolved_object, related=self.permissions_related
)
except PermissionDenied:
return None
else:
try:
Permission.check_permissions(
requester=request.user, permissions=self.permissions
)
except PermissionDenied:
return None
# Check to see if link has conditional display function and only
# display it if the result of the conditional display function is
# True
if self.condition:
if not self.condition(context):
return None
resolved_link = ResolvedLink(
current_view_name=current_view_name, link=self
)
if self.view:
view_name = Variable('"{}"'.format(self.view))
if isinstance(self.args, list) or isinstance(self.args, tuple):
# TODO: Don't check for instance check for iterable in try/except
# block. This update required changing all 'args' argument in
# links.py files to be iterables and not just strings.
args = [Variable(arg) for arg in self.args]
else:
args = [Variable(self.args)]
# If we were passed an instance of the view context object we are
# resolving, inject it into the context. This help resolve links for
# object lists.
if resolved_object:
context['resolved_object'] = resolved_object
try:
kwargs = self.kwargs(context)
except TypeError:
# Is not a callable
kwargs = self.kwargs
kwargs = {key: Variable(value) for key, value in kwargs.items()}
# Use Django's exact {% url %} code to resolve the link
node = URLNode(
view_name=view_name, args=args, kwargs=kwargs, asvar=None
)
try:
resolved_link.url = node.render(context)
except Exception as exception:
logger.error(
'Error resolving link "%s" URL; %s', self.text, exception
)
elif self.url:
resolved_link.url = self.url
# This is for links that should be displayed but that are not clickable
if self.conditional_disable:
resolved_link.disabled = self.conditional_disable(context)
else:
resolved_link.disabled = False
# Lets a new link keep the same URL query string of the current URL
if self.keep_query:
# Sometimes we are required to remove a key from the URL QS
parsed_url = furl(
force_str(
request.get_full_path() or request.META.get(
'HTTP_REFERER', resolve_url(settings.LOGIN_REDIRECT_URL)
)
)
)
for key in self.remove_from_query:
try:
parsed_url.query.remove(key)
except KeyError:
pass
# Use the link's URL but with the previous URL querystring
new_url = furl(resolved_link.url)
new_url.args = parsed_url.querystr
resolved_link.url = new_url.url
resolved_link.context = context
return resolved_link
return self.link.text(context=self.context)
except TypeError:
return self.link.text
class Separator(Link):

View File

@@ -24,10 +24,11 @@ class LinkClassTestCase(GenericViewTestCase):
def setUp(self):
super(LinkClassTestCase, self).setUp()
self.add_test_view(test_object=self.group)
self.add_test_view(test_object=self._test_case_group)
self.namespace = PermissionNamespace(
label=TEST_PERMISSION_NAMESPACE_TEXT, name=TEST_PERMISSION_NAMESPACE_NAME
label=TEST_PERMISSION_NAMESPACE_TEXT,
name=TEST_PERMISSION_NAMESPACE_NAME
)
self.permission = self.namespace.add_permission(
@@ -49,7 +50,7 @@ class LinkClassTestCase(GenericViewTestCase):
self.login_user()
link = Link(
permissions=(self.permission,), text=TEST_LINK_TEXT,
permission=self.permission, text=TEST_LINK_TEXT,
view=TEST_VIEW_NAME
)
@@ -65,11 +66,11 @@ class LinkClassTestCase(GenericViewTestCase):
self.login_user()
link = Link(
permissions=(self.permission,), text=TEST_LINK_TEXT,
permission=self.permission, text=TEST_LINK_TEXT,
view=TEST_VIEW_NAME
)
self.role.permissions.add(self.permission.stored_permission)
self._test_case_role.permissions.add(self.permission.stored_permission)
response = self.get(TEST_VIEW_NAME)
response.context.update({'request': response.wsgi_request})
@@ -84,12 +85,12 @@ class LinkClassTestCase(GenericViewTestCase):
self.login_user()
link = Link(
permissions=(self.permission,), text=TEST_LINK_TEXT,
permission=self.permission, text=TEST_LINK_TEXT,
view=TEST_VIEW_NAME
)
acl = AccessControlList.objects.create(
content_object=self.group, role=self.role
content_object=self._test_case_group, role=self._test_case_role
)
acl.permissions.add(self.permission.stored_permission)
@@ -156,10 +157,11 @@ class MenuClassTestCase(GenericViewTestCase):
def setUp(self):
super(MenuClassTestCase, self).setUp()
self.add_test_view(test_object=self.group)
self.add_test_view(test_object=self._test_case_group)
self.namespace = PermissionNamespace(
label=TEST_PERMISSION_NAMESPACE_TEXT, name=TEST_PERMISSION_NAMESPACE_NAME
label=TEST_PERMISSION_NAMESPACE_TEXT,
name=TEST_PERMISSION_NAMESPACE_NAME
)
self.permission = self.namespace.add_permission(