2023-12-05 07:42:52 +01:00
|
|
|
from dataclasses import asdict, dataclass
|
2024-09-24 23:27:28 +02:00
|
|
|
from typing import TYPE_CHECKING
|
2022-05-12 06:27:31 +02:00
|
|
|
from urllib.parse import urlencode
|
2020-09-12 04:18:53 +02:00
|
|
|
|
2020-08-07 01:09:47 +02:00
|
|
|
import orjson
|
2020-01-29 20:41:23 +01:00
|
|
|
from django.conf import settings
|
2020-09-15 03:04:07 +02:00
|
|
|
from django.contrib.auth.views import redirect_to_login
|
2020-01-29 20:41:23 +01:00
|
|
|
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
|
2020-05-08 06:37:58 +02:00
|
|
|
from django.template.response import TemplateResponse
|
2023-12-05 07:42:52 +01:00
|
|
|
from django.urls import reverse
|
2024-02-08 13:37:55 +01:00
|
|
|
from pydantic import Json
|
2020-01-29 20:41:23 +01:00
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
from corporate.lib.decorator import (
|
|
|
|
authenticated_remote_realm_management_endpoint,
|
|
|
|
authenticated_remote_server_management_endpoint,
|
|
|
|
)
|
2024-03-15 03:03:11 +01:00
|
|
|
from corporate.models import CustomerPlan, get_current_plan_by_customer, get_customer_by_realm
|
2020-05-08 06:37:58 +02:00
|
|
|
from zerver.context_processors import get_realm_from_request, latest_info_context
|
2024-02-01 16:24:05 +01:00
|
|
|
from zerver.decorator import add_google_analytics, zulip_login_required
|
2022-11-17 09:30:48 +01:00
|
|
|
from zerver.lib.github import (
|
|
|
|
InvalidPlatformError,
|
|
|
|
get_latest_github_release_download_link_for_platform,
|
|
|
|
)
|
2022-08-30 07:47:43 +02:00
|
|
|
from zerver.lib.realm_description import get_realm_text_description
|
|
|
|
from zerver.lib.realm_icon import get_realm_icon_url
|
2021-06-10 03:59:43 +02:00
|
|
|
from zerver.lib.subdomains import is_subdomain_root_or_alias
|
2024-02-08 13:37:55 +01:00
|
|
|
from zerver.lib.typed_endpoint import typed_endpoint
|
2020-01-29 20:41:23 +01:00
|
|
|
from zerver.models import Realm
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2024-09-24 23:27:28 +02:00
|
|
|
if TYPE_CHECKING:
|
|
|
|
from corporate.lib.stripe import RemoteRealmBillingSession, RemoteServerBillingSession
|
|
|
|
|
2020-01-29 20:41:23 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
@add_google_analytics
|
2024-07-12 02:30:23 +02:00
|
|
|
def apps_view(request: HttpRequest, platform: str | None = None) -> HttpResponse:
|
apps: Fix redirect from /apps -> https://zulip.com/apps/.
When this code was moved from being in zerver in 21a2fd482eae, it kept
the `if ZILENCER_ENABLED` blocks. Since ZILENCER and CORPORATE are
generally either both on or both off, the if statement became
mostly-unnecessary.
However, because tests cannot easily remove elements from
INSTALLED_APPS and re-determine URL resolution, we switch to checking
`if CORPORATE_ENABLED` as a guard, and leave these in-place.
The other side effect of this is that with e54ded49c44c, most Zulip
deployments started to 404 requests for `/apps` instead of redirecting
them to `https://zulip.com/apps/` since they no longer had any path
configured for `/apps`. Unfortunately, this URL is in widespread use
in the app (e.g. in links from the Welcome Bot), so we should ensure
that it does successfully redirect.
Add the `/apps` path to `zerver`, but only if not CORPORATE_ENABLED,
so the URLs do not overlap.
2022-12-16 15:17:00 +01:00
|
|
|
if not settings.CORPORATE_ENABLED:
|
|
|
|
# This seems impossible (CORPORATE_ENABLED set to false when
|
|
|
|
# rendering a "corporate" view) -- but we add it to make
|
|
|
|
# testing possible. Tests default to running with the
|
|
|
|
# "corporate" app installed, and unsetting that is difficult,
|
|
|
|
# as one cannot easily reload the URL resolution -- so we add
|
|
|
|
# a redirect here, equivalent to the one zerver would have
|
|
|
|
# installed when "corporate" is not enabled, to make the
|
|
|
|
# behaviour testable with CORPORATE_ENABLED set to false.
|
|
|
|
return HttpResponseRedirect("https://zulip.com/apps/", status=301)
|
|
|
|
return TemplateResponse(
|
|
|
|
request,
|
|
|
|
"corporate/apps.html",
|
|
|
|
)
|
2020-01-29 20:41:23 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-10-22 13:39:55 +02:00
|
|
|
def app_download_link_redirect(request: HttpRequest, platform: str) -> HttpResponse:
|
|
|
|
try:
|
|
|
|
download_link = get_latest_github_release_download_link_for_platform(platform)
|
|
|
|
return HttpResponseRedirect(download_link, status=302)
|
2022-11-17 09:30:48 +01:00
|
|
|
except InvalidPlatformError:
|
2020-10-22 13:39:55 +02:00
|
|
|
return TemplateResponse(request, "404.html", status=404)
|
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
def is_customer_on_free_trial(customer_plan: CustomerPlan) -> bool:
|
|
|
|
return customer_plan.status in (
|
|
|
|
CustomerPlan.FREE_TRIAL,
|
|
|
|
CustomerPlan.DOWNGRADE_AT_END_OF_FREE_TRIAL,
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class PlansPageContext:
|
|
|
|
sponsorship_url: str
|
2024-07-12 02:30:23 +02:00
|
|
|
free_trial_days: int | None
|
2023-12-05 07:42:52 +01:00
|
|
|
on_free_trial: bool = False
|
|
|
|
sponsorship_pending: bool = False
|
|
|
|
is_sponsored: bool = False
|
|
|
|
|
|
|
|
is_cloud_realm: bool = False
|
|
|
|
is_self_hosted_realm: bool = False
|
|
|
|
|
|
|
|
is_new_customer: bool = False
|
|
|
|
on_free_tier: bool = False
|
2024-07-12 02:30:23 +02:00
|
|
|
customer_plan: CustomerPlan | None = None
|
2023-12-05 09:55:44 +01:00
|
|
|
is_legacy_server_with_scheduled_upgrade: bool = False
|
2024-07-12 02:30:23 +02:00
|
|
|
legacy_server_new_plan: CustomerPlan | None = None
|
|
|
|
requested_sponsorship_plan: str | None = None
|
2023-12-05 07:42:52 +01:00
|
|
|
|
|
|
|
billing_base_url: str = ""
|
|
|
|
|
2023-12-18 23:57:32 +01:00
|
|
|
tier_self_hosted_basic: int = CustomerPlan.TIER_SELF_HOSTED_BASIC
|
2023-12-18 13:02:36 +01:00
|
|
|
tier_self_hosted_business: int = CustomerPlan.TIER_SELF_HOSTED_BUSINESS
|
|
|
|
tier_cloud_standard: int = CustomerPlan.TIER_CLOUD_STANDARD
|
2024-04-16 10:19:00 +02:00
|
|
|
tier_cloud_plus: int = CustomerPlan.TIER_CLOUD_PLUS
|
2023-12-18 13:02:36 +01:00
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
@add_google_analytics
|
2020-01-29 20:41:23 +01:00
|
|
|
def plans_view(request: HttpRequest) -> HttpResponse:
|
2024-09-24 23:27:28 +02:00
|
|
|
from corporate.lib.stripe import get_free_trial_days
|
|
|
|
|
2020-01-29 20:41:23 +01:00
|
|
|
realm = get_realm_from_request(request)
|
2023-12-05 07:42:52 +01:00
|
|
|
context = PlansPageContext(
|
|
|
|
is_cloud_realm=True,
|
|
|
|
sponsorship_url=reverse("sponsorship_request"),
|
2023-12-10 05:13:00 +01:00
|
|
|
free_trial_days=get_free_trial_days(False),
|
2023-12-05 07:42:52 +01:00
|
|
|
is_sponsored=realm is not None and realm.plan_type == Realm.PLAN_TYPE_STANDARD_FREE,
|
|
|
|
)
|
2021-06-10 03:59:43 +02:00
|
|
|
if is_subdomain_root_or_alias(request):
|
|
|
|
# If we're on the root domain, we make this link first ask you which organization.
|
2023-12-05 07:42:52 +01:00
|
|
|
context.sponsorship_url = f"/accounts/go/?{urlencode({'next': context.sponsorship_url})}"
|
2020-06-09 12:24:32 +02:00
|
|
|
|
2020-01-29 20:41:23 +01:00
|
|
|
if realm is not None:
|
2021-10-18 23:28:17 +02:00
|
|
|
if realm.plan_type == Realm.PLAN_TYPE_SELF_HOSTED and settings.PRODUCTION:
|
2023-03-23 03:53:14 +01:00
|
|
|
return HttpResponseRedirect("https://zulip.com/plans/")
|
2020-01-29 20:41:23 +01:00
|
|
|
if not request.user.is_authenticated:
|
2023-03-23 03:53:14 +01:00
|
|
|
return redirect_to_login(next="/plans/")
|
2020-07-15 22:18:32 +02:00
|
|
|
if request.user.is_guest:
|
|
|
|
return TemplateResponse(request, "404.html", status=404)
|
2023-12-05 07:42:52 +01:00
|
|
|
|
2022-12-15 20:27:17 +01:00
|
|
|
customer = get_customer_by_realm(realm)
|
2023-12-05 07:42:52 +01:00
|
|
|
context.on_free_tier = customer is None and not context.is_sponsored
|
2022-12-15 20:27:17 +01:00
|
|
|
if customer is not None:
|
2023-12-05 07:42:52 +01:00
|
|
|
context.sponsorship_pending = customer.sponsorship_pending
|
|
|
|
context.customer_plan = get_current_plan_by_customer(customer)
|
|
|
|
if context.customer_plan is None:
|
|
|
|
# Cloud realms on free tier don't have active customer plan unless they are sponsored.
|
|
|
|
context.on_free_tier = not context.is_sponsored
|
|
|
|
else:
|
|
|
|
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
|
|
|
|
|
|
|
|
context.is_new_customer = (
|
|
|
|
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
|
|
|
|
)
|
|
|
|
return TemplateResponse(
|
|
|
|
request,
|
|
|
|
"corporate/plans.html",
|
|
|
|
context=asdict(context),
|
|
|
|
)
|
2020-06-09 12:24:32 +02:00
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
|
|
|
|
@add_google_analytics
|
|
|
|
@authenticated_remote_realm_management_endpoint
|
|
|
|
def remote_realm_plans_page(
|
2024-09-24 23:27:28 +02:00
|
|
|
request: HttpRequest, billing_session: "RemoteRealmBillingSession"
|
2023-12-19 12:24:15 +01:00
|
|
|
) -> HttpResponse:
|
2024-09-24 23:27:28 +02:00
|
|
|
from corporate.lib.stripe import get_configured_fixed_price_plan_offer, get_free_trial_days
|
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
customer = billing_session.get_customer()
|
|
|
|
context = PlansPageContext(
|
|
|
|
is_self_hosted_realm=True,
|
|
|
|
sponsorship_url=reverse(
|
|
|
|
"remote_realm_sponsorship_page", args=(billing_session.remote_realm.uuid,)
|
|
|
|
),
|
2023-12-10 05:13:00 +01:00
|
|
|
free_trial_days=get_free_trial_days(True),
|
2023-12-05 07:42:52 +01:00
|
|
|
billing_base_url=billing_session.billing_base_url,
|
|
|
|
is_sponsored=billing_session.is_sponsored(),
|
2023-12-12 06:40:25 +01:00
|
|
|
requested_sponsorship_plan=billing_session.get_sponsorship_plan_name(customer, True),
|
2023-12-05 07:42:52 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
context.on_free_tier = customer is None and not context.is_sponsored
|
2023-12-19 12:24:15 +01:00
|
|
|
if customer is not None: # nocoverage
|
2023-12-05 07:42:52 +01:00
|
|
|
context.sponsorship_pending = customer.sponsorship_pending
|
|
|
|
context.customer_plan = get_current_plan_by_customer(customer)
|
2024-01-31 19:20:52 +01:00
|
|
|
|
|
|
|
if customer.required_plan_tier is not None:
|
|
|
|
configure_fixed_price_plan = get_configured_fixed_price_plan_offer(
|
|
|
|
customer, customer.required_plan_tier
|
|
|
|
)
|
|
|
|
# Free trial is disabled for customers with fixed-price plan configured.
|
|
|
|
if configure_fixed_price_plan is not None:
|
|
|
|
context.free_trial_days = None
|
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
if context.customer_plan is None:
|
|
|
|
context.on_free_tier = not context.is_sponsored
|
|
|
|
else:
|
2023-12-05 23:20:32 +01:00
|
|
|
context.on_free_tier = (
|
|
|
|
context.customer_plan.tier
|
|
|
|
in (
|
|
|
|
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
|
|
|
|
CustomerPlan.TIER_SELF_HOSTED_BASE,
|
|
|
|
)
|
|
|
|
and not context.is_sponsored
|
2023-12-11 18:00:42 +01:00
|
|
|
)
|
2023-12-05 07:42:52 +01:00
|
|
|
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
|
2023-12-11 18:00:42 +01:00
|
|
|
context.is_legacy_server_with_scheduled_upgrade = (
|
|
|
|
context.customer_plan.status == CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END
|
|
|
|
)
|
|
|
|
if context.is_legacy_server_with_scheduled_upgrade:
|
|
|
|
assert context.customer_plan.end_date is not None
|
|
|
|
context.legacy_server_new_plan = CustomerPlan.objects.get(
|
|
|
|
customer=customer,
|
|
|
|
billing_cycle_anchor=context.customer_plan.end_date,
|
|
|
|
status=CustomerPlan.NEVER_STARTED,
|
|
|
|
)
|
2023-12-05 07:42:52 +01:00
|
|
|
|
2024-04-08 11:58:02 +02:00
|
|
|
if billing_session.customer_plan_exists():
|
|
|
|
# Free trial is disabled for existing customers.
|
2024-03-15 03:03:11 +01:00
|
|
|
context.free_trial_days = None
|
2024-02-22 05:30:41 +01:00
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
context.is_new_customer = (
|
|
|
|
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
|
|
|
|
)
|
|
|
|
return TemplateResponse(
|
|
|
|
request,
|
|
|
|
"corporate/plans.html",
|
|
|
|
context=asdict(context),
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
@add_google_analytics
|
|
|
|
@authenticated_remote_server_management_endpoint
|
|
|
|
def remote_server_plans_page(
|
2024-09-24 23:27:28 +02:00
|
|
|
request: HttpRequest, billing_session: "RemoteServerBillingSession"
|
2023-12-19 12:24:15 +01:00
|
|
|
) -> HttpResponse:
|
2024-09-24 23:27:28 +02:00
|
|
|
from corporate.lib.stripe import get_configured_fixed_price_plan_offer, get_free_trial_days
|
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
customer = billing_session.get_customer()
|
|
|
|
context = PlansPageContext(
|
|
|
|
is_self_hosted_realm=True,
|
|
|
|
sponsorship_url=reverse(
|
|
|
|
"remote_server_sponsorship_page", args=(billing_session.remote_server.uuid,)
|
|
|
|
),
|
2023-12-10 05:13:00 +01:00
|
|
|
free_trial_days=get_free_trial_days(True),
|
2023-12-05 07:42:52 +01:00
|
|
|
billing_base_url=billing_session.billing_base_url,
|
|
|
|
is_sponsored=billing_session.is_sponsored(),
|
2023-12-12 06:40:25 +01:00
|
|
|
requested_sponsorship_plan=billing_session.get_sponsorship_plan_name(customer, True),
|
2023-12-05 07:42:52 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
context.on_free_tier = customer is None and not context.is_sponsored
|
2023-12-19 12:24:15 +01:00
|
|
|
if customer is not None: # nocoverage
|
2023-12-05 07:42:52 +01:00
|
|
|
context.sponsorship_pending = customer.sponsorship_pending
|
|
|
|
context.customer_plan = get_current_plan_by_customer(customer)
|
2024-01-31 19:20:52 +01:00
|
|
|
|
|
|
|
if customer.required_plan_tier is not None:
|
|
|
|
configure_fixed_price_plan = get_configured_fixed_price_plan_offer(
|
|
|
|
customer, customer.required_plan_tier
|
|
|
|
)
|
|
|
|
# Free trial is disabled for customers with fixed-price plan configured.
|
|
|
|
if configure_fixed_price_plan is not None:
|
|
|
|
context.free_trial_days = None
|
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
if context.customer_plan is None:
|
|
|
|
context.on_free_tier = not context.is_sponsored
|
|
|
|
else:
|
|
|
|
context.on_free_tier = context.customer_plan.tier in (
|
|
|
|
CustomerPlan.TIER_SELF_HOSTED_LEGACY,
|
|
|
|
CustomerPlan.TIER_SELF_HOSTED_BASE,
|
|
|
|
)
|
|
|
|
context.on_free_trial = is_customer_on_free_trial(context.customer_plan)
|
2023-12-05 09:55:44 +01:00
|
|
|
context.is_legacy_server_with_scheduled_upgrade = (
|
|
|
|
context.customer_plan.status == CustomerPlan.SWITCH_PLAN_TIER_AT_PLAN_END
|
|
|
|
)
|
|
|
|
if context.is_legacy_server_with_scheduled_upgrade:
|
|
|
|
assert context.customer_plan.end_date is not None
|
|
|
|
context.legacy_server_new_plan = CustomerPlan.objects.get(
|
|
|
|
customer=customer,
|
|
|
|
billing_cycle_anchor=context.customer_plan.end_date,
|
|
|
|
status=CustomerPlan.NEVER_STARTED,
|
|
|
|
)
|
2023-12-05 07:42:52 +01:00
|
|
|
|
2024-04-08 11:58:02 +02:00
|
|
|
if billing_session.customer_plan_exists():
|
|
|
|
# Free trial is disabled for existing customers.
|
2024-02-22 05:30:41 +01:00
|
|
|
context.free_trial_days = None
|
|
|
|
|
2023-12-05 07:42:52 +01:00
|
|
|
context.is_new_customer = (
|
|
|
|
not context.on_free_tier and context.customer_plan is None and not context.is_sponsored
|
|
|
|
)
|
2020-05-08 06:37:58 +02:00
|
|
|
return TemplateResponse(
|
|
|
|
request,
|
2022-08-03 10:45:56 +02:00
|
|
|
"corporate/plans.html",
|
2023-12-05 07:42:52 +01:00
|
|
|
context=asdict(context),
|
2020-05-08 06:37:58 +02:00
|
|
|
)
|
2020-01-29 20:41:23 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
@add_google_analytics
|
2020-01-29 20:41:23 +01:00
|
|
|
def team_view(request: HttpRequest) -> HttpResponse:
|
2020-04-07 19:27:07 +02:00
|
|
|
if not settings.ZILENCER_ENABLED:
|
2021-02-12 08:20:45 +01:00
|
|
|
return HttpResponseRedirect("https://zulip.com/team/", status=301)
|
2020-04-07 19:27:07 +02:00
|
|
|
|
|
|
|
try:
|
2020-08-07 01:09:47 +02:00
|
|
|
with open(settings.CONTRIBUTOR_DATA_FILE_PATH, "rb") as f:
|
|
|
|
data = orjson.loads(f.read())
|
2020-04-07 19:27:07 +02:00
|
|
|
except FileNotFoundError:
|
2023-03-22 23:16:28 +01:00
|
|
|
data = {"contributors": [], "date": "Never ran."}
|
2020-01-29 20:41:23 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
return TemplateResponse(
|
2020-01-29 20:41:23 +01:00
|
|
|
request,
|
2022-08-16 13:10:28 +02:00
|
|
|
"corporate/team.html",
|
2020-01-29 20:41:23 +01:00
|
|
|
context={
|
2024-02-16 22:56:36 +01:00
|
|
|
# Sync this with team_params_schema in base_page_params.ts.
|
2021-02-12 08:20:45 +01:00
|
|
|
"page_params": {
|
2024-02-16 22:56:36 +01:00
|
|
|
"page_type": "team",
|
2021-02-12 08:20:45 +01:00
|
|
|
"contributors": data["contributors"],
|
2020-01-29 20:41:23 +01:00
|
|
|
},
|
2021-02-12 08:20:45 +01:00
|
|
|
"date": data["date"],
|
2020-01-29 20:41:23 +01:00
|
|
|
},
|
|
|
|
)
|
2020-01-29 00:05:06 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
@add_google_analytics
|
|
|
|
def landing_view(request: HttpRequest, template_name: str) -> HttpResponse:
|
2023-12-16 08:49:40 +01:00
|
|
|
context = latest_info_context()
|
2024-04-16 10:19:00 +02:00
|
|
|
context.update(
|
|
|
|
{
|
|
|
|
"billing_base_url": "",
|
|
|
|
"tier_cloud_standard": str(CustomerPlan.TIER_CLOUD_STANDARD),
|
|
|
|
"tier_cloud_plus": str(CustomerPlan.TIER_CLOUD_PLUS),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2023-12-16 08:49:40 +01:00
|
|
|
return TemplateResponse(request, template_name, context)
|
2020-05-08 06:37:58 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-05-08 06:37:58 +02:00
|
|
|
@add_google_analytics
|
|
|
|
def hello_view(request: HttpRequest) -> HttpResponse:
|
2022-08-16 13:27:45 +02:00
|
|
|
return TemplateResponse(request, "corporate/hello.html", latest_info_context())
|
2022-08-30 07:47:43 +02:00
|
|
|
|
|
|
|
|
|
|
|
@add_google_analytics
|
|
|
|
def communities_view(request: HttpRequest) -> HttpResponse:
|
|
|
|
eligible_realms = []
|
|
|
|
unique_org_type_ids = set()
|
2023-10-11 20:08:20 +02:00
|
|
|
want_to_be_advertised_realms = (
|
|
|
|
Realm.objects.filter(
|
|
|
|
want_advertise_in_communities_directory=True,
|
|
|
|
)
|
|
|
|
.exclude(
|
|
|
|
# Filter out realms who haven't changed their description from the default.
|
|
|
|
description="",
|
|
|
|
)
|
|
|
|
.order_by("name")
|
|
|
|
)
|
2022-08-30 07:47:43 +02:00
|
|
|
for realm in want_to_be_advertised_realms:
|
2022-10-10 13:23:18 +02:00
|
|
|
open_to_public = not realm.invite_required and not realm.emails_restricted_to_domains
|
|
|
|
if realm.allow_web_public_streams_access() or open_to_public:
|
2023-07-22 00:34:11 +02:00
|
|
|
[org_type] = (
|
|
|
|
org_type
|
|
|
|
for org_type in Realm.ORG_TYPES
|
|
|
|
if Realm.ORG_TYPES[org_type]["id"] == realm.org_type
|
|
|
|
)
|
2022-08-30 07:47:43 +02:00
|
|
|
eligible_realms.append(
|
|
|
|
{
|
|
|
|
"id": realm.id,
|
|
|
|
"name": realm.name,
|
2024-05-06 15:27:22 +02:00
|
|
|
"realm_url": realm.url,
|
2022-08-30 07:47:43 +02:00
|
|
|
"logo_url": get_realm_icon_url(realm),
|
|
|
|
"description": get_realm_text_description(realm),
|
2023-07-22 00:34:11 +02:00
|
|
|
"org_type_key": org_type,
|
2022-08-30 07:47:43 +02:00
|
|
|
}
|
|
|
|
)
|
|
|
|
unique_org_type_ids.add(realm.org_type)
|
|
|
|
|
2023-10-11 20:08:20 +02:00
|
|
|
# Custom list of org filters to show.
|
|
|
|
CATEGORIES_TO_OFFER = [
|
2023-10-11 20:04:36 +02:00
|
|
|
"opensource",
|
|
|
|
"research",
|
|
|
|
"community",
|
|
|
|
]
|
|
|
|
|
2022-08-30 07:47:43 +02:00
|
|
|
# Remove org_types for which there are no open organizations.
|
|
|
|
org_types = dict()
|
2023-10-11 20:08:20 +02:00
|
|
|
for org_type in CATEGORIES_TO_OFFER:
|
2022-08-30 07:47:43 +02:00
|
|
|
if Realm.ORG_TYPES[org_type]["id"] in unique_org_type_ids:
|
|
|
|
org_types[org_type] = Realm.ORG_TYPES[org_type]
|
|
|
|
|
2023-10-11 20:08:20 +02:00
|
|
|
# This code is not required right bot could be useful in future.
|
|
|
|
# If we ever decided to show all the ORG_TYPES.
|
|
|
|
# Remove `Unspecified` ORG_TYPE
|
|
|
|
# org_types.pop("unspecified", None)
|
2022-08-30 07:47:43 +02:00
|
|
|
|
2022-09-13 13:18:13 +02:00
|
|
|
# Change display name of non-profit orgs.
|
2023-10-11 20:08:20 +02:00
|
|
|
# if org_types.get("nonprofit"): # nocoverage
|
|
|
|
# org_types["nonprofit"]["name"] = "Non-profit"
|
2022-09-13 13:18:13 +02:00
|
|
|
|
2022-08-30 07:47:43 +02:00
|
|
|
return TemplateResponse(
|
|
|
|
request,
|
|
|
|
"corporate/communities.html",
|
|
|
|
context={
|
|
|
|
"eligible_realms": eligible_realms,
|
|
|
|
"org_types": org_types,
|
|
|
|
},
|
|
|
|
)
|
2024-02-01 16:24:05 +01:00
|
|
|
|
|
|
|
|
|
|
|
@zulip_login_required
|
|
|
|
def invoices_page(request: HttpRequest) -> HttpResponseRedirect:
|
2024-09-24 23:27:28 +02:00
|
|
|
from corporate.lib.stripe import RealmBillingSession
|
|
|
|
|
2024-02-01 16:24:05 +01:00
|
|
|
user = request.user
|
|
|
|
assert user.is_authenticated
|
|
|
|
|
|
|
|
if not user.has_billing_access:
|
|
|
|
return HttpResponseRedirect(reverse("billing_page"))
|
|
|
|
|
|
|
|
billing_session = RealmBillingSession(user=user, realm=user.realm)
|
|
|
|
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
|
|
|
return HttpResponseRedirect(list_invoices_session_url)
|
|
|
|
|
|
|
|
|
|
|
|
@authenticated_remote_realm_management_endpoint
|
|
|
|
def remote_realm_invoices_page(
|
2024-09-24 23:27:28 +02:00
|
|
|
request: HttpRequest, billing_session: "RemoteRealmBillingSession"
|
2024-02-01 16:24:05 +01:00
|
|
|
) -> HttpResponseRedirect:
|
|
|
|
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
|
|
|
return HttpResponseRedirect(list_invoices_session_url)
|
|
|
|
|
|
|
|
|
|
|
|
@authenticated_remote_server_management_endpoint
|
|
|
|
def remote_server_invoices_page(
|
2024-09-24 23:27:28 +02:00
|
|
|
request: HttpRequest, billing_session: "RemoteServerBillingSession"
|
2024-02-01 16:24:05 +01:00
|
|
|
) -> HttpResponseRedirect:
|
|
|
|
list_invoices_session_url = billing_session.get_past_invoices_session_url()
|
|
|
|
return HttpResponseRedirect(list_invoices_session_url)
|
2024-02-08 13:37:55 +01:00
|
|
|
|
|
|
|
|
|
|
|
@zulip_login_required
|
|
|
|
@typed_endpoint
|
|
|
|
def customer_portal(
|
|
|
|
request: HttpRequest,
|
|
|
|
*,
|
|
|
|
return_to_billing_page: Json[bool] = False,
|
|
|
|
manual_license_management: Json[bool] = False,
|
2024-07-12 02:30:23 +02:00
|
|
|
tier: Json[int] | None = None,
|
2024-03-04 00:44:59 +01:00
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2024-02-08 13:37:55 +01:00
|
|
|
) -> HttpResponseRedirect:
|
2024-09-24 23:27:28 +02:00
|
|
|
from corporate.lib.stripe import RealmBillingSession
|
|
|
|
|
2024-02-08 13:37:55 +01:00
|
|
|
user = request.user
|
|
|
|
assert user.is_authenticated
|
|
|
|
|
|
|
|
if not user.has_billing_access:
|
|
|
|
return HttpResponseRedirect(reverse("billing_page"))
|
|
|
|
|
|
|
|
billing_session = RealmBillingSession(user=user, realm=user.realm)
|
|
|
|
review_billing_information_url = billing_session.get_stripe_customer_portal_url(
|
2024-03-04 00:44:59 +01:00
|
|
|
return_to_billing_page, manual_license_management, tier, setup_payment_by_invoice
|
2024-02-08 13:37:55 +01:00
|
|
|
)
|
|
|
|
return HttpResponseRedirect(review_billing_information_url)
|
|
|
|
|
|
|
|
|
|
|
|
@typed_endpoint
|
2024-09-24 23:27:28 +02:00
|
|
|
@authenticated_remote_realm_management_endpoint
|
2024-02-08 13:37:55 +01:00
|
|
|
def remote_realm_customer_portal(
|
|
|
|
request: HttpRequest,
|
2024-09-24 23:27:28 +02:00
|
|
|
billing_session: "RemoteRealmBillingSession",
|
2024-02-08 13:37:55 +01:00
|
|
|
*,
|
|
|
|
return_to_billing_page: Json[bool] = False,
|
|
|
|
manual_license_management: Json[bool] = False,
|
2024-07-12 02:30:23 +02:00
|
|
|
tier: Json[int] | None = None,
|
2024-03-04 00:44:59 +01:00
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2024-02-08 13:37:55 +01:00
|
|
|
) -> HttpResponseRedirect:
|
|
|
|
review_billing_information_url = billing_session.get_stripe_customer_portal_url(
|
2024-03-04 00:44:59 +01:00
|
|
|
return_to_billing_page, manual_license_management, tier, setup_payment_by_invoice
|
2024-02-08 13:37:55 +01:00
|
|
|
)
|
|
|
|
return HttpResponseRedirect(review_billing_information_url)
|
|
|
|
|
|
|
|
|
|
|
|
@typed_endpoint
|
2024-09-24 23:27:28 +02:00
|
|
|
@authenticated_remote_server_management_endpoint
|
2024-02-08 13:37:55 +01:00
|
|
|
def remote_server_customer_portal(
|
|
|
|
request: HttpRequest,
|
2024-09-24 23:27:28 +02:00
|
|
|
billing_session: "RemoteServerBillingSession",
|
2024-02-08 13:37:55 +01:00
|
|
|
*,
|
|
|
|
return_to_billing_page: Json[bool] = False,
|
|
|
|
manual_license_management: Json[bool] = False,
|
2024-07-12 02:30:23 +02:00
|
|
|
tier: Json[int] | None = None,
|
2024-03-04 00:44:59 +01:00
|
|
|
setup_payment_by_invoice: Json[bool] = False,
|
2024-02-08 13:37:55 +01:00
|
|
|
) -> HttpResponseRedirect:
|
|
|
|
review_billing_information_url = billing_session.get_stripe_customer_portal_url(
|
2024-03-04 00:44:59 +01:00
|
|
|
return_to_billing_page, manual_license_management, tier, setup_payment_by_invoice
|
2024-02-08 13:37:55 +01:00
|
|
|
)
|
|
|
|
return HttpResponseRedirect(review_billing_information_url)
|