From b81fb8cf7140b06b3c64075e03f437c14e7c76a3 Mon Sep 17 00:00:00 2001 From: kaiyou <pierre@jaury.eu> Date: Tue, 12 May 2020 17:28:55 +0200 Subject: [PATCH] Add quick profile creation for the first profile --- hiboo/models.py | 3 ++ hiboo/profile/__init__.py | 21 ++++++++----- hiboo/profile/format.py | 6 ++-- hiboo/profile/forms.py | 12 +++++++- hiboo/profile/login.py | 34 +++++++++++++--------- hiboo/profile/templates/profile_quick.html | 30 +++++++++++++++++++ 6 files changed, 82 insertions(+), 24 deletions(-) create mode 100644 hiboo/profile/templates/profile_quick.html diff --git a/hiboo/models.py b/hiboo/models.py index eb40676c..fd1205da 100644 --- a/hiboo/models.py +++ b/hiboo/models.py @@ -162,6 +162,9 @@ class Service(db.Model): same_username = db.Column(db.Boolean(), nullable=False, default=False) config = db.Column(mutable.MutableDict.as_mutable(JSONEncoded)) + def check_username(self, username): + return Profile.query.filter_by(service_uuid=self.uuid, username=username).first() + class Profile(db.Model): """ A profile is a per-service custom identity. diff --git a/hiboo/profile/__init__.py b/hiboo/profile/__init__.py index 61168d90..fa7865f6 100644 --- a/hiboo/profile/__init__.py +++ b/hiboo/profile/__init__.py @@ -15,13 +15,18 @@ from hiboo.profile import login, admin, forms, cli def get_profile(service, **redirect_args): + query = models.Profile.query.filter_by( + service_uuid=service.uuid, + user_uuid=flask_login.current_user.uuid, + status=models.Profile.ACTIVE + ) form = forms.ProfilePickForm() if form.validate_on_submit(): - profile = models.Profile.query.get(form.profile_uuid.data) - if not (profile and - profile.user == flask_login.current_user and - profile.service == service and - profile.status == models.Profile.ACTIVE): - return None - return profile - utils.force_redirect(utils.url_for("profile.pick", **redirect_args)) + return query.filter_by(uuid=form.profile_uuid.data).first() + count = query.count() + if count > 1: + utils.force_redirect(utils.url_for("profile.pick", **redirect_args)) + elif count == 1: + return query.one() + else: + utils.force_redirect(utils.url_for("profile.create_quick", **redirect_args)) diff --git a/hiboo/profile/format.py b/hiboo/profile/format.py index c5390316..9033bcd7 100644 --- a/hiboo/profile/format.py +++ b/hiboo/profile/format.py @@ -44,15 +44,17 @@ class ProfileFormat(object): def coalesce(cls, username): """ Transform a username into its valid form """ - return filter(cls.allowed.__contains__, cls.transform(username)) + return ''.join(filter(cls.allowed.__contains__, cls.transform(username))) @classmethod def alternatives(cls, username): """ Generate alternate usernames for a given username """ + yield username index = 1 while True: - yield username + "_" + str(index) + yield username + str(index) + index += 1 register = ProfileFormat.register diff --git a/hiboo/profile/forms.py b/hiboo/profile/forms.py index 51c4721e..51679114 100644 --- a/hiboo/profile/forms.py +++ b/hiboo/profile/forms.py @@ -1,4 +1,4 @@ -from wtforms import validators, fields +from wtforms import validators, fields, widgets from flask_babel import lazy_gettext as _ import flask_wtf @@ -9,6 +9,16 @@ class ProfileForm(flask_wtf.FlaskForm): comment = fields.StringField(_('Comment')) submit = fields.SubmitField(_('Create profile')) + def force_username(self, username): + self.username.data = username + self.username.render_kw = {"readonly": True} + + def hide_fields(self): + self.username.widget = widgets.HiddenInput() + self.username.label = "" + self.comment.widget = widgets.HiddenInput() + self.comment.label = "" + class ProfilePickForm(flask_wtf.FlaskForm): profile_uuid = fields.TextField('profile', []) diff --git a/hiboo/profile/login.py b/hiboo/profile/login.py index 07741731..1eb65c24 100644 --- a/hiboo/profile/login.py +++ b/hiboo/profile/login.py @@ -2,7 +2,6 @@ from hiboo.profile import blueprint, forms, formats from hiboo import models, utils, security from hiboo import user as hiboo_user from passlib import context, hash -from wtforms import validators from flask_babel import lazy_gettext as _ import flask @@ -18,12 +17,14 @@ def pick(service_uuid): return flask.render_template("profile_pick.html", service=service, profiles=profiles, form=form) +@blueprint.route("/create_quick/<service_uuid>", methods=["GET", "POST"], defaults={"quick": True}, endpoint="create_quick") @blueprint.route("/create_for/<service_uuid>", methods=["GET", "POST"], defaults={"create_for": True}, endpoint="create_for") @blueprint.route("/create/<service_uuid>", methods=["GET", "POST"]) @security.authentication_required() -def create(service_uuid, create_for=False): +def create(service_uuid, create_for=False, quick=False): service = models.Service.query.get(service_uuid) or flask.abort(404) status = models.Profile.ACTIVE + format = formats[service.profile_format] # If the admin passed a user uuid, use that one, otherwise ignore it if create_for and flask_login.current_user.is_admin: user = hiboo_user.get_user(intent="profile.create_for", create_for=None) @@ -48,22 +49,28 @@ def create(service_uuid, create_for=False): elif len(profiles) >= service.max_profiles or service.policy == models.Service.MANAGED: flask.flash(_("Your profile creation requires approval"), "warning") status = models.Profile.REQUEST - # Actually display the form form = forms.ProfileForm() - form.username.validators = formats[service.profile_format].validators() + form.username.validators = format.validators() + submit = form.validate_on_submit() + # If this is a quick creation, prefill the username with a generated one + if quick: + for username in format.alternatives(format.coalesce(user.username)): + if not service.check_username(username): + form.force_username(username) + break + form.hide_fields() + # If same_username is enabled, enforce the username if service.same_username: - form.username.data = user.username - form.username.render_kw = {"readonly": True} - if form.validate_on_submit(): - username = user.username if service.same_username else form.username.data - existing = models.Profile.query.filter_by(service_uuid=service_uuid, username=username).first() - if existing: + form.force_username(user.username) + # Handle the creation form + if submit: + if service.check_username(form.username.data): flask.flash(_("A profile with that username exists already"), "danger") else: profile = models.Profile() - profile.username = username profile.user = user profile.service = service + profile.username = form.username.data profile.comment = form.comment.data profile.status = status models.db.session.add(profile) @@ -71,8 +78,9 @@ def create(service_uuid, create_for=False): user=user, service=service, profile=profile) models.db.session.commit() return flask.redirect(utils.url_or_intent("account.home")) - return flask.render_template("profile_create.html", form=form, service=service, - user=user, create_for=create_for) + # Display either the quick version or the full version (one can switch from one to another) + return flask.render_template("profile_quick.html" if quick else "profile_create.html", + form=form, service=service, user=user, create_for=create_for) @blueprint.route("/claim/<service_uuid>", methods=["GET", "POST"]) diff --git a/hiboo/profile/templates/profile_quick.html b/hiboo/profile/templates/profile_quick.html new file mode 100644 index 00000000..9de36475 --- /dev/null +++ b/hiboo/profile/templates/profile_quick.html @@ -0,0 +1,30 @@ +{% extends "base.html" %} + +{% set service_name = service.name %} +{% set username = form.username.data %} + +{% block title %}{% trans %}Sign up{% endtrans %}{% endblock %} +{% block subtitle %} + {% trans service_name %}for the service {{ service_name }}{% endtrans %} +{% endblock %} + +{% block content %} +<div class="row"> + <div class="col-md-4 col-s-6 col-xs-12"> + <div class="box box-widget widget-user-2"> + <div class="widget-user-header bg-primary"> + <form method="POST" class="form"> + {{ form.hidden_tag() }} + <input type="hidden" name="username" value="{{ form.username.data }}"> + <input type="submit" value="Sign up" style="opacity: 0.8" class="btn btn-lg btn-flat bg-gray text-black pull-right"> + </form> + <h3 class="widget-header-username">{{ form.username.data }}</h3> + <h5 class="widget-header-desc">{% trans service_name %}Your new profile for {{ service_name }}{% endtrans %}</h5> + </div> + <div class="box-footer"> + <p>{% trans service_name %}This is the first time you sign into {{ service_name }}, we first need to sign you up. You may wish to pick another username for that application. If you do, please click the "Custom profile" button.{% endtrans %}</p> + </div> + </div> + </div> +</div> +{% endblock %} -- GitLab