1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/ihatemoney_ynh.git synced 2024-09-03 19:26:15 +02:00

Fix #24 on Authentication and New project forms usability.

- Do not display anymore the identifier field in home.

- Let the user enter the id if the slug generated from project name already exists as a project id.

- Moved get_billform_for from 'utils' to 'forms', to avoid issue (was 'from forms import ...' into utils,
  and 'from utils import ...' into forms, which causeed an error).
This commit is contained in:
Arnaud Bos 2011-09-11 05:25:42 +02:00
parent f48fc22335
commit 88cd2f8675
5 changed files with 62 additions and 29 deletions

View file

@ -2,6 +2,8 @@ from flaskext.wtf import *
from wtforms.widgets import html_params from wtforms.widgets import html_params
from models import Project, Person, Bill from models import Project, Person, Bill
from datetime import datetime from datetime import datetime
from jinja2 import Markup
from utils import slugify
def select_multi_checkbox(field, ul_class='', **kwargs): def select_multi_checkbox(field, ul_class='', **kwargs):
@ -18,16 +20,41 @@ def select_multi_checkbox(field, ul_class='', **kwargs):
return u''.join(html) return u''.join(html)
def get_billform_for(request, project, set_default=True):
"""Return an instance of BillForm configured for a particular project.
:set_default: if set to True, on GET methods (usually when we want to
display the default form, it will call set_default on it.
"""
form = BillForm()
form.payed_for.choices = form.payer.choices = [(str(m.id), m.name) for m in project.active_members]
form.payed_for.default = [str(m.id) for m in project.active_members]
if set_default and request.method == "GET":
form.set_default()
return form
class ProjectForm(Form): class ProjectForm(Form):
name = TextField("Project name", validators=[Required()]) name = TextField("Project name", validators=[Required()])
id = TextField("Project identifier", validators=[Required()]) id = TextField("Project identifier", validators=[Required()])
password = PasswordField("Password", validators=[Required()]) password = PasswordField("Private code", validators=[Required()])
contact_email = TextField("Email", validators=[Required(), Email()]) contact_email = TextField("Email", validators=[Required(), Email()])
submit = SubmitField("Create the project") submit = SubmitField("Create the project")
def validate_id(form, field): def validate_id(form, field):
if Project.query.get(field.data): form.id.data = slugify(field.data)
raise ValidationError("This project id is already used") if Project.query.get(form.id.data):
raise ValidationError(Markup("""The project identifier is used
to log in and for the URL of the project.
<br />
We tried to generate an identifier for you but
a projet with this identifier already exists.
<br />
Please create a new identifier you will be able
to remember.
"""))
def save(self): def save(self):
"""Create a new project with the information given by this form. """Create a new project with the information given by this form.
@ -42,7 +69,7 @@ class ProjectForm(Form):
class AuthenticationForm(Form): class AuthenticationForm(Form):
id = TextField("Project identifier", validators=[Required()]) id = TextField("Project identifier", validators=[Required()])
password = PasswordField("Password", validators=[Required()]) password = PasswordField("Private code", validators=[Required()])
submit = SubmitField("Get in") submit = SubmitField("Get in")

View file

@ -1,5 +1,5 @@
from web import main, db, mail from web import main, db, mail
import api #import api
from flask import * from flask import *

View file

@ -17,8 +17,11 @@
</div> <!-- /clearfix --> </div> <!-- /clearfix -->
{% endmacro %} {% endmacro %}
{% macro submit(field, cancel=False) -%} {% macro submit(field, cancel=False, home=False) -%}
<div class="actions"> <div class="actions">
{% if home %}
<a href="{{ url_for(".home") }}" class="btn">Back Home</a>
{% endif %}
<button type="submit" class="btn primary">{{ field.name }}</button> <button type="submit" class="btn primary">{{ field.name }}</button>
{% if cancel %} {% if cancel %}
<button id="cancel-form" type="reset" class="btn">Cancel</button> <button id="cancel-form" type="reset" class="btn">Cancel</button>
@ -42,12 +45,14 @@
{% include "display_errors.html" %} {% include "display_errors.html" %}
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
{{ input(form.name) }} {% if not home %}
{{ input(form.id) }} {{ input(form.id) }}
{% endif %}
{{ input(form.name) }}
{{ input(form.password) }} {{ input(form.password) }}
{{ input(form.contact_email) }} {{ input(form.contact_email) }}
{% if not home %} {% if not home %}
{{ submit(form.submit) }} {{ submit(form.submit, home=True) }}
{% endif %} {% endif %}
{% endmacro %} {% endmacro %}

