tailbone/tailbone/views/roles.py

170 lines
6.3 KiB
Python

# -*- coding: utf-8 -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2016 Lance Edgar
#
# This file is part of Rattail.
#
# Rattail is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Role Views
"""
from __future__ import unicode_literals, absolute_import
from rattail.db import model
from rattail.db.auth import has_permission, administrator_role, guest_role, authenticated_role
import formalchemy
from webhelpers.html import HTML, tags
from tailbone.db import Session
from tailbone.views import MasterView
from tailbone.views.continuum import VersionView, version_defaults
from tailbone.newgrids import AlchemyGrid, GridAction
class PermissionsField(formalchemy.Field):
"""
Custom field for role permissions.
"""
def sync(self):
if not self.is_readonly():
role = self.model
role.permissions = self.renderer.deserialize()
def PermissionsFieldRenderer(permissions, *args, **kwargs):
class PermissionsFieldRenderer(formalchemy.FieldRenderer):
def deserialize(self):
perms = []
i = len(self.name) + 1
for key in self.params:
if key.startswith(self.name):
perms.append(key[i:])
return perms
def _render(self, readonly=False, **kwargs):
role = self.field.model
admin = administrator_role(Session())
if role is admin:
html = HTML.tag('p', c="This is the administrative role; "
"it has full access to the entire system.")
if not readonly:
html += tags.hidden(self.name, value='') # ugly hack..or good idea?
else:
html = ''
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
inner = HTML.tag('p', c=permissions[groupkey]['label'])
perms = permissions[groupkey]['perms']
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
checked = has_permission(Session(), role, key,
include_guest=False,
include_authenticated=False)
label = perms[key]['label']
if readonly:
span = HTML.tag('span', c="[X]" if checked else "[ ]")
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
else:
inner += tags.checkbox(self.name + '-' + key,
checked=checked, label=label)
html += HTML.tag('div', class_='group', c=inner)
return html
def render(self, **kwargs):
return self._render(**kwargs)
def render_readonly(self, **kwargs):
return self._render(readonly=True, **kwargs)
return PermissionsFieldRenderer
class RolesView(MasterView):
"""
Master view for the Role model.
"""
model_class = model.Role
def configure_grid(self, g):
g.filters['name'].default_active = True
g.filters['name'].default_verb = 'contains'
g.default_sortkey = 'name'
g.configure(
include=[
g.name,
],
readonly=True)
def configure_fieldset(self, fs):
fs.append(PermissionsField('permissions'))
permissions = self.request.registry.settings.get('tailbone_permissions', {})
fs.permissions.set(renderer=PermissionsFieldRenderer(permissions))
fs.configure(
include=[
fs.name,
fs.permissions,
])
def template_kwargs_view(self, **kwargs):
role = kwargs['instance']
if role.users:
# TODO: This is the first attempt at using a new grid outside of
# the context of a primary master grid. The API here is really
# much hairier than I'd like... Looks like we shouldn't need a key
# for this one, for instance (no settings required), but there is
# plenty of room for improvement here.
users = sorted(role.users, key=lambda u: u.username)
users = AlchemyGrid('roles.users', self.request, data=users, model_class=model.User,
main_actions=[
GridAction('view', icon='zoomin',
url=lambda r, i: self.request.route_url('users.view', uuid=r.uuid)),
])
users.configure(include=[users.username], readonly=True)
kwargs['users'] = users
else:
kwargs['users'] = None
kwargs['guest_role'] = guest_role(Session())
kwargs['authenticated_role'] = authenticated_role(Session())
return kwargs
def before_delete(self, role):
admin = administrator_role(Session())
guest = guest_role(Session())
authenticated = authenticated_role(Session())
if role in (admin, guest, authenticated):
self.request.session.flash("You may not delete the {} role.".format(role.name), 'error')
return self.redirect(self.request.get_referrer(default=self.request.route_url('roles')))
class RoleVersionView(VersionView):
"""
View which shows version history for a role.
"""
parent_class = model.Role
route_model_view = 'roles.view'
def includeme(config):
RolesView.defaults(config)
version_defaults(config, RoleVersionView, 'role')