Browse Source

working on the first form

rework
Holger Frey 7 years ago
parent
commit
494957aeba
  1. 3
      ordr/__init__.py
  2. 6
      ordr/schemas/__init__.py
  3. 5
      ordr/schemas/account.py
  4. 9
      ordr/schemas/helpers.py
  5. 28
      ordr/templates/account/registration_form.jinja2
  6. 46
      ordr/templates/deform/checked_password.pt
  7. 110
      ordr/templates/deform/form.pt
  8. 55
      ordr/templates/deform/mapping_item.pt
  9. 17
      ordr/templates/deform/readonly/textinput.pt
  10. 18
      ordr/templates/deform/textinput.pt
  11. 3
      ordr/templates/layout.jinja2
  12. 15
      ordr/views/__init__.py
  13. 26
      ordr/views/registration.py

3
ordr/__init__.py

@ -19,6 +19,5 @@ def main(global_config, **settings):
config.include('.resources') config.include('.resources')
config.include('.schemas') config.include('.schemas')
config.include('.security') config.include('.security')
config.add_static_view('static', 'static', cache_max_age=3600) config.include('.views')
config.scan()
return config.make_wsgi_app() return config.make_wsgi_app()

6
ordr/schemas/__init__.py

@ -35,10 +35,12 @@ class CSRFSchema(colander.Schema):
:param kwargs: :param kwargs:
additional parameters for the form rendering. additional parameters for the form rendering.
''' '''
schema = cls().bind(request=request)
if action is None: if action is None:
action = request.resource_url(request.context, request.view_name) action = request.resource_url(request.context, request.view_name)
schema = cls().bind(request=request) settings = {'col_label': 3, 'col_input': 9, 'action': action}
return deform.Form(schema, action=action, **kwargs) settings.update(kwargs)
return deform.Form(schema, **settings)
def includeme(config): def includeme(config):

5
ordr/schemas/account.py

@ -16,7 +16,9 @@ class RegistrationSchema(CSRFSchema):
username = colander.SchemaNode( username = colander.SchemaNode(
colander.String(), colander.String(),
widget=deform.widget.TextInputWidget(readonly=True), widget=deform.widget.TextInputWidget(
readonly=True,
),
description='automagically generated for you', description='automagically generated for you',
validator=deferred_unique_username_validator, validator=deferred_unique_username_validator,
) )
@ -34,3 +36,4 @@ class RegistrationSchema(CSRFSchema):
colander.String(), colander.String(),
widget=deform.widget.CheckedPasswordWidget() widget=deform.widget.CheckedPasswordWidget()
) )

9
ordr/schemas/helpers.py

@ -45,10 +45,11 @@ 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 not in (None, request.context.model): if user is not None:
# allow existing email addresses if if user != getattr(request.context, 'model', None):
# it belongs to the user that is currently edited # allow existing email addresses if
raise colander.Invalid(node, 'Email address in use') # it belongs to the user that is currently edited
raise colander.Invalid(node, 'Email address in use')
return validate_unique_email return validate_unique_email

28
ordr/templates/account/registration_form.jinja2

@ -1,5 +1,31 @@
{% extends "ordr:templates/layout.jinja2" %} {% extends "ordr:templates/layout.jinja2" %}
{% block content %} {% block content %}
{{ context.get_registration_form().render()|safe }} <div class="row justify-content-md-center mt-3">
<div class="col-6">
<h2>Registration</h2>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-2">
<p class="text-primary">
Step 1: Registration
</p>
</div>
<div class="col-2">
<p class="text-secondary">
Step 2: Validate Email
</p>
</div>
<div class="col-2">
<p class="text-secondary">
Step 3: Finished
</p>
</div>
</div>
<div class="row justify-content-md-center mt-3">
<div class="col-6">
{{ form.render()|safe }}
</div>
</div>
{% endblock content %} {% endblock content %}

46
ordr/templates/deform/checked_password.pt