View file

@ -1,24 +1,19 @@
import re
from functools import wraps from functools import wraps
from flask import redirect, url_for, session, request from flask import redirect, url_for, session, request
from werkzeug.routing import HTTPException, RoutingException from werkzeug.routing import HTTPException, RoutingException
from models import Bill, Project def slugify(value):
from forms import BillForm """Normalizes string, converts to lowercase, removes non-alpha characters,
and converts spaces to hyphens.
def get_billform_for(project, set_default=True): Copy/Pasted from ametaireau/pelican/utils itself took from django sources.
"""Return an instance of BillForm configured for a particular project.
:set_default: if set to True, on GET methods (usually when we want to
display the default form, it will call set_default on it.
""" """
form = BillForm() if type(value) == unicode:
form.payed_for.choices = form.payer.choices = [(str(m.id), m.name) for m in project.active_members] import unicodedata
form.payed_for.default = [str(m.id) for m in project.active_members] value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
value = unicode(re.sub('[^\w\s-]', '', value).strip().lower())
if set_default and request.method == "GET": return re.sub('[-\s]+', '-', value)
form.set_default()
return form
class Redirect303(HTTPException, RoutingException): class Redirect303(HTTPException, RoutingException):
"""Raise if the map requests a redirect. This is for example the case if """Raise if the map requests a redirect. This is for example the case if

View file

@ -5,9 +5,9 @@ from flaskext.mail import Mail, Message
# local modules # local modules
from models import db, Project, Person, Bill from models import db, Project, Person, Bill
from forms import (ProjectForm, AuthenticationForm, BillForm, MemberForm, from forms import (get_billform_for, ProjectForm, AuthenticationForm, BillForm,
InviteForm, CreateArchiveForm) MemberForm, InviteForm, CreateArchiveForm)
from utils import get_billform_for, Redirect303 from utils import Redirect303
""" """
The blueprint for the web interface. The blueprint for the web interface.
@ -111,6 +111,12 @@ def create_project():
form.name.data = request.values['project_id'] form.name.data = request.values['project_id']
if request.method == "POST": if request.method == "POST":
# At first, we don't want the user to bother with the identifier
# so it will automatically be missing because not displayed into the form
# Thus we fill it with the same value as the filled name, the validation will
# take care of the slug
if not form.id.data:
form.id.data = form.name.data
if form.validate(): if form.validate():
# save the object in the db # save the object in the db
project = form.save() project = form.save()
@ -178,7 +184,7 @@ def list_bills():
bills = g.project.get_bills() bills = g.project.get_bills()
return render_template("list_bills.html", return render_template("list_bills.html",
bills=bills, member_form=MemberForm(g.project), bills=bills, member_form=MemberForm(g.project),
bill_form=get_billform_for(g.project) bill_form=get_billform_for(request, g.project)
) )
@main.route("/<project_id>/members/add", methods=["GET", "POST"]) @main.route("/<project_id>/members/add", methods=["GET", "POST"])
@ -224,7 +230,7 @@ def remove_member(member_id):
@main.route("/<project_id>/add", methods=["GET", "POST"]) @main.route("/<project_id>/add", methods=["GET", "POST"])
def add_bill(): def add_bill():
form = get_billform_for(g.project) form = get_billform_for(request, g.project)
if request.method == 'POST': if request.method == 'POST':
if form.validate(): if form.validate():
bill = Bill() bill = Bill()
@ -250,7 +256,7 @@ def delete_bill(bill_id):
@main.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"]) @main.route("/<project_id>/edit/<int:bill_id>", methods=["GET", "POST"])
def edit_bill(bill_id): def edit_bill(bill_id):
bill = Bill.query.get_or_404(bill_id) bill = Bill.query.get_or_404(bill_id)
form = get_billform_for(g.project, set_default=False) form = get_billform_for(request, g.project, set_default=False)
if request.method == 'POST' and form.validate(): if request.method == 'POST' and form.validate():
form.save(bill) form.save(bill)
db.session.commit() db.session.commit()