Remove Portfolio User

This commit is contained in:
George Drummond 2019-03-26 15:45:32 -04:00
parent 575cfce5e4
commit 6f1eb43de4
No known key found for this signature in database
GPG Key ID: 296DD6077123BF17
8 changed files with 145 additions and 1 deletions

View File

@ -121,6 +121,15 @@ class PortfolioRoles(object):
)
return PermissionSets.get_many(perms_set_names)
@classmethod
def disable(cls, portfolio_role):
portfolio_role.status = PortfolioRoleStatus.DISABLED
db.session.add(portfolio_role)
db.session.commit()
return portfolio_role
@classmethod
def update(cls, portfolio_role, set_names):
new_permission_sets = PortfolioRoles._permission_sets_for_names(set_names)

View File

@ -15,6 +15,7 @@ import atst.forms.portfolio_member as member_forms
from atst.models.permissions import Permissions
from atst.domain.permission_sets import PermissionSets
from atst.domain.authz.decorator import user_can_access_decorator as user_can
from atst.utils.flash import formatted_flash as flash
@portfolios_bp.route("/portfolios")
@ -174,3 +175,30 @@ def portfolio_reports(portfolio_id):
expiration_date=expiration_date,
remaining_days=remaining_days,
)
@portfolios_bp.route(
"/portfolios/<portfolio_id>/members/<member_id>/delete", methods=["POST"]
)
@user_can(Permissions.EDIT_PORTFOLIO_USERS, message="update portfolio members")
def remove_member(portfolio_id, member_id):
if member_id == str(g.current_user.id):
raise UnauthorizedError(
user=user, message="you cant remove yourself from the portfolio"
)
portfolio = Portfolios.get(g.current_user, portfolio_id)
portfolio_role = PortfolioRoles.get(portfolio_id=portfolio_id, user_id=member_id)
PortfolioRoles.disable(portfolio_role=portfolio_role)
flash("portfolio_member_removed", member_name=portfolio_role.user.full_name)
return redirect(
url_for(
"portfolios.portfolio_admin",
portfolio_id=portfolio.id,
_anchor="portfolio-members",
fragment="portfolio-members",
)
)

View File

@ -138,6 +138,11 @@ MESSAGES = {
""",
"category": "error",
},
"portfolio_member_removed": {
"title_template": "Portfolio Member Removed",
"message_template": "You have successfully removed {{ member_name }} from the portfolio.",
"category": "success",
},
}

View File

@ -291,6 +291,10 @@
height: 4rem;
}
.usa-button-danger {
background: $color-red;
}
.members-table-footer {
float: right;
padding: 3 * $gap;

View File

@ -1,3 +1,7 @@
{% from "components/confirmation_button.html" import ConfirmationButton %}
{% set modal_id = "portfolio_id_{}_user_id_{}".format(portfolio.id, user.id) %}
{% for subform in member_perms_form.members_permissions %}
<tr>
<td class='name'>{{ subform.member.data }}
@ -14,7 +18,10 @@
<td>{{ OptionsInput(subform.perms_reporting, label=False) }}</td>
<td>{{ OptionsInput(subform.perms_portfolio_mgmt, label=False) }}</td>
<td><button type="button" class='{{ archive_button_class }}'>{{ "portfolios.members.archive_button" | translate }}</button>
<td>
<a v-on:click="openModal('{{ modal_id }}')" class='usa-button {{ archive_button_class }}'>
{{ "portfolios.members.archive_button" | translate }}
</a>
</td>
{{ subform.user_id() }}
</tr>

View File

@ -1,6 +1,9 @@
{% from "components/icon.html" import Icon %}
{% from "components/options_input.html" import OptionsInput %}
{% from "components/modal.html" import Modal %}
{% from "components/alert.html" import Alert %}
<section class="member-list" id="portfolio-members">
<div class='responsive-table-wrapper panel'>
{% if g.matchesPath("portfolio-members") %}
@ -51,6 +54,34 @@
{% endif %}
</form>
{% if user_can(permissions.EDIT_PORTFOLIO_USERS) %}
{% for member in portfolio.members %}
{% set modal_id = "portfolio_id_{}_user_id_{}".format(portfolio.id, user.id) %}
{% call Modal(name=modal_id, dismissable=False) %}
<h1>Are you sure you want to archive this user?</h1>
{{
Alert(
title="Warning! You are about to archive a user from the portfolio admin.",
message="User will be removed from the portfolio, but their log history will be retained.",
level="warning"
)
}}
<div class="action-group">
<form method="POST" action="{{ url_for('portfolios.remove_member', portfolio_id=portfolio.id, member_id=member.user_id) }}">
{{ member_perms_form.csrf_token }}
<button class="usa-button usa-button-danger">
{{ "portfolios.members.archive_button" | translate }}
</button>
</form>
<a v-on:click="closeModal('d')">Cancel</a>
</div>
{% endcall %}
{% endfor %}
{% endif %}
<div class="members-table-footer">
<div class="action-group">
{% if user_can(permissions.EDIT_PORTFOLIO_USERS) %}

View File

@ -29,3 +29,11 @@ def test_add_portfolio_role_with_permission_sets():
]
actual_names = [prms.name for prms in port_role.permission_sets]
assert expected_names == expected_names
def test_disable_portfolio_role():
portfolio_role = PortfolioRoleFactory.create(status=PortfolioRoleStatus.ACTIVE)
assert portfolio_role.status == PortfolioRoleStatus.ACTIVE
PortfolioRoles.disable(portfolio_role=portfolio_role)
assert portfolio_role.status == PortfolioRoleStatus.DISABLED

View File

@ -2,6 +2,8 @@ from flask import url_for
from atst.domain.permission_sets import PermissionSets
from atst.models.permissions import Permissions
from atst.domain.portfolio_roles import PortfolioRoles
from atst.models.portfolio_role import Status as PortfolioRoleStatus
from tests.factories import (
random_future_date,
@ -81,6 +83,56 @@ def test_portfolio_admin_screen_when_not_ppoc(client, user_session):
assert translate("fragments.ppoc.update_btn").encode("utf8") not in response.data
def test_remove_portfolio_member(client, user_session):
portfolio = PortfolioFactory.create()
user = UserFactory.create()
PortfolioRoleFactory.create(portfolio=portfolio, user=user)
user_session(portfolio.owner)
response = client.post(
url_for(
"portfolios.remove_member", portfolio_id=portfolio.id, member_id=user.id
),
follow_redirects=False,
)
assert response.status_code == 302
assert response.headers["Location"] == url_for(
"portfolios.portfolio_admin",
portfolio_id=portfolio.id,
_anchor="portfolio-members",
fragment="portfolio-members",
_external=True,
)
assert (
PortfolioRoles.get(portfolio_id=portfolio.id, user_id=user.id).status
== PortfolioRoleStatus.DISABLED
)
def test_remove_portfolio_member_self(client, user_session):
portfolio = PortfolioFactory.create()
user_session(portfolio.owner)
response = client.post(
url_for(
"portfolios.remove_member",
portfolio_id=portfolio.id,
member_id=portfolio.owner.id,
),
follow_redirects=False,
)
assert response.status_code == 500
assert (
PortfolioRoles.get(portfolio_id=portfolio.id, user_id=portfolio.owner.id).status
== PortfolioRoleStatus.ACTIVE
)
def test_portfolio_reports(client, user_session):
portfolio = PortfolioFactory.create(
applications=[