コード例 #1
0
def comment(project, issue):
    """Submits a new comment."""
    text = request.form.get('text')
    error = comment_validation(text)
    if error is not None:
        flash(error)
    else:
        new_comment = Comment(text=text, user=current_user, issue=issue)
        db.session.add(new_comment)
        if current_user == issue.assignee:
            admin_reviewer_add_notification(
                project,
                'new comment',
                {
                    'avatar': current_user.avatar(),
                    'fullname': current_user.fullname(),
                    'projectId': project.id,
                },
                issue.id,
            )
        else:
            issue.assignee.add_notification(
                'new comment',
                {
                    'avatar': current_user.avatar(),
                    'fullname': current_user.fullname(),
                    'projectId': project.id,
                },
                issue.id,
            )
        db.session.commit()
    return redirect(url_for('issue.issue', id=project.id, issue_id=issue.id))
コード例 #2
0
def close(project, issue):
    """Marks an issue as closed.

    Args:
        project:
            in: close_issue_permission_required() decorator
            type: Project
            description: A Project object whose id is the same as the id in the path.
        issue:
            in: close_issue_permission_required() decorator
            type: Issue
            description: An Issue object whose id is the same as the id in the path.
        url:
            in: formData
            type: String
            description: The url to be redirected to.

    Responses:
        302:
            description: Redirect to the url page.
        400:
            description: Bad request.
        403:
            description: Current user does not have the permission.
        404:
            description: Project or issue does not exist.
    """

    redirect_url = request.form.get('url')
    issue.status = 'Closed'
    issue.closed_timestamp = time()

    if issue.assignee is None:
        issue.creator.add_notification(
            'mark closed',
            {
                'avatar': current_user.avatar(),
                'fullname': current_user.fullname(),
                'issueTitle': issue.title,
            },
        )
    else:
        issue.assignee.add_notification(
            'mark closed',
            {
                'avatar': current_user.avatar(),
                'fullname': current_user.fullname(),
                'issueTitle': issue.title,
            },
        )

    db.session.commit()
    return redirect(redirect_url)
コード例 #3
0
def create(user_project):
    """Creates a new issue.

    Args:
        user_project:
            in: permission_required() decorator
            type: UserProject
            description: A UserProject object whose user_id belongs to the current user
            and project_id is the same as the query parameter - id.
        title:
            in: formData
            type: string
            description: The new issue's title.
        description:
            in: formData
            type: string
            description: The new issue's description.

    Responses:
        302:
            description: Redirect to project page.
        403:
            description: Forbidden.
        404:
            description: Project not found.
    """

    title = request.form.get('title')
    description = request.form.get('description')

    project = user_project.project
    error = create_validation(title, description)
    if error is not None:
        flash(error)
    else:
        new_issue = Issue(
            title=title,
            description=description,
            creator=current_user,
            project=project,
        )
        db.session.add(new_issue)
        admin_reviewer_add_notification(
            project,
            'new issue',
            {
                'avatar': current_user.avatar(),
                'fullname': current_user.fullname(),
                'projectTitle': project.title,
                'issueTitle': title,
            },
        )
        db.session.commit()
        os.makedirs(
            os.path.join(current_app.config['UPLOAD_PATH'], str(new_issue.id)))

    return redirect(url_for('project.project', id=project.id))
コード例 #4
0
ファイル: models.py プロジェクト: liamzhou77/issueless
 def add_basic_notification(self, name, title):
     self.add_notification(
         name,
         {
             'avatar': current_user.avatar(),
             'fullname': current_user.fullname(),
             'projectTitle': title,
         },
     )
