Browse Source

added account settings

php2python
Holger Frey 7 years ago
parent
commit
35537e1ae2
  1. 5
      ordr2/resources/base.py
  2. 93
      ordr2/schemas/account.py
  3. 14
      ordr2/schemas/helpers.py
  4. 9
      ordr2/static/css/style.css
  5. 24
      ordr2/templates/account/settings.jinja2
  6. 2
      ordr2/templates/deform/select_disabled.pt
  7. 2
      ordr2/templates/deform/textinput_disabled.pt
  8. 64
      ordr2/views/account.py

5
ordr2/resources/base.py

@ -6,14 +6,15 @@ class BaseResource(object):
__parent__ = None __parent__ = None
__name__ = None __name__ = None
_request = None request = None
model = None
nav_highlight = None nav_highlight = None
def __init__(self, name, parent): def __init__(self, name, parent):
self.__name__ = name self.__name__ = name
self.__parent__ = parent self.__parent__ = parent
self._request = parent._request self.request = parent.request
def __acl__(self): def __acl__(self):
return [ DENY_ALL ] return [ DENY_ALL ]

93
ordr2/schemas/account.py

@ -1,13 +1,18 @@
import colander import colander
import deform import deform
from ordr2.models import Role
from . import CSRFSchema from . import CSRFSchema
from .helpers import ( from .helpers import (
deferred_unique_email_validator, deferred_unique_email_validator,
deferred_unique_username_validator deferred_unique_username_validator,
deferred_password_validator
) )
ROLES = [(role.name, role.value.capitalize()) for role in Role]
# schema for user registration # schema for user registration
class RegistrationSchema(CSRFSchema): class RegistrationSchema(CSRFSchema):
@ -45,3 +50,89 @@ class RegistrationSchema(CSRFSchema):
settings.update(override) settings.update(override)
return super().as_form(request, **settings) return super().as_form(request, **settings)
# schema for user settings
class UserSchema(CSRFSchema):
''' user settings schema '''
user_name = colander.SchemaNode(
colander.String(),
widget=deform.widget.TextInputWidget(
template='textinput_disabled.pt'
),
)
first_name = colander.SchemaNode(
colander.String()
)
last_name = colander.SchemaNode(
colander.String()
)
email = colander.SchemaNode(
colander.String(),
validator=deferred_unique_email_validator
)
role = colander.SchemaNode(
colander.String(),
widget=deform.widget.SelectWidget(values=ROLES)
)
@classmethod
def as_form(cls, request, **override):
settings = {
'buttons': ('Save changes', 'Cancel'),
'css_class': 'form-horizontal',
}
settings.update(override)
return super().as_form(request, **settings)
class ChangePasswordSchema(CSRFSchema):
''' change password of an account '''
new_password = colander.SchemaNode(
colander.String(),
widget=deform.widget.CheckedPasswordWidget(),
missing=''
)
@classmethod
def as_form(cls, request, **override):
settings = {
'buttons': ('Change Password', 'Cancel'),
'css_class': 'form-horizontal'
}
settings.update(override)
return super().as_form(request, **settings)
class ConfirmSettingsSchema(CSRFSchema):
''' confirm changes with current password '''
current_password = colander.SchemaNode(
colander.String(),
widget=deform.widget.PasswordWidget(),
description='Enter your current password to confirm changes',
validator=deferred_password_validator
)
class SettingsSchema(CSRFSchema):
general = UserSchema()
change_password = ChangePasswordSchema()
confirm_changes = ConfirmSettingsSchema()
@classmethod
def as_form(cls, request, **override):
settings = {
'buttons': ('Save Settings', 'Cancel'),
'css_class': 'form-horizontal user-settings'
}
settings.update(override)
form = super().as_form(request, **settings)
# disable the role field for user settings
form['general']['role'].widget.template='select_disabled.pt'
form['general']['role'].widget=deform.widget.TextInputWidget(
template='textinput_disabled.pt'
)
return form

14
ordr2/schemas/helpers.py

@ -44,8 +44,20 @@ def deferred_unique_email_validator(node, kw):
email_validator(node, value) # raises exception on invalid address email_validator(node, value) # raises exception on invalid address
request = kw.get('request') request = kw.get('request')
user = request.dbsession.query(User).filter_by(email=value).first() user = request.dbsession.query(User).filter_by(email=value).first()
if user is not None: if user not in (request.user, request.context.model):
# allow existing email addresses if
# it belongs to the current logged in user or
# an administrator edits a user
raise colander.Invalid(node, 'Email address in use') raise colander.Invalid(node, 'Email address in use')
return validate_unique_email return validate_unique_email
@colander.deferred
def deferred_password_validator(node, kw):
''' checks password confirmation for settings '''
def validate_password_confirmation(node, value):
request = kw.get('request')
if request.user is None or not request.user.check_password(value):
raise colander.Invalid(node, 'Wrong password')
return validate_password_confirmation

