update user/role management (now it works)

This commit is contained in:
Lance Edgar 2012-11-12 07:41:03 -08:00
parent e95a23ead8
commit 9b2589ca12
13 changed files with 225 additions and 219 deletions

View file

@ -89,7 +89,9 @@ class Role(Base):
creator=lambda x: Permission(permission=x), creator=lambda x: Permission(permission=x),
getset_factory=getset_factory) getset_factory=getset_factory)
_users = relationship(UserRole, backref='role') _users = relationship(
UserRole, backref='role',
cascade='save-update, merge, delete, delete-orphan')
users = association_proxy('_users', 'user', users = association_proxy('_users', 'user',
creator=lambda x: UserRole(user=x), creator=lambda x: UserRole(user=x),
getset_factory=getset_factory) getset_factory=getset_factory)

View file

@ -54,6 +54,9 @@ class AlchemyGrid(Grid):
self._formalchemy_grid.prettify = prettify self._formalchemy_grid.prettify = prettify
self.noclick_fields = [] self.noclick_fields = []
def __delattr__(self, attr):
delattr(self._formalchemy_grid, attr)
def __getattr__(self, attr): def __getattr__(self, attr):
return getattr(self._formalchemy_grid, attr) return getattr(self._formalchemy_grid, attr)

View file

@ -1,17 +1,33 @@
/****************************** /******************************
* perms.css * Permission Lists
******************************/ ******************************/
div.field-couple.permissions div.field p.group { div.field-wrapper.permissions div.field div.group {
margin-bottom: 10px;
}
div.field-wrapper.permissions div.field div.group p {
font-weight: bold; font-weight: bold;
} }
div.field-couple.permissions div.field label { div.field-wrapper.permissions div.field label {
float: none; float: none;
font-weight: normal; font-weight: normal;
} }
div.field-couple.permissions div.field label input { div.field-wrapper.permissions div.field label input {
margin-left: 15px;
margin-right: 10px;
}
div.field-wrapper.permissions div.field div.group p.perm {
font-weight: normal;
margin-left: 15px;
}
div.field-wrapper.permissions div.field div.group p.perm span {
font-family: monospace;
/* font-weight: bold; */
margin-right: 10px; margin-right: 10px;
} }

View file

@ -79,6 +79,13 @@ def context_found(event):
return has_permission(request.user, perm, session=Session()) return has_permission(request.user, perm, session=Session())
request.has_perm = has_perm request.has_perm = has_perm
def has_any_perm(perms):
for perm in perms:
if has_permission(request.user, perm, session=Session()):
return True
return False
request.has_any_perm = has_any_perm
def get_referrer(default=None): def get_referrer(default=None):
if request.params.get('referrer'): if request.params.get('referrer'):
return request.params['referrer'] return request.params['referrer']

View file

@ -1,3 +1,18 @@
<%inherit file="/edbob/crud.mako" /> <%inherit file="/form.mako" />
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+h.literal(str(form.fieldset.model))}</%def>
<%def name="head_tags()">
${parent.head_tags()}
<script type="text/javascript">
$(function() {
$('a.delete').click(function() {
if (! confirm("Do you really wish to delete this object?")) {
return false;
}
});
});
</script>
</%def>
${parent.body()} ${parent.body()}

View file

@ -1,5 +0,0 @@
<%inherit file="/form.mako" />
<%def name="title()">${"New "+form.pretty_name if form.creating else form.pretty_name+' : '+h.literal(str(form.fieldset.model))}</%def>
${parent.body()}

View file

@ -1,7 +1,7 @@
<div class="fieldset"> <div class="fieldset">
% for field in fieldset.render_fields.itervalues(): % for field in fieldset.render_fields.itervalues():
% if field.requires_label: % if field.requires_label:
<div class="field-wrapper"> <div class="field-wrapper ${field.name}">
${field.label_tag()|n} ${field.label_tag()|n}
<div class="field"> <div class="field">
${field.render_readonly()} ${field.render_readonly()}

View file

@ -1,2 +0,0 @@
<%inherit file="/base.mako" />
${parent.body()}

View file