コード例 #5
0
def assign(project, issue):
    """Assigns an issue.

    Args:
        project:
            in: assign_issue_permission_required() decorator
            type: Project
            description: A Project object whose id is the same as the id in the path.
        issue:
            in: assign_issue_permission_required() decorator
            type: Issue
            description: An Issue object whose id is the same as the id in the path.
        priority:
            in: formData
            type: String
            description: The priority level of the issue.
        assignee_id:
            in: formData
            type: int
            description: The assignee's id.

    Responses:
        302:
            description: Redirect to project page.
        400:
            description: Bad request.
        403:
            description: Current user does not have the permission.
        404:
            description: Project or issue does not exist.
    """

    priority = request.form.get('priority')
    assignee_id = request.form.get('assignee_id')

    error = assign_validation(project, issue, priority, assignee_id)

    if error is not None:
        flash(error)
    else:
        issue.priority = priority
        issue.status = 'In Progress'
        issue.assignee_id = assignee_id
        if issue.assignee != current_user:
            issue.assignee.add_notification(
                'assign issue',
                {
                    'avatar': current_user.avatar(),
                    'fullname': current_user.fullname(),
                    'projectId': project.id,
                },
                issue.id,
            )
        db.session.commit()

    return redirect(url_for('project.project', id=project.id))
コード例 #6
0
ファイル: views.py プロジェクト: liamzhou77/issueless
def change_role(user_project):
    """Changes a member's role.

    Changes a member's role. If member's previous role is Reviewer, demote to Developer.
    Otherwise, promote to Reviewer. Notifies the member.

    Produces:
        application/json

    Args:
        user_project:
            in: permission_required() decorator
            type: UserProject
            description: A UserProject object whose user_id belongs to the current user
                and project_id is the same as the query parameter - id.
        user_id:
            in: json
            type: int
            description: Id of the user to be promoted or demoted.

    Responses:
        200:
            description: Change successfully.
        400:
            description: Bad request.
        404:
            description: User not found.
        422:
            description: Unprocessable.
    """

    body = request.get_json()
    user_id = body.get('user_id')
    if user_id is None:
        abort(400)

    project = user_project.project
    user = User.query.get_or_404(user_id)
    user_project = change_role_validation(project, user)

    new_role = user_project.change_role()
    user.add_notification(
        'change role',
        {
            'avatar': current_user.avatar(),
            'fullname': current_user.fullname(),
            'projectTitle': project.title,
            'newRole': new_role.name,
        },
    )
    db.session.commit()

    return {'success': True}
コード例 #7
0
def delete(project, issue):
    """Deletes an issue.

    Produces:
        application/json
        text/html

    Args:
        project:
            in: delete_issue_permission_required() decorator
            type: Project
            description: A Project object whose id is the same as the id in the path.
        issue:
            in: delete_issue_permission_required() decorator
            type: Issue
            description: An Issue object whose id is the same as the id in the path.

    Responses:
        302:
            description: Redirect to project page.
        400:
            description: Bad request.
        403:
            description: Forbidden.
        404:
            description: Project or issue does not exist.
    """

    db.session.delete(issue)
    data = {
        'avatar': current_user.avatar(),
        'fullname': current_user.fullname(),
        'projectTitle': project.title,
        'issueTitle': issue.title,
    }
    if issue.creator != current_user:
        issue.creator.add_notification(
            'delete issue',
            data,
        )
    if issue.assignee is not None and issue.assignee != current_user:
        issue.assignee.add_notification(
            'delete issue',
            data,
        )

    db.session.commit()
    shutil.rmtree(
        os.path.join(current_app.config['UPLOAD_PATH'], str(issue.id)),
        ignore_errors=True,
    )
    return redirect(url_for('project.project', id=project.id))
コード例 #8
0
def restore(project, issue):
    """Restores a closed or resolved issue back to previous status.

    Restores a closed or resolved issue back to previous status. Restores resolved
    issue's status to In Progress. Restores closed issue's status to Open or In
    Progress based on if there is an existing assignee.

    Args:
        project:
            in: assign_issue_permission_required() decorator
            type: Project
            description: A Project object whose id is the same as the id in the path.
        issue:
            in: assign_issue_permission_required() decorator
            type: Issue
            description: An Issue object whose id is the same as the id in the path.
        url:
            in: formData
            type: String
            description: The url to be redirected to.

    Responses:
        302:
            description: Redirect to the url page.
        400:
            description: Bad request.
        403:
            description: Current user does not have the permission.
        404:
            description: Project or issue does not exist.
    """

    redirect_url = request.form.get('url')
    if issue.status == 'Closed':
        if issue.assignee is None:
            status = 'Open'
            admin_reviewer_add_notification(
                project,
                'mark open',
                {
                    'avatar': current_user.avatar(),
                    'fullname': current_user.fullname(),
                    'issueTitle': issue.title,
                },
            )
        else:
            status = 'In Progress'
            if issue.assignee != current_user:
                issue.assignee.add_notification(
                    'mark in progress',
                    {
                        'avatar': current_user.avatar(),
                        'fullname': current_user.fullname(),
                        'issueTitle': issue.title,
                        'preStatus': issue.status,
                        'projectId': project.id,
                    },
                    issue.id,
                )
        issue.closed_timestamp = None
    else:
        status = 'In Progress'
        if issue.assignee != current_user:
            issue.assignee.add_notification(
                'mark in progress',
                {
                    'avatar': current_user.avatar(),
                    'fullname': current_user.fullname(),
                    'issueTitle': issue.title,
                    'preStatus': issue.status,
                    'projectId': project.id,
                },
                issue.id,
            )
        issue.resolved_timestamp = None
    issue.status = status
    db.session.commit()
    return redirect(redirect_url)