@ -0,0 +1,46 @@
<div i18n:domain="deform" tal:omit-tag=""
tal:define="oid oid|field.oid;
name name|field.name;
css_class css_class|field.widget.css_class;
style style|field.widget.style;
required required|'required' if field.required else None;
was_validated True if field.get_root().error else False;
is_invalid is_invalid|field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping';
is_valid was_validated and not is_invalid;
">
${field.start_mapping()}
<div>
<input type="password"
name="${name}"
value="${field.widget.redisplay and cstruct or ''}"
tal:attributes="class string: form-control ${css_class or ''} ${'is-invalid' if is_invalid else ''} ${'is-valid' if is_valid else ''};
style style;
required required;"
id="${oid}"
i18n:attributes="placeholder"
placeholder="Password"/>
</div>
<div class="mt-2">
<input type="password"
name="${name}-confirm"
value="${field.widget.redisplay and confirm or ''}"
tal:attributes="class string: form-control ${css_class or ''} ${'is-invalid' if is_invalid else ''} ${'is-valid' if is_valid else ''};
style style;
required required;"
id="${oid}-confirm"
i18n:attributes="placeholder"
placeholder="Confirm Password"/>
<!--! error message must directly follow input field for bootstrap 4 -->
<div class="invalid-feedback"
tal:define="errstr 'error-%s' % field.oid"
tal:repeat="msg field.error.messages()"
i18n:translate=""
tal:attributes="id repeat.msg.index==0 and errstr or
('%s-%s' % (errstr, repeat.msg.index))"
tal:condition="is_invalid">
${msg}
</div>
</div>
${field.end_mapping()}
</div>

110
ordr/templates/deform/form.pt

@ -0,0 +1,110 @@
<form
tal:define="style style|field.widget.style;
css_class css_class|string:${field.widget.css_class or field.css_class or ''};
item_template item_template|field.widget.item_template;
autocomplete autocomplete|field.autocomplete;
title title|field.title;
errormsg errormsg|field.errormsg;
description description|field.description;
buttons buttons|field.buttons;
use_ajax use_ajax|field.use_ajax;
ajax_options ajax_options|field.ajax_options;
formid formid|field.formid;
action action|field.action or None;
method method|field.method;
col_label col_label|field.col_label;
col_input col_input|field.col_input;
was_validated True if field.get_root().error else False;"
tal:attributes="autocomplete autocomplete;
style style;
class css_class;
action action;"
id="${formid}"
method="${method}"
enctype="multipart/form-data"
accept-charset="utf-8"
i18n:domain="deform"
>
<fieldset class="deform-form-fieldset">
<legend tal:condition="title">${title}}</legend>
<input type="hidden" name="_charset_" />
<input type="hidden" name="__formid__" value="${formid}"/>
<p class="section first" tal:condition="description">
${description}
</p>
<div tal:repeat="child field"
tal:replace="structure child.render_template(item_template)"/>
<div class="form-row deform-form-buttons">
<div class="col-${col_label}"></div>
<div class="form-group col-{$col_input} mt-4">
<tal:loop tal:repeat="button buttons">
<button
tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';"
tal:attributes="disabled button.disabled if button.disabled else None"
id="${formid+button.name}"
name="${button.name}"
type="${button.type}"
class="btn ${button.css_class or btn_disposition}"
value="${button.value}"
tal:condition="button.type != 'link'">
<span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span>
${button.title}
</button>
<a
tal:define="btn_disposition repeat.button.start and 'btn-primary' or 'btn-default';
btn_href button.value|''"
class="btn ${button.css_class or btn_disposition}"
id="${field.formid + button.name}"
href="${btn_href}"
tal:condition="button.type == 'link'">
<span tal:condition="button.icon" class="glyphicon glyphicon-${button.icon}"></span>
${button.title}
</a>
</tal:loop>
</div>
</div>
</fieldset>
<script type="text/javascript" tal:condition="use_ajax">
$(function() {
// jquery handler for .ready() called
deform.addCallback(
'${formid}',
function(oid) {
var target = '#' + oid;
var options = {
target: target,
replaceTarget: true,
success: function() {
deform.processCallbacks();
deform.focusFirstInput(target);
},
beforeSerialize: function() {
// See http://bit.ly/1agBs9Z (hack to fix tinymce-related ajax bug)
if ('tinymce' in window) {
$(tinymce.get()).each(
function(i, el) {
var content = el.getContent();
var editor_input = document.getElementById(el.id);
editor_input.value = content;
});
}
}
};
var extra_options = ${ajax_options} || {};
$('#' + oid).ajaxForm($.extend(options, extra_options));
}
);
});
</script>
</form>

55
ordr/templates/deform/mapping_item.pt

