parent
8b1e0f74bb
commit
09ab83d1e4
4 changed files with 111 additions and 2 deletions
|
|
@ -0,0 +1,42 @@
|
||||||
|
{% extends "frontend/base.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% block html_title %}
|
||||||
|
{% block page_title %}
|
||||||
|
{% translate "Accept Organization Invitation" %}
|
||||||
|
{% endblock page_title %}
|
||||||
|
{% endblock html_title %}
|
||||||
|
{% block content %}
|
||||||
|
<section class="section">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-content">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<i class="bi bi-info-circle"></i>
|
||||||
|
{% blocktranslate with org_name=invitation.organization.name role=invitation.get_role_display %}
|
||||||
|
You have been invited to join <strong>{{ org_name }}</strong> as a <strong>{{ role }}</strong>.
|
||||||
|
{% endblocktranslate %}
|
||||||
|
</div>
|
||||||
|
{% if user.email|lower != invitation.email|lower %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<i class="bi bi-exclamation-triangle"></i>
|
||||||
|
{% blocktranslate with invitation_email=invitation.email user_email=user.email %}
|
||||||
|
<strong>Note:</strong> This invitation was sent to <strong>{{ invitation_email }}</strong>,
|
||||||
|
but you are currently logged in as <strong>{{ user_email }}</strong>.
|
||||||
|
{% endblocktranslate %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="d-flex justify-content-end gap-2">
|
||||||
|
<a href="{% url 'frontend:organization.selection' %}"
|
||||||
|
class="btn btn-secondary">{% translate "Cancel" %}</a>
|
||||||
|
<button type="submit" class="btn btn-primary">
|
||||||
|
<i class="bi bi-check-circle"></i> {% translate "Accept Invitation" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endblock content %}
|
||||||
|
|
@ -6,6 +6,11 @@ from servala.frontend import views
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("accounts/profile/", views.ProfileView.as_view(), name="profile"),
|
path("accounts/profile/", views.ProfileView.as_view(), name="profile"),
|
||||||
path("accounts/logout/", views.LogoutView.as_view(), name="logout"),
|
path("accounts/logout/", views.LogoutView.as_view(), name="logout"),
|
||||||
|
path(
|
||||||
|
"invitations/<str:secret>/accept/",
|
||||||
|
views.InvitationAcceptView.as_view(),
|
||||||
|
name="invitation.accept",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"organizations/",
|
"organizations/",
|
||||||
views.OrganizationSelectionView.as_view(),
|
views.OrganizationSelectionView.as_view(),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ from .generic import (
|
||||||
custom_500,
|
custom_500,
|
||||||
)
|
)
|
||||||
from .organization import (
|
from .organization import (
|
||||||
|
InvitationAcceptView,
|
||||||
OrganizationCreateView,
|
OrganizationCreateView,
|
||||||
OrganizationDashboardView,
|
OrganizationDashboardView,
|
||||||
OrganizationUpdateView,
|
OrganizationUpdateView,
|
||||||
|
|
@ -25,6 +26,7 @@ from .support import SupportView
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
"IndexView",
|
"IndexView",
|
||||||
|
"InvitationAcceptView",
|
||||||
"LogoutView",
|
"LogoutView",
|
||||||
"OrganizationCreateView",
|
"OrganizationCreateView",
|
||||||
"OrganizationDashboardView",
|
"OrganizationDashboardView",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
from django.shortcuts import redirect
|
from django.contrib import messages
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.decorators import method_decorator
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import CreateView, DetailView
|
from django.views.generic import CreateView, DetailView, TemplateView
|
||||||
|
from django_scopes import scopes_disabled
|
||||||
from rules.contrib.views import AutoPermissionRequiredMixin
|
from rules.contrib.views import AutoPermissionRequiredMixin
|
||||||
|
|
||||||
from servala.core.models import (
|
from servala.core.models import (
|
||||||
BillingEntity,
|
BillingEntity,
|
||||||
Organization,
|
Organization,
|
||||||
|
OrganizationInvitation,
|
||||||
OrganizationMembership,
|
OrganizationMembership,
|
||||||
ServiceInstance,
|
ServiceInstance,
|
||||||
)
|
)
|
||||||
|
|
@ -103,3 +109,57 @@ class OrganizationUpdateView(OrganizationViewMixin, HtmxUpdateView):
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return self.request.path
|
return self.request.path
|
||||||
|
|
||||||
|
|
||||||
|
@method_decorator(scopes_disabled(), name="dispatch")
|
||||||
|
class InvitationAcceptView(TemplateView):
|
||||||
|
template_name = "frontend/organizations/invitation_accept.html"
|
||||||
|
|
||||||
|
def get_invitation(self):
|
||||||
|
secret = self.kwargs.get("secret")
|
||||||
|
return get_object_or_404(OrganizationInvitation, secret=secret)
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
invitation = self.get_invitation()
|
||||||
|
|
||||||
|
if invitation.is_accepted:
|
||||||
|
messages.warning(
|
||||||
|
request,
|
||||||
|
_("This invitation has already been accepted."),
|
||||||
|
)
|
||||||
|
return redirect("frontend:organization.selection")
|
||||||
|
if not request.user.is_authenticated:
|
||||||
|
request.session["invitation_next"] = request.path
|
||||||
|
messages.info(
|
||||||
|
request,
|
||||||
|
_("Please log in or sign up to accept this invitation."),
|
||||||
|
)
|
||||||
|
return redirect(f"{reverse('account_login')}?next={request.path}")
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["invitation"] = self.get_invitation()
|
||||||
|
return context
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
invitation = self.get_invitation()
|
||||||
|
invitation.accepted_by = request.user
|
||||||
|
invitation.accepted_at = timezone.now()
|
||||||
|
invitation.save()
|
||||||
|
|
||||||
|
OrganizationMembership.objects.get_or_create(
|
||||||
|
user=request.user,
|
||||||
|
organization=invitation.organization,
|
||||||
|
defaults={"role": invitation.role},
|
||||||
|
)
|
||||||
|
|
||||||
|
messages.success(
|
||||||
|
request,
|
||||||
|
_("You have successfully joined {organization}!").format(
|
||||||
|
organization=invitation.organization.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
request.session.pop("invitation_next", None)
|
||||||
|
return redirect(invitation.organization.urls.base)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue