snapshot of https://github.com/zulip/zulip.git @ 48c69911c7dd4e03b20552eb3bd77ad8151c1357 (2026-09-15)
Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled

This commit is contained in:
upstream-mirror
2026-09-15 15:05:30 +05:00
commit 64898b2c79
8999 changed files with 1757358 additions and 0 deletions

View File

View File

@@ -0,0 +1,23 @@
import os
from django.http import HttpRequest, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from zerver.decorator import require_post
from zerver.lib.cache import get_cache_backend
from zerver.lib.per_request_cache import flush_per_request_caches
from zerver.lib.response import json_success
from zerver.models.clients import clear_client_cache
ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../")
# This is used only by the Puppeteer tests to clear all the cache after each run.
@csrf_exempt
@require_post
def remove_caches(request: HttpRequest) -> HttpResponse: # nocoverage
cache = get_cache_backend(None)
cache.clear()
clear_client_cache()
flush_per_request_caches()
return json_success(request)

View File

@@ -0,0 +1,20 @@
from urllib.parse import urljoin
from django.http import HttpRequest, HttpResponse, HttpResponseForbidden
from django.shortcuts import redirect
from django.utils.http import url_has_allowed_host_and_scheme
from zerver.lib.camo import is_camo_url_valid
def handle_camo_url(
request: HttpRequest, digest: str, received_url: str
) -> HttpResponse: # nocoverage
original_url = bytes.fromhex(received_url).decode()
if is_camo_url_valid(digest, original_url):
original_url = urljoin("/", original_url)
if url_has_allowed_host_and_scheme(original_url, allowed_hosts=None):
return redirect(original_url)
return HttpResponseForbidden("<p>Not a valid URL.</p>")
else:
return HttpResponseForbidden("<p>Not a valid URL.</p>")

View File