@ -0,0 +1,55 @@
<div tal:define="error_class error_class|field.widget.error_class;
description description|field.description;
title title|field.title;
oid oid|field.oid;
hidden hidden|field.widget.hidden;
category category|field.widget.category;
structural hidden or category == 'structural';
required required|'required' if field.required else None;
was_validated True if field.get_root().error else False;
is_invalid is_invalid|field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping';
col_label col_label|field.col_label;
col_input col_input|field.col_input;"
class="form-group form-row ${field.error and 'has-error' or ''} ${field.widget.item_css_class or ''} ${field.default_item_css_class()}"
title="${description}"
id="item-${oid}"
tal:omit-tag="structural"
i18n:domain="deform">
<label for="${oid}"
class="control-label col-${col_label} col-form-label ${required and 'required' or ''}"
tal:condition="not structural"
id="req-${oid}"
>
${title}
</label>
<div class="col-${col_input}">
<div tal:define="input_prepend field.widget.input_prepend | None;
input_append field.widget.input_append | None"
tal:omit-tag="not (input_prepend or input_append)"
class="input-group">
<div class="input-group-prepend" tal:condition="input_prepend">
<div class="input-group-text">${input_prepend}</div>
</div>
<span tal:replace="structure field.serialize(cstruct).strip()"></span>
<div class="input-group-append" tal:condition="input_append">
<div class="input-group-text">${input_append}</div>
</div>
</div>
<div class="invalid-feedback"
tal:define="errstr 'error-%s' % field.oid"
tal:repeat="msg field.error.messages()"
i18n:translate=""
tal:attributes="id repeat.msg.index==0 and errstr or
('%s-%s' % (errstr, repeat.msg.index))"
tal:condition="is_invalid">
${msg}
</div>
<small tal:condition="field.description and not field.widget.hidden"
class="form-text text-muted" >
${field.description}
</small>
</div>
</div>

17
ordr/templates/deform/readonly/textinput.pt

@ -0,0 +1,17 @@
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
mask mask|field.widget.mask;
mask_placeholder mask_placeholder|field.widget.mask_placeholder;
style style|field.widget.style;
was_validated True if field.get_root().error else False;
is_invalid is_invalid|field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping';
is_valid was_validated and not is_invalid;
"
tal:omit-tag="">
<input type="text" name="${name}" value="${cstruct}"
tal:attributes="class string: form-control ${css_class or ''} ${'is-invalid' if is_invalid else ''} ${'is-valid' if is_valid else ''};
style style"
id="${oid}"
readonly="readonly"/>
</span>

18
ordr/templates/deform/textinput.pt

@ -0,0 +1,18 @@
<span tal:define="name name|field.name;
css_class css_class|field.widget.css_class;
oid oid|field.oid;
mask mask|field.widget.mask;
mask_placeholder mask_placeholder|field.widget.mask_placeholder;
style style|field.widget.style;
required required|'required' if field.required else None;
was_validated True if field.get_root().error else False;
is_invalid is_invalid|field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping';
is_valid was_validated and not is_invalid;
"
tal:omit-tag="">
<input type="text" name="${name}" value="${cstruct}"
tal:attributes="class string: form-control ${css_class or ''} ${'is-invalid' if is_invalid else ''} ${'is-valid' if is_valid else ''};
style style;
required required"
id="${oid}"/>
</span>

3
ordr/templates/layout.jinja2

@ -15,6 +15,9 @@
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
<!-- Deform form renderin gcss -->
{# <link rel="stylesheet" href="{{request.static_url('deform:static/css/form.css')}}" type="text/css" media="screen" /> #}
<!-- Custom styles for this scaffold --> <!-- Custom styles for this scaffold -->
<link href="{{request.static_url('ordr:static/style.css')}}" rel="stylesheet"> <link href="{{request.static_url('ordr:static/style.css')}}" rel="stylesheet">
</head> </head>

15
ordr/views/__init__.py

@ -0,0 +1,15 @@
''' views (sub) package for ordr '''
def includeme(config):
'''
Initialize the views in a Pyramid app.
Activate this setup using ``config.include('ordr2.views')``.
'''
settings = config.get_settings()
age = int(settings.get('static_views.cache_max_age', 3600))
config.add_static_view('static', 'ordr:static', cache_max_age=age)
config.add_static_view('deform', 'deform:static', cache_max_age=age)
config.scan()

26
ordr/views/registration.py

@ -1,4 +1,6 @@
# from pyramid.httpexceptions import HTTPFound import deform
from pyramid.httpexceptions import HTTPFound
from pyramid.view import view_config from pyramid.view import view_config
# from ordr.models import User # from ordr.models import User
@ -11,4 +13,24 @@ from pyramid.view import view_config
renderer='ordr:templates/account/registration_form.jinja2' renderer='ordr:templates/account/registration_form.jinja2'
) )
def registration_form(context, request): def registration_form(context, request):
return {} form = context.get_registration_form()
return {'form': form}
@view_config(
context='ordr.resources.account.RegistrationResource',
permission='view',
request_method='POST',
renderer='ordr:templates/account/registration_form.jinja2'
)
def registration_form_processing(context, request):
if 'Cancel' in request.POST:
return HTTPFound(request.resource_url(request.root))
form = context.get_registration_form()
data = request.POST.items()
try:
appstruct = form.validate(data)
except deform.ValidationFailure as e:
pass
return {'form': form}