コード例 #9
0
def edit(project, issue):
    """Edits an issue.

    Edits an issue's title and description. If the project's status is In Progress,
    edits the priority and assignee too.

    Produces:
        application/json
        text/html

    Args:
        project:
            in: edit_issue_permission_required() decorator
            type: Project
            description: A Project object whose id is the same as the id in the path.
        issue:
            in: edit_issue_permission_required() decorator
            type: Issue
            description: An Issue object whose id is the same as the id in the path.
        title:
            in: json
            type: string
            description: The issue's new title.
        description:
            in: json
            type: string
            description: The issue's new description.
        priority:
            in: json
            type: String
            description: The priority level of the issue.
        assignee_id:
            in: json
            type: String
            description: The assignee's id.


    Responses:
        200:
            description: Edit successfully.
        400:
            description: Bad request.
        403:
            description: Forbidden.
        404:
            description: Project or issue does not exist.
        422:
            description: Unprocessable.
    """

    body = request.get_json()
    title = body.get('title')
    description = body.get('description')
    if None in (title, description):
        abort(400)

    if issue.status == 'In Progress':
        priority = body.get('priority')
        assignee_id = body.get('assignee_id')
        if None in (priority, assignee_id):
            abort(400)

        new_assignee = edit_validation(issue, title, description, project,
                                       priority, assignee_id)
        issue.priority = priority
        if issue.assignee != new_assignee:
            if issue.assignee != current_user:
                issue.assignee.add_notification(
                    'remove assignee',
                    {
                        'avatar': current_user.avatar(),
                        'fullname': current_user.fullname(),
                        'issueTitle': issue.title,
                    },
                )
            if new_assignee != current_user:
                new_assignee.add_notification(
                    'assign issue',
                    {
                        'avatar': current_user.avatar(),
                        'fullname': current_user.fullname(),
                        'projectId': project.id,
                    },
                    issue.id,
                )
            issue.assignee_id = assignee_id
    else:
        edit_validation(issue, title, description)

    issue.title = title
    issue.description = description
    db.session.commit()

    return {'success': True}
コード例 #10
0
ファイル: views.py プロジェクト: liamzhou77/issueless
def invite(user_project):
    """Searches for users to be invited or sends invitation.

    GET:
        Args:
            search:
                in: query
                type: string
                description: The search term.

        Responses:
            200:
                description: Searched user's information.

    POST:
        Args:
            target:
                in: json
                type: string
                description: The target to be searched with.
            role:
                in: json
                type: string
                description: Role name to be assigned to the invited user.

        Responses:
            200:
                description: Invitation successfully sent.
            400:
                description: Bad request.
            422:
                description: Unprocessable.

    Produces:
        application/json
        text/html

    Args:
        user_project:
            in: permission_required() decorator
            type: UserProject
            description: A UserProject object whose user_id belongs to the current user
                and project_id is the same as the query parameter - id.

    Responses:
        403:
            description: Current user is not a member of the project or does not have
                the permission.
        404:
            description: Project does not exist.
    """

    project = user_project.project

    if request.method == 'POST':
        body = request.get_json()
        target = body.get('target')
        role_name = body.get('role')
        if None in (target, role_name):
            abort(400)

        user = invite_validation(project, target, role_name)
        if user is not None:
            data = {
                'avatar': current_user.avatar(),
                'fullname': current_user.fullname(),
                'projectTitle': project.title,
                'roleName': role_name,
            }
            user.add_notification('invitation', data, target_id=project.id)
            db.session.commit()

        return {'success': True}

    search_term = request.args.get('search')
    if search_term is None:
        return {'success': True, 'users': []}
    if search_term[-1] == ' ':
        search_term = search_term[:-1]
    ilike_regex = f'{search_term}%'

    users = (
        User.query.filter(
            User.username.ilike(ilike_regex)
            | db.func.concat(User.first_name, ' ', User.last_name).ilike(ilike_regex)
        )
        .order_by(db.func.concat(User.first_name, ' ', User.last_name))
        .limit(10)
        .all()
    )

    return {
        'success': True,
        'users': [
            {
                'fullname': user.fullname(),
                'username': user.username,
                'avatar': user.avatar(),
                'joined': user in project.users,
            }
            for user in users
        ],
    }
