mirror of https://github.com/zulip/zulip.git
remote_billing: Add endpoint and a helper to make deactivation links.
This is a general link for logging into the billing system on behalf of a server, but it's tied to the .contact_email and takes the user straight to the /deactivate/ page via the next_page mechanism.
This commit is contained in:
parent
fa9e3fb35c
commit
e515574b3e
|
@ -3828,6 +3828,9 @@ def do_change_remote_server_plan_type(remote_server: RemoteZulipServer, plan_typ
|
|||
|
||||
@transaction.atomic
|
||||
def do_deactivate_remote_server(remote_server: RemoteZulipServer) -> None:
|
||||
# TODO: This should also ensure that the server doesn't have an active plan,
|
||||
# and deactivate it otherwise. (Like do_deactivate_realm does.)
|
||||
|
||||
if remote_server.deactivated:
|
||||
billing_logger.warning(
|
||||
"Cannot deactivate remote server with ID %d, server has already been deactivated.",
|
||||
|
|
|
@ -15,6 +15,7 @@ from corporate.lib.remote_billing_util import (
|
|||
RemoteBillingIdentityDict,
|
||||
RemoteBillingUserDict,
|
||||
)
|
||||
from corporate.views.remote_billing_page import generate_confirmation_link_for_server_deactivation
|
||||
from zerver.lib.remote_server import send_server_data_to_push_bouncer
|
||||
from zerver.lib.test_classes import BouncerTestCase
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
|
@ -755,3 +756,37 @@ class LegacyServerLoginTest(BouncerTestCase):
|
|||
)
|
||||
|
||||
self.assert_json_error(result, "You must accept the Terms of Service to proceed.")
|
||||
|
||||
|
||||
class TestGenerateDeactivationLink(BouncerTestCase):
|
||||
def test_generate_deactivation_link(self) -> None:
|
||||
server = self.server
|
||||
confirmation_url = generate_confirmation_link_for_server_deactivation(
|
||||
server, validity_in_minutes=60
|
||||
)
|
||||
|
||||
result = self.client_get(confirmation_url, subdomain="selfhosting")
|
||||
self.assert_in_success_response(
|
||||
["Log in to Zulip plan management", server.contact_email], result
|
||||
)
|
||||
payload = {"full_name": "test", "tos_consent": "true"}
|
||||
result = self.client_post(confirmation_url, payload, subdomain="selfhosting")
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertEqual(result["Location"], f"/server/{server.uuid!s}/deactivate/")
|
||||
|
||||
result = self.client_get(result["Location"], subdomain="selfhosting")
|
||||
self.assert_in_success_response(
|
||||
[
|
||||
"You are about to deactivate this server's",
|
||||
server.hostname,
|
||||
f'action="/server/{server.uuid!s}/deactivate/"',
|
||||
],
|
||||
result,
|
||||
)
|
||||
result = self.client_post(
|
||||
f"/server/{server.uuid!s}/deactivate/", {"confirmed": "true"}, subdomain="selfhosting"
|
||||
)
|
||||
self.assert_in_success_response([f"Registration deactivated for {server.hostname}"], result)
|
||||
|
||||
server.refresh_from_db()
|
||||
self.assertEqual(server.deactivated, True)
|
||||
|
|
|
@ -8,6 +8,7 @@ from corporate.views.billing_page import (
|
|||
billing_page,
|
||||
remote_realm_billing_page,
|
||||
remote_server_billing_page,
|
||||
remote_server_deactivate_page,
|
||||
update_plan,
|
||||
update_plan_for_remote_realm,
|
||||
update_plan_for_remote_server,
|
||||
|
@ -230,6 +231,11 @@ urlpatterns += [
|
|||
remote_server_sponsorship_page,
|
||||
name="remote_server_sponsorship_page",
|
||||
),
|
||||
path(
|
||||
"server/<server_uuid>/deactivate/",
|
||||
remote_server_deactivate_page,
|
||||
name="remote_server_deactivate_page",
|
||||
),
|
||||
path(
|
||||
"serverlogin/",
|
||||
remote_billing_legacy_server_login,
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import logging
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Dict, Literal, Optional
|
||||
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
||||
from django.http import HttpRequest, HttpResponse, HttpResponseNotAllowed, HttpResponseRedirect
|
||||
from django.shortcuts import render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from corporate.lib.decorator import (
|
||||
authenticated_remote_realm_management_endpoint,
|
||||
|
@ -14,9 +15,11 @@ from corporate.lib.stripe import (
|
|||
RemoteRealmBillingSession,
|
||||
RemoteServerBillingSession,
|
||||
UpdatePlanRequest,
|
||||
do_deactivate_remote_server,
|
||||
)
|
||||
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
||||
from zerver.decorator import process_as_post, require_billing_access, zulip_login_required
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.request import REQ, has_request_variables
|
||||
from zerver.lib.response import json_success
|
||||
from zerver.lib.typed_endpoint import typed_endpoint
|
||||
|
@ -274,3 +277,35 @@ def update_plan_for_remote_server(
|
|||
)
|
||||
billing_session.do_update_plan(update_plan_request)
|
||||
return json_success(request)
|
||||
|
||||
|
||||
@authenticated_remote_server_management_endpoint
|
||||
@typed_endpoint
|
||||
def remote_server_deactivate_page(
|
||||
request: HttpRequest,
|
||||
billing_session: RemoteServerBillingSession,
|
||||
*,
|
||||
confirmed: Literal[None, "true"] = None,
|
||||
) -> HttpResponse:
|
||||
if request.method not in ["GET", "POST"]: # nocoverage
|
||||
return HttpResponseNotAllowed(["GET", "POST"])
|
||||
|
||||
remote_server = billing_session.remote_server
|
||||
if request.method == "GET":
|
||||
context = {
|
||||
"server_hostname": remote_server.hostname,
|
||||
"action_url": reverse(remote_server_deactivate_page, args=[str(remote_server.uuid)]),
|
||||
}
|
||||
return render(request, "corporate/remote_billing_server_deactivate.html", context=context)
|
||||
|
||||
assert request.method == "POST"
|
||||
if confirmed is None: # nocoverage
|
||||
# Should be impossible if the user is using the UI.
|
||||
raise JsonableError(_("Parameter 'confirmed' is required"))
|
||||
|
||||
do_deactivate_remote_server(remote_server)
|
||||
return render(
|
||||
request,
|
||||
"corporate/remote_billing_server_deactivated_success.html",
|
||||
context={"server_hostname": remote_server.hostname},
|
||||
)
|
||||
|
|
|
@ -54,8 +54,8 @@ from zilencer.models import (
|
|||
billing_logger = logging.getLogger("corporate.stripe")
|
||||
|
||||
|
||||
VALID_NEXT_PAGES = [None, "sponsorship", "upgrade", "billing", "plans"]
|
||||
VALID_NEXT_PAGES_TYPE = Literal[None, "sponsorship", "upgrade", "billing", "plans"]
|
||||
VALID_NEXT_PAGES = [None, "sponsorship", "upgrade", "billing", "plans", "deactivate"]
|
||||
VALID_NEXT_PAGES_TYPE = Literal[None, "sponsorship", "upgrade", "billing", "plans", "deactivate"]
|
||||
|
||||
REMOTE_BILLING_SIGNED_ACCESS_TOKEN_VALIDITY_IN_SECONDS = 2 * 60 * 60
|
||||
# We use units of hours here so that we can pass this through to the
|
||||
|
@ -654,3 +654,19 @@ def remote_billing_legacy_server_from_login_confirmation_link(
|
|||
return HttpResponseRedirect(
|
||||
reverse("remote_server_billing_page", args=(remote_server_uuid,))
|
||||
)
|
||||
|
||||
|
||||
def generate_confirmation_link_for_server_deactivation(
|
||||
remote_server: RemoteZulipServer, validity_in_minutes: int
|
||||
) -> str:
|
||||
obj = PreregistrationRemoteServerBillingUser.objects.create(
|
||||
email=remote_server.contact_email,
|
||||
remote_server=remote_server,
|
||||
next_page="deactivate",
|
||||
)
|
||||
url = create_remote_billing_confirmation_link(
|
||||
obj,
|
||||
Confirmation.REMOTE_SERVER_BILLING_LEGACY_LOGIN,
|
||||
validity_in_minutes=validity_in_minutes,
|
||||
)
|
||||
return url
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
{% extends "zerver/portico.html" %}
|
||||
{% set entrypoint = "upgrade" %}
|
||||
|
||||
{% block title %}
|
||||
<title>{{ _("Deactivate server registration?") }} | Zulip</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block portico_content %}
|
||||
<div id="server-deactivate-page" class="register-account flex full-page">
|
||||
<div class="center-block new-style">
|
||||
|
||||
<div class="pitch">
|
||||
<h1>
|
||||
Deactivate registration for {{ server_hostname }}?
|
||||
</h1>
|
||||
</div>
|
||||
<div class="white-box">
|
||||
<div id="server-deactivate-details">
|
||||
<form id="server-deactivate-form" method="post" action="{{ action_url }}">
|
||||
{{ csrf_input }}
|
||||
<p class="not-editable-realm-field">
|
||||
You are about to deactivate this server's
|
||||
registration with
|
||||
the <a href="https://zulip.readthedocs.io/en/stable/production/mobile-push-notifications.html">Zulip
|
||||
Mobile Push Notification Service</a>. This
|
||||
will disable delivery of mobile push
|
||||
notifications for all organizations hosted
|
||||
on <b>{{ server_hostname }}</b>.
|
||||
</p>
|
||||
<input type="hidden" name="confirmed" value="true" />
|
||||
<div class="upgrade-button-container">
|
||||
<button type="submit" id="server-deactivate-button" class="stripe-button-el invoice-button">
|
||||
<span class="server-deactivate-button-text">Deactivate registration</span>
|
||||
<img class="loader remote-billing-button-loader" src="{{ static('images/loading/loader-white.svg') }}" alt="" />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
<div class="input-box upgrade-page-field">
|
||||
<div class="support-link not-editable-realm-field">
|
||||
Questions? Contact <a href="mailto:support@zulip.com">support@zulip.com</a>.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -0,0 +1,27 @@
|
|||
{% extends "zerver/portico_signup.html" %}
|
||||
{% set entrypoint = "upgrade" %}
|
||||
|
||||
{% block title %}
|
||||
<title>Server registration deactivated | Zulip</title>
|
||||
{% endblock %}
|
||||
|
||||
{% block portico_content %}
|
||||
<div class="app portico-page">
|
||||
<div class="app-main portico-page-container center-block flex full-page account-creation account-email-confirm-container new-style">
|
||||
<div class="pitch">
|
||||
<h1>
|
||||
Registration deactivated for {{ server_hostname }}
|
||||
</h1>
|
||||
</div>
|
||||
<div class="inline-block">
|
||||
<div class="white-box">
|
||||
<p>Your server's registration has been deactivated.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block customhead %}
|
||||
{{ super() }}
|
||||
{% endblock %}
|
|
@ -0,0 +1,16 @@
|
|||
import $ from "jquery";
|
||||
|
||||
export function initialize(): void {
|
||||
$("#server-deactivate-form").validate({
|
||||
submitHandler(form) {
|
||||
$("#server-deactivate-form").find(".loader").css("display", "inline-block");
|
||||
$("#server-deactivate-button .server-deactivate-button-text").hide();
|
||||
|
||||
form.submit();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
$(() => {
|
||||
initialize();
|
||||
});
|
|
@ -28,6 +28,7 @@
|
|||
"./src/billing/upgrade",
|
||||
"jquery-validation",
|
||||
"./src/billing/remote_billing_auth",
|
||||
"./src/billing/deactivate_server",
|
||||
"./styles/portico/billing.css"
|
||||
],
|
||||
"billing-event-status": [
|
||||
|
|
Loading…
Reference in New Issue