@ -0,0 +1,18 @@
<%inherit file="edbob.pyramid:templates/crud.mako" />
<%def name="head_tags()">
${parent.head_tags()}
${h.stylesheet_link(request.static_url('edbob.pyramid:static/css/perms.css'))}
</%def>
<%def name="context_menu_items()">
<li>${h.link_to("Back to Roles", url('roles'))}</li>
% if form.readonly:
<li>${h.link_to("Edit this Role", url('role.update', uuid=form.fieldset.model.uuid))}</li>
% elif form.updating:
<li>${h.link_to("View this Role", url('role.read', uuid=form.fieldset.model.uuid))}</li>
% endif
<li>${h.link_to("Delete this Role", url('role.delete', uuid=form.fieldset.model.uuid), class_='delete')}</li>
</%def>
${parent.body()}

View file

@ -1,10 +1,11 @@
<%inherit file="/roles/base.mako" /> <%inherit file="/grid.mako" />
<%inherit file="/index.mako" />
<%def name="title()">Roles</%def> <%def name="title()">Roles</%def>
<%def name="context_menu_items()"> <%def name="context_menu_items()">
<li>${h.link_to("Create a new Role", url('role.new'))}</li> % if request.has_perm('roles.create'):
<li>${h.link_to("Create a new Role", url('role.create'))}</li>
% endif
</%def> </%def>
${parent.body()} ${parent.body()}

View file

@ -1,15 +0,0 @@
<%inherit file="/roles/base.mako" />
<%inherit file="/crud.mako" />
<%def name="crud_name()">Role</%def>
<%def name="head_tags()">
${parent.head_tags()}
${h.stylesheet_link(request.static_url('edbob.pyramid:static/css/perms.css'))}
</%def>
<%def name="menu()">
<p>${h.link_to("Back to Roles", url('roles.list'))}</p>
</%def>
${parent.body()}

View file

