mirror of https://github.com/zulip/zulip.git
org_settings: Add backend for `realm_jitsi_server_url` setting.
This commit adds a `jitsi_server_url` field to the Realm model, which will be used to save the URL of the custom Jitsi Meet server. In the database, `None` will encode the server-level default. We can't readily use `None` in the API, as it could be confused with "field not sent". Therefore, we will use the string "default" for this purpose. We have also introduced `server_jitsi_server_url` in the `/register` API. This will be used to display the server's default Jitsi server URL in the settings UI. The existing `jitsi_server_url` will now be calculated as `realm_jitsi_server_url || server_jitsi_server_url`. Fixes a part of #17914. Co-authored-by: Gaurav Pandey <gauravguitarrocks@gmail.com>
This commit is contained in:
parent
cb0aaa5197
commit
be653dd5b4
|
@ -20,6 +20,17 @@ format used by the Zulip server that they are interacting with.
|
|||
|
||||
## Changes in Zulip 8.0
|
||||
|
||||
**Feature level 212**
|
||||
|
||||
* [`GET /events`](/api/get-events), [`POST /register`](/api/register-queue),
|
||||
`PATCH /realm`: Added the `jitsi_server_url` field to the `realm` object,
|
||||
allowing organizations to set a custom Jitsi Meet server. Previously, this
|
||||
was only available as a server-level configuration.
|
||||
|
||||
* [`POST /register`](/api/register-queue): Added `server_jitsi_server_url`
|
||||
fields to the `realm` object. The existing `jitsi_server_url` will now be
|
||||
calculated as `realm_jitsi_server_url || server_jitsi_server_url`.
|
||||
|
||||
**Feature level 211**
|
||||
|
||||
* [`POST /streams/{stream_id}/delete_topic`](/api/delete-topic),
|
||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
|||
# Changes should be accompanied by documentation explaining what the
|
||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||
API_FEATURE_LEVEL = 211
|
||||
API_FEATURE_LEVEL = 212
|
||||
|
||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||
# only when going from an old version of the code to a newer version. Bump
|
||||
|
|
|
@ -866,6 +866,8 @@ def check_realm_update(
|
|||
assert isinstance(value, property_type)
|
||||
elif property_type == (int, type(None)):
|
||||
assert isinstance(value, int)
|
||||
elif property_type == (str, type(None)):
|
||||
assert isinstance(value, str)
|
||||
else:
|
||||
raise AssertionError(f"Unexpected property type {property_type}")
|
||||
|
||||
|
|
|
@ -337,10 +337,15 @@ def fetch_initial_state_data(
|
|||
state["realm_push_notifications_enabled"] = push_notifications_enabled()
|
||||
state["realm_default_external_accounts"] = get_default_external_accounts()
|
||||
|
||||
if settings.JITSI_SERVER_URL is not None:
|
||||
state["jitsi_server_url"] = settings.JITSI_SERVER_URL.rstrip("/")
|
||||
else: # nocoverage
|
||||
state["jitsi_server_url"] = None
|
||||
server_default_jitsi_server_url = (
|
||||
settings.JITSI_SERVER_URL.rstrip("/") if settings.JITSI_SERVER_URL is not None else None
|
||||
)
|
||||
state["server_jitsi_server_url"] = server_default_jitsi_server_url
|
||||
state["jitsi_server_url"] = (
|
||||
realm.jitsi_server_url
|
||||
if realm.jitsi_server_url is not None
|
||||
else server_default_jitsi_server_url
|
||||
)
|
||||
|
||||
if realm.notifications_stream and not realm.notifications_stream.deactivated:
|
||||
notifications_stream = realm.notifications_stream
|
||||
|
@ -1068,6 +1073,13 @@ def apply_event(
|
|||
state["zulip_plan_is_not_limited"] = event["value"] != Realm.PLAN_TYPE_LIMITED
|
||||
state["realm_upload_quota_mib"] = event["extra_data"]["upload_quota"]
|
||||
|
||||
if field == "realm_jitsi_server_url":
|
||||
state["jitsi_server_url"] = (
|
||||
state["realm_jitsi_server_url"]
|
||||
if state["realm_jitsi_server_url"] is not None
|
||||
else state["server_jitsi_server_url"]
|
||||
)
|
||||
|
||||
policy_permission_dict = {
|
||||
"create_public_stream_policy": "can_create_public_streams",
|
||||
"create_private_stream_policy": "can_create_private_streams",
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# Generated by Django 4.2.5 on 2023-09-19 17:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0474_realmuserdefault_web_stream_unreads_count_display_policy_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="realm",
|
||||
name="jitsi_server_url",
|
||||
field=models.URLField(default=None, null=True),
|
||||
),
|
||||
]
|
|
@ -671,6 +671,9 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
default=VIDEO_CHAT_PROVIDERS["jitsi_meet"]["id"]
|
||||
)
|
||||
|
||||
JITSI_SERVER_SPECIAL_VALUES_MAP = {"default": None}
|
||||
jitsi_server_url = models.URLField(null=True, default=None)
|
||||
|
||||
# Please access this via get_giphy_rating_options.
|
||||
GIPHY_RATING_OPTIONS = {
|
||||
"disabled": {
|
||||
|
@ -740,6 +743,7 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
invite_required=bool,
|
||||
invite_to_realm_policy=int,
|
||||
invite_to_stream_policy=int,
|
||||
jitsi_server_url=(str, type(None)),
|
||||
mandatory_topics=bool,
|
||||
message_content_allowed_in_email_notifications=bool,
|
||||
message_content_edit_limit_seconds=(int, type(None)),
|
||||
|
|
|
@ -4365,6 +4365,19 @@ paths:
|
|||
|
||||
**Changes**: None added as an option in Zulip 3.0 (feature level 1)
|
||||
to disable video call UI.
|
||||
jitsi_server_url:
|
||||
type: string
|
||||
nullable: true
|
||||
description: |
|
||||
The URL of the custom Jitsi Meet server configured in this organization's
|
||||
settings.
|
||||
|
||||
`null`, the default, means that the organization is using the should use the
|
||||
server-level configuration, `server_jitsi_server_url`.
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 212). Previously, this was only
|
||||
available as a server-level configuration, and required a server restart to
|
||||
change.
|
||||
waiting_period_threshold:
|
||||
type: integer
|
||||
description: |
|
||||
|
@ -13553,6 +13566,21 @@ paths:
|
|||
|
||||
**Changes**: None added as an option in Zulip 3.0 (feature level 1)
|
||||
to disable video call UI.
|
||||
realm_jitsi_server_url:
|
||||
type: string
|
||||
nullable: true
|
||||
description: |
|
||||
The URL of the custom Jitsi Meet server configured in this organization's
|
||||
settings.
|
||||
|
||||
`null`, the default, means that the organization is using the should use the
|
||||
server-level configuration, `server_jitsi_server_url`. A correct client
|
||||
supporting only the modern API should use `realm_jitsi_server_url ||
|
||||
server_jitsi_server_url` to create calls.
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 212). Previously, this was only
|
||||
available as a server-level configuration, which was available via the
|
||||
`jitsi_server_url` field.
|
||||
realm_giphy_rating:
|
||||
type: integer
|
||||
description: |
|
||||
|
@ -14009,10 +14037,21 @@ paths:
|
|||
on the external site.
|
||||
jitsi_server_url:
|
||||
type: string
|
||||
deprecated: true
|
||||
description: |
|
||||
Present if `realm` is present in `fetch_event_types`.
|
||||
|
||||
The base URL the organization uses to create Jitsi video calls.
|
||||
The base URL to be used to create Jitsi video calls. Equals
|
||||
`realm_jitsi_server_url || server_jitsi_server_url`.
|
||||
|
||||
**Changes**: Deprecated in Zulip 8.0 (feature level 212) and will
|
||||
eventually be removed. Previously, the Jitsi server to use was not
|
||||
configurable on a per-realm basis, and this field contained the server's
|
||||
configured Jitsi server. (Which is now provided as
|
||||
`server_jitsi_server_url`). Clients supporting older versions should fall
|
||||
back to this field when creating calls: using `realm_jitsi_server_url ||
|
||||
server_jitsi_server_url` with newer servers and using `jitsi_server_url`
|
||||
with servers below feature level 212.
|
||||
development_environment:
|
||||
type: boolean
|
||||
description: |
|
||||
|
@ -14164,6 +14203,16 @@ paths:
|
|||
since the last request.
|
||||
|
||||
**Changes**: New in Zulip 6.0 (feature level 140).
|
||||
server_jitsi_server_url:
|
||||
type: string
|
||||
nullable: true
|
||||
description: |
|
||||
The URL of the Jitsi server that the Zulip server is configured to use by
|
||||
default; the organization-level setting `realm_jitsi_server_url` takes
|
||||
precedence over this setting when both are set.
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 212). Previously, this value
|
||||
was available as the now-deprecated `jitsi_server_url`.
|
||||
event_queue_longpoll_timeout_seconds:
|
||||
type: integer
|
||||
description: |
|
||||
|
|
|
@ -2933,6 +2933,7 @@ class RealmPropertyActionTest(BaseAction):
|
|||
video_chat_provider=[
|
||||
Realm.VIDEO_CHAT_PROVIDERS["jitsi_meet"]["id"],
|
||||
],
|
||||
jitsi_server_url=["https://jitsi1.example.com", "https://jitsi2.example.com"],
|
||||
giphy_rating=[
|
||||
Realm.GIPHY_RATING_OPTIONS["disabled"]["id"],
|
||||
],
|
||||
|
|
|
@ -150,6 +150,7 @@ class HomeTest(ZulipTestCase):
|
|||
"realm_invite_to_realm_policy",
|
||||
"realm_invite_to_stream_policy",
|
||||
"realm_is_zephyr_mirror_realm",
|
||||
"realm_jitsi_server_url",
|
||||
"realm_linkifiers",
|
||||
"realm_logo_source",
|
||||
"realm_logo_url",
|
||||
|
@ -194,6 +195,7 @@ class HomeTest(ZulipTestCase):
|
|||
"server_generation",
|
||||
"server_inline_image_preview",
|
||||
"server_inline_url_embed_preview",
|
||||
"server_jitsi_server_url",
|
||||
"server_name_changes_disabled",
|
||||
"server_needs_upgrade",
|
||||
"server_presence_offline_threshold_seconds",
|
||||
|
|
|
@ -864,6 +864,36 @@ class RealmTest(ZulipTestCase):
|
|||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_success(result)
|
||||
|
||||
def test_jitsi_server_url(self) -> None:
|
||||
self.login("iago")
|
||||
realm = get_realm("zulip")
|
||||
self.assertEqual(realm.video_chat_provider, Realm.VIDEO_CHAT_PROVIDERS["jitsi_meet"]["id"])
|
||||
|
||||
req = dict(jitsi_server_url=orjson.dumps("").decode())
|
||||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_error(result, "jitsi_server_url is not an allowed_type")
|
||||
|
||||
req = dict(jitsi_server_url=orjson.dumps("invalidURL").decode())
|
||||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_error(result, "jitsi_server_url is not an allowed_type")
|
||||
|
||||
req = dict(jitsi_server_url=orjson.dumps(12).decode())
|
||||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_error(result, "jitsi_server_url is not an allowed_type")
|
||||
|
||||
valid_url = "https://jitsi.example.com"
|
||||
req = dict(jitsi_server_url=orjson.dumps(valid_url).decode())
|
||||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_success(result)
|
||||
realm = get_realm("zulip")
|
||||
self.assertEqual(realm.jitsi_server_url, valid_url)
|
||||
|
||||
req = dict(jitsi_server_url=orjson.dumps("default").decode())
|
||||
result = self.client_patch("/json/realm", req)
|
||||
self.assert_json_success(result)
|
||||
realm = get_realm("zulip")
|
||||
self.assertEqual(realm.jitsi_server_url, None)
|
||||
|
||||
def test_do_create_realm(self) -> None:
|
||||
realm = do_create_realm("realm_string_id", "realm name")
|
||||
|
||||
|
@ -1179,6 +1209,11 @@ class RealmAPITest(ZulipTestCase):
|
|||
).decode(),
|
||||
),
|
||||
],
|
||||
jitsi_server_url=[
|
||||
dict(
|
||||
jitsi_server_url=orjson.dumps("https://example.jit.si").decode(),
|
||||
),
|
||||
],
|
||||
giphy_rating=[
|
||||
Realm.GIPHY_RATING_OPTIONS["y"]["id"],
|
||||
Realm.GIPHY_RATING_OPTIONS["r"]["id"],
|
||||
|
@ -1200,7 +1235,7 @@ class RealmAPITest(ZulipTestCase):
|
|||
if vals is None:
|
||||
raise AssertionError(f"No test created for {name}")
|
||||
|
||||
if name == "video_chat_provider":
|
||||
if name in ("video_chat_provider", "jitsi_server_url"):
|
||||
self.set_up_db(name, vals[0][name])
|
||||
realm = self.update_with_api_multiple_value(vals[0])
|
||||
self.assertEqual(getattr(realm, name), orjson.loads(vals[0][name]))
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Any, Dict, Optional, Union
|
||||
from typing import Any, Dict, Mapping, Optional, Union
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.http import HttpRequest, HttpResponse
|
||||
|
@ -37,12 +37,23 @@ from zerver.lib.validator import (
|
|||
check_int_in,
|
||||
check_string_in,
|
||||
check_string_or_int,
|
||||
check_union,
|
||||
check_url,
|
||||
to_non_negative_int,
|
||||
)
|
||||
from zerver.models import Realm, RealmReactivationStatus, RealmUserDefault, UserProfile
|
||||
from zerver.views.user_settings import check_settings_values
|
||||
|
||||
|
||||
def parse_jitsi_server_url(
|
||||
value: str, special_values_map: Mapping[str, Optional[str]]
|
||||
) -> Optional[str]:
|
||||
if value in special_values_map:
|
||||
return special_values_map[value]
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@require_realm_admin
|
||||
@has_request_variables
|
||||
def update_realm(
|
||||
|
@ -131,6 +142,13 @@ def update_realm(
|
|||
json_validator=check_int_in(Realm.WILDCARD_MENTION_POLICY_TYPES), default=None
|
||||
),
|
||||
video_chat_provider: Optional[int] = REQ(json_validator=check_int, default=None),
|
||||
jitsi_server_url_raw: Optional[str] = REQ(
|
||||
"jitsi_server_url",
|
||||
json_validator=check_union(
|
||||
[check_string_in(list(Realm.JITSI_SERVER_SPECIAL_VALUES_MAP.keys())), check_url]
|
||||
),
|
||||
default=None,
|
||||
),
|
||||
giphy_rating: Optional[int] = REQ(json_validator=check_int, default=None),
|
||||
default_code_block_language: Optional[str] = REQ(default=None),
|
||||
digest_weekday: Optional[int] = REQ(
|
||||
|
@ -276,6 +294,28 @@ def update_realm(
|
|||
"move_messages_between_streams_limit_seconds"
|
||||
] = move_messages_between_streams_limit_seconds
|
||||
|
||||
jitsi_server_url: Optional[str] = None
|
||||
if jitsi_server_url_raw is not None:
|
||||
jitsi_server_url = parse_jitsi_server_url(
|
||||
jitsi_server_url_raw,
|
||||
Realm.JITSI_SERVER_SPECIAL_VALUES_MAP,
|
||||
)
|
||||
|
||||
# We handle the "None" case separately here because
|
||||
# in the loop below, do_set_realm_property is called only when
|
||||
# the setting value is not "None". For values other than "None",
|
||||
# the loop itself sets the value of 'jitsi_server_url' by
|
||||
# calling do_set_realm_property.
|
||||
if jitsi_server_url is None and realm.jitsi_server_url is not None:
|
||||
do_set_realm_property(
|
||||
realm,
|
||||
"jitsi_server_url",
|
||||
jitsi_server_url,
|
||||
acting_user=user_profile,
|
||||
)
|
||||
|
||||
data["jitsi_server_url"] = jitsi_server_url
|
||||
|
||||
# The user of `locals()` here is a bit of a code smell, but it's
|
||||
# restricted to the elements present in realm.property_types.
|
||||
#
|
||||
|
|
Loading…
Reference in New Issue