@@ -0,0 +1,165 @@
from typing import Any
from django.conf import settings
from django.contrib.auth import authenticate
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.utils.translation import gettext as _
from django.views.decorators.csrf import csrf_exempt
from zerver.context_processors import get_realm_from_request
from zerver.decorator import do_login, require_post
from zerver.lib.exceptions import (
AuthenticationFailedError,
InvalidSubdomainError,
JsonableError,
RealmDeactivatedError,
UserDeactivatedError,
)
from zerver.lib.response import json_success
from zerver.lib.subdomains import get_subdomain
from zerver.lib.typed_endpoint import typed_endpoint
from zerver.lib.validator import validate_login_email
from zerver.models import Realm, UserProfile
from zerver.models.realms import get_realm
from zerver.views.auth import get_safe_redirect_to
from zerver.views.errors import config_error
from zproject.backends import dev_auth_enabled
def get_dev_users(realm: Realm | None = None, extra_users_count: int = 10) -> list[UserProfile]:
# Development environments usually have only a few users, but
# it still makes sense to limit how many extra users we render to
# support performance testing with DevAuthBackend.
if realm is not None:
users_query = UserProfile.objects.select_related("realm").filter(
is_bot=False, is_active=True, realm=realm
)
else:
users_query = UserProfile.objects.select_related("realm").filter(
is_bot=False, is_active=True
)
shakespearian_users = users_query.exclude(email__startswith="extrauser").order_by("email")
extra_users = users_query.filter(email__startswith="extrauser").order_by("email")
# Limit the number of extra users we offer by default
extra_users = extra_users[0:extra_users_count]
users = [*shakespearian_users, *extra_users]
return users
def add_dev_login_context(realm: Realm | None, context: dict[str, Any]) -> None:
users = get_dev_users(realm)
context["current_realm"] = realm
context["all_realms"] = Realm.objects.filter(deactivated_redirect__isnull=True)
def sort(lst: list[UserProfile]) -> list[UserProfile]:
return sorted(lst, key=lambda u: u.delivery_email)
context["direct_owners"] = sort([u for u in users if u.is_realm_owner])
context["direct_admins"] = sort([u for u in users if u.is_realm_admin and not u.is_realm_owner])
context["guest_users"] = sort([u for u in users if u.is_guest])
context["direct_moderators"] = sort([u for u in users if u.role == UserProfile.ROLE_MODERATOR])
context["direct_users"] = sort([u for u in users if not (u.is_guest or u.is_moderator)])
@csrf_exempt
@typed_endpoint
def dev_direct_login(
request: HttpRequest,
*,
next: str = "/",
) -> HttpResponse:
# This function allows logging in without a password and should only be called
# in development environments. It may be called if the DevAuthBackend is included
# in settings.AUTHENTICATION_BACKENDS
if (not dev_auth_enabled()) or settings.PRODUCTION:
# This check is probably not required, since authenticate would fail without
# an enabled DevAuthBackend.
return config_error(request, "dev_not_supported")
subdomain = get_subdomain(request)
realm = get_realm(subdomain)
if request.POST.get("prefers_web_public_view") == "Anonymous login":
redirect_to = get_safe_redirect_to(next, realm.url)
return HttpResponseRedirect(redirect_to)
email = request.POST["direct_email"]
user_profile = authenticate(dev_auth_username=email, realm=realm)
if user_profile is None:
return config_error(request, "dev_not_supported")
assert isinstance(user_profile, UserProfile)
do_login(request, user_profile)
redirect_to = get_safe_redirect_to(next, user_profile.realm.url)
return HttpResponseRedirect(redirect_to)
def check_dev_auth_backend() -> None:
if settings.PRODUCTION:
raise JsonableError(_("Endpoint not available in production."))
if not dev_auth_enabled():
raise JsonableError(_("DevAuthBackend not enabled."))
@csrf_exempt
@require_post
@typed_endpoint
def api_dev_fetch_api_key(request: HttpRequest, *, username: str) -> HttpResponse:
"""This function allows logging in without a password on the Zulip
mobile apps when connecting to a Zulip development environment. It
requires DevAuthBackend to be included in settings.AUTHENTICATION_BACKENDS.
"""
check_dev_auth_backend()
# Django invokes authenticate methods by matching arguments, and this
# authentication flow will not invoke LDAP authentication because of
# this condition of Django so no need to check if LDAP backend is
# enabled.
validate_login_email(username)
realm = get_realm_from_request(request)
if realm is None:
raise InvalidSubdomainError
return_data: dict[str, bool] = {}
user_profile = authenticate(dev_auth_username=username, realm=realm, return_data=return_data)
if return_data.get("inactive_realm"):
raise RealmDeactivatedError
if return_data.get("inactive_user"):
raise UserDeactivatedError
if return_data.get("invalid_subdomain"): # nocoverage
raise InvalidSubdomainError
if user_profile is None:
# Since we're not actually checking passwords, this condition
# is when one's attempting to send an email address that
# doesn't have an account, i.e. it's definitely invalid username.
raise AuthenticationFailedError
assert isinstance(user_profile, UserProfile)
do_login(request, user_profile)
api_key = user_profile.api_key
return json_success(
request,
data={"api_key": api_key, "email": user_profile.delivery_email, "user_id": user_profile.id},
)
@csrf_exempt
def api_dev_list_users(request: HttpRequest) -> HttpResponse:
check_dev_auth_backend()
users = get_dev_users()
return json_success(
request,
data=dict(
direct_admins=[
dict(email=u.delivery_email, realm_url=u.realm.url)
for u in users
if u.is_realm_admin
],
direct_users=[
dict(email=u.delivery_email, realm_url=u.realm.url)
for u in users
if not u.is_realm_admin
],
),
)

View File

