def change_email_request(): """Respond to existing user's request to change their email.""" form = ChangeEmailForm() if form.validate_on_submit(): if current_user.verify_password(form.password.data): new_email = form.email.data token = current_user.generate_email_change_token(new_email) change_email_link = url_for('account.change_email', token=token, _external=True) send_email_async( app=current_app._get_current_object(), recipient=new_email, subject='Confirm Your New Email', template='account/email/change_email', # current_user is a LocalProxy, we want the underlying user # object user=current_user._get_current_object(), change_email_link=change_email_link) flash('A confirmation link has been sent to {}.'.format(new_email), 'warning') return redirect(url_for('main.index')) else: flash('Invalid email or password.', 'form-error') return render_template_with_nav_info('account/manage.html', form=form)
def invite_user(): """Invites a new user to create an account and set their own password.""" form = InviteUserForm() if form.validate_on_submit(): user = User(role=form.role.data, first_name=form.first_name.data, last_name=form.last_name.data, email=form.email.data) db.session.add(user) db.session.commit() token = user.generate_confirmation_token() invite_link = url_for('account.join_from_invite', user_id=user.id, token=token, _external=True) send_email_async( app=current_app._get_current_object(), recipient=user.email, subject='You Are Invited To Join', template='account/email/invite', user=user, invite_link=invite_link, ) flash('User {} successfully invited'.format(user.full_name()), 'form-success') return render_template_with_nav_info('admin/new_user.html', form=form)
def contact(control_list_id): """ This routes allows user to send email to list administrators """ control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) form = SendMailToAdmin(prefix="mail") if request.method == "POST" and form.validate_on_submit(): control_list_link = url_for('control_lists_bp.get', control_list_id=control_list_id, _external=True) email.send_email_async( app=current_app._get_current_object(), bcc=[u[3] for u in control_list.owners] + [current_user.email], recipient=[], subject='[Pyrrha Control List] ' + form.title.data, template='control_lists/email/contact', # current_user is a LocalProxy, we want the underlying user # object user=current_user._get_current_object(), message=form.message.data, control_list_title=control_list.name, url=control_list_link) flash('The email has been sent to the control list administrators.', 'success') return redirect( url_for('control_lists_bp.contact', control_list_id=control_list_id)) return render_template_with_nav_info('control_lists/contact.html', form=form, control_list=control_list)
def reset_password_request(): """Respond to existing user's request to reset their password.""" if not current_user.is_anonymous: return redirect(url_for('main.index')) form = RequestResetPasswordForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user: token = user.generate_password_reset_token() reset_link = url_for('account.reset_password', token=token, _external=True) send_email_async(app=current_app._get_current_object(), recipient=user.email, subject='Reset Your Password', template='account/email/reset_password', user=user, reset_link=reset_link, next=request.args.get('next')) flash( 'A password reset link has been sent to {}.'.format( form.email.data), 'warning') return redirect(url_for('account.login')) return render_template_with_nav_info('account/reset_password.html', form=form)
def rename(control_list_id): """ This routes allows user to send email to list administrators """ control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) form = Rename(prefix="rename") control_list_link = url_for('control_lists_bp.get', control_list_id=control_list_id, _external=True) if not is_owner and not current_user.is_admin(): flash("You are not an owner of the list.", category="error") return redirect(control_list_link) if request.method == "POST" and form.validate_on_submit(): control_list.name = form.title.data db.session.add(control_list) try: db.session.commit() flash("The name of the list has been updated.", category="success") except: flash( "There was an error when we tried to rename your control list.", category="error") return redirect(control_list_link) return render_template_with_nav_info('control_lists/rename.html', form=form, control_list=control_list)
def registered_users(): """View all registered users.""" users = User.query.all() roles = Role.query.all() return render_template_with_nav_info('admin/registered_users.html', current_user=current_user, users=users, roles=roles)
def information_edit(control_list_id, control_list): if request.method == "POST": control_list.description = request.form.get("cl_description") control_list.language = request.form.get("cl_language") control_list.notes = request.form.get("cl_notes") control_list.bibliography = request.form.get("cl_bibliography") return render_template_with_nav_info('control_lists/information_edit.html', control_list=control_list)
def get(control_list_id): control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) return render_template_with_nav_info( "control_lists/control_list.html", control_list=control_list, is_owner=is_owner, can_edit=is_owner or current_user.is_admin(), )
def edit(cl_id, allowed_type): """ Find allowed values and allow their edition :param cl_id: Id of the Control List :param allowed_type: Type of allowed value (lemma, morph, POS) """ if allowed_type not in ["lemma", "POS", "morph"]: raise NotFound("Unknown type of resource.") control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=cl_id, user=current_user) can_edit = is_owner or current_user.is_admin() if not can_edit: return abort(403) # In case of Post if request.method == "POST": allowed_values = request.form.get("allowed_values") if allowed_type == "lemma": allowed_values = [ x.replace('\r', '') for x in allowed_values.split("\n") if len(x.replace('\r', '').strip()) > 0 ] elif allowed_type == "POS": allowed_values = [ x.replace('\r', '') for x in allowed_values.split(",") if len(x.replace('\r', '').strip()) > 0 ] else: allowed_values = list(StringDictReader(allowed_values)) success = control_list.update_allowed_values(allowed_type, allowed_values) if success: flash("Control List Updated", category="success") else: flash("An error occured", category="error") values = control_list.get_allowed_values(allowed_type=allowed_type, order_by="id") if allowed_type == "lemma": format_message = "This should be formatted as a list of lemma separated by new line" values = "\n".join([d.label for d in values]) elif allowed_type == "POS": format_message = "This should be formatted as a list of POS separated by comma and no space" values = ",".join([d.label for d in values]) else: format_message = "The TSV should at least have the header : label and could have a readable column for human" values = "\n".join( ["label\treadable"] + ["{}\t{}".format(d.label, d.readable) for d in values]) return render_template_with_nav_info("control_lists/edit.html", format_message=format_message, values=values, allowed_type=allowed_type, control_list=control_list)
def dashboard(): """admin dashboard page.""" if current_user.is_admin(): corpora = db.session.query(Corpus).all() control_lists = db.session.query(ControlLists).all() else: corpora = Corpus.for_user(current_user) control_lists = ControlLists.for_user(current_user) return render_template_with_nav_info('main/dashboard.html', current_user=current_user, dashboard_corpora=corpora, dashboard_control_lists=control_lists)
def manage_control_lists_user(cl_id): """ Save or display corpus accesses :param cl_id: ID of the control list """ control_list = ControlLists.query.filter(ControlLists.id == cl_id).first() can_read = current_user.is_admin() or control_list.has_access(current_user) can_edit = current_user.is_admin() or control_list.is_owned_by(current_user) if can_read: # only owners can give/remove access & promote a user to owner if request.method == "POST" and can_edit: users = [ User.query.filter(User.id == user_id).first() for user_id in [int(u) for u in request.form.getlist("user_id")] ] ownerships = [int(u) for u in request.form.getlist("ownership") if u.isdigit()] # previous rights prev_cu = ControlListsUser.query.filter(ControlListsUser.control_lists_id == cl_id).all() # should not be able to delete the last owner if len(prev_cu) > 0 and True not in set([user.id in ownerships for user in users]): abort(403) # update corpus users try: for cu in prev_cu: db.session.delete(cu) for cu in [ ControlListsUser(control_lists_id=control_list.id, user_id=user.id, is_owner=user.id in ownerships) for user in users ]: db.session.add(cu) db.session.commit() flash('Modifications have been saved.', 'success') except Exception as e: db.session.rollback() raise e return redirect(url_for('main.manage_control_lists_user', cl_id=cl_id)) else: # GET method users = User.query.all() roles = Role.query.all() return render_template_with_nav_info( 'main/dashboard_manage_control_lists_users.html', control_list=control_list, current_user=current_user, users=users, roles=roles, can_read=can_read, can_edit=can_edit ) else: return abort(403)
def login(): """Log in an existing user.""" form = LoginForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user is not None and user.password_hash is not None and \ user.verify_password(form.password.data): login_user(user, form.remember_me.data) flash('You are now logged in. Welcome back!', 'success') return redirect(request.args.get('next') or url_for('main.index')) else: flash('Invalid email or password.', 'form-error') return render_template_with_nav_info('account/login.html', form=form)
def change_password(): """Change an existing user's password.""" form = ChangePasswordForm() if form.validate_on_submit(): if current_user.verify_password(form.old_password.data): current_user.password = form.new_password.data db.session.add(current_user) db.session.commit() flash('Your password has been updated.', 'form-success') return redirect(url_for('main.index')) else: flash('Original password is invalid.', 'form-error') return render_template_with_nav_info('account/manage.html', form=form)
def new_user(): """Create a new user.""" form = NewUserForm() if form.validate_on_submit(): user = User(role=form.role.data, first_name=form.first_name.data, last_name=form.last_name.data, email=form.email.data, password=form.password.data) db.session.add(user) db.session.commit() flash('User {} successfully created'.format(user.full_name()), 'form-success') return render_template_with_nav_info('admin/new_user.html', form=form)
def propose_as_public(control_list_id): """ This routes allows user to send email to application administrators to propose a list as public for everyone to use """ control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) if not is_owner: flash("You are not an owner of the list.", category="error") return redirect( url_for("control_lists_bp.get", control_list_id=control_list_id)) elif control_list.public != PublicationStatus.private: flash("This list is already public or submitted.", category="warning") return redirect( url_for("control_lists_bp.get", control_list_id=control_list_id)) form = SendMailToAdmin(prefix="mail") if form.validate_on_submit(): admins = User.get_admins() control_list_link = url_for('control_lists_bp.get', control_list_id=control_list_id, _external=True) control_list.public = PublicationStatus.submitted db.session.add(control_list) try: email.send_email_async( app=current_app._get_current_object(), bcc=[u.email for u in admins] + [current_user.email], recipient=[], subject='[Pyrrha Control List] ' + form.title.data, template='control_lists/email/contact', # current_user is a LocalProxy, we want the underlying user # object user=current_user._get_current_object(), message=form.message.data, control_list_title=control_list.name, url=control_list_link) flash('The email has been sent to the administrators.', 'success') db.session.commit() except Exception: db.session.rollback() flash("There was an error during the messaging step") return render_template_with_nav_info( 'control_lists/propose_as_public.html', form=form, control_list=control_list)
def change_user_email(user_id): """Change a user's email.""" user = User.query.filter_by(id=user_id).first() if user is None: abort(404) form = ChangeUserEmailForm() if form.validate_on_submit(): user.email = form.email.data db.session.add(user) db.session.commit() flash( 'Email for user {} successfully changed to {}.'.format( user.full_name(), user.email), 'form-success') return render_template_with_nav_info('admin/manage_user.html', user=user, form=form)
def reset_password(token): """Reset an existing user's password.""" if not current_user.is_anonymous: return redirect(url_for('main.index')) form = ResetPasswordForm() if form.validate_on_submit(): user = User.query.filter_by(email=form.email.data).first() if user is None: flash('Invalid email address.', 'form-error') return redirect(url_for('main.index')) if user.reset_password(token, form.new_password.data): flash('Your password has been updated.', 'form-success') return redirect(url_for('account.login')) else: flash('The password reset link is invalid or has expired.', 'form-error') return redirect(url_for('main.index')) return render_template_with_nav_info('account/reset_password.html', form=form)
def change_account_type(user_id): """Change a user's account type.""" if current_user.id == user_id: flash( 'You cannot change the type of your own account. Please ask ' 'another administrator to do this.', 'danger') return redirect(url_for('admin.user_info', user_id=user_id)) user = User.query.get(user_id) if user is None: abort(404) form = ChangeAccountTypeForm() if form.validate_on_submit(): user.role = form.role.data db.session.add(user) db.session.commit() flash( 'Role for user {} successfully changed to {}.'.format( user.full_name(), user.role.name), 'form-success') return render_template_with_nav_info('admin/manage_user.html', user=user, form=form)
def register(): """Register a new user, and send them a confirmation email.""" form = RegistrationForm() if form.validate_on_submit(): user = User(first_name=form.first_name.data, last_name=form.last_name.data, email=form.email.data, password=form.password.data) db.session.add(user) db.session.commit() token = user.generate_confirmation_token() confirm_link = url_for('account.confirm', token=token, _external=True) send_email_async(app=current_app._get_current_object(), recipient=user.email, subject='Confirm Your Account', template='account/email/confirm', user=user, confirm_link=confirm_link) flash('A confirmation link has been sent to {}.'.format(user.email), 'warning') return redirect(url_for('main.index')) return render_template_with_nav_info('account/register.html', form=form)
def read_allowed_values(control_list_id, allowed_type): if allowed_type not in ["POS", "morph"]: flash("The category you selected is wrong petit coquin !", category="error") return redirect(url_for(".get", control_list_id=control_list_id)) control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) kwargs = {} template = "control_lists/read.html" allowed_values = control_list.get_allowed_values( allowed_type=allowed_type).all() return render_template_with_nav_info(template=template, control_list=control_list, is_owner=is_owner, can_edit=is_owner or current_user.is_admin(), allowed_type=allowed_type, allowed_values=allowed_values, readable=allowed_type == "morph", **kwargs)
def change_account_status(user_id): """Change a user's account status (active/inactive).""" user = User.query.get(user_id) if user is None: abort(404) form = ChangeAccountStatusForm() if form.validate_on_submit(): user.confirmed = not user.confirmed db.session.add(user) db.session.commit() flash( 'Status for user {} successfully changed to {}.'.format( user.full_name(), 'Confirmed' if user.confirmed else 'Unconfirmed'), 'form-success') else: form = ChangeAccountStatusForm(status=user.confirmed) return render_template_with_nav_info('admin/manage_user.html', user=user, form=form)
def lemma_list(control_list_id): control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) can_edit = is_owner or current_user.is_admin() if request.method == "DELETE" and can_edit: value = request.args.get("id") lemma = AllowedLemma.query.get_or_404(value) try: AllowedLemma.query.filter( AllowedLemma.id == lemma.id, AllowedLemma.control_list == control_list_id).delete() db.session.commit() return "", 200 except Exception as E: db.session.rollback() return abort(403) elif request.method == "UPDATE" and request.mimetype == "application/json" and can_edit: form = request.get_json().get("lemmas", None) if not form: return abort(400, jsonify({"message": "No lemma were passed."})) lemmas = list(set(form.split())) try: AllowedLemma.add_batch(lemmas, control_list.id, _commit=True) return jsonify({"message": "Data saved"}) except ValueError as E: db.session.rollback() return make_response(jsonify({"message": str(E)}), 400) except sqlalchemy.exc.StatementError as E: db.session.rollback() error = str(E.orig) if error.startswith("UNIQUE constraint failed"): return make_response( jsonify({ "message": "One of the lemma you submitted already exist. " "Remove this lemma and resubmit." }), 400) return make_response( jsonify( {"message": "Database error. Contact the administrator."}), 400) except Exception as E: db.session.rollback() return make_response(jsonify({"message": "Unknown Error"}), 400) elif request.method == "GET": kwargs = {} page = request.args.get("page", "1") page = (page.isnumeric()) and int(page) or 1 limit = request.args.get("limit", "1000") limit = (limit.isnumeric()) and int(limit) or 1 kw = strip_or_none(request.args.get("kw", "")) template = "control_lists/read_lemma.html" allowed_values = control_list.get_allowed_values(allowed_type="lemma", kw=kw).paginate( page=page, per_page=limit) kwargs["kw"] = kw return render_template_with_nav_info(template=template, control_list=control_list, is_owner=is_owner, allowed_type="lemma", can_edit=is_owner or current_user.is_admin(), allowed_values=allowed_values, readable=False, **kwargs) return abort(405)
def unconfirmed(): """Catch users with unconfirmed emails.""" if current_user.is_anonymous or current_user.confirmed: return redirect(url_for('main.index')) return render_template_with_nav_info('account/unconfirmed.html')
def information_read(control_list_id): control_list, is_owner = ControlLists.get_linked_or_404( control_list_id=control_list_id, user=current_user) return render_template_with_nav_info('control_lists/information_read.html', control_list=control_list)
def delete_user_request(user_id): """Request deletion of a user's account.""" user = User.query.filter_by(id=user_id).first() if user is None: abort(404) return render_template_with_nav_info('admin/manage_user.html', user=user)
def manage(): """Display a user's account information.""" return render_template_with_nav_info('account/manage.html', user=current_user, form=None)
def user_info(user_id): """View a user's profile.""" user = User.query.filter_by(id=user_id).first() if user is None: abort(404) return render_template_with_nav_info('admin/manage_user.html', user=user)