9
ordr2/static/css/style.css

@ -722,3 +722,12 @@ hgroup .info {
/*================ STYLES FOR php2python BRANCH ================*/ /*================ STYLES FOR php2python BRANCH ================*/
input[value="password:mapping"] + div { margin-bottom:10px; } input[value="password:mapping"] + div { margin-bottom:10px; }
input[value="new_password:mapping"] + div { margin-bottom:10px; }
.form-horizontal.user-settings fieldset > .controls { margin-left:0; }
.user-settings .panel-heading {
font-size:150%;
padding-top: 20px;
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid #aaa;}

24
ordr2/templates/account/settings.jinja2

@ -0,0 +1,24 @@
{% extends "ordr2:templates/layout.jinja2" %}
{% import 'ordr2:templates/macros.jinja2' as macros with context %}
{% block subtitle %} Account | Settings {% endblock subtitle %}
{% block content %}
<div class="content controls">
<div class="container-fluid">
<div class="row-fluid">
<div class="page-controls">
<h1>Account Settings</h1>
</div>
</div>
<div class="row">
<div class="span8">
{{ macros.flash_messages() }}
{{form.render()|safe}}
</div>
</div>
</div>
</div>
{% endblock content %}

2
ordr2/templates/deform/select_disabled.pt

@ -18,7 +18,7 @@
multiple multiple; multiple multiple;
size size; size size;
style style;" style style;"
disabled="disabled"> readonly="readonly">
<tal:loop tal:repeat="item values"> <tal:loop tal:repeat="item values">
<optgroup tal:condition="isinstance(item, optgroup_class)" <optgroup tal:condition="isinstance(item, optgroup_class)"
tal:attributes="label item.label"> tal:attributes="label item.label">

2
ordr2/templates/deform/textinput_disabled.pt

@ -10,7 +10,7 @@
tal:attributes="class string: form-control ${css_class or ''}; tal:attributes="class string: form-control ${css_class or ''};
style style" style style"
id="${oid}" id="${oid}"
disabled="disabled"/> readonly="readonly"/>
<script tal:condition="mask" type="text/javascript"> <script tal:condition="mask" type="text/javascript">
deform.addCallback( deform.addCallback(
'${oid}', '${oid}',

64
ordr2/views/account.py

@ -6,7 +6,7 @@ from pyramid.security import remember, forget
from pyramid.view import view_config from pyramid.view import view_config
from ordr2.models import User, Role from ordr2.models import User, Role
from ordr2.schemas.account import RegistrationSchema from ordr2.schemas.account import RegistrationSchema, SettingsSchema
# user log in and log out # user log in and log out
@ -111,8 +111,6 @@ def registration_form_processing(context, request):
return {'form': form} return {'form': form}
# form validation successfull, create user # form validation successfull, create user
print('USER', appstruct['user_name'])
print('POST', list(request.POST.items()))
account = User( account = User(
user_name=appstruct['user_name'], user_name=appstruct['user_name'],
first_name=appstruct['first_name'], first_name=appstruct['first_name'],
@ -146,3 +144,63 @@ def registration_form_processing(context, request):
def registration_sucessful(context, request): def registration_sucessful(context, request):
''' registration was sucessfull ''' ''' registration was sucessfull '''
return {} return {}
# user settings
@view_config(
context='ordr2:resources.Account',
name='settings',
permission='settings',
request_method='GET',
renderer='ordr2:templates/account/settings.jinja2'
)
def settings_form(context, request):
''' display the user settings form '''
form = SettingsSchema.as_form(request)
form_data = {
'general': {
'user_name': request.user.user_name,
'first_name': request.user.first_name,
'last_name': request.user.last_name,
'email': request.user.email,
'role': request.user.role.value.capitalize()
}
}
form.set_appstruct(form_data)
return {'form': form}
@view_config(
context='ordr2:resources.Account',
name='settings',
permission='settings',
request_method='POST',
renderer='ordr2:templates/account/settings.jinja2'
)
def settings_form_processing(context, request):
''' display the user settings form '''
form = SettingsSchema.as_form(request)
data = request.POST.items()
try:
appstruct = form.validate(data)
except deform.ValidationFailure as e:
return {'form': form}
# form validation sucessful, change settings
request.user.first_name = appstruct['general']['first_name']
request.user.last_name = appstruct['general']['last_name']
request.user.email = appstruct['general']['email']
if appstruct['change_password']['new_password']:
request.user.set_password(appstruct['change_password']['new_password'])
if len(appstruct['change_password']['new_password']) < 8:
request.flash(
'warning',
'You should really consider using a longer password.'
)
request.flash('success', 'Your account information has been updated.')
return {'form': form}