Refactor user and role views to use master3
This commit is contained in:
parent
789bdef190
commit
86cfc59d33
|
@ -81,12 +81,6 @@ div.error-messages div.ui-state-error {
|
|||
margin: 0.5em 0 0 0;
|
||||
}
|
||||
|
||||
div.error {
|
||||
color: #dd6666;
|
||||
font-weight: bold;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
ul.error {
|
||||
color: #dd6666;
|
||||
font-weight: bold;
|
||||
|
|
|
@ -45,47 +45,54 @@ div.fieldset {
|
|||
* Fieldsets
|
||||
******************************/
|
||||
|
||||
div.field-wrapper {
|
||||
.field-wrapper {
|
||||
clear: both;
|
||||
min-height: 30px;
|
||||
overflow: auto;
|
||||
padding: 5px;
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
div.field-wrapper.error {
|
||||
.field-wrapper.error {
|
||||
background-color: #ddcccc;
|
||||
border: 2px solid #dd6666;
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
div.field-wrapper label {
|
||||
display: block;
|
||||
float: left;
|
||||
.field-wrapper .field-row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.field-wrapper label {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
width: 15em;
|
||||
font-weight: bold;
|
||||
margin-top: 2px;
|
||||
padding-top: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.field-wrapper.error label {
|
||||
color: Black;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field-error {
|
||||
.field-wrapper .field-error {
|
||||
padding: 1em 0 0.5em 1em;
|
||||
}
|
||||
|
||||
.field-wrapper .field-error .error-msg {
|
||||
color: #dd6666;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field {
|
||||
display: block;
|
||||
float: left;
|
||||
margin-bottom: 5px;
|
||||
.field-wrapper .field {
|
||||
display: table-cell;
|
||||
line-height: 25px;
|
||||
}
|
||||
|
||||
div.field-wrapper div.field input[type=text],
|
||||
div.field-wrapper div.field input[type=password],
|
||||
div.field-wrapper div.field select,
|
||||
div.field-wrapper div.field textarea {
|
||||
.field-wrapper .field input[type=text],
|
||||
.field-wrapper .field input[type=password],
|
||||
.field-wrapper .field select,
|
||||
.field-wrapper .field textarea {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
|
@ -94,7 +101,7 @@ label input[type="radio"] {
|
|||
margin-right: 0.5em;
|
||||
}
|
||||
|
||||
div.field ul {
|
||||
.field ul {
|
||||
margin: 0px;
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
|
|
@ -3,34 +3,30 @@
|
|||
* Permission Lists
|
||||
******************************/
|
||||
|
||||
div.field-wrapper.permissions {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field div.group {
|
||||
.field-wrapper.permissions .field .group {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field div.group p {
|
||||
.field-wrapper.permissions .field .group p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field label {
|
||||
float: none;
|
||||
.field-wrapper.permissions .field label {
|
||||
display: inline;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field label input {
|
||||
.field-wrapper.permissions .field label input {
|
||||
margin-left: 15px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field div.group p.perm {
|
||||
.field-wrapper.permissions .field .group p.perm {
|
||||
font-weight: normal;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
div.field-wrapper.permissions div.field div.group p.perm span {
|
||||
.field-wrapper.permissions .field .group p.perm span {
|
||||
font-family: monospace;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
|
28
tailbone/templates/deform/permissions.pt
Normal file
28
tailbone/templates/deform/permissions.pt
Normal file
|
@ -0,0 +1,28 @@
|
|||
<div tal:define="oid oid|field.oid;
|
||||
true_val true_val|field.widget.true_val;"
|
||||
tal:omit-tag="">
|
||||
${field.start_mapping()}
|
||||
|
||||
<tal:loop tal:repeat="groupkey sorted(permissions, key=lambda k: permissions[k]['label'].lower())">
|
||||
<div tal:define="perms permissions[groupkey]['perms'];"
|
||||
class="group">
|
||||
<p class="group">${permissions[groupkey]['label']}</p>
|
||||
|
||||
<tal:loop tal:repeat="key sorted(perms, key=lambda p: perms[p]['label'].lower())">
|
||||
<div class="perm">
|
||||
<label>
|
||||
<input type="checkbox"
|
||||
name="${key}"
|
||||
id="${oid}-${key}"
|
||||
value="${true_val}"
|
||||
tal:attributes="checked python:field.widget.get_checked_value(cstruct, key);" />
|
||||
${perms[key]['label']}
|
||||
</label>
|
||||
</div>
|
||||
</tal:loop>
|
||||
|
||||
</div>
|
||||
</tal:loop>
|
||||
|
||||
${field.end_mapping()}
|
||||
</div>
|
|
@ -26,11 +26,13 @@ ${h.csrf_token(request)}
|
|||
## <div class="field-error">${error}</div>
|
||||
## % endfor
|
||||
% if field.error:
|
||||
<div class="field-error">${field.error.msg}</div>
|
||||
<div class="field-error"><span class="error-msg">${field.error.msg}</span></div>
|
||||
% endif
|
||||
<label for="${field.oid}">${field.title}</label>
|
||||
<div class="field">
|
||||
${field.serialize()|n}
|
||||
<div class="field-row">
|
||||
<label for="${field.oid}">${field.title}</label>
|
||||
<div class="field">
|
||||
${field.serialize()|n}
|
||||
</div>
|
||||
</div>
|
||||
% if form.has_helptext(field.name):
|
||||
<span class="instructions">${form.render_helptext(field.name)}</span>
|
||||
|
|
|
@ -28,9 +28,14 @@ from __future__ import unicode_literals, absolute_import
|
|||
|
||||
import copy
|
||||
|
||||
import wtforms
|
||||
from rattail.db.auth import has_permission
|
||||
from rattail.core import Object
|
||||
|
||||
from tailbone.views import MasterView2 as MasterView
|
||||
import wtforms
|
||||
from webhelpers2.html import HTML
|
||||
|
||||
from tailbone.db import Session
|
||||
from tailbone.views import MasterView3 as MasterView
|
||||
|
||||
|
||||
class PrincipalMasterView(MasterView):
|
||||
|
@ -95,3 +100,33 @@ class PrincipalMasterView(MasterView):
|
|||
permission='{}.find_by_perm'.format(permission_prefix))
|
||||
config.add_tailbone_permission(permission_prefix, '{}.find_by_perm'.format(permission_prefix),
|
||||
"Find all {} with permission X".format(model_title_plural))
|
||||
|
||||
|
||||
class PermissionsRenderer(Object):
|
||||
permissions = None
|
||||
include_guest = False
|
||||
include_authenticated = False
|
||||
|
||||
def __call__(self, principal, field):
|
||||
self.principal = principal
|
||||
return self.render()
|
||||
|
||||
def render(self):
|
||||
principal = self.principal
|
||||
html = ''
|
||||
for groupkey in sorted(self.permissions, key=lambda k: self.permissions[k]['label'].lower()):
|
||||
inner = HTML.tag('p', c=self.permissions[groupkey]['label'])
|
||||
perms = self.permissions[groupkey]['perms']
|
||||
rendered = False
|
||||
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
|
||||
checked = has_permission(Session(), principal, key,
|
||||
include_guest=self.include_guest,
|
||||
include_authenticated=self.include_authenticated)
|
||||
if checked:
|
||||
label = perms[key]['label']
|
||||
span = HTML.tag('span', c="[X]" if checked else "[ ]")
|
||||
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
|
||||
rendered = True
|
||||
if rendered:
|
||||
html += HTML.tag('div', class_='group', c=inner)
|
||||
return html or "(none granted)"
|
||||
|
|
|
@ -26,17 +26,18 @@ Role Views
|
|||
|
||||
from __future__ import unicode_literals, absolute_import
|
||||
|
||||
import six
|
||||
from sqlalchemy import orm
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.db.auth import has_permission, administrator_role, guest_role, authenticated_role
|
||||
|
||||
import formalchemy as fa
|
||||
from formalchemy.fields import IntegerFieldRenderer
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
|
||||
from tailbone import forms, grids
|
||||
from tailbone import grids
|
||||
from tailbone.db import Session
|
||||
from tailbone.views.principal import PrincipalMasterView
|
||||
from tailbone.views.principal import PrincipalMasterView, PermissionsRenderer
|
||||
|
||||
|
||||
class RolesView(PrincipalMasterView):
|
||||
|
@ -51,6 +52,12 @@ class RolesView(PrincipalMasterView):
|
|||
'session_timeout',
|
||||
]
|
||||
|
||||
form_fields = [
|
||||
'name',
|
||||
'session_timeout',
|
||||
'permissions',
|
||||
]
|
||||
|
||||
def configure_grid(self, g):
|
||||
super(RolesView, self).configure_grid(g)
|
||||
g.filters['name'].default_active = True
|
||||
|
@ -58,21 +65,39 @@ class RolesView(PrincipalMasterView):
|
|||
g.set_sort_defaults('name')
|
||||
g.set_link('name')
|
||||
|
||||
def _preconfigure_fieldset(self, fs):
|
||||
fs.append(PermissionsField('permissions'))
|
||||
permissions = self.request.registry.settings.get('tailbone_permissions', {})
|
||||
fs.permissions.set(renderer=forms.renderers.PermissionsFieldRenderer(permissions))
|
||||
fs.session_timeout.set(renderer=SessionTimeoutRenderer)
|
||||
if (self.viewing or self.editing) and fs.model is guest_role(self.Session()):
|
||||
fs.session_timeout.set(readonly=True, attrs={'applicable': False})
|
||||
def configure_form(self, f):
|
||||
super(RolesView, self).configure_form(f)
|
||||
role = f.model_instance
|
||||
|
||||
def configure_fieldset(self, fs):
|
||||
fs.configure(
|
||||
include=[
|
||||
fs.name,
|
||||
fs.session_timeout,
|
||||
fs.permissions,
|
||||
])
|
||||
# permissions
|
||||
self.tailbone_permissions = self.request.registry.settings.get('tailbone_permissions', {})
|
||||
f.set_renderer('permissions', PermissionsRenderer(permissions=self.tailbone_permissions))
|
||||
f.set_node('permissions', colander.Set())
|
||||
f.set_widget('permissions', PermissionsWidget(permissions=self.tailbone_permissions))
|
||||
if self.editing:
|
||||
granted = []
|
||||
for groupkey in self.tailbone_permissions:
|
||||
for key in self.tailbone_permissions[groupkey]['perms']:
|
||||
if has_permission(self.Session(), role, key, include_guest=False, include_authenticated=False):
|
||||
granted.append(key)
|
||||
f.set_default('permissions', granted)
|
||||
|
||||
# session_timeout
|
||||
f.set_renderer('session_timeout', self.render_session_timeout)
|
||||
if self.editing and role is guest_role(self.Session()):
|
||||
f.set_readonly('session_timeout')
|
||||
|
||||
def render_session_timeout(self, role, field):
|
||||
if role is guest_role(self.Session()):
|
||||
return "(not applicable)"
|
||||
if role.session_timeout is None:
|
||||
return ""
|
||||
return six.text_type(role.session_timeout)
|
||||
|
||||
def objectify(self, form, data):
|
||||
role = super(RolesView, self).objectify(form, data)
|
||||
role.permissions = data['permissions']
|
||||
return role
|
||||
|
||||
def template_kwargs_view(self, **kwargs):
|
||||
role = kwargs['instance']
|
||||
|
@ -86,14 +111,14 @@ class RolesView(PrincipalMasterView):
|
|||
main_actions=actions)
|
||||
else:
|
||||
kwargs['users'] = None
|
||||
kwargs['guest_role'] = guest_role(Session())
|
||||
kwargs['authenticated_role'] = authenticated_role(Session())
|
||||
kwargs['guest_role'] = guest_role(self.Session())
|
||||
kwargs['authenticated_role'] = authenticated_role(self.Session())
|
||||
return kwargs
|
||||
|
||||
def before_delete(self, role):
|
||||
admin = administrator_role(Session())
|
||||
guest = guest_role(Session())
|
||||
authenticated = authenticated_role(Session())
|
||||
admin = administrator_role(self.Session())
|
||||
guest = guest_role(self.Session())
|
||||
authenticated = authenticated_role(self.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')))
|
||||
|
@ -110,23 +135,25 @@ class RolesView(PrincipalMasterView):
|
|||
return roles
|
||||
|
||||
|
||||
class SessionTimeoutRenderer(IntegerFieldRenderer):
|
||||
class PermissionsWidget(dfwidget.Widget):
|
||||
template = 'permissions'
|
||||
permissions = None
|
||||
true_val = 'true'
|
||||
|
||||
def render_readonly(self, **kwargs):
|
||||
if not kwargs.pop('applicable', True):
|
||||
return "(not applicable)"
|
||||
return super(SessionTimeoutRenderer, self).render_readonly(**kwargs)
|
||||
def deserialize(self, field, pstruct):
|
||||
return [key for key, val in pstruct.items()
|
||||
if val == self.true_val]
|
||||
|
||||
def get_checked_value(self, cstruct, value):
|
||||
if cstruct is colander.null:
|
||||
return False
|
||||
return value in cstruct
|
||||
|
||||
class PermissionsField(fa.Field):
|
||||
"""
|
||||
Custom field for role permissions.
|
||||
"""
|
||||
|
||||
def sync(self):
|
||||
if not self.is_readonly():
|
||||
role = self.model
|
||||
role.permissions = self.renderer.deserialize()
|
||||
def serialize(self, field, cstruct, **kw):
|
||||
kw.setdefault('permissions', self.permissions)
|
||||
template = self.template
|
||||
values = self.get_template_values(field, cstruct, kw)
|
||||
return field.renderer(template, **values)
|
||||
|
||||
|
||||
def includeme(config):
|
||||
|
|
|
@ -28,102 +28,22 @@ from __future__ import unicode_literals, absolute_import
|
|||
|
||||
import copy
|
||||
|
||||
import six
|
||||
from sqlalchemy import orm
|
||||
|
||||
from rattail.db import model
|
||||
from rattail.db.auth import guest_role, authenticated_role, set_user_password, has_permission
|
||||
|
||||
import wtforms
|
||||
import formalchemy
|
||||
from formalchemy.fields import SelectFieldRenderer
|
||||
import colander
|
||||
from deform import widget as dfwidget
|
||||
from webhelpers2.html import HTML, tags
|
||||
|
||||
from tailbone import forms
|
||||
from tailbone import forms2 as forms
|
||||
from tailbone.db import Session
|
||||
from tailbone.views import MasterView2 as MasterView
|
||||
from tailbone.views.principal import PrincipalMasterView
|
||||
from tailbone.views import MasterView3 as MasterView
|
||||
from tailbone.views.principal import PrincipalMasterView, PermissionsRenderer
|
||||
|
||||
|
||||
def unique_username(value, field):
|
||||
user = field.parent.model
|
||||
query = Session.query(model.User).filter(model.User.username == value)
|
||||
if user.uuid:
|
||||
query = query.filter(model.User.uuid != user.uuid)
|
||||
if query.count():
|
||||
raise formalchemy.ValidationError("Username must be unique.")
|
||||
|
||||
|
||||
def passwords_match(value, field):
|
||||
if field.parent.confirm_password.value != value:
|
||||
raise formalchemy.ValidationError("Passwords do not match")
|
||||
return value
|
||||
|
||||
|
||||
class PasswordFieldRenderer(formalchemy.PasswordFieldRenderer):
|
||||
|
||||
def render(self, **kwargs):
|
||||
return tags.password(self.name, value='', maxlength=self.length, **kwargs)
|
||||
|
||||
|
||||
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:
|
||||
set_user_password(self.model, password)
|
||||
|
||||
|
||||
def RolesFieldRenderer(request):
|
||||
|
||||
class RolesFieldRenderer(SelectFieldRenderer):
|
||||
|
||||
def render_readonly(self, **kwargs):
|
||||
roles = Session.query(model.Role)
|
||||
html = ''
|
||||
for uuid in self.value:
|
||||
role = roles.get(uuid)
|
||||
link = tags.link_to(
|
||||
role.name, request.route_url('roles.view', 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):
|
||||
return Session.query(model.Role.name, model.Role.uuid)\
|
||||
.filter(model.Role.uuid != guest_role(Session()).uuid)\
|
||||
.filter(model.Role.uuid != authenticated_role(Session()).uuid)\
|
||||
.order_by(model.Role.name)\
|
||||
.all()
|
||||
|
||||
def sync(self):
|
||||
if not self.is_readonly():
|
||||
user = self.model
|
||||
roles = Session.query(model.Role)
|
||||
data = self.renderer.deserialize()
|
||||
user.roles = [roles.get(x) for x in data]
|
||||
|
||||
|
||||
class UsersView(PrincipalMasterView):
|
||||
"""
|
||||
Master view for the User model.
|
||||
|
@ -133,6 +53,29 @@ class UsersView(PrincipalMasterView):
|
|||
model_row_class = model.UserEvent
|
||||
has_versions = True
|
||||
|
||||
grid_columns = [
|
||||
'username',
|
||||
'person',
|
||||
'active',
|
||||
]
|
||||
|
||||
form_fields = [
|
||||
'username',
|
||||
'person',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'display_name',
|
||||
'active',
|
||||
'active_sticky',
|
||||
'password',
|
||||
'roles',
|
||||
]
|
||||
|
||||
row_grid_columns = [
|
||||
'type_code',
|
||||
'occurred',
|
||||
]
|
||||
|
||||
mergeable = True
|
||||
merge_additive_fields = [
|
||||
'sent_message_count',
|
||||
|
@ -147,17 +90,6 @@ class UsersView(PrincipalMasterView):
|
|||
'active',
|
||||
]
|
||||
|
||||
grid_columns = [
|
||||
'username',
|
||||
'person',
|
||||
'active',
|
||||
]
|
||||
|
||||
row_grid_columns = [
|
||||
'type_code',
|
||||
'occurred',
|
||||
]
|
||||
|
||||
def query(self, session):
|
||||
return session.query(model.User)\
|
||||
.outerjoin(model.Person)\
|
||||
|
@ -191,45 +123,119 @@ class UsersView(PrincipalMasterView):
|
|||
g.set_link('last_name')
|
||||
g.set_link('display_name')
|
||||
|
||||
def _preconfigure_fieldset(self, fs):
|
||||
fs.username.set(renderer=forms.renderers.StrippedTextFieldRenderer, validate=unique_username)
|
||||
fs.person.set(renderer=forms.renderers.PersonFieldRenderer, options=[])
|
||||
fs.append(PasswordField('password', label="Set Password"))
|
||||
fs.append(formalchemy.Field('confirm_password', renderer=PasswordFieldRenderer))
|
||||
fs.append(RolesField('roles', renderer=RolesFieldRenderer(self.request), size=10))
|
||||
fs.append(forms.AssociationProxyField('first_name'))
|
||||
fs.append(forms.AssociationProxyField('last_name'))
|
||||
fs.append(forms.AssociationProxyField('display_name', label="Full Name"))
|
||||
def unique_username(self, node, value):
|
||||
query = self.Session.query(model.User)\
|
||||
.filter(model.User.username == value)
|
||||
if self.editing:
|
||||
user = self.get_instance()
|
||||
query = query.filter(model.User.uuid != user.uuid)
|
||||
if query.count():
|
||||
raise colander.Invalid(node, "Username must be unique")
|
||||
|
||||
# hm this should work according to MDN but doesn't seem to...
|
||||
# https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||
fs.username.attrs(autocomplete='new-password')
|
||||
fs.password.attrs(autocomplete='new-password')
|
||||
fs.confirm_password.attrs(autocomplete='new-password')
|
||||
def configure_form(self, f):
|
||||
super(UsersView, self).configure_form(f)
|
||||
user = f.model_instance
|
||||
|
||||
# username
|
||||
f.set_validator('username', self.unique_username)
|
||||
|
||||
# person
|
||||
f.set_renderer('person', self.render_person)
|
||||
if self.creating or self.editing:
|
||||
f.replace('person', 'person_uuid')
|
||||
f.set_node('person_uuid', colander.String(), missing=colander.null)
|
||||
person_display = ""
|
||||
if self.request.method == 'POST':
|
||||
if self.request.POST.get('person_uuid'):
|
||||
person = self.Session.query(model.Person).get(self.request.POST['person_uuid'])
|
||||
if person:
|
||||
person_display = six.text_type(person)
|
||||
elif self.editing:
|
||||
person_display = six.text_type(user.person or '')
|
||||
people_url = self.request.route_url('people.autocomplete')
|
||||
f.set_widget('person_uuid', forms.widgets.JQueryAutocompleteWidget(
|
||||
field_display=person_display, service_url=people_url))
|
||||
f.set_label('person_uuid', "Person")
|
||||
|
||||
# password
|
||||
f.set_widget('password', dfwidget.CheckedPasswordWidget())
|
||||
f.set_label('password', "Set Password")
|
||||
# if self.creating:
|
||||
# f.set_required('password')
|
||||
|
||||
# roles
|
||||
f.set_renderer('roles', self.render_roles)
|
||||
if self.creating or self.editing:
|
||||
roles = self.get_possible_roles().all()
|
||||
role_values = [(s.uuid, six.text_type(s)) for s in roles]
|
||||
f.set_node('roles', colander.Set())
|
||||
f.set_widget('roles', dfwidget.SelectWidget(multiple=True,
|
||||
size=len(roles),
|
||||
values=role_values))
|
||||
if self.editing:
|
||||
f.set_default('roles', [r.uuid for r in user.roles])
|
||||
|
||||
f.set_label('display_name', "Full Name")
|
||||
|
||||
# # hm this should work according to MDN but doesn't seem to...
|
||||
# # https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion
|
||||
# fs.username.attrs(autocomplete='new-password')
|
||||
# fs.password.attrs(autocomplete='new-password')
|
||||
# fs.confirm_password.attrs(autocomplete='new-password')
|
||||
|
||||
def configure_fieldset(self, fs):
|
||||
fs.configure(
|
||||
include=[
|
||||
fs.username,
|
||||
fs.person,
|
||||
fs.first_name,
|
||||
fs.last_name,
|
||||
fs.display_name,
|
||||
fs.active,
|
||||
fs.active_sticky,
|
||||
fs.password,
|
||||
fs.confirm_password,
|
||||
fs.roles,
|
||||
])
|
||||
if self.viewing:
|
||||
permissions = self.request.registry.settings.get('tailbone_permissions', {})
|
||||
renderer = forms.renderers.PermissionsFieldRenderer(permissions,
|
||||
include_guest=True,
|
||||
include_authenticated=True)
|
||||
fs.append(formalchemy.Field('permissions', renderer=renderer))
|
||||
f.append('permissions')
|
||||
f.set_renderer('permissions', PermissionsRenderer(permissions=permissions,
|
||||
include_guest=True,
|
||||
include_authenticated=True))
|
||||
|
||||
if self.viewing or self.deleting:
|
||||
del fs.password
|
||||
del fs.confirm_password
|
||||
f.remove('password')
|
||||
|
||||
def get_possible_roles(self):
|
||||
excluded = [
|
||||
guest_role(self.Session()).uuid,
|
||||
authenticated_role(self.Session()).uuid,
|
||||
]
|
||||
return self.Session.query(model.Role)\
|
||||
.filter(~model.Role.uuid.in_(excluded))\
|
||||
.order_by(model.Role.name)
|
||||
|
||||
def objectify(self, form, data):
|
||||
user = super(UsersView, self).objectify(form, data)
|
||||
if data['password']:
|
||||
set_user_password(user, data['password'])
|
||||
self.update_roles(user, data)
|
||||
return user
|
||||
|
||||
def update_roles(self, user, data):
|
||||
old_roles = set([r.uuid for r in user.roles])
|
||||
new_roles = data['roles']
|
||||
for uuid in new_roles:
|
||||
if uuid not in old_roles:
|
||||
user._roles.append(model.UserRole(role_uuid=uuid))
|
||||
for uuid in old_roles:
|
||||
if uuid not in new_roles:
|
||||
role = self.Session.query(model.Role).get(uuid)
|
||||
user.roles.remove(role)
|
||||
|
||||
def render_person(self, user, field):
|
||||
person = user.person
|
||||
if not person:
|
||||
return ""
|
||||
text = six.text_type(person)
|
||||
url = self.request.route_url('people.view', uuid=person.uuid)
|
||||
return tags.link_to(person, url)
|
||||
|
||||
def render_roles(self, user, field):
|
||||
roles = user.roles
|
||||
items = []
|
||||
for role in roles:
|
||||
text = role.name
|
||||
url = self.request.route_url('roles.view', uuid=role.uuid)
|
||||
items.append(HTML.tag('li', c=tags.link_to(text, url)))
|
||||
return HTML.tag('ul', c=items)
|
||||
|
||||
def editable_instance(self, user):
|
||||
if self.rattail_config.demo():
|
||||
|
|
Loading…
Reference in a new issue