Merge pull request #294 from dod-ccpo/edit-workspace-#160300728
Edit workspace #160300728
This commit is contained in:
commit
f984c95060
@ -0,0 +1,43 @@
|
||||
"""add edit workspace information permission
|
||||
|
||||
Revision ID: 4c425f17bfe8
|
||||
Revises: 2572be7fb7fc
|
||||
Create Date: 2018-09-17 13:14:38.781744
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
from sqlalchemy.orm.session import Session
|
||||
|
||||
from atst.models.role import Role
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '4c425f17bfe8'
|
||||
down_revision = '2572be7fb7fc'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
session = Session(bind=op.get_bind())
|
||||
|
||||
owner_and_admin = session.query(Role).filter(Role.name.in_(["owner", "admin"])).all()
|
||||
for role in owner_and_admin:
|
||||
role.add_permission(Permissions.EDIT_WORKSPACE_INFORMATION)
|
||||
session.add(role)
|
||||
|
||||
session.flush()
|
||||
session.commit()
|
||||
|
||||
|
||||
def downgrade():
|
||||
session = Session(bind=op.get_bind())
|
||||
|
||||
owner_and_admin = session.query(Role).filter(Role.name.in_(["owner", "admin"])).all()
|
||||
for role in owner_and_ccpo:
|
||||
role.remove_permission(Permissions.EDIT_WORKSPACE_INFORMATION)
|
||||
session.add(role)
|
||||
|
||||
session.flush()
|
||||
session.commit()
|
@ -27,7 +27,7 @@ class Workspaces(object):
|
||||
return ScopedWorkspace(user, workspace)
|
||||
|
||||
@classmethod
|
||||
def get_for_update(cls, user, workspace_id):
|
||||
def get_for_update_projects(cls, user, workspace_id):
|
||||
workspace = WorkspacesQuery.get(workspace_id)
|
||||
Authorization.check_workspace_permission(
|
||||
user, workspace, Permissions.ADD_APPLICATION_IN_WORKSPACE, "add project"
|
||||
@ -35,6 +35,18 @@ class Workspaces(object):
|
||||
|
||||
return workspace
|
||||
|
||||
@classmethod
|
||||
def get_for_update_information(cls, user, workspace_id):
|
||||
workspace = WorkspacesQuery.get(workspace_id)
|
||||
Authorization.check_workspace_permission(
|
||||
user,
|
||||
workspace,
|
||||
Permissions.EDIT_WORKSPACE_INFORMATION,
|
||||
"update workspace information",
|
||||
)
|
||||
|
||||
return workspace
|
||||
|
||||
@classmethod
|
||||
def get_by_request(cls, request):
|
||||
return WorkspacesQuery.get_by_request(request)
|
||||
@ -98,3 +110,10 @@ class Workspaces(object):
|
||||
workspace_role = WorkspacesQuery.create_workspace_role(user, role, workspace)
|
||||
WorkspacesQuery.add_and_commit(workspace_role)
|
||||
return workspace_role
|
||||
|
||||
@classmethod
|
||||
def update(cls, workspace, new_data):
|
||||
if "name" in new_data:
|
||||
workspace.name = new_data["name"]
|
||||
|
||||
WorkspacesQuery.add_and_commit(workspace)
|
||||
|
17
atst/forms/workspace.py
Normal file
17
atst/forms/workspace.py
Normal file
@ -0,0 +1,17 @@
|
||||
from wtforms.fields import StringField
|
||||
from wtforms.validators import Length
|
||||
|
||||
from .forms import ValidatedForm
|
||||
|
||||
|
||||
class WorkspaceForm(ValidatedForm):
|
||||
name = StringField(
|
||||
"Workspace Name",
|
||||
validators=[
|
||||
Length(
|
||||
min=4,
|
||||
max=50,
|
||||
message="Workspace names must be at least 4 and not more than 50 characters",
|
||||
)
|
||||
],
|
||||
)
|
@ -20,6 +20,7 @@ class Permissions(object):
|
||||
VIEW_ASSIGNED_ATAT_ROLE_CONFIGURATIONS = "view_assigned_atat_role_configurations"
|
||||
VIEW_ASSIGNED_CSP_ROLE_CONFIGURATIONS = "view_assigned_csp_role_configurations"
|
||||
|
||||
EDIT_WORKSPACE_INFORMATION = "edit_workspace_information"
|
||||
DEACTIVATE_WORKSPACE = "deactivate_workspace"
|
||||
VIEW_ATAT_PERMISSIONS = "view_atat_permissions"
|
||||
TRANSFER_OWNERSHIP_OF_WORKSPACE = "transfer_ownership_of_workspace"
|
||||
|
@ -17,6 +17,7 @@ from atst.domain.workspace_users import WorkspaceUsers
|
||||
from atst.forms.new_project import NewProjectForm
|
||||
from atst.forms.new_member import NewMemberForm
|
||||
from atst.forms.edit_member import EditMemberForm
|
||||
from atst.forms.workspace import WorkspaceForm
|
||||
from atst.domain.authz import Authorization
|
||||
from atst.models.permissions import Permissions
|
||||
|
||||
@ -50,12 +51,32 @@ def workspaces():
|
||||
return render_template("workspaces/index.html", page=5, workspaces=workspaces)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/edit")
|
||||
def workspace(workspace_id):
|
||||
workspace = Workspaces.get_for_update_information(g.current_user, workspace_id)
|
||||
form = WorkspaceForm(data={"name": workspace.name})
|
||||
return render_template("workspaces/edit.html", form=form, workspace=workspace)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/projects")
|
||||
def workspace_projects(workspace_id):
|
||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||
return render_template("workspaces/projects/index.html", workspace=workspace)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/edit", methods=["POST"])
|
||||
def edit_workspace(workspace_id):
|
||||
workspace = Workspaces.get_for_update_information(g.current_user, workspace_id)
|
||||
form = WorkspaceForm(http_request.form)
|
||||
if form.validate():
|
||||
Workspaces.update(workspace, form.data)
|
||||
return redirect(
|
||||
url_for("workspaces.workspace_projects", workspace_id=workspace.id)
|
||||
)
|
||||
else:
|
||||
return render_template("workspaces/edit.html", form=form, workspace=workspace)
|
||||
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>")
|
||||
def show_workspace(workspace_id):
|
||||
return redirect(url_for("workspaces.workspace_projects", workspace_id=workspace_id))
|
||||
@ -98,7 +119,7 @@ def workspace_reports(workspace_id):
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/projects/new")
|
||||
def new_project(workspace_id):
|
||||
workspace = Workspaces.get_for_update(g.current_user, workspace_id)
|
||||
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
|
||||
form = NewProjectForm()
|
||||
return render_template(
|
||||
"workspaces/projects/new.html", workspace=workspace, form=form
|
||||
@ -107,7 +128,7 @@ def new_project(workspace_id):
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/projects/new", methods=["POST"])
|
||||
def create_project(workspace_id):
|
||||
workspace = Workspaces.get_for_update(g.current_user, workspace_id)
|
||||
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
|
||||
form = NewProjectForm(http_request.form)
|
||||
|
||||
if form.validate():
|
||||
@ -130,7 +151,7 @@ def create_project(workspace_id):
|
||||
|
||||
@bp.route("/workspaces/<workspace_id>/projects/<project_id>/edit")
|
||||
def edit_project(workspace_id, project_id):
|
||||
workspace = Workspaces.get_for_update(g.current_user, workspace_id)
|
||||
workspace = Workspaces.get_for_update_projects(g.current_user, workspace_id)
|
||||
project = Projects.get(g.current_user, workspace, project_id)
|
||||
form = NewProjectForm(
|
||||
name=project.name,
|
||||
|
@ -78,5 +78,11 @@ export default {
|
||||
match: /[0-9]{2}\w?$/,
|
||||
unmask: [],
|
||||
validationError: 'Please enter a valid BA Code. Note that it should be two digits, followed by a letter.'
|
||||
}
|
||||
},
|
||||
workspaceName: {
|
||||
mask: false,
|
||||
match: /^.{4,50}$/,
|
||||
unmask: [],
|
||||
validationError: 'Workspace names must be at least 4 and not more than 50 characters'
|
||||
},
|
||||
}
|
||||
|
@ -194,6 +194,7 @@
|
||||
&--validation {
|
||||
|
||||
&--anything,
|
||||
&--workspaceName,
|
||||
&--email {
|
||||
input {
|
||||
max-width: 30em;
|
||||
|
@ -35,5 +35,15 @@
|
||||
href=url_for("workspaces.workspace_reports", workspace_id=workspace.id),
|
||||
active=request.url_rule.rule.startswith('/workspaces/<workspace_id>/reports')
|
||||
) }}
|
||||
|
||||
{% if user_can(permissions.EDIT_WORKSPACE_INFORMATION) %}
|
||||
{{ SidenavItem(
|
||||
"Workspace Settings",
|
||||
href=url_for("workspaces.workspace", workspace_id=workspace.id),
|
||||
active=request.url_rule.rule.startswith('/workspaces/<workspace_id>/edit'),
|
||||
subnav=None
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
43
templates/workspaces/edit.html
Normal file
43
templates/workspaces/edit.html
Normal file
@ -0,0 +1,43 @@
|
||||
{% extends "workspaces/base.html" %}
|
||||
|
||||
{% from "components/icon.html" import Icon %}
|
||||
{% from "components/alert.html" import Alert %}
|
||||
{% from "components/text_input.html" import TextInput %}
|
||||
|
||||
|
||||
{% block workspace_content %}
|
||||
|
||||
{% if form.errors %}
|
||||
{{ Alert('There were some errors',
|
||||
message="<p>Please see below.</p>",
|
||||
level='error'
|
||||
) }}
|
||||
{% endif %}
|
||||
|
||||
<form method="POST" action="{{ url_for('workspaces.edit_workspace', workspace_id=workspace.id) }}" autocomplete="false">
|
||||
{{ form.csrf_token }}
|
||||
|
||||
<div class="panel">
|
||||
|
||||
<div class="panel__heading">
|
||||
<h1>Workspace Settings</h1>
|
||||
</div>
|
||||
|
||||
<div class="panel__content">
|
||||
{{ TextInput(form.name, validation="workspaceName") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class='action-group'>
|
||||
<button type="submit" class="usa-button usa-button-big usa-button-primary" tabindex="0">Save</button>
|
||||
<a href='{{ url_for("workspaces.workspace_projects", workspace_id=workspace.id) }}' class='action-group__action icon-link'>
|
||||
{{ Icon('x') }}
|
||||
<span>Cancel</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@ -63,16 +63,16 @@ def test_workspaces_get_ensures_user_is_in_workspace(workspace, workspace_owner)
|
||||
Workspaces.get(outside_user, workspace.id)
|
||||
|
||||
|
||||
def test_get_for_update_allows_owner(workspace, workspace_owner):
|
||||
Workspaces.get_for_update(workspace_owner, workspace.id)
|
||||
def test_get_for_update_projects_allows_owner(workspace, workspace_owner):
|
||||
Workspaces.get_for_update_projects(workspace_owner, workspace.id)
|
||||
|
||||
|
||||
def test_get_for_update_blocks_developer(workspace):
|
||||
def test_get_for_update_projects_blocks_developer(workspace):
|
||||
developer = UserFactory.create()
|
||||
WorkspaceUsers.add(developer, workspace.id, "developer")
|
||||
|
||||
with pytest.raises(UnauthorizedError):
|
||||
Workspaces.get_for_update(developer, workspace.id)
|
||||
Workspaces.get_for_update_projects(developer, workspace.id)
|
||||
|
||||
|
||||
def test_can_create_workspace_user(workspace, workspace_owner):
|
||||
@ -234,3 +234,19 @@ def test_for_user_returns_all_workspaces_for_ccpo(workspace, workspace_owner):
|
||||
|
||||
sams_workspaces = Workspaces.for_user(sam)
|
||||
assert len(sams_workspaces) == 2
|
||||
|
||||
|
||||
def test_get_for_update_information():
|
||||
workspace_owner = UserFactory.create()
|
||||
workspace = Workspaces.create(RequestFactory.create(creator=workspace_owner))
|
||||
owner_ws = Workspaces.get_for_update_information(workspace_owner, workspace.id)
|
||||
assert workspace == owner_ws
|
||||
|
||||
admin = UserFactory.create()
|
||||
Workspaces.add_member(workspace, admin, "admin")
|
||||
admin_ws = Workspaces.get_for_update_information(admin, workspace.id)
|
||||
assert workspace == admin_ws
|
||||
|
||||
ccpo = UserFactory.from_atat_role("ccpo")
|
||||
with pytest.raises(UnauthorizedError):
|
||||
Workspaces.get_for_update_information(ccpo, workspace.id)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from flask import url_for
|
||||
|
||||
from tests.factories import UserFactory, WorkspaceFactory
|
||||
from atst.domain.workspaces import Workspaces
|
||||
from atst.models.workspace_user import WorkspaceUser
|
||||
@ -51,3 +53,17 @@ def test_user_without_permission_has_no_add_member_link(client, user_session):
|
||||
'href="/workspaces/{}/members/new"'.format(workspace.id).encode()
|
||||
not in response.data
|
||||
)
|
||||
|
||||
|
||||
def test_update_workspace_name(client, user_session):
|
||||
user = UserFactory.create()
|
||||
workspace = WorkspaceFactory.create()
|
||||
Workspaces._create_workspace_role(user, workspace, "admin")
|
||||
user_session(user)
|
||||
response = client.post(
|
||||
url_for("workspaces.edit_workspace", workspace_id=workspace.id),
|
||||
data={"name": "a cool new name"},
|
||||
follow_redirects=True,
|
||||
)
|
||||
assert response.status_code == 200
|
||||
assert workspace.name == "a cool new name"
|
||||
|
Loading…
x
Reference in New Issue
Block a user