Skip to content

Commit

Permalink
Feature/member edit ux (#698)
Browse files Browse the repository at this point in the history
Co-authored-by: Mark Janssen <[email protected]>
Co-authored-by: HeleenSG <[email protected]>
Co-authored-by: Jeroen Dekkers <[email protected]>
Co-authored-by: Patrick <[email protected]>
  • Loading branch information
5 people authored Apr 24, 2023
1 parent 977cfd5 commit af08d69
Show file tree
Hide file tree
Showing 29 changed files with 296 additions and 121 deletions.
1 change: 1 addition & 0 deletions rocky/.ci/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ services:
volumes:
- .:/app/rocky
- ./.ci/run_rocky.sh:/app/run_rocky.sh
- ../octopoes/octopoes:/app/rocky/octopoes
env_file:
- .ci/.env.test

Expand Down
24 changes: 17 additions & 7 deletions rocky/account/forms/account_setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.contrib.auth.password_validation import validate_password
from django.utils.translation import gettext_lazy as _
from tools.enums import SCAN_LEVEL
from tools.forms.base import BaseRockyForm
from tools.forms.base import BaseRockyForm, BaseRockyModelForm
from tools.models import (
GROUP_ADMIN,
GROUP_CLIENT,
Expand Down Expand Up @@ -135,7 +135,7 @@ def set_user(self):
)


class OrganizationMemberAddForm(UserAddForm, forms.ModelForm):
class OrganizationMemberAddForm(UserAddForm, BaseRockyModelForm):
"""
Form to add a new member
"""
Expand Down Expand Up @@ -172,18 +172,28 @@ class Meta:
fields = ("account_type", "name", "email", "password")


class OrganizationMemberEditForm(forms.ModelForm):
class OrganizationMemberEditForm(BaseRockyModelForm):
trusted_clearance_level = forms.ChoiceField(
required=False,
label=_("Trusted clearance level"),
label=_("Assigned clearance level"),
choices=[(-1, "")] + SCAN_LEVEL.choices,
help_text=_("Select a clearance level you trust this member with."),
widget=forms.RadioSelect(attrs={"radio_paws": True}),
)

blocked = forms.BooleanField(
required=False,
label=_("Blocked"),
help_text=_("Set the members status to blocked, so they don't have access to the organization anymore."),
widget=forms.CheckboxInput(),
)

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

self.fields["blocked"].widget.attrs["field_form_label"] = "Status"
if self.instance.user.is_superuser:
self.fields["trusted_clearance_level"].disabled = True
self.fields["acknowledged_clearance_level"].label = _("Accepted clearance level")
self.fields["acknowledged_clearance_level"].required = False
self.fields["acknowledged_clearance_level"].widget.attrs[
"fixed_paws"
Expand All @@ -202,10 +212,10 @@ def save(self, commit=True):

class Meta:
model = OrganizationMember
fields = ["status", "trusted_clearance_level", "acknowledged_clearance_level"]
fields = ["blocked", "trusted_clearance_level", "acknowledged_clearance_level"]


class OrganizationForm(forms.ModelForm):
class OrganizationForm(BaseRockyModelForm):
"""
Form to create a new organization.
"""
Expand Down
2 changes: 1 addition & 1 deletion rocky/account/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def setup(self, request, *args, **kwargs):
except OrganizationMember.DoesNotExist:
raise Http404()

if self.organization_member.status == OrganizationMember.STATUSES.BLOCKED:
if self.organization_member.blocked:
raise PermissionDenied()

self.octopoes_api_connector = OctopoesAPIConnector(settings.OCTOPOES_API, organization_code)
Expand Down
5 changes: 1 addition & 4 deletions rocky/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,7 @@ def organizations(self) -> List[Organization]:
"""
if self.is_superuser:
return self.all_organizations
return [
m.organization
for m in filter(lambda o: o.status is not OrganizationMember.STATUSES.BLOCKED, self.organization_members)
]
return [m.organization for m in self.organization_members if not m.blocked]

@cached_property
def organizations_including_blocked(self) -> List[Organization]:
Expand Down
2 changes: 2 additions & 0 deletions rocky/assets/src/bundles/app/css/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
@import "vendors/graph-override.scss";
@import "vendors/two-factor.scss";
@import "vendors/manon-overrides/dl.scss";
@import "vendors/manon-overrides/form-radio.scss";
@import "vendors/manon-overrides/form-fieldset-required.scss";
@import "vendors/manon-overrides/link.scss";
@import "vendors/manon-overrides/tile.scss";

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* Form fieldset - Variables */

:root {
--form-horizontal-view-fieldset-label-margin-top: 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* Form fieldset - Variables */

:root {
--form-radio-margin: 0.25rem var(--form-radio-margin-right) 0.25rem 0;
}
3 changes: 3 additions & 0 deletions rocky/assets/src/bundles/app/css/themes/soft/manon/form.scss
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,7 @@

/* Fieldset */
--form-fieldset-legend-font-weight: 600;

/* Nota bene */
--form-horizontal-view-fieldset-nota-bene-required-margin-bottom: 0.5rem;
}
2 changes: 2 additions & 0 deletions rocky/assets/src/bundles/app/css/themes/soft/soft.scss
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
@import "manon/button-icon"; /* Icon button */
@import "manon/description-list";
@import "manon/filter";
@import "manon/form-fieldset";
@import "manon/form-radio";
@import "manon/layout";
@import "manon/login-meta";
@import "manon/navigation";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/* Form fieldset required */

form.horizontal-view div.required {

label {
margin-top: calc(var(--form-horizontal-view-fieldset-nota-bene-required-margin-bottom) + var(--form-input-min-height) /2);
}

.help-button {
margin-top: calc(var(--form-horizontal-view-fieldset-nota-bene-required-margin-bottom) + var(--form-input-min-height) /2);
}

select + div + .help-button {
margin-top: 2rem;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/* Form radio */

form input[type="radio"] {
margin: var(--form-radio-margin);
}
2 changes: 2 additions & 0 deletions rocky/onboarding/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ def post(self, request, *args, **kwargs):
def set_member_onboarded(self):
member = OrganizationMember.objects.get(user=self.request.user, organization=self.organization)
member.onboarded = True
member.status = OrganizationMember.STATUSES.ACTIVE
member.save()


Expand Down Expand Up @@ -648,5 +649,6 @@ def get(self, request, *args, **kwargs):
redteam_group.user_set.add(self.request.user)
return redirect(reverse("step_introduction", kwargs={"organization_code": self.organization.code}))
member.onboarded = True
member.status = OrganizationMember.STATUSES.ACTIVE
member.save()
return redirect(reverse("crisis_room"))
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% load static %}

{% block content %}
{% include "header.html" with view_type="onboarding" %}
{% include "header.html" %}

<main id="main-content">
<section>
Expand Down
28 changes: 17 additions & 11 deletions rocky/rocky/templates/organizations/organization_member_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
<div>
<h1>{% translate "Organization" %}: {{ organization.name }}</h1>
{% blocktranslate with organization_name=organization.name %}
An overview of "{{ organization_name }}" its members.
{% endblocktranslate %}
An overview of "{{ organization_name }}" its members.
{% endblocktranslate %}
<h2>{% translate "Members" %}</h2>
{% if perms.tools.add_organizationmember %}
<div class="horizontal-view toolbar">
Expand All @@ -33,7 +33,7 @@ <h2>{% translate "Members" %}</h2>
<th>{% translate "Status" %}</th>
<th>{% translate "Added" %}</th>
<th>{% translate "Assigned clearance level" %}</th>
<th>{% translate "Agreed clearance level" %}</th>
<th>{% translate "Accepted clearance level" %}</th>
<th>{% translate "Edit" %}</th>
<th>{% translate "Blocked" %}</th>
</tr>
Expand All @@ -60,7 +60,8 @@ <h2>{% translate "Members" %}</h2>
{% if member.user.is_superuser %}
<span class="active"></span><span>&nbsp;{% translate "Active" %}</span>
{% else %}
<span class="{{ member.status }}"></span><span>&nbsp;{{ member.status|title }}</span>
<span class="{% if member.blocked %}blocked{% else %}{{ member.status }}{% endif %}"></span>
<span>&nbsp;{% if member.blocked %}{% translate "Blocked" %}{% else %}{{ member.status|title }}{% endif %}</span>
{% endif %}
<br>
</td>
Expand Down Expand Up @@ -88,17 +89,22 @@ <h2>{% translate "Members" %}</h2>
{% endif %}
</td>
<td>
<a href="{% url "organization_member_edit" organization.code member.id %}">
<button class="icon ti-edit action-button">{% translate "Edit" %}</button>
</a>
{% if member.user.is_superuser %}
<button class="icon ti-edit action-button disabled">{% translate "Edit" %}</button>
{% else %}
<a href="{% url "organization_member_edit" organization.code member.id %}"><button class="icon ti-edit action-button">{% translate "Edit" %}</button></a>
{% endif %}
</td>
<td>
{% if member.status == "blocked" %}
{% if member.blocked %}
{% include "partials/single_action_checkbox_form.html" with input_checked=member.blocked input_disabled=member.user.is_superuser action="unblock" key="member_id" value=member.id %}

{% else %}
{% include "partials/single_action_checkbox_form.html" with input_checked=member.blocked input_disabled=member.user.is_superuser action="block" key="member_id" value=member.id %}

{% comment %} Disable the "block" checkbox for members that are super users or for the member of the user that's logged in so they can't lock themself out of the organisation by accident{% endcomment %}
{% if member.user.is_superuser or member.user.email == request.user.email %}
{% include "partials/single_action_checkbox_form.html" with input_checked=member.blocked input_disabled="disabled" action="block" key="member_id" value=member.id %}
{% else %}
{% include "partials/single_action_checkbox_form.html" with input_checked=member.blocked action="block" key="member_id" value=member.id %}
{% endif %}
{% endif %}
</td>
</tr>
Expand Down
71 changes: 35 additions & 36 deletions rocky/rocky/templates/partials/form/field_input.html
Original file line number Diff line number Diff line change
@@ -1,46 +1,45 @@
{% load i18n %}

<fieldset>
<legend>
{{ field.label_tag }}
</legend>
{% if form_view != "vertical" %}
<div {% if field.field.required %} class="required"{% endif %}>
{% if field.field.required %}
<span class="nota-bene">{% translate "This field is required" %}</span>
{% endif %}
{% if not field.field.widget.attrs.fixed_paws %}
{{ field }}
{% elif field.field.widget.attrs.fixed_paws < 0 %}
{% translate "Unset" %}
{% else %}
{% include "partials/scan_level_indicator.html" with value=field.field.widget.attrs.fixed_paws custom_class=field.field.widget.attrs.class %}
<div {% if field.field.required %} class="required"{% endif %}>
{{ field.label_tag }}

{% endif %}
{% include "partials/form/field_input_help_text.html" with help_text=field.help_text %}
{% include "partials/form/field_input_errors.html" %}

{% if form_name == 'login' and field.name == "username" %}
<div class="input-link">
<a href="{% url 'recover_email' %}">{% translate "Forgot email" %}</a>
</div>
{% endif %}
{% if form_name == 'login' and field.name == "password" %}
<div class="input-link">
<a href="{% url 'password_reset' %}">{% translate "Forgot password" %}</a>
</div>
{% endif %}
</div>
</fieldset>
{% else %}
{{ field.label_tag }}
<div {% if field.field.required %} class="required"{% endif %}>
{% if form_view != "vertical" %}
<div>
{% if field.field.required %}
<span class="nota-bene">{% translate "This field is required" %}</span>
{% endif %}
{{ field }}
{% if not field.field.widget.attrs.fixed_paws %}
{{ field }}
{% elif field.field.widget.attrs.fixed_paws < 0 %}
{% translate "Not set" %}
{% else %}
{% include "partials/scan_level_indicator.html" with value=field.field.widget.attrs.fixed_paws custom_class=field.field.widget.attrs.class %}
{% endif %}

{% include "partials/form/field_input_help_text.html" with help_text=field.help_text %}
{% include "partials/form/field_input_errors.html" %}

{% if form_name == "login" and field.name == "username" %}
<div class="input-link">
<a href="{% url "recover_email" %}">{% translate "Forgot email" %}</a>
</div>
{% endif %}
{% if form_name == "login" and field.name == "password" %}
<div class="input-link">
<a href="{% url "password_reset" %}">{% translate "Forgot password" %}</a>
</div>
{% endif %}
</div>
{% endif %}
{% else %}
<div>
{% if field.field.required %}
<span class="nota-bene">{% translate "This field is required" %}</span>
{% endif %}

{{ field }}

{% include "partials/form/field_input_help_text.html" with help_text=field.help_text %}
{% include "partials/form/field_input_errors.html" %}
</div>
{% endif %}
</div>
27 changes: 15 additions & 12 deletions rocky/rocky/templates/partials/form/field_input_checkbox.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

{% if field.field.widget.allow_multiple_selected %}
{% include "partials/form/field_input.html" %}

{% else %}
<div class="checkbox {% if field.field.required %}required{% endif %}">
{% if field.field.required %}
<span class="nota-bene">{% translate "This field is required" %}</span>
{% endif %}
<div class="horizontal-view">
{{ field }}
{{ field.label_tag }}
</div>
{% include "partials/form/field_input_help_text.html" with help_text=field.help_text %}
{% include "partials/form/field_input_errors.html" %}
<div class="{% if field.field.required %}required{% endif %}">
<label>
{{ field.field.widget.attrs.field_form_label }}
</label>
<div>
{% if field.field.required %}
<span class="nota-bene">{% translate "This field is required" %}</span>
{% endif %}

{{ field }}
{{ field.label_tag }}

</div>
{% include "partials/form/field_input_help_text.html" with help_text=field.help_text %}
{% include "partials/form/field_input_errors.html" %}
</div>
</div>
{% endif %}
10 changes: 4 additions & 6 deletions rocky/rocky/templates/partials/form/field_input_radio.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<fieldset>
<legend>
{{ field.label_tag }}
</legend>
<div>
{{ field.label_tag }}

{% for choice in field %}
<div class="horizontal-view">
<input type="{% if choice.data.value < 0 %}hidden{% else %}radio{% endif %}"
Expand All @@ -13,7 +12,6 @@
<label>
{% if choice.data.attrs.radio_paws %}
{% include "partials/scan_level_indicator.html" with value=choice.data.value custom_class=choice.data.attrs.class %}

{% else %}
{{ choice.choice_label }}
{% endif %}
Expand All @@ -23,4 +21,4 @@

</div>
{% endfor %}
</fieldset>
</div>
2 changes: 0 additions & 2 deletions rocky/rocky/templates/partials/form/field_input_wrapper.html
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
{# include template for input type, with fallback field_input.html #}
{% if field.field.widget.input_type in "checkbox,radio,hidden" %}
{% include "partials/form/field_input_"|add:field.field.widget.input_type|add:".html" %}

{% else %}
{% include "partials/form/field_input.html" %}

{% endif %}
1 change: 0 additions & 1 deletion rocky/rocky/templates/partials/form/fieldset.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
{% for field in fields %}
{% if not fieldset or field.name in fieldset.split %}
{% include "partials/form/field_input_wrapper.html" %}

{% endif %}
{% endfor %}
</fieldset>
Loading

0 comments on commit af08d69

Please sign in to comment.