Compare commits
No commits in common. "714cd9be5452adf635b4e602c19c83b07e63074b" and "ce34afa10a8dd18aebaa13666bc3eff4180c76e4" have entirely different histories.
714cd9be54
...
ce34afa10a
9 changed files with 55 additions and 211 deletions
|
|
@ -328,8 +328,9 @@ class CrdModelFormMixin:
|
||||||
field.required = False
|
field.required = False
|
||||||
|
|
||||||
# Mark advanced fields with a CSS class and data attribute
|
# Mark advanced fields with a CSS class and data attribute
|
||||||
|
advanced_fields = getattr(self, "ADVANCED_FIELDS", [])
|
||||||
for name, field in self.fields.items():
|
for name, field in self.fields.items():
|
||||||
if self.is_field_advanced(name):
|
if name in advanced_fields:
|
||||||
field.widget.attrs.update(
|
field.widget.attrs.update(
|
||||||
{
|
{
|
||||||
"class": (
|
"class": (
|
||||||
|
|
@ -355,17 +356,6 @@ class CrdModelFormMixin:
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_field_advanced(self, field_name):
|
|
||||||
advanced_fields = getattr(self, "ADVANCED_FIELDS", [])
|
|
||||||
return field_name in advanced_fields or any(
|
|
||||||
field_name.startswith(f"{af}.") for af in advanced_fields
|
|
||||||
)
|
|
||||||
|
|
||||||
def are_all_fields_advanced(self, field_list):
|
|
||||||
if not field_list:
|
|
||||||
return False
|
|
||||||
return all(self.is_field_advanced(field_name) for field_name in field_list)
|
|
||||||
|
|
||||||
def get_fieldsets(self):
|
def get_fieldsets(self):
|
||||||
fieldsets = []
|
fieldsets = []
|
||||||
|
|
||||||
|
|
@ -381,7 +371,6 @@ class CrdModelFormMixin:
|
||||||
"fields": general_fields,
|
"fields": general_fields,
|
||||||
"fieldsets": [],
|
"fieldsets": [],
|
||||||
"has_mandatory": self.has_mandatory_fields(general_fields),
|
"has_mandatory": self.has_mandatory_fields(general_fields),
|
||||||
"is_advanced": self.are_all_fields_advanced(general_fields),
|
|
||||||
}
|
}
|
||||||
if all(
|
if all(
|
||||||
[
|
[
|
||||||
|
|
@ -448,9 +437,6 @@ class CrdModelFormMixin:
|
||||||
title = f"{fieldset['title']}: {sub_fieldset['title']}: "
|
title = f"{fieldset['title']}: {sub_fieldset['title']}: "
|
||||||
for field in sub_fieldset["fields"]:
|
for field in sub_fieldset["fields"]:
|
||||||
self.strip_title(field, title)
|
self.strip_title(field, title)
|
||||||
sub_fieldset["is_advanced"] = self.are_all_fields_advanced(
|
|
||||||
sub_fieldset["fields"]
|
|
||||||
)
|
|
||||||
nested_fieldsets_list.append(sub_fieldset)
|
nested_fieldsets_list.append(sub_fieldset)
|
||||||
|
|
||||||
fieldset["fieldsets"] = nested_fieldsets_list
|
fieldset["fieldsets"] = nested_fieldsets_list
|
||||||
|
|
@ -467,8 +453,6 @@ class CrdModelFormMixin:
|
||||||
all_fields.extend(sub_fieldset["fields"])
|
all_fields.extend(sub_fieldset["fields"])
|
||||||
fieldset["has_mandatory"] = self.has_mandatory_fields(all_fields)
|
fieldset["has_mandatory"] = self.has_mandatory_fields(all_fields)
|
||||||
|
|
||||||
fieldset["is_advanced"] = self.are_all_fields_advanced(all_fields)
|
|
||||||
|
|
||||||
fieldsets.append(fieldset)
|
fieldsets.append(fieldset)
|
||||||
|
|
||||||
# Add 'others' tab if there are any fields
|
# Add 'others' tab if there are any fields
|
||||||
|
|
@ -479,7 +463,6 @@ class CrdModelFormMixin:
|
||||||
"fields": others,
|
"fields": others,
|
||||||
"fieldsets": [],
|
"fieldsets": [],
|
||||||
"has_mandatory": self.has_mandatory_fields(others),
|
"has_mandatory": self.has_mandatory_fields(others),
|
||||||
"is_advanced": self.are_all_fields_advanced(others),
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import secrets
|
||||||
|
|
||||||
import rules
|
import rules
|
||||||
import urlman
|
import urlman
|
||||||
from auditlog.registry import auditlog
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.sites.shortcuts import get_current_site
|
from django.contrib.sites.shortcuts import get_current_site
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
|
|
@ -468,7 +467,6 @@ class OrganizationInvitation(ServalaModelMixin, models.Model):
|
||||||
|
|
||||||
class urls(urlman.Urls):
|
class urls(urlman.Urls):
|
||||||
accept = "/invitations/{self.secret}/accept/"
|
accept = "/invitations/{self.secret}/accept/"
|
||||||
delete = "{self.organization.urls.details}invitations/{self.pk}/delete/"
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Organization invitation")
|
verbose_name = _("Organization invitation")
|
||||||
|
|
@ -538,7 +536,3 @@ The Servala Team"""
|
||||||
recipient_list=[self.email],
|
recipient_list=[self.email],
|
||||||
fail_silently=False,
|
fail_silently=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
auditlog.register(OrganizationInvitation, serialize_data=True)
|
|
||||||
auditlog.register(OrganizationMembership, serialize_data=True)
|
|
||||||
|
|
|
||||||
|
|
@ -14,26 +14,20 @@ def has_organization_role(user, org, roles):
|
||||||
|
|
||||||
@rules.predicate
|
@rules.predicate
|
||||||
def is_organization_owner(user, obj):
|
def is_organization_owner(user, obj):
|
||||||
from servala.core.models.organization import OrganizationRole
|
|
||||||
|
|
||||||
if hasattr(obj, "organization"):
|
if hasattr(obj, "organization"):
|
||||||
org = obj.organization
|
org = obj.organization
|
||||||
else:
|
else:
|
||||||
org = obj
|
org = obj
|
||||||
return has_organization_role(user, org, [OrganizationRole.OWNER])
|
return has_organization_role(user, org, ["owner"])
|
||||||
|
|
||||||
|
|
||||||
@rules.predicate
|
@rules.predicate
|
||||||
def is_organization_admin(user, obj):
|
def is_organization_admin(user, obj):
|
||||||
from servala.core.models.organization import OrganizationRole
|
|
||||||
|
|
||||||
if hasattr(obj, "organization"):
|
if hasattr(obj, "organization"):
|
||||||
org = obj.organization
|
org = obj.organization
|
||||||
else:
|
else:
|
||||||
org = obj
|
org = obj
|
||||||
return has_organization_role(
|
return has_organization_role(user, org, ["owner", "admin"])
|
||||||
user, org, [OrganizationRole.OWNER, OrganizationRole.ADMIN]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@rules.predicate
|
@rules.predicate
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
<div class="dynamic-array-widget"
|
<div class="dynamic-array-widget"
|
||||||
id="{{ widget.attrs.id|default:'id_'|add:widget.name }}_container"
|
id="{{ widget.attrs.id|default:'id_'|add:widget.name }}_container"
|
||||||
data-name="{{ widget.name }}"
|
data-name="{{ widget.name }}">
|
||||||
{% for name, value in widget.attrs.items %}{% if value is not False and name != "id" and name != "class" %} {{ name }}{% if value is not True %}="{{ value|stringformat:'s' }}"{% endif %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}>
|
|
||||||
<div class="array-items">
|
<div class="array-items">
|
||||||
{% for item in value_list %}
|
{% for item in value_list %}
|
||||||
<div class="array-item d-flex mb-2">
|
<div class="array-item d-flex mb-2">
|
||||||
|
|
|
||||||
|
|
@ -67,25 +67,18 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endpartialdef members-list %}
|
|
||||||
{% partialdef pending-invitations-card %}
|
|
||||||
{% if pending_invitations %}
|
{% if pending_invitations %}
|
||||||
<div class="card">
|
<h5 class="mt-4">
|
||||||
<div class="card-header">
|
|
||||||
<h4 class="card-title">
|
|
||||||
<i class="bi bi-envelope"></i> {% translate "Pending Invitations" %}
|
<i class="bi bi-envelope"></i> {% translate "Pending Invitations" %}
|
||||||
</h4>
|
</h5>
|
||||||
</div>
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="table-responsive">
|
<div class="table-responsive">
|
||||||
<table class="table table-hover">
|
<table class="table table-sm">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% translate "Email" %}</th>
|
<th>{% translate "Email" %}</th>
|
||||||
<th>{% translate "Role" %}</th>
|
<th>{% translate "Role" %}</th>
|
||||||
<th>{% translate "Sent" %}</th>
|
<th>{% translate "Sent" %}</th>
|
||||||
<th>{% translate "Actions" %}</th>
|
<th>{% translate "Link" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
|
@ -103,30 +96,14 @@
|
||||||
onclick="navigator.clipboard.writeText('{{ request.scheme }}://{{ request.get_host }}{{ invitation.urls.accept }}'); this.textContent='Copied!'">
|
onclick="navigator.clipboard.writeText('{{ request.scheme }}://{{ request.get_host }}{{ invitation.urls.accept }}'); this.textContent='Copied!'">
|
||||||
<i class="bi bi-clipboard"></i> {% translate "Copy Link" %}
|
<i class="bi bi-clipboard"></i> {% translate "Copy Link" %}
|
||||||
</button>
|
</button>
|
||||||
<form method="post"
|
|
||||||
action="{{ invitation.urls.delete }}"
|
|
||||||
style="display: inline"
|
|
||||||
hx-post="{{ invitation.urls.delete }}"
|
|
||||||
hx-target="#pending-invitations-card"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
hx-confirm="{% translate 'Are you sure you want to delete this invitation?' %}">
|
|
||||||
{% csrf_token %}
|
|
||||||
<input type="hidden" name="fragment" value="pending-invitations-card">
|
|
||||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
|
||||||
<i class="bi bi-trash"></i> {% translate "Delete" %}
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endpartialdef pending-invitations-card %}
|
{% endpartialdef members-list %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<section class="section">
|
<section class="section">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|
@ -237,7 +214,6 @@
|
||||||
<div class="card-body">{% partial members-list %}</div>
|
<div class="card-body">{% partial members-list %}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="pending-invitations-card">{% partial pending-invitations-card %}</div>
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h4 class="card-title">
|
<h4 class="card-title">
|
||||||
|
|
@ -246,22 +222,6 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="alert alert-light mb-3">
|
|
||||||
<h6>
|
|
||||||
<i class="bi bi-info-circle"></i> {% translate "Role Permissions" %}
|
|
||||||
</h6>
|
|
||||||
<ul class="mb-0">
|
|
||||||
<li>
|
|
||||||
<strong>{% translate "Owner" %}:</strong> {% translate "Can manage all organization settings, members, services, and can appoint administrators." %}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>{% translate "Administrator" %}:</strong> {% translate "Can manage members, invite users, and manage all services and instances." %}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>{% translate "Member" %}:</strong> {% translate "Can view organization details, create and manage their own service instances." %}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<form method="post" class="form">
|
<form method="post" class="form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="row">{{ invitation_form }}</div>
|
<div class="row">{{ invitation_form }}</div>
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,7 @@
|
||||||
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
<ul class="nav nav-tabs" id="myTab" role="tablist">
|
||||||
{% for fieldset in form.get_fieldsets %}
|
{% for fieldset in form.get_fieldsets %}
|
||||||
{% if not fieldset.hidden %}
|
{% if not fieldset.hidden %}
|
||||||
<li class="nav-item{% if fieldset.is_advanced %} advanced-field-group collapse{% endif %}"
|
<li class="nav-item" role="presentation">
|
||||||
role="presentation">
|
|
||||||
<button class="nav-link {% if forloop.first %}active{% endif %}{% if fieldset.has_mandatory %} has-mandatory{% endif %}"
|
<button class="nav-link {% if forloop.first %}active{% endif %}{% if fieldset.has_mandatory %} has-mandatory{% endif %}"
|
||||||
id="{{ fieldset.title|slugify }}-tab"
|
id="{{ fieldset.title|slugify }}-tab"
|
||||||
data-bs-toggle="tab"
|
data-bs-toggle="tab"
|
||||||
|
|
@ -49,12 +48,10 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for subfieldset in fieldset.fieldsets %}
|
{% for subfieldset in fieldset.fieldsets %}
|
||||||
{% if subfieldset.fields %}
|
{% if subfieldset.fields %}
|
||||||
<div {% if subfieldset.is_advanced %}class="advanced-field-group collapse"{% endif %}>
|
|
||||||
<h4 class="mt-3">{{ subfieldset.title }}</h4>
|
<h4 class="mt-3">{{ subfieldset.title }}</h4>
|
||||||
{% for field in subfieldset.fields %}
|
{% for field in subfieldset.fields %}
|
||||||
{% with field=form|get_field:field %}{{ field.as_field_group }}{% endwith %}
|
{% with field=form|get_field:field %}{{ field.as_field_group }}{% endwith %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,6 @@ urlpatterns = [
|
||||||
views.OrganizationUpdateView.as_view(),
|
views.OrganizationUpdateView.as_view(),
|
||||||
name="organization.details",
|
name="organization.details",
|
||||||
),
|
),
|
||||||
path(
|
|
||||||
"details/invitations/<int:pk>/delete/",
|
|
||||||
views.InvitationDeleteView.as_view(),
|
|
||||||
name="invitation.delete",
|
|
||||||
),
|
|
||||||
path(
|
path(
|
||||||
"services/",
|
"services/",
|
||||||
views.ServiceListView.as_view(),
|
views.ServiceListView.as_view(),
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ from .generic import (
|
||||||
)
|
)
|
||||||
from .organization import (
|
from .organization import (
|
||||||
InvitationAcceptView,
|
InvitationAcceptView,
|
||||||
InvitationDeleteView,
|
|
||||||
OrganizationCreateView,
|
OrganizationCreateView,
|
||||||
OrganizationDashboardView,
|
OrganizationDashboardView,
|
||||||
OrganizationUpdateView,
|
OrganizationUpdateView,
|
||||||
|
|
@ -28,7 +27,6 @@ from .support import SupportView
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"IndexView",
|
"IndexView",
|
||||||
"InvitationAcceptView",
|
"InvitationAcceptView",
|
||||||
"InvitationDeleteView",
|
|
||||||
"LogoutView",
|
"LogoutView",
|
||||||
"OrganizationCreateView",
|
"OrganizationCreateView",
|
||||||
"OrganizationDashboardView",
|
"OrganizationDashboardView",
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ from django.utils import timezone
|
||||||
from django.utils.decorators import method_decorator
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.functional import cached_property
|
from django.utils.functional import cached_property
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import CreateView, DeleteView, DetailView, TemplateView
|
from django.views.generic import CreateView, DetailView, TemplateView
|
||||||
from django_scopes import scopes_disabled
|
from django_scopes import scopes_disabled
|
||||||
from rules.contrib.views import AutoPermissionRequiredMixin
|
from rules.contrib.views import AutoPermissionRequiredMixin
|
||||||
|
|
||||||
|
|
@ -14,6 +14,7 @@ from servala.core.models import (
|
||||||
Organization,
|
Organization,
|
||||||
OrganizationInvitation,
|
OrganizationInvitation,
|
||||||
OrganizationMembership,
|
OrganizationMembership,
|
||||||
|
OrganizationRole,
|
||||||
ServiceInstance,
|
ServiceInstance,
|
||||||
)
|
)
|
||||||
from servala.frontend.forms.organization import (
|
from servala.frontend.forms.organization import (
|
||||||
|
|
@ -21,11 +22,7 @@ from servala.frontend.forms.organization import (
|
||||||
OrganizationForm,
|
OrganizationForm,
|
||||||
OrganizationInvitationForm,
|
OrganizationInvitationForm,
|
||||||
)
|
)
|
||||||
from servala.frontend.views.mixins import (
|
from servala.frontend.views.mixins import HtmxUpdateView, OrganizationViewMixin
|
||||||
HtmxUpdateView,
|
|
||||||
HtmxViewMixin,
|
|
||||||
OrganizationViewMixin,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class OrganizationCreateView(AutoPermissionRequiredMixin, CreateView):
|
class OrganizationCreateView(AutoPermissionRequiredMixin, CreateView):
|
||||||
|
|
@ -111,8 +108,10 @@ class OrganizationDashboardView(
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class OrganizationMembershipMixin:
|
class OrganizationUpdateView(OrganizationViewMixin, HtmxUpdateView):
|
||||||
template_name = "frontend/organizations/update.html"
|
template_name = "frontend/organizations/update.html"
|
||||||
|
form_class = OrganizationForm
|
||||||
|
fragments = ("org-name", "org-name-edit", "members-list")
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def user_role(self):
|
def user_role(self):
|
||||||
|
|
@ -127,9 +126,10 @@ class OrganizationMembershipMixin:
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
def can_manage_members(self):
|
def can_manage_members(self):
|
||||||
return self.request.user.has_perm(
|
return self.user_role in [
|
||||||
"core.change_organization", self.request.organization
|
OrganizationRole.ADMIN,
|
||||||
)
|
OrganizationRole.OWNER,
|
||||||
|
]
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
@ -159,18 +159,6 @@ class OrganizationMembershipMixin:
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class OrganizationUpdateView(
|
|
||||||
OrganizationViewMixin, OrganizationMembershipMixin, HtmxUpdateView
|
|
||||||
):
|
|
||||||
form_class = OrganizationForm
|
|
||||||
fragments = (
|
|
||||||
"org-name",
|
|
||||||
"org-name-edit",
|
|
||||||
"members-list",
|
|
||||||
"pending-invitations-card",
|
|
||||||
)
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
if "invite_email" in request.POST:
|
if "invite_email" in request.POST:
|
||||||
return self.handle_invitation(request)
|
return self.handle_invitation(request)
|
||||||
|
|
@ -211,11 +199,7 @@ class OrganizationUpdateView(
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
for error in form.errors.values():
|
for error in form.errors.values():
|
||||||
for error_msg in error:
|
messages.error(request, error.as_text())
|
||||||
messages.error(request, error_msg)
|
|
||||||
|
|
||||||
if self.is_htmx and self._get_fragment():
|
|
||||||
return self.get(request, *self.args, **self.kwargs)
|
|
||||||
|
|
||||||
return redirect(self.get_success_url())
|
return redirect(self.get_success_url())
|
||||||
|
|
||||||
|
|
@ -275,61 +259,3 @@ class InvitationAcceptView(TemplateView):
|
||||||
|
|
||||||
request.session.pop("invitation_next", None)
|
request.session.pop("invitation_next", None)
|
||||||
return redirect(invitation.organization.urls.base)
|
return redirect(invitation.organization.urls.base)
|
||||||
|
|
||||||
|
|
||||||
class InvitationDeleteView(HtmxViewMixin, OrganizationMembershipMixin, DeleteView):
|
|
||||||
model = OrganizationInvitation
|
|
||||||
http_method_names = ["get", "post"]
|
|
||||||
fragments = ("pending-invitations-card",)
|
|
||||||
|
|
||||||
def get_queryset(self):
|
|
||||||
return OrganizationInvitation.objects.filter(accepted_by__isnull=True)
|
|
||||||
|
|
||||||
def get_success_url(self):
|
|
||||||
return self.object.organization.urls.details
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
organization = self.request.organization
|
|
||||||
context["pending_invitations"] = OrganizationInvitation.objects.filter(
|
|
||||||
organization=organization, accepted_by__isnull=True
|
|
||||||
).order_by("-created_at")
|
|
||||||
return context
|
|
||||||
|
|
||||||
def _check_permission(self):
|
|
||||||
return self.request.user.has_perm(
|
|
||||||
"core.change_organization", self.request.organization
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_object(self):
|
|
||||||
if self.request.method == "POST" and self.is_htmx:
|
|
||||||
try:
|
|
||||||
return super().get_object()
|
|
||||||
except Exception:
|
|
||||||
return
|
|
||||||
return super().get_object()
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
|
||||||
self.object = self.get_object()
|
|
||||||
organization = self.object.organization
|
|
||||||
|
|
||||||
if not self._check_permission():
|
|
||||||
if not self.is_htmx:
|
|
||||||
messages.error(
|
|
||||||
request,
|
|
||||||
_("You do not have permission to delete this invitation."),
|
|
||||||
)
|
|
||||||
return redirect(organization.urls.details)
|
|
||||||
|
|
||||||
email = self.object.email
|
|
||||||
self.object.delete()
|
|
||||||
if not self.is_htmx:
|
|
||||||
messages.success(
|
|
||||||
request,
|
|
||||||
_("Invitation for {email} has been deleted.").format(email=email),
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.is_htmx and self._get_fragment():
|
|
||||||
return self.get(request, *args, **kwargs)
|
|
||||||
|
|
||||||
return redirect(self.get_success_url())
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue