diff --git a/accounts/forms.py b/accounts/forms.py index a49fa5f667b5684a6ef4cdb0ad58bdf2058d2569..ddbef68e763a6ebbae891faaafc6d7f14ee787f6 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -2,7 +2,131 @@ from django.contrib.auth.forms import UserCreationForm from accounts.models import EmailUser -class CreateAccountForm(UserCreationForm): +from django.utils.safestring import mark_safe + + +class FormRenderMixin: + """ A mixin that can be included in any form to make it render to html as we want + it on this website. + + The following class variables can be adjusted to tweak display: + + * `tooltip_helptexts`: a list of fields whose helptexts, if any, should be rendered + as a question mark's tooltip instead of being inlined. + * `field_groups`: if `None`, the fields will be rendered in order, as eg. `as_p` + would. + Else, can be set to any nested list structure, containing each field name exactly + once. The structure `[['a', 'b'], ['c']]` would then group together the fields a + and b, then group together the single field c. + + """ + + tooltip_helptexts = [] + field_groups = None + + class BadFieldGroups(Exception): + pass + + def as_html(self): + """ Render the form to html """ + + def get_field_treelike(): + def map_to_field(treelike): + if isinstance(treelike, str): + if treelike in self.fields: + return { + "field": self[treelike], + "tooltip": treelike in self.tooltip_helptexts, + } + raise self.BadFieldFroups + return list(map(map_to_field, treelike)) + + if self.field_groups is not None: + return map_to_field(self.field_groups) + else: + return [ + list( + map( + lambda field: { + "field": self[field], + "tooltip": field in self.tooltip_helptexts, + }, + self.fields, + ) + ) + ] + + def gen_html(treelike): + def gen_node(subtree): + if isinstance(subtree, list): + return '<div class="fieldgroup">\n{}</div>'.format( + gen_html(subtree) + ) + else: # Simple field + inline_helptext_html = ( + ( + ' <span class="helptext inline_helptext">' + "{inline_helptext}</span>\n" + ).format(inline_helptext=subtree["field"].help_text) + if subtree["field"].help_text and not subtree["tooltip"] + else "" + ) + tooltip_html = ( + ( + '<span class="tooltip" tabindex="0">\n' + '<i class="fa fa-question-circle" aria-hidden="true"></i>\n' + '<span class="tooltiptext">\n' + " {tooltiphtml}\n" + "</span>\n" + "</span>" + ).format(tooltiphtml=subtree["field"].help_text) + if subtree["field"].help_text and subtree["tooltip"] + else "" + ) + + field_classes = "formfield" + if subtree["field"].errors: + field_classes += " error_field" + + labelled_input_classes = "labelled_input" + if subtree["field"].field.widget.input_type in [ + "checkbox", + "radio", + ]: + labelled_input_classes += " checkbox_input" + + html = ( + '<div class="{field_classes}" id="formfield_{label_for}">\n' + ' <div class="{labelled_input_classes}">\n' + ' <div class="label_line">\n' + ' <label for="{label_for}">{label_text} :</label>\n{errors}' + " </div>\n" + " {field_html}\n" + ' <div class="help">{tooltip}{inline_helptext_html}</div>\n' + " </div>\n" + "</div>" + ).format( + field_classes=field_classes, + labelled_input_classes=labelled_input_classes, + errors=subtree["field"].errors or "", + label_for=subtree["field"].id_for_label, + label_text=subtree["field"].label, + tooltip=tooltip_html, + inline_helptext_html=inline_helptext_html, + field_html=subtree["field"], + ) + return html + + return "\n".join(map(gen_node, treelike)) + + fields_html = gen_html(get_field_treelike()) + with_errors = "{form_errors}\n{fields}\n".format( + form_errors=self.non_field_errors() if self.non_field_errors() else "", + fields=fields_html, + ) + return mark_safe(with_errors) + +class CreateAccountForm(FormRenderMixin, UserCreationForm): """Form used to register a new user""" class Meta: model = EmailUser diff --git a/accounts/views.py b/accounts/views.py index 3212e210b23088bf4cef9068e43d9ed1a3872171..1abacb6efb4b15b21343ede7799a444de1f2be9c 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -29,4 +29,4 @@ def create_account(request): return redirect('home') else: form = CreateAccountForm() - return render(request, 'registration/create_account.html', {'form': form}) + return render(request, 'create_account.html', {'form': form}) diff --git a/home/static/css/style.css b/home/static/css/style.css index 56d9f127f261f505ccd5f5aaa964eda0559454b0..64c0524cdd59786f5ef3fe0c249aaf209e3ba17e 100644 --- a/home/static/css/style.css +++ b/home/static/css/style.css @@ -183,9 +183,16 @@ ul.errorlist { color: red; font-size: 0.8em; list-style-type: none; - padding-left: 5px; + padding: 0 5px 0 0; + margin: 0; +} +div.formfield { + padding-top: 5px +} +div.label_line { + font-weight: bold; + padding: 5px; } - div.error { color: red; border: 2px solid red; diff --git a/home/templates/registration/create_account.html b/home/templates/registration/create_account.html index d2ca278489fbd60d247581ef57333e110d1d55b2..d5b4c161d201fb6154bbe5d57a0a7463e282bd17 100644 --- a/home/templates/registration/create_account.html +++ b/home/templates/registration/create_account.html @@ -7,9 +7,8 @@ {% endif %} <form method="post" action="{% url 'accounts:create' %}"> {% csrf_token %} - <table> - {{ form }} - </table> + {{ form.as_html }} + <br> <input type="submit" value="Valider"> </form> {% endblock %}