コード例 #11
0
def create_idea(box_id, idea_id):

    # if  id == 0 create new idea, otherwise update existed idea by id
    # authenticate user:
    idea_box = get_idea_box(box_id, current_user)

    # log out unathorized user:
    # if idea_box empty then current user belong to different company
    # if  idea box already closed the user modified the url field
    if not idea_box or not is_open(idea_box.Boxes.close_at):
        return unathorized("You cannot to edit this Idea.", "error")

    current_idea = Ideas.query.get(idea_id)
    colleague = current_user
    current_user.is_admin = False

    if idea_id > 0 and current_idea.colleague_id != current_user.id:
        # this idea belong to different colleague than the current user, check updata_box privileg:
        if not is_auth_box(current_user):
            return unathorized("You don't hane privileg to edit this Idea.",
                               "error")
        else:
            # current user is an admin with privileg to edit/delete boxes and ideas:
            current_user.is_admin = True
            colleague = Colleagues.query.get(current_idea.colleague_id)

    form = CreateIdeaForm()
    # change sign-input's labels to the name of current user (name must be hidden for Admins!):
    form.sign.choices = [
        ("incognito", "incognito"),
        (current_user.user_name, current_user.user_name),
        (current_user.first_name, current_user.first_name),
        (current_user.fullname(), current_user.fullname())
    ] if not current_user.is_admin else [(current_idea.sign,
                                          current_idea.sign)]

    if form.validate_on_submit():
        print("submitted")
        success = ""
        error = ""
        if idea_id == 0:
            # instantiate new Idea:
            idea = Ideas(idea=form.idea.data,
                         sign=form.sign.data,
                         box_id=box_id,
                         colleague_id=current_user.id)

            db.session.add(idea)
            success = "Thank you for sharing your Idea."
            error = "Any error occured when post your Idea. Please try again."

        else:
            # edit existed idea:
            error = "Any error occured when edited your Idea. Please try again."
            if current_idea.idea != form.idea.data:
                current_idea.idea = form.idea.data
                success += "Your idea successfully edited.\n"
            if current_idea.sign != form.sign.data:
                current_idea.sign = form.sign.data
                success += f"Your sign changed to {current_idea.sign}.\n"

        try:
            db.session.commit()
            flash(success, "inform")
            return redirect(url_for("idea_box", id=box_id))
        except:
            db.session.rollback()
            flash(error, "error")
            return redirect(
                url_for("create_idea", box_id=box_id, idea_id=idea_id))

    if idea_id > 0:
        # edit mode:
        form.submit.label.text = "Edit my Idea" if not current_user.is_admin else f"Edit {colleague.first_name}'s Idea"
        form.idea.data = current_idea.idea
        form.sign.data = current_idea.sign
    else:
        form.sign.data = current_user.first_name  # set first name by default checked

    return render_template(
        "create_idea.html",
        update_box=is_auth_box(
            current_user),  # to add edit icon to authorized admin
        box=idea_box.Boxes,
        avatar="incognito-cut.svg"
        if form.sign.data == "incognito" else get_avatar(colleague),
        form=form,
        colleague=colleague,
        change_logo=is_auth_company(
            current_user
        ),  # to add click event to change logo for authorized admin
        logo=get_logo(current_user),
        nav=get_nav(current_user))