@@ -0,0 +1,167 @@
import os
from contextlib import suppress
from urllib.parse import urlencode
import orjson
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect, render
from django.views.decorators.http import require_safe
from confirmation.models import Confirmation
from zerver.actions.realm_settings import do_send_realm_reactivation_email
from zerver.actions.user_settings import do_change_user_delivery_email
from zerver.actions.users import change_user_is_active
from zerver.lib.email_notifications import enqueue_welcome_emails, send_account_registered_email
from zerver.lib.response import json_success
from zerver.lib.typed_endpoint import typed_endpoint
from zerver.models import Realm
from zerver.models.realms import get_realm
from zerver.models.streams import get_realm_stream
from zerver.models.users import get_user_by_delivery_email
from zerver.views.invite import INVITATION_LINK_VALIDITY_MINUTES
from zproject.email_backends import get_forward_address, set_forward_address
ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
@typed_endpoint
def email_page(request: HttpRequest, *, forward_address: str | None = None) -> HttpResponse:
if request.method == "POST":
assert forward_address is not None
set_forward_address(forward_address)
return json_success(request)
try:
with open(settings.EMAIL_CONTENT_LOG_PATH, "r+") as f:
content = f.read()
except FileNotFoundError:
content = ""
return render(
request,
"zerver/development/email_log.html",
{"log": content, "forward_address": get_forward_address()},
)
def clear_emails(request: HttpRequest) -> HttpResponse:
with suppress(FileNotFoundError):
os.remove(settings.EMAIL_CONTENT_LOG_PATH)
return redirect(email_page)
@require_safe
def generate_all_emails(request: HttpRequest) -> HttpResponse:
# We import Django test client and override_settings inside
# the view function, because it isn't needed in production
# elsewhere, and not importing it saves ~50ms of unnecessary
# manage.py startup time.
from django.test import Client, override_settings
# override_settings is used as a hacky workaround to disable
# rate limiting; this is fine because this view only runs in
# the development environment.
with override_settings(RATE_LIMITING=False):
client = Client()
# write fake data for all variables
registered_email = "hamlet@zulip.com"
unregistered_email_1 = "new-person@zulip.com"
unregistered_email_2 = "new-person-2@zulip.com"
invite_expires_in_minutes = INVITATION_LINK_VALIDITY_MINUTES
realm = get_realm("zulip")
other_realm = Realm.objects.exclude(string_id="zulip").first()
user = get_user_by_delivery_email(registered_email, realm)
# Password reset emails
# active account in realm
result = client.post(
"/accounts/password/reset/", {"email": registered_email}, HTTP_HOST=realm.host
)
assert result.status_code == 302
# deactivated user
change_user_is_active(user, False)
result = client.post(
"/accounts/password/reset/", {"email": registered_email}, HTTP_HOST=realm.host
)
assert result.status_code == 302
change_user_is_active(user, True)
# account on different realm
assert other_realm is not None
result = client.post(
"/accounts/password/reset/", {"email": registered_email}, HTTP_HOST=other_realm.host
)
assert result.status_code == 302
# no account anywhere
result = client.post(
"/accounts/password/reset/", {"email": unregistered_email_1}, HTTP_HOST=realm.host
)
assert result.status_code == 302
# Confirm account email
result = client.post(
"/accounts/home/", {"email": unregistered_email_1}, HTTP_HOST=realm.host
)
assert result.status_code == 302
# Find account email
result = client.post("/accounts/find/", {"emails": registered_email}, HTTP_HOST=realm.host)
assert result.status_code == 200
# New login email
logged_in = client.login(dev_auth_username=registered_email, realm=realm)
assert logged_in
# New user invite and reminder emails
stream = get_realm_stream("Denmark", user.realm.id)
result = client.post(
"/json/invites",
{
"invitee_emails": unregistered_email_2,
"invite_expires_in_minutes": invite_expires_in_minutes,
"stream_ids": orjson.dumps([stream.id]).decode(),
},
HTTP_HOST=realm.host,
)
assert result.status_code == 200
# Verification for new email
result = client.patch(
"/json/settings",
urlencode({"email": "hamlets-new@zulip.com"}),
content_type="application/x-www-form-urlencoded",
HTTP_HOST=realm.host,
)
assert result.status_code == 200
# Email change successful
key = (
Confirmation.objects.filter(type=Confirmation.EMAIL_CHANGE)
.latest("id")
.confirmation_key
)
user_profile = get_user_by_delivery_email(registered_email, realm)
result = client.post("/accounts/confirm_new_email/", {"key": key})
assert result.status_code == 200
# Reset the email value so we can run this again
do_change_user_delivery_email(user_profile, registered_email, acting_user=None)
# Initial email with new account information for normal user
send_account_registered_email(user_profile)
# Onboarding emails for normal user
enqueue_welcome_emails(user_profile)
# Initial email with new account information for admin user
send_account_registered_email(get_user_by_delivery_email("iago@zulip.com", realm))
# Onboarding emails for admin user
enqueue_welcome_emails(
get_user_by_delivery_email("iago@zulip.com", realm), realm_creation=True
)
# Realm reactivation email
do_send_realm_reactivation_email(realm, acting_user=None)
return redirect(email_page)

View File