@ -26,75 +26,83 @@
``edbob.pyramid.views.roles`` -- Role Views ``edbob.pyramid.views.roles`` -- Role Views
""" """
import transaction
from pyramid.httpexceptions import HTTPFound from pyramid.httpexceptions import HTTPFound
from formalchemy import Field, FieldRenderer import formalchemy
from webhelpers.html import literal from webhelpers.html import tags
from webhelpers.html.tags import checkbox, hidden from webhelpers.html.builder import HTML
import edbob import edbob
from edbob.db.auth import administrator_role, has_permission from edbob.db import auth
from edbob.pyramid import filters
from edbob.pyramid import forms
from edbob.pyramid import grids
from edbob.pyramid import Session from edbob.pyramid import Session
from edbob.pyramid.views import SearchableAlchemyGridView, CrudView
def filter_map(): default_permissions = [
return filters.get_filter_map(
edbob.Role,
ilike=['name'])
def search_config(request, fmap): ("People", [
return filters.get_search_config( ('people.list', "List People"),
'roles.list', request, fmap, ('people.read', "View Person"),
include_filter_name=True, ('people.create', "Create Person"),
filter_type_name='lk') ('people.update', "Edit Person"),
('people.delete', "Delete Person"),
]),
def search_form(config): ("Roles", [
return filters.get_search_form(config) ('roles.list', "List Roles"),
('roles.read', "View Role"),
('roles.create', "Create Role"),
('roles.update', "Edit Role"),
('roles.delete', "Delete Role"),
]),
def grid_config(request, search, fmap): ("Users", [
return grids.get_grid_config( ('users.list', "List Users"),
'roles.list', request, search, ('users.read', "View User"),
filter_map=fmap, sort='name') ('users.create', "Create User"),
('users.update', "Edit User"),
def sort_map(): ('users.delete', "Delete User"),
return grids.get_sort_map(edbob.Role, ['name']) ]),
]
def query(config):
smap = sort_map()
q = Session.query(edbob.Role)
q = filters.filter_query(q, config)
q = grids.sort_query(q, config, smap)
return q
def roles(request): class RolesGrid(SearchableAlchemyGridView):
fmap = filter_map() mapped_class = edbob.Role
config = search_config(request, fmap) config_prefix = 'roles'
search = search_form(config) sort = 'name'
config = grid_config(request, search, fmap)
roles = grids.get_pager(query, config)
g = forms.AlchemyGrid( def filter_map(self):
edbob.Role, roles, config, return self.make_filter_map(ilike=['name'])
gridurl=request.route_url('roles.list'),
objurl='role.edit')
g.configure( def filter_config(self):
include=[ return self.make_filter_config(
g.name, include_filter_name=True,
], filter_type_name='lk')
readonly=True)
grid = g.render(class_='clickable roles') def sort_map(self):
return grids.render_grid(request, grid, search) return self.make_sort_map('name')
def grid(self):
g = self.make_grid()
g.configure(
include=[
g.name,
],
readonly=True)
if self.request.has_perm('roles.read'):
g.clickable = True
g.click_route_name = 'role.read'
if self.request.has_perm('roles.update'):
g.editable = True
g.edit_route_name = 'role.update'
if self.request.has_perm('roles.delete'):
g.deletable = True
g.delete_route_name = 'role.delete'
return g
class PermissionsField(Field): class PermissionsField(formalchemy.Field):
def sync(self): def sync(self):
if not self.is_readonly(): if not self.is_readonly():
@ -102,154 +110,108 @@ class PermissionsField(Field):
role.permissions = self.renderer.deserialize() role.permissions = self.renderer.deserialize()
class PermissionsFieldRenderer(FieldRenderer): def PermissionsFieldRenderer(permissions, *args, **kwargs):
available_permissions = [ perms = permissions
("Batches", [
('batches.list', "List Batches"),
('batches.edit', "Edit Batch"),
('batches.create', "Create Batch"),
]),
("Roles", [
('roles.list', "List Roles"),
('roles.edit', "Edit Role"),
('roles.create', "Create Role"),
]),
]
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):
# result = literal('')
# for group_name, group_label, perm_list in self.field.model_value:
# rendered_group_name = literal('<p class="permission-group">' + group_label + '</p>\n')
# if readonly:
# result += literal('<tr><td colspan="2">') + rendered_group_name + literal('</td></tr>')
# else:
# result += rendered_group_name
# result += literal('<div>')
# for perm_name, perm_label, checked in perm_list:
# if readonly:
# result += literal('<tr>'
# + '<td class="permission">' + ('[X]' if checked else '[&nbsp; ]') + '</td>'
# + '<td class="permission-label">' + perm_label + '</td>'
# + '</tr>\n')
# else:
# name = '.'.join((self.name, group_name, perm_name))
# result += check_box(name, label=perm_label, checked=checked)
# if not readonly:
# result += literal('</div>')
# if readonly:
# return literal('<table class="permissions">') + result + literal('</table>')
# return literal('<div class="permissions">') + result + literal('</div>')
role = self.field.model
if role is administrator_role(Session()):
res = literal('<p>This is the administrative role; '
'it has full access to the entire system.</p>')
if not readonly:
res += hidden(self.name, value='') # ugly hack..or good idea?
else:
res = ''
for group, perms in self.available_permissions:
res += literal('<p class="group">%s</p>' % group)
for perm, title in perms:
if readonly:
res += literal('<p>%s</p>' % title)
else:
checked = has_permission(role, perm)
res += checkbox(self.name + '-' + perm,
checked=checked, label=title)
return res
def render(self, **kwargs):
return self._render(**kwargs)
def render_readonly(self, **kwargs):
return self._render(readonly=True, **kwargs)
def role_fieldset(role, request):
fs = forms.make_fieldset(role, url=request.route_url,
url_action=request.current_route_url(),
route_name='roles.list')
fs.append(PermissionsField('permissions', class PermissionsFieldRenderer(formalchemy.FieldRenderer):
renderer=PermissionsFieldRenderer))
fs.configure( permissions = perms
include=[
fs.name,
fs.permissions,
])
if not fs.edit: def deserialize(self):
del fs.permissions perms = []
i = len(self.name) + 1
for key in self.params:
if key.startswith(self.name):
perms.append(key[i:])
return perms
return fs def _render(self, readonly=False, **kwargs):
role = self.field.model
admin = auth.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 group, perms in self.permissions:
inner = HTML.tag('p', c=group)
for perm, title in perms:
checked = auth.has_permission(role, perm, Session())
if readonly:
span = HTML.tag('span', c="[X]" if checked else "[ ]")
inner += HTML.tag('p', class_='perm', c=span + ' ' + title)
else:
checked = auth.has_permission(role, perm, Session())
inner += tags.checkbox(self.name + '-' + perm,
checked=checked, label=title)
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
def new_role(request): class RoleCrud(CrudView):
fs = role_fieldset(edbob.Role, request) mapped_class = edbob.Role
if request.POST: home_route = 'roles'
fs.rebind(data=request.params) permissions = default_permissions
if fs.validate():
with transaction.manager: def fieldset(self, role):
fs.sync() fs = self.make_fieldset(role)
fs.model = Session.merge(fs.model) fs.append(PermissionsField(
request.session.flash("%s \"%s\" has been %s." % ( 'permissions',
fs.crud_title, fs.get_display_text(), renderer=PermissionsFieldRenderer(self.permissions)))
'updated' if fs.edit else 'created')) fs.configure(
home = request.route_url('roles.list') include=[
fs.name,
fs.permissions,
])
return fs
return HTTPFound(location=home) def pre_delete(self, model):
admin = auth.administrator_role(Session())
return {'fieldset': fs, 'crud': True} guest = auth.guest_role(Session())
if model in (admin, guest):
self.request.session.flash("You may not delete the %s role." % str(model), 'error')
def edit_role(request): return HTTPFound(location=self.request.get_referrer())
uuid = request.matchdict['uuid']
role = Session.query(edbob.Role).get(uuid) if uuid else None
assert role
fs = role_fieldset(role, request)
if request.POST:
fs.rebind(data=request.params)
if fs.validate():
with transaction.manager:
Session.add(fs.model)
fs.sync()
request.session.flash("%s \"%s\" has been %s." % (
fs.crud_title, fs.get_display_text(),
'updated' if fs.edit else 'created'))
home = request.route_url('roles.list')
return HTTPFound(location=home)
return {'fieldset': fs, 'crud': True}
def includeme(config): def includeme(config):
config.add_route('roles', '/roles')
config.add_view(RolesGrid, route_name='roles',
renderer='/roles/index.mako',
permission='roles.list')
config.add_route('roles.list', '/roles') settings = config.get_settings()
config.add_view(roles, route_name='roles.list', renderer='/roles/index.mako', perms = settings.get('edbob.permissions')
permission='roles.list', http_cache=0) if perms:
RoleCrud.permissions = perms
config.add_route('role.new', '/roles/new') config.add_route('role.create', '/roles/new')
config.add_view(new_role, route_name='role.new', renderer='/roles/role.mako', config.add_view(RoleCrud, attr='create', route_name='role.create',
permission='roles.create', http_cache=0) renderer='/roles/crud.mako',
permission='roles.create')
config.add_route('role.edit', '/roles/{uuid}/edit') config.add_route('role.read', '/roles/{uuid}')
config.add_view(edit_role, route_name='role.edit', renderer='/roles/role.mako', config.add_view(RoleCrud, attr='read', route_name='role.read',
permission='roles.edit', http_cache=0) renderer='/roles/crud.mako',
permission='roles.read')
config.add_route('role.update', '/roles/{uuid}/edit')
config.add_view(RoleCrud, attr='update', route_name='role.update',
renderer='/roles/crud.mako',
permission='roles.update')
config.add_route('role.delete', '/roles/{uuid}/delete')
config.add_view(RoleCrud, attr='delete', route_name='role.delete',
permission='roles.delete')

View file

@ -95,7 +95,7 @@ class _RolesFieldRenderer(SelectFieldRenderer):
role = roles.get(uuid) role = roles.get(uuid)
res += literal('<li>%s</li>' % ( res += literal('<li>%s</li>' % (
tags.link_to(role.name, tags.link_to(role.name,
self.request.route_url('role.edit', uuid=role.uuid)))) self.request.route_url('role.read', uuid=role.uuid))))
res += literal('</ul>') res += literal('</ul>')
return res return res
@ -206,6 +206,10 @@ class UserCrud(CrudView):
fs.roles, fs.roles,
]) ])
if self.readonly:
del fs.password
del fs.confirm_password
# if fs.edit and user.person: # if fs.edit and user.person:
if isinstance(user, edbob.User) and user.person: if isinstance(user, edbob.User) and user.person:
fs.person.set(readonly=True, fs.person.set(readonly=True,