#!/usr/bin/env python # -*- coding: utf-8 -*- ################################################################################ # # edbob -- Pythonic Software Framework # Copyright © 2010-2012 Lance Edgar # # This file is part of edbob. # # edbob 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. # # edbob 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 edbob. If not, see . # ################################################################################ """ ``edbob.pyramid.views.users`` -- User Views """ from webhelpers.html import tags from webhelpers.html.builder import HTML import formalchemy from formalchemy.fields import SelectFieldRenderer import edbob from edbob.db import auth from edbob.pyramid import Session from edbob.pyramid.views import SearchableAlchemyGridView, CrudView class UsersGrid(SearchableAlchemyGridView): mapped_class = edbob.User config_prefix = 'users' sort = 'username' def join_map(self): return { 'person': lambda q: q.outerjoin(edbob.Person), } def filter_map(self): return self.make_filter_map( ilike=['username'], person=self.filter_ilike(edbob.Person.display_name)) def filter_config(self): return self.make_filter_config( include_filter_username=True, filter_type_username='lk', include_filter_person=True, filter_type_person='lk') def sort_map(self): return self.make_sort_map( 'username', person=self.sorter(edbob.Person.display_name)) def grid(self): g = self.make_grid() g.configure( include=[ g.username, g.person, ], readonly=True) if self.request.has_perm('users.read'): g.clickable = True g.click_route_name = 'user.read' if self.request.has_perm('users.update'): g.editable = True g.edit_route_name = 'user.update' if self.request.has_perm('users.delete'): g.deletable = True g.delete_route_name = 'user.delete' return g def RolesFieldRenderer(request): class RolesFieldRenderer(SelectFieldRenderer): def render_readonly(self, **kwargs): roles = Session.query(edbob.Role) html = '' for uuid in self.value: role = roles.get(uuid) link = tags.link_to( role.name, request.route_url('role.read', uuid=role.uuid)) html += HTML.tag('li', c=link) html = HTML.tag('ul', c=html) return html return RolesFieldRenderer class RolesField(formalchemy.Field): def __init__(self, name, **kwargs): kwargs.setdefault('value', self.get_value) kwargs.setdefault('options', self.get_options()) kwargs.setdefault('multiple', True) super(RolesField, self).__init__(name, **kwargs) def get_value(self, user): return [x.uuid for x in user.roles] def get_options(self): q = Session.query(edbob.Role.name, edbob.Role.uuid) q = q.filter(edbob.Role.uuid != auth.guest_role(Session()).uuid) q = q.order_by(edbob.Role.name) return q.all() def sync(self): if not self.is_readonly(): user = self.model roles = Session.query(edbob.Role) data = self.renderer.deserialize() user.roles = [roles.get(x) for x in data] class _ProtectedPersonRenderer(formalchemy.FieldRenderer): def render_readonly(self, **kwargs): res = str(self.person) res += tags.hidden('User--person_uuid', value=self.field.parent.person_uuid.value) return res def ProtectedPersonRenderer(uuid): person = Session.query(edbob.Person).get(uuid) assert person return type('ProtectedPersonRenderer', (_ProtectedPersonRenderer,), {'person': person}) class _LinkedPersonRenderer(formalchemy.FieldRenderer): def render_readonly(self, **kwargs): return tags.link_to(str(self.raw_value), self.request.route_url('person.edit', uuid=self.value)) def LinkedPersonRenderer(request): return type('LinkedPersonRenderer', (_LinkedPersonRenderer,), {'request': request}) class PasswordFieldRenderer(formalchemy.PasswordFieldRenderer): def render(self, **kwargs): return tags.password(self.name, value='', maxlength=self.length, **kwargs) def passwords_match(value, field): if field.parent.confirm_password.value != value: raise formalchemy.ValidationError("Passwords do not match") return value class PasswordField(formalchemy.Field): def __init__(self, *args, **kwargs): kwargs.setdefault('value', lambda x: x.password) kwargs.setdefault('renderer', PasswordFieldRenderer) kwargs.setdefault('validate', passwords_match) super(PasswordField, self).__init__(*args, **kwargs) def sync(self): if not self.is_readonly(): password = self.renderer.deserialize() if password: auth.set_user_password(self.model, password) class UserCrud(CrudView): mapped_class = edbob.User home_route = 'users' def fieldset(self, user): fs = self.make_fieldset(user) fs.append(PasswordField('password')) fs.append(formalchemy.Field('confirm_password', renderer=PasswordFieldRenderer)) fs.append(RolesField('roles', renderer=RolesFieldRenderer(self.request))) fs.configure( include=[ fs.username, fs.person, fs.password.label("Set Password"), fs.confirm_password, fs.roles, ]) if self.readonly: del fs.password del fs.confirm_password # if fs.edit and user.person: if isinstance(user, edbob.User) and user.person: fs.person.set(readonly=True, renderer=LinkedPersonRenderer(self.request)) return fs def validation_failed(self, fs): if not fs.edit and fs.person_uuid.value: fs.person.set(readonly=True, renderer=ProtectedPersonRenderer(fs.person_uuid.value)) def includeme(config): config.add_route('users', '/users') config.add_view(UsersGrid, route_name='users', renderer='/users/index.mako', permission='users.list') config.add_route('user.create', '/users/new') config.add_view(UserCrud, attr='create', route_name='user.create', renderer='/users/crud.mako', permission='users.create') config.add_route('user.read', '/users/{uuid}') config.add_view(UserCrud, attr='read', route_name='user.read', renderer='/users/crud.mako', permission='users.read') config.add_route('user.update', '/users/{uuid}/edit') config.add_view(UserCrud, attr='update', route_name='user.update', renderer='/users/crud.mako', permission='users.update') config.add_route('user.delete', '/users/{uuid}/delete') config.add_view(UserCrud, attr='delete', route_name='user.delete', permission='users.delete')