@@ -0,0 +1,57 @@
import os
import werkzeug
from django.conf import settings
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
def help_dev_mode_view(request: HttpRequest, subpath: str = "") -> HttpResponse:
"""
Dev only view that displays help information for setting up the
help center dev server in the default `run-dev` mode where the
help center server is not running. Also serves raw MDX content when
`raw` query param is passed is passed.
"""
def read_mdx_file(filename: str) -> HttpResponse:
file_path = os.path.join(
settings.DEPLOY_ROOT, "starlight_help", "src", "content", "docs", f"{filename}.mdx"
)
try:
with open(file_path, encoding="utf-8") as f:
content = f.read()
return HttpResponse(content, content_type="text/plain")
except OSError:
return HttpResponse("Error reading MDX file", status=500)
mdx_file_exists = False
is_requesting_raw_file = request.GET.get("raw") == ""
if subpath:
subpath = werkzeug.utils.secure_filename(subpath)
raw_url = f"/help/{subpath}?raw"
mdx_path = os.path.join(
settings.DEPLOY_ROOT, "starlight_help", "src", "content", "docs", f"{subpath}.mdx"
)
mdx_file_exists = os.path.exists(mdx_path) and "/include/" not in mdx_path
if mdx_file_exists and is_requesting_raw_file:
return read_mdx_file(subpath)
else:
if request.path.endswith("/"):
raw_url = "/help/?raw"
else:
raw_url = "/help?raw"
mdx_file_exists = True
if is_requesting_raw_file:
return read_mdx_file("index")
return render(
request,
"zerver/development/dev_help.html",
{
"subpath": subpath,
"mdx_file_exists": mdx_file_exists,
"raw_url": raw_url,
},
)

View File

