diff --git a/edbob/pyramid/static/css/edbob.css b/edbob/pyramid/static/css/edbob.css index 5417a07..e09bcb0 100644 --- a/edbob/pyramid/static/css/edbob.css +++ b/edbob/pyramid/static/css/edbob.css @@ -142,6 +142,10 @@ body > #container { margin: 8px 20px auto auto; } +#login a.username { + font-weight: bold; +} + #user-menu { float: left; } diff --git a/edbob/pyramid/static/css/forms.css b/edbob/pyramid/static/css/forms.css index 3047756..b698cfd 100644 --- a/edbob/pyramid/static/css/forms.css +++ b/edbob/pyramid/static/css/forms.css @@ -38,11 +38,18 @@ div.fieldset { div.field-wrapper { clear: both; - overflow: auto; min-height: 30px; + overflow: auto; + padding: 5px; +} + +div.field-wrapper.error { + background-color: #ddcccc; + border: 2px solid #dd6666; } div.field-wrapper label { + color: #000000; display: block; float: left; width: 140px; diff --git a/edbob/pyramid/templates/edbob/base.mako b/edbob/pyramid/templates/base.mako similarity index 96% rename from edbob/pyramid/templates/edbob/base.mako rename to edbob/pyramid/templates/base.mako index f3af0bf..b026322 100644 --- a/edbob/pyramid/templates/edbob/base.mako +++ b/edbob/pyramid/templates/base.mako @@ -37,7 +37,7 @@

${self.title()}

% if request.user: - logged in as ${request.user.display_name} + ${h.link_to(request.user.display_name, url('change_password'), class_='username')} (${h.link_to("logout", url('logout'))}) % else: ${h.link_to("login", url('login'))} diff --git a/edbob/pyramid/templates/change_password.mako b/edbob/pyramid/templates/change_password.mako new file mode 100644 index 0000000..e00d2a5 --- /dev/null +++ b/edbob/pyramid/templates/change_password.mako @@ -0,0 +1,15 @@ +<%inherit file="/base.mako" /> + +<%def name="title()">Change Password + +
+ ${h.form(url('change_password'))} + ${form.referrer_field()} + ${form.field_div('current_password', form.password('current_password'))} + ${form.field_div('new_password', form.password('new_password'))} + ${form.field_div('confirm_password', form.password('confirm_password'))} +
+ ${h.submit('submit', "Change Password")} +
+ ${h.end_form()} +
diff --git a/edbob/pyramid/views/auth.py b/edbob/pyramid/views/auth.py index 09344aa..3f55619 100644 --- a/edbob/pyramid/views/auth.py +++ b/edbob/pyramid/views/auth.py @@ -26,17 +26,50 @@ ``edbob.pyramid.views.auth`` -- Auth Views """ -import formencode from pyramid.httpexceptions import HTTPFound from pyramid.security import remember, forget + +import formencode from pyramid_simpleform import Form -from pyramid_simpleform.renderers import FormRenderer +import pyramid_simpleform.renderers + +from webhelpers.html import tags +from webhelpers.html.builder import HTML import edbob -from edbob.db.auth import authenticate_user +from edbob.db.auth import authenticate_user, set_user_password from edbob.pyramid import Session +from edbob.util import prettify +class FormRenderer(pyramid_simpleform.renderers.FormRenderer): + """ + Customized form renderer. Provides some extra methods for convenience. + """ + + # Note that as of this writing, this renderer is used only by the + # ``change_password`` view. This should probably change, and this class + # definition should be moved elsewhere. + + def field_div(self, name, field, label=None): + errors = self.errors_for(name) + if errors: + errors = [HTML.tag('div', class_='field-error', c=x) for x in errors] + errors = tags.literal('').join(errors) + + label = HTML.tag('label', for_=name, c=label or prettify(name)) + inner = HTML.tag('div', class_='field', c=field) + + outer_class = 'field-wrapper' + if errors: + outer_class += ' error' + outer = HTML.tag('div', class_=outer_class, c=(errors or '') + label + inner) + return outer + + def referrer_field(self): + return self.hidden('referrer', value=self.form.request.get_referrer()) + + class UserLogin(formencode.Schema): allow_extra_fields = True filter_extra_fields = True @@ -92,6 +125,47 @@ def logout(request): return HTTPFound(location=referrer, headers=headers) +class CurrentPasswordCorrect(formencode.validators.FancyValidator): + + def _to_python(self, value, state): + user = state + if not authenticate_user(user.username, value, session=Session()): + raise formencode.Invalid("The password is incorrect.", value, state) + return value + + +class ChangePassword(formencode.Schema): + + allow_extra_fields = True + filter_extra_fields = True + + current_password = formencode.All( + formencode.validators.NotEmpty(), + CurrentPasswordCorrect()) + + new_password = formencode.validators.NotEmpty() + confirm_password = formencode.validators.NotEmpty() + + chained_validators = [formencode.validators.FieldsMatch( + 'new_password', 'confirm_password')] + + +def change_password(request): + """ + Allows a user to change his or her password. + """ + + if not request.user: + return HTTPFound(location=request.route_url('home')) + + form = Form(request, schema=ChangePassword, state=request.user) + if form.validate(): + set_user_password(request.user, form.data['new_password']) + return HTTPFound(location=request.get_referrer()) + + return {'form': FormRenderer(form)} + + def includeme(config): config.add_route('login', '/login') @@ -99,3 +173,6 @@ def includeme(config): config.add_route('logout', '/logout') config.add_view(logout, route_name='logout') + + config.add_route('change_password', '/change-password') + config.add_view(change_password, route_name='change_password', renderer='/change_password.mako')