From bfb645bd740b9a1a8fba4404e282879814473641 Mon Sep 17 00:00:00 2001 From: Holger Frey Date: Wed, 1 Apr 2020 18:22:39 +0200 Subject: [PATCH] added form schemas --- ordr3/schemas/__init__.py | 18 +++++++++ ordr3/schemas/account.py | 81 +++++++++++++++++++++++++++++++++++++++ ordr3/schemas/base.py | 49 +++++++++++++++++++++++ 3 files changed, 148 insertions(+) create mode 100644 ordr3/schemas/__init__.py create mode 100644 ordr3/schemas/account.py create mode 100644 ordr3/schemas/base.py diff --git a/ordr3/schemas/__init__.py b/ordr3/schemas/__init__.py new file mode 100644 index 0000000..afcf793 --- /dev/null +++ b/ordr3/schemas/__init__.py @@ -0,0 +1,18 @@ +""" schemas package """ + +from deform import Form +from pkg_resources import resource_filename + + +def includeme(config): + """ Defining form templates overrides + + Activate this setup using ``config.include('ordr3.schemas')``. + """ + # this allows to use the request object like this: + # request.flash(channel, message, description) + ordr_templates = resource_filename("ordr3", "templates/deform") + deform_templates = resource_filename("deform", "templates") + search_path = (ordr_templates, deform_templates) + # search_path = (ordr_templates, ) + Form.set_zpt_renderer(search_path) diff --git a/ordr3/schemas/account.py b/ordr3/schemas/account.py new file mode 100644 index 0000000..4195847 --- /dev/null +++ b/ordr3/schemas/account.py @@ -0,0 +1,81 @@ +""" Schemas for form input and validation """ + +import deform +import colander + +from .base import CSRFSchema + + +@colander.deferred +def deferred_unique_username_validator(node, kw): + """ checks if an username is not registered already """ + + def validate_unique_username(node, value): + request = kw.get("request") + try: + request.repo.get_user_by_username(value) + raise colander.Invalid(node, "User name already registered") + except StopIteration: + pass + + return validate_unique_username + + +@colander.deferred +def deferred_unique_email_validator(node, kw): + """ checks if an email is not registered already """ + email_validator = colander.Email() + + def validate_unique_email(node, value): + email_validator(node, value) # raises exception on invalid address + request = kw.get("request") + try: + request.repo.get_user_by_email(value) + raise colander.Invalid(node, "Email address in use") + except StopIteration: + pass + + return validate_unique_email + + +class RegistrationSchema(CSRFSchema): + """ new user registration """ + + user_name = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + template="textinput_disabled.pt", css_class="o3-reg-username" + ), + description="automagically generated for you", + validator=deferred_unique_username_validator, + ) + first_name = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + css_class="o3-reg-firstname o3-reg-username-source" + ), + ) + last_name = colander.SchemaNode( + colander.String(), + widget=deform.widget.TextInputWidget( + css_class="o3-reg-lastname o3-reg-username-source" + ), + ) + email = colander.SchemaNode( + colander.String(), + validator=deferred_unique_email_validator, + widget=deform.widget.TextInputWidget(template="email.pt",), + ) + password = colander.SchemaNode( + colander.String(), + widget=deform.widget.PasswordWidget(template="viewable_password.pt"), + ) + + @classmethod + def as_form(cls, request, **override): + settings = { + "buttons": ("Create Account", "Cancel"), + "css_class": "form-horizontal registration", + } + settings.update(override) + return super().as_form(request, **settings) diff --git a/ordr3/schemas/base.py b/ordr3/schemas/base.py new file mode 100644 index 0000000..c929ed5 --- /dev/null +++ b/ordr3/schemas/base.py @@ -0,0 +1,49 @@ +""" Schemas for form input and validation """ + +import deform +import colander +from pyramid.csrf import get_csrf_token, check_csrf_token + + +@colander.deferred +def deferred_csrf_default(node, kw): + """ sets the current csrf token """ + request = kw.get("request") + return get_csrf_token(request) + + +@colander.deferred +def deferred_csrf_validator(node, kw): + """ validates a submitted csrf token """ + + def validate_csrf(node, value): + request = kw.get("request") + if not check_csrf_token(request, raises=False): + raise colander.Invalid(node, "Bad CSRF token") + + return validate_csrf + + +# Base Schema + + +class CSRFSchema(colander.Schema): + """ base class for schemas with csrf validation """ + + csrf_token = colander.SchemaNode( + colander.String(), + default=deferred_csrf_default, + validator=deferred_csrf_validator, + widget=deform.widget.HiddenWidget(), + ) + + @classmethod + def as_form(cls, request, **kwargs): + """ returns the schema as a form """ + url = kwargs.pop("url", None) + if not url: + url = request.resource_url(request.context, request.view_name) + schema = cls().bind(request=request) + settings = {"template": "form.pt"} + settings.update(kwargs) + return deform.Form(schema, action=url, **settings)