@@ -0,0 +1,158 @@
import os
from contextlib import suppress
from typing import TYPE_CHECKING, Any
import orjson
from django.http import HttpRequest, HttpResponse
from django.http.response import HttpResponseBase
from django.shortcuts import render
from django.test import Client
from pydantic import Json
from zerver.lib.exceptions import JsonableError, ResourceNotFoundError
from zerver.lib.integrations import INCOMING_WEBHOOK_INTEGRATIONS
from zerver.lib.response import json_success
from zerver.lib.typed_endpoint import PathOnly, typed_endpoint
from zerver.lib.webhooks.common import call_fixture_to_headers, standardize_headers
from zerver.models import UserProfile
from zerver.models.realms import get_realm
if TYPE_CHECKING:
from django.test.client import _MonkeyPatchedWSGIResponse as TestHttpResponse
ZULIP_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../")
def get_webhook_integrations() -> list[str]:
return [integration.name for integration in INCOMING_WEBHOOK_INTEGRATIONS]
def get_valid_integration_name(name: str) -> str | None:
for integration_name in get_webhook_integrations():
if name == integration_name:
return integration_name
return None
def dev_panel(request: HttpRequest) -> HttpResponse:
integrations = get_webhook_integrations()
bots = UserProfile.objects.filter(is_bot=True, bot_type=UserProfile.INCOMING_WEBHOOK_BOT)
context = {
"integrations": integrations,
"bots": bots,
# We set isolated_page to avoid clutter from footer/header.
"isolated_page": True,
}
return render(request, "zerver/development/integrations_dev_panel.html", context)
def send_webhook_fixture_message(
url: str, body: str, is_json: bool, custom_headers: dict[str, Any]
) -> "TestHttpResponse":
client = Client()
realm = get_realm("zulip")
standardized_headers = standardize_headers(custom_headers)
http_host = standardized_headers.pop("HTTP_HOST", realm.host)
if is_json:
content_type = standardized_headers.pop("HTTP_CONTENT_TYPE", "application/json")
else:
content_type = standardized_headers.pop("HTTP_CONTENT_TYPE", "text/plain")
return client.post(
url,
body,
content_type=content_type,
follow=False,
secure=False,
headers=None,
query_params=None,
HTTP_HOST=http_host,
**standardized_headers,
)
@typed_endpoint
def get_fixtures(request: HttpRequest, *, integration_name: PathOnly[str]) -> HttpResponse:
valid_integration_name = get_valid_integration_name(integration_name)
if not valid_integration_name:
raise ResourceNotFoundError(f'"{integration_name}" is not a valid webhook integration.')
fixtures = {}
fixtures_dir = os.path.join(ZULIP_PATH, f"zerver/webhooks/{valid_integration_name}/fixtures")
if not os.path.exists(fixtures_dir):
msg = f'The integration "{valid_integration_name}" does not have fixtures.'
raise ResourceNotFoundError(msg)
for fixture in os.listdir(fixtures_dir):
fixture_path = os.path.join(fixtures_dir, fixture)
with open(fixture_path) as f:
body = f.read()
# The file extension will be used to determine the type.
with suppress(orjson.JSONDecodeError):
body = orjson.loads(body)
headers_raw = call_fixture_to_headers(
valid_integration_name, "".join(fixture.split(".")[:-1])
)
def fix_name(header: str) -> str: # nocoverage
return header.removeprefix("HTTP_") # HTTP_ is a prefix intended for Django.
headers = {fix_name(k): v for k, v in headers_raw.items()}
fixtures[fixture] = {"body": body, "headers": headers}
return json_success(request, data={"fixtures": fixtures})
@typed_endpoint
def check_send_webhook_fixture_message(
request: HttpRequest,
*,
url: str,
body: str,
is_json: Json[bool],
custom_headers: str,
) -> HttpResponseBase:
try:
custom_headers_dict = orjson.loads(custom_headers)
except orjson.JSONDecodeError as ve: # nocoverage
raise JsonableError(f"Custom HTTP headers are not in a valid JSON format. {ve}") # nolint
response = send_webhook_fixture_message(url, body, is_json, custom_headers_dict)
if response.status_code == 200:
responses = [{"status_code": response.status_code, "message": response.content.decode()}]
return json_success(request, data={"responses": responses})
else: # nocoverage
return response
@typed_endpoint
def send_all_webhook_fixture_messages(
request: HttpRequest, *, url: str, integration_name: str
) -> HttpResponse:
valid_integration_name = get_valid_integration_name(integration_name)
if not valid_integration_name: # nocoverage
raise ResourceNotFoundError(f'"{integration_name}" is not a valid webhook integration.')
fixtures_dir = os.path.join(ZULIP_PATH, f"zerver/webhooks/{valid_integration_name}/fixtures")
if not os.path.exists(fixtures_dir):
msg = f'The integration "{valid_integration_name}" does not have fixtures.'
raise ResourceNotFoundError(msg)
responses = []
for fixture in os.listdir(fixtures_dir):
fixture_path = os.path.join(fixtures_dir, fixture)
with open(fixture_path) as f:
content = f.read()
x = fixture.split(".")
fixture_name, fixture_format = "".join(_ for _ in x[:-1]), x[-1]
headers = call_fixture_to_headers(valid_integration_name, fixture_name)
is_json = fixture_format == "json"
response = send_webhook_fixture_message(url, content, is_json, headers)
responses.append(
{
"status_code": response.status_code,
"fixture_name": fixture,
"message": response.content.decode(),
}
)
return json_success(request, data={"responses": responses})

View File

