예제 #1
0
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)
예제 #2
0
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)
예제 #3
0
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)
예제 #4
0
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)
예제 #5
0
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)
예제 #6
0
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)
예제 #7
0
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)
예제 #8
0
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(),
    )
예제 #9
0
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)
예제 #10
0
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)
예제 #11
0
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)
예제 #12
0
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)
예제 #13
0
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)
예제 #14
0
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)
예제 #15
0
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)
예제 #16
0
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)
예제 #17
0
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)
예제 #18
0
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)
예제 #19
0
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)
예제 #20
0
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)
예제 #21
0
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)
예제 #22
0
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)
예제 #23
0
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')
예제 #24
0
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)
예제 #25
0
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)
예제 #26
0
def manage():
    """Display a user's account information."""
    return render_template_with_nav_info('account/manage.html',
                                         user=current_user,
                                         form=None)
예제 #27
0
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)