@@ -0,0 +1,104 @@
from typing import TYPE_CHECKING, Any, cast
from django.conf import settings
from django.http import HttpRequest, HttpResponse, HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from confirmation.models import Confirmation, create_confirmation_link
from zerver.context_processors import get_realm_from_request
from zerver.lib.response import json_success
from zerver.models import Realm, UserProfile
from zerver.views.auth import (
create_preregistration_realm,
create_preregistration_user,
redirect_and_log_into_subdomain,
)
from zerver.views.registration import accounts_register, create_demo_helper
from zproject.backends import ExternalAuthResult
if TYPE_CHECKING:
from django.http.request import _ImmutableQueryDict
# This is used only by the Puppeteer test in realm-creation.ts.
def confirmation_key(request: HttpRequest) -> HttpResponse:
return json_success(request, data=request.session["confirmation_key"])
def modify_postdata(request: HttpRequest, **kwargs: Any) -> None:
new_post = request.POST.copy()
for key, value in kwargs.items():
new_post[key] = value
new_post._mutable = False
request.POST = cast("_ImmutableQueryDict", new_post)
@csrf_exempt
def register_development_user(request: HttpRequest) -> HttpResponse:
realm = get_realm_from_request(request)
if realm is None: # nocoverage
return HttpResponseRedirect(
f"{settings.EXTERNAL_URI_SCHEME}{settings.REALM_HOSTS['zulip']}/devtools/register_user/",
status=307,
)
count = UserProfile.objects.count()
name = f"user-{count}"
email = f"{name}@zulip.com"
prereg = create_preregistration_user(email, realm, password_required=False)
activation_url = create_confirmation_link(prereg, Confirmation.USER_REGISTRATION)
key = activation_url.split("/")[-1]
# Need to add test data to POST request as it doesn't originally contain the required parameters
modify_postdata(request, key=key, full_name=name, password="test", terms="true")
return accounts_register(request)
@csrf_exempt
def register_development_realm(request: HttpRequest) -> HttpResponse:
count = UserProfile.objects.count()
name = f"user-{count}"
email = f"{name}@zulip.com"
realm_name = f"realm-{count}"
realm_type = Realm.ORG_TYPES["business"]["id"]
realm_default_language = "en"
realm_subdomain = realm_name
prereg_realm = create_preregistration_realm(
email, realm_name, realm_subdomain, realm_type, realm_default_language
)
activation_url = create_confirmation_link(
prereg_realm, Confirmation.NEW_REALM_USER_REGISTRATION, no_associated_realm_object=True
)
key = activation_url.split("/")[-1]
# Need to add test data to POST request as it doesn't originally contain the required parameters
modify_postdata(
request,
key=key,
realm_name=realm_name,
realm_type=realm_type,
realm_default_language=realm_default_language,
full_name=name,
password="test",
realm_subdomain=realm_subdomain,
terms="true",
how_realm_creator_found_zulip="ad",
how_realm_creator_found_zulip_extra_context="test",
)
return accounts_register(request)
@csrf_exempt
def register_demo_development_realm(request: HttpRequest) -> HttpResponse:
user_profile = create_demo_helper(
request,
realm_type=Realm.ORG_TYPES["education"]["id"],
realm_default_language="en",
how_realm_creator_found_zulip="existing_user",
how_realm_creator_found_zulip_extra_context="test",
timezone="US/Pacific",
)
return redirect_and_log_into_subdomain(
ExternalAuthResult(user_profile=user_profile, data_dict={"is_realm_creation": True})
)

View File

@@ -0,0 +1,63 @@
import os
from django.http import HttpRequest, HttpResponse
from django.shortcuts import render
background_colors = [
{"name": "Default background", "css_var": "--color-background"},
{"name": "Popover background", "css_var": "--color-background-popover-menu"},
{"name": "Modal background", "css_var": "--color-background-modal"},
{"name": "Compose background", "css_var": "--color-compose-box-background"},
]
def get_svg_filenames() -> list[str]:
icons_dir = os.path.join(os.path.dirname(__file__), "../../../web/icons")
# Get all .svg file names from the directory
svg_files = [f for f in os.listdir(icons_dir) if f.endswith(".svg")]
# Remove the .svg extension from the file names
icon_names = [os.path.splitext(f)[0] for f in svg_files]
# Sort the list alphabetically
return sorted(icon_names)
def showroom_component_buttons(request: HttpRequest) -> HttpResponse:
context = {
"background_colors": background_colors,
"icons": get_svg_filenames(),
"page_is_showroom": True,
"showroom_component": "buttons",
"doc_root_title": "Button styles browser",
# We set isolated_page to avoid clutter from footer/header.
"isolated_page": True,
}
return render(request, "zerver/development/showroom/buttons.html", context)
def showroom_component_banners(request: HttpRequest) -> HttpResponse:
context = {
"background_colors": background_colors,
"icons": get_svg_filenames(),
"page_is_showroom": True,
"showroom_component": "banners",
"doc_root_title": "Banner styles browser",
# We set isolated_page to avoid clutter from footer/header.
"isolated_page": True,
}
return render(request, "zerver/development/showroom/banners.html", context)
def showroom_component_inputs(request: HttpRequest) -> HttpResponse:
context = {
"background_colors": background_colors,
"icons": get_svg_filenames(),
"page_is_showroom": True,
"showroom_component": "inputs",
"doc_root_title": "Input styles browser",
# We set isolated_page to avoid clutter from footer/header.
"isolated_page": True,
}
return render(request, "zerver/development/showroom/inputs.html", context)