registration: Set the organization language at creation time.

In this commit, we add a new dropdown 'Organization language' on
the `/new` and `/realm/register` pages. This dropdown allows setting
the language of the organization during its creation. This allows
messages from Welcome Bot and introductory messages in streams to be
internationalized.

Fixes a part of #25729.
This commit is contained in:
Hemant Umre 2023-09-13 01:28:58 +05:30 committed by Tim Abbott
parent f8aac58a6a
commit ac1f711fef
14 changed files with 191 additions and 18 deletions

View File

@ -30,6 +30,7 @@ page can be easily identified in it's respective JavaScript file -->
<input type="hidden" class="email" id="email" value="{{ email }}" name="email"/>&nbsp; <input type="hidden" class="email" id="email" value="{{ email }}" name="email"/>&nbsp;
<input type="hidden" class="realm_name" value="{{ new_realm_name }}" name="realm_name"/>&nbsp; <input type="hidden" class="realm_name" value="{{ new_realm_name }}" name="realm_name"/>&nbsp;
<input type="hidden" class="realm_type" value="{{ realm_type }}" name="realm_type"/>&nbsp; <input type="hidden" class="realm_type" value="{{ realm_type }}" name="realm_type"/>&nbsp;
<input type="hidden" class="realm_default_language" value="{{ realm_default_language }}" name="realm_default_language"/>&nbsp;
<input type="hidden" class="realm_subdomain" value="{{ realm_subdomain }}" name="realm_subdomain"/>&nbsp; <input type="hidden" class="realm_subdomain" value="{{ realm_subdomain }}" name="realm_subdomain"/>&nbsp;
</form> </form>
{% else %} {% else %}

View File

@ -34,6 +34,23 @@
<label for="realm_type" class="inline-block label-title">{{ _('Organization type') }}</label> <label for="realm_type" class="inline-block label-title">{{ _('Organization type') }}</label>
</div> </div>
<div class="input-box">
<div class="inline-block relative">
<select name="realm_default_language" id="realm_default_language">
{% for language in language_list %}
<option value="{{ language.code }}" {% if form.realm_default_language.value() == language.code %}selected{% endif %} >{{ _(language.name) }}</option>
{% endfor %}
</select>
</div>
<label for="realm_default_language" class="inline-block label-title">
{{ _('Organization language') }}
<a href="/help/configure-organization-language" target="_blank" rel="noopener noreferrer">
<i class="fa fa-question-circle-o" aria-hidden="true"></i>
</a>
</label>
</div>
<div class="input-box"> <div class="input-box">
<label class="static org-url"> <label class="static org-url">
{{ _('Organization URL') }} {{ _('Organization URL') }}

View File

@ -52,6 +52,10 @@ Form is validated both client-side using jquery-validation (see signup.js) and s
<label for="id_realm_type" class="inline-block label-title">{{ _('Organization type') }}</label> <label for="id_realm_type" class="inline-block label-title">{{ _('Organization type') }}</label>
<div id="id_realm_type" class="not-editable-realm-field">{{ selected_realm_type_name }}</div> <div id="id_realm_type" class="not-editable-realm-field">{{ selected_realm_type_name }}</div>
</div> </div>
<div class="input-box">
<label for="id_realm_default_language" class="inline-block label-title">{{ _('Organization language') }}</label>
<div id="id_realm_default_language" class="not-editable-realm-field">{{ selected_realm_default_language_name }}</div>
</div>
<div class="input-box"> <div class="input-box">
<label for="id_realm_subdomain" class="inline-block label-title">{{ _('Organization URL') }}</label> <label for="id_realm_subdomain" class="inline-block label-title">{{ _('Organization URL') }}</label>
<div id="id_realm_subdomain" class="not-editable-realm-field">{% if form.realm_subdomain.value() %}{{ form.realm_subdomain.value() }}.{% endif %}{{external_host}}</div> <div id="id_realm_subdomain" class="not-editable-realm-field">{% if form.realm_subdomain.value() %}{{ form.realm_subdomain.value() }}.{% endif %}{{external_host}}</div>

View File

@ -159,6 +159,7 @@ def do_create_realm(
invite_required: Optional[bool] = None, invite_required: Optional[bool] = None,
plan_type: Optional[int] = None, plan_type: Optional[int] = None,
org_type: Optional[int] = None, org_type: Optional[int] = None,
default_language: Optional[str] = None,
date_created: Optional[datetime.datetime] = None, date_created: Optional[datetime.datetime] = None,
is_demo_organization: bool = False, is_demo_organization: bool = False,
enable_read_receipts: Optional[bool] = None, enable_read_receipts: Optional[bool] = None,
@ -184,6 +185,8 @@ def do_create_realm(
kwargs["plan_type"] = plan_type kwargs["plan_type"] = plan_type
if org_type is not None: if org_type is not None:
kwargs["org_type"] = org_type kwargs["org_type"] = org_type
if default_language is not None:
kwargs["default_language"] = default_language
if enable_spectator_access is not None: if enable_spectator_access is not None:
if enable_spectator_access: if enable_spectator_access:
# Realms with LIMITED plan cannot have spectators enabled. # Realms with LIMITED plan cannot have spectators enabled.

View File

@ -14,6 +14,7 @@ from version import (
ZULIP_VERSION, ZULIP_VERSION,
) )
from zerver.lib.exceptions import InvalidSubdomainError from zerver.lib.exceptions import InvalidSubdomainError
from zerver.lib.i18n import get_language_list
from zerver.lib.realm_description import get_realm_rendered_description, get_realm_text_description from zerver.lib.realm_description import get_realm_rendered_description, get_realm_text_description
from zerver.lib.realm_icon import get_realm_icon_url from zerver.lib.realm_icon import get_realm_icon_url
from zerver.lib.request import RequestNotes from zerver.lib.request import RequestNotes
@ -259,6 +260,7 @@ def latest_info_context() -> Dict[str, str]:
def get_realm_create_form_context() -> Dict[str, Any]: def get_realm_create_form_context() -> Dict[str, Any]:
context = { context = {
"language_list": get_language_list(),
"MAX_REALM_NAME_LENGTH": str(Realm.MAX_REALM_NAME_LENGTH), "MAX_REALM_NAME_LENGTH": str(Realm.MAX_REALM_NAME_LENGTH),
"MAX_REALM_SUBDOMAIN_LENGTH": str(Realm.MAX_REALM_SUBDOMAIN_LENGTH), "MAX_REALM_SUBDOMAIN_LENGTH": str(Realm.MAX_REALM_SUBDOMAIN_LENGTH),
"root_domain_available": is_root_domain_available(), "root_domain_available": is_root_domain_available(),

View File

@ -28,6 +28,7 @@ from zerver.lib.email_validation import (
email_reserved_for_system_bots_error, email_reserved_for_system_bots_error,
) )
from zerver.lib.exceptions import JsonableError, RateLimitedError from zerver.lib.exceptions import JsonableError, RateLimitedError
from zerver.lib.i18n import get_language_list
from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain
from zerver.lib.rate_limiter import RateLimitedObject, rate_limit_request_by_ip from zerver.lib.rate_limiter import RateLimitedObject, rate_limit_request_by_ip
from zerver.lib.send_email import FromAddress, send_email from zerver.lib.send_email import FromAddress, send_email
@ -140,6 +141,7 @@ class RealmDetailsForm(forms.Form):
realm_type = forms.TypedChoiceField( realm_type = forms.TypedChoiceField(
coerce=int, choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()] coerce=int, choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()]
) )
realm_default_language = forms.ChoiceField(choices=[])
realm_name = forms.CharField(max_length=Realm.MAX_REALM_NAME_LENGTH) realm_name = forms.CharField(max_length=Realm.MAX_REALM_NAME_LENGTH)
def __init__(self, *args: Any, **kwargs: Any) -> None: def __init__(self, *args: Any, **kwargs: Any) -> None:
@ -147,6 +149,9 @@ class RealmDetailsForm(forms.Form):
del kwargs["realm_creation"] del kwargs["realm_creation"]
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields["realm_default_language"] = forms.ChoiceField(
choices=[(lang["code"], lang["name"]) for lang in get_language_list()],
)
def clean_realm_subdomain(self) -> str: def clean_realm_subdomain(self) -> str:
if not self.realm_creation: if not self.realm_creation:
@ -192,6 +197,10 @@ class RegistrationForm(RealmDetailsForm):
choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()], choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()],
required=self.realm_creation, required=self.realm_creation,
) )
self.fields["realm_default_language"] = forms.ChoiceField(
choices=[(lang["code"], lang["name"]) for lang in get_language_list()],
required=self.realm_creation,
)
def clean_full_name(self) -> str: def clean_full_name(self) -> str:
try: try:

View File

@ -820,6 +820,7 @@ Output:
source_realm_id: str = "", source_realm_id: str = "",
key: Optional[str] = None, key: Optional[str] = None,
realm_type: int = Realm.ORG_TYPES["business"]["id"], realm_type: int = Realm.ORG_TYPES["business"]["id"],
realm_default_language: str = "en",
enable_marketing_emails: Optional[bool] = None, enable_marketing_emails: Optional[bool] = None,
email_address_visibility: Optional[int] = None, email_address_visibility: Optional[int] = None,
is_demo_organization: bool = False, is_demo_organization: bool = False,
@ -840,6 +841,7 @@ Output:
"realm_name": realm_name, "realm_name": realm_name,
"realm_subdomain": realm_subdomain, "realm_subdomain": realm_subdomain,
"realm_type": realm_type, "realm_type": realm_type,
"realm_default_language": realm_default_language,
"key": key if key is not None else find_key_by_email(email), "key": key if key is not None else find_key_by_email(email),
"timezone": timezone, "timezone": timezone,
"terms": True, "terms": True,
@ -874,12 +876,14 @@ Output:
realm_subdomain: str, realm_subdomain: str,
realm_name: str, realm_name: str,
realm_type: int = Realm.ORG_TYPES["business"]["id"], realm_type: int = Realm.ORG_TYPES["business"]["id"],
realm_default_language: str = "en",
realm_in_root_domain: Optional[str] = None, realm_in_root_domain: Optional[str] = None,
) -> "TestHttpResponse": ) -> "TestHttpResponse":
payload = { payload = {
"email": email, "email": email,
"realm_name": realm_name, "realm_name": realm_name,
"realm_type": realm_type, "realm_type": realm_type,
"realm_default_language": realm_default_language,
"realm_subdomain": realm_subdomain, "realm_subdomain": realm_subdomain,
} }
if realm_in_root_domain is not None: if realm_in_root_domain is not None:

View File

@ -0,0 +1,20 @@
# Generated by Django 4.2.5 on 2023-09-12 19:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
(
"zerver",
"0483_rename_escape_navigates_to_default_view_realmuserdefault_web_escape_navigates_to_home_view_and_more",
),
]
operations = [
migrations.AddField(
model_name="preregistrationrealm",
name="default_language",
field=models.CharField(default="en", max_length=50),
),
]

View File

@ -2434,6 +2434,10 @@ class PreregistrationRealm(models.Model):
default=Realm.ORG_TYPES["unspecified"]["id"], default=Realm.ORG_TYPES["unspecified"]["id"],
choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()], choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()],
) )
default_language = models.CharField(
default="en",
max_length=MAX_LANGUAGE_ID_LENGTH,
)
string_id = models.CharField(max_length=Realm.MAX_REALM_SUBDOMAIN_LENGTH) string_id = models.CharField(max_length=Realm.MAX_REALM_SUBDOMAIN_LENGTH)
email = models.EmailField() email = models.EmailField()

View File

@ -310,6 +310,7 @@ class TestGenerateRealmCreationLink(ZulipTestCase):
"email": email, "email": email,
"realm_name": "Zulip test", "realm_name": "Zulip test",
"realm_type": Realm.ORG_TYPES["business"]["id"], "realm_type": Realm.ORG_TYPES["business"]["id"],
"realm_default_language": "en",
"realm_subdomain": "custom-test", "realm_subdomain": "custom-test",
}, },
) )
@ -337,12 +338,13 @@ class TestGenerateRealmCreationLink(ZulipTestCase):
"email": email, "email": email,
"realm_name": realm_name, "realm_name": realm_name,
"realm_type": Realm.ORG_TYPES["business"]["id"], "realm_type": Realm.ORG_TYPES["business"]["id"],
"realm_default_language": "en",
"realm_subdomain": string_id, "realm_subdomain": string_id,
}, },
) )
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertEqual( self.assertEqual(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}", f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}",
result["Location"], result["Location"],
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])

View File

@ -748,7 +748,7 @@ class PasswordResetTest(ZulipTestCase):
self.assert_in_success_response(["/accounts/home/"], result) self.assert_in_success_response(["/accounts/home/"], result)
result = self.client_get( result = self.client_get(
"/accounts/new/send_confirm/?email=alice@example.com&realm_name=Zulip+test&realm_type=10&realm_subdomain=zuliptest" "/accounts/new/send_confirm/?email=alice@example.com&realm_name=Zulip+test&realm_type=10&realm_default_language=en&realm_subdomain=zuliptest"
) )
self.assert_in_success_response(["/new/"], result) self.assert_in_success_response(["/new/"], result)
@ -1256,7 +1256,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(org_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(org_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1264,6 +1264,7 @@ class RealmCreationTest(ZulipTestCase):
prereg_realm = PreregistrationRealm.objects.get(email=email) prereg_realm = PreregistrationRealm.objects.get(email=email)
self.assertEqual(prereg_realm.name, "Zulip Test") self.assertEqual(prereg_realm.name, "Zulip Test")
self.assertEqual(prereg_realm.org_type, Realm.ORG_TYPES["business"]["id"]) self.assertEqual(prereg_realm.org_type, Realm.ORG_TYPES["business"]["id"])
self.assertEqual(prereg_realm.default_language, "en")
self.assertEqual(prereg_realm.string_id, string_id) self.assertEqual(prereg_realm.string_id, string_id)
# Check confirmation email has the correct subject and body, extract # Check confirmation email has the correct subject and body, extract
@ -1295,6 +1296,7 @@ class RealmCreationTest(ZulipTestCase):
# Check defaults # Check defaults
self.assertEqual(realm.org_type, Realm.ORG_TYPES["business"]["id"]) self.assertEqual(realm.org_type, Realm.ORG_TYPES["business"]["id"])
self.assertEqual(realm.default_language, "en")
self.assertEqual(realm.emails_restricted_to_domains, False) self.assertEqual(realm.emails_restricted_to_domains, False)
self.assertEqual(realm.invite_required, True) self.assertEqual(realm.invite_required, True)
@ -1397,7 +1399,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1443,7 +1445,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1492,7 +1494,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1552,7 +1554,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1597,7 +1599,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1648,7 +1650,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=35&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=35&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1683,6 +1685,61 @@ class RealmCreationTest(ZulipTestCase):
self.assertIn("Using Zulip for a class guide", welcome_msg.content) self.assertIn("Using Zulip for a class guide", welcome_msg.content)
self.assertIn("demo organization", welcome_msg.content) self.assertIn("demo organization", welcome_msg.content)
@override_settings(OPEN_REALM_CREATION=True)
def test_create_realm_with_custom_language(self) -> None:
email = "user1@test.com"
password = "test"
string_id = "custom-test"
realm_name = "Zulip Test"
realm_language = "it"
# Make sure the realm does not exist
with self.assertRaises(Realm.DoesNotExist):
get_realm(string_id)
# Create new realm with the email
result = self.submit_realm_creation_form(
email,
realm_subdomain=string_id,
realm_name=realm_name,
realm_default_language=realm_language,
)
self.assertEqual(result.status_code, 302)
self.assertTrue(
result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language={realm_language}&realm_subdomain={string_id}"
)
)
result = self.client_get(result["Location"])
self.assert_in_response("check your email", result)
prereg_realm = PreregistrationRealm.objects.get(email=email)
# Check default_language field of PreregistrationRealm object
self.assertEqual(prereg_realm.default_language, realm_language)
# Visit the confirmation link.
confirmation_url = self.get_confirmation_url_from_outbox(email)
result = self.client_get(confirmation_url)
self.assertEqual(result.status_code, 200)
result = self.submit_reg_form_for_user(
email,
password,
realm_subdomain=string_id,
realm_name=realm_name,
realm_default_language=realm_language,
)
self.assertEqual(result.status_code, 302)
result = self.client_get(result["Location"], subdomain=string_id)
self.assertEqual(result.status_code, 302)
self.assertEqual(result["Location"], "http://custom-test.testserver")
# Make sure the realm is created and check default_language field
realm = get_realm(string_id)
self.assertEqual(realm.string_id, string_id)
self.assertEqual(realm.default_language, realm_language)
@override_settings(OPEN_REALM_CREATION=True, FREE_TRIAL_DAYS=30) @override_settings(OPEN_REALM_CREATION=True, FREE_TRIAL_DAYS=30)
def test_create_realm_during_free_trial(self) -> None: def test_create_realm_during_free_trial(self) -> None:
password = "test" password = "test"
@ -1700,7 +1757,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_subdomain={string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1756,7 +1813,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(first_realm_name)}&realm_type=10&realm_subdomain={first_string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(first_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={first_string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])
@ -1770,7 +1827,7 @@ class RealmCreationTest(ZulipTestCase):
self.assertEqual(result.status_code, 302) self.assertEqual(result.status_code, 302)
self.assertTrue( self.assertTrue(
result["Location"].endswith( result["Location"].endswith(
f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(second_realm_name)}&realm_type=10&realm_subdomain={second_string_id}" f"/accounts/new/send_confirm/?email={urllib.parse.quote(email)}&realm_name={urllib.parse.quote_plus(second_realm_name)}&realm_type=10&realm_default_language=en&realm_subdomain={second_string_id}"
) )
) )
result = self.client_get(result["Location"]) result = self.client_get(result["Location"])

View File

@ -143,12 +143,14 @@ def create_preregistration_realm(
name: str, name: str,
string_id: str, string_id: str,
org_type: int, org_type: int,
default_language: str,
) -> PreregistrationRealm: ) -> PreregistrationRealm:
return PreregistrationRealm.objects.create( return PreregistrationRealm.objects.create(
email=email, email=email,
name=name, name=name,
string_id=string_id, string_id=string_id,
org_type=org_type, org_type=org_type,
default_language=default_language,
) )

View File

@ -65,8 +65,11 @@ def register_development_realm(request: HttpRequest) -> HttpResponse:
email = f"{name}@zulip.com" email = f"{name}@zulip.com"
realm_name = f"realm-{count}" realm_name = f"realm-{count}"
realm_type = Realm.ORG_TYPES["business"]["id"] realm_type = Realm.ORG_TYPES["business"]["id"]
realm_default_language = "en"
realm_subdomain = realm_name realm_subdomain = realm_name
prereg_realm = create_preregistration_realm(email, realm_name, realm_subdomain, realm_type) prereg_realm = create_preregistration_realm(
email, realm_name, realm_subdomain, realm_type, realm_default_language
)
activation_url = create_confirmation_link( activation_url = create_confirmation_link(
prereg_realm, Confirmation.REALM_CREATION, realm_creation=True prereg_realm, Confirmation.REALM_CREATION, realm_creation=True
) )
@ -77,6 +80,7 @@ def register_development_realm(request: HttpRequest) -> HttpResponse:
key=key, key=key,
realm_name=realm_name, realm_name=realm_name,
realm_type=realm_type, realm_type=realm_type,
realm_default_language=realm_default_language,
full_name=name, full_name=name,
password="test", password="test",
realm_subdomain=realm_subdomain, realm_subdomain=realm_subdomain,
@ -91,11 +95,14 @@ def register_demo_development_realm(request: HttpRequest) -> HttpResponse:
# Demo organization owners are not required to provide a name or email. # Demo organization owners are not required to provide a name or email.
name = "Your name" name = "Your name"
email = "" email = ""
realm_default_language = "en"
realm_name = generate_demo_realm_name() realm_name = generate_demo_realm_name()
realm_type = Realm.ORG_TYPES["unspecified"]["id"] realm_type = Realm.ORG_TYPES["unspecified"]["id"]
realm_subdomain = realm_name realm_subdomain = realm_name
email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_NOBODY
prereg_realm = create_preregistration_realm(email, realm_name, realm_subdomain, realm_type) prereg_realm = create_preregistration_realm(
email, realm_name, realm_subdomain, realm_type, realm_default_language
)
activation_url = create_confirmation_link( activation_url = create_confirmation_link(
prereg_realm, Confirmation.REALM_CREATION, realm_creation=True prereg_realm, Confirmation.REALM_CREATION, realm_creation=True
) )
@ -106,6 +113,7 @@ def register_demo_development_realm(request: HttpRequest) -> HttpResponse:
key=key, key=key,
realm_name=realm_name, realm_name=realm_name,
realm_type=realm_type, realm_type=realm_type,
realm_default_language=realm_default_language,
email_address_visibility=email_address_visibility, email_address_visibility=email_address_visibility,
full_name=name, full_name=name,
password="test", password="test",

View File

@ -52,7 +52,11 @@ from zerver.forms import (
) )
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
from zerver.lib.exceptions import RateLimitedError from zerver.lib.exceptions import RateLimitedError
from zerver.lib.i18n import get_default_language_for_new_user from zerver.lib.i18n import (
get_browser_language_code,
get_default_language_for_new_user,
get_language_name,
)
from zerver.lib.pysa import mark_sanitized from zerver.lib.pysa import mark_sanitized
from zerver.lib.rate_limiter import rate_limit_request_by_ip from zerver.lib.rate_limiter import rate_limit_request_by_ip
from zerver.lib.request import REQ, has_request_variables from zerver.lib.request import REQ, has_request_variables
@ -70,6 +74,7 @@ from zerver.lib.validator import (
) )
from zerver.lib.zephyr import compute_mit_user_fullname from zerver.lib.zephyr import compute_mit_user_fullname
from zerver.models import ( from zerver.models import (
MAX_LANGUAGE_ID_LENGTH,
DisposableEmailError, DisposableEmailError,
DomainNotAllowedForRealmError, DomainNotAllowedForRealmError,
EmailContainsPlusError, EmailContainsPlusError,
@ -195,6 +200,16 @@ def get_selected_realm_type_name(prereg_realm: Optional[PreregistrationRealm]) -
return get_org_type_display_name(prereg_realm.org_type) return get_org_type_display_name(prereg_realm.org_type)
def get_selected_realm_default_language_name(
prereg_realm: Optional[PreregistrationRealm],
) -> Optional[str]:
if prereg_realm is None:
# We show the selected realm language only when creating new realm.
return None
return get_language_name(prereg_realm.default_language)
@add_google_analytics @add_google_analytics
@require_post @require_post
def realm_register(*args: Any, **kwargs: Any) -> HttpResponse: def realm_register(*args: Any, **kwargs: Any) -> HttpResponse:
@ -361,6 +376,7 @@ def registration_helper(
initial_data = { initial_data = {
"realm_name": prereg_realm.name, "realm_name": prereg_realm.name,
"realm_type": prereg_realm.org_type, "realm_type": prereg_realm.org_type,
"realm_default_language": prereg_realm.default_language,
"realm_subdomain": prereg_realm.string_id, "realm_subdomain": prereg_realm.string_id,
} }
@ -440,11 +456,13 @@ def registration_helper(
string_id = form.cleaned_data["realm_subdomain"] string_id = form.cleaned_data["realm_subdomain"]
realm_name = form.cleaned_data["realm_name"] realm_name = form.cleaned_data["realm_name"]
realm_type = form.cleaned_data["realm_type"] realm_type = form.cleaned_data["realm_type"]
realm_default_language = form.cleaned_data["realm_default_language"]
is_demo_organization = form.cleaned_data["is_demo_organization"] is_demo_organization = form.cleaned_data["is_demo_organization"]
realm = do_create_realm( realm = do_create_realm(
string_id, string_id,
realm_name, realm_name,
org_type=realm_type, org_type=realm_type,
default_language=realm_default_language,
is_demo_organization=is_demo_organization, is_demo_organization=is_demo_organization,
prereg_realm=prereg_realm, prereg_realm=prereg_realm,
) )
@ -654,6 +672,9 @@ def registration_helper(
"corporate_enabled": settings.CORPORATE_ENABLED, "corporate_enabled": settings.CORPORATE_ENABLED,
"default_email_address_visibility": default_email_address_visibility, "default_email_address_visibility": default_email_address_visibility,
"selected_realm_type_name": get_selected_realm_type_name(prereg_realm), "selected_realm_type_name": get_selected_realm_type_name(prereg_realm),
"selected_realm_default_language_name": get_selected_realm_default_language_name(
prereg_realm
),
"email_address_visibility_admins_only": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_ADMINS, "email_address_visibility_admins_only": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_ADMINS,
"email_address_visibility_moderators": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_MODERATORS, "email_address_visibility_moderators": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_MODERATORS,
"email_address_visibility_nobody": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_NOBODY, "email_address_visibility_nobody": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_NOBODY,
@ -727,8 +748,11 @@ def prepare_realm_activation_url(
realm_name: str, realm_name: str,
string_id: str, string_id: str,
org_type: int, org_type: int,
default_language: str,
) -> str: ) -> str:
prereg_realm = create_preregistration_realm(email, realm_name, string_id, org_type) prereg_realm = create_preregistration_realm(
email, realm_name, string_id, org_type, default_language
)
activation_url = create_confirmation_link( activation_url = create_confirmation_link(
prereg_realm, Confirmation.REALM_CREATION, realm_creation=True prereg_realm, Confirmation.REALM_CREATION, realm_creation=True
) )
@ -802,9 +826,15 @@ def create_realm(request: HttpRequest, creation_key: Optional[str] = None) -> Ht
email = form.cleaned_data["email"] email = form.cleaned_data["email"]
realm_name = form.cleaned_data["realm_name"] realm_name = form.cleaned_data["realm_name"]
realm_type = form.cleaned_data["realm_type"] realm_type = form.cleaned_data["realm_type"]
realm_default_language = form.cleaned_data["realm_default_language"]
realm_subdomain = form.cleaned_data["realm_subdomain"] realm_subdomain = form.cleaned_data["realm_subdomain"]
activation_url = prepare_realm_activation_url( activation_url = prepare_realm_activation_url(
email, request.session, realm_name, realm_subdomain, realm_type email,
request.session,
realm_name,
realm_subdomain,
realm_type,
realm_default_language,
) )
if key_record is not None and key_record.presume_email_valid: if key_record is not None and key_record.presume_email_valid:
# The user has a token created from the server command line; # The user has a token created from the server command line;
@ -830,13 +860,21 @@ def create_realm(request: HttpRequest, creation_key: Optional[str] = None) -> Ht
"email": email, "email": email,
"realm_name": realm_name, "realm_name": realm_name,
"realm_type": realm_type, "realm_type": realm_type,
"realm_default_language": realm_default_language,
"realm_subdomain": realm_subdomain, "realm_subdomain": realm_subdomain,
} }
) )
url = append_url_query_string(new_realm_send_confirm_url, query) url = append_url_query_string(new_realm_send_confirm_url, query)
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
else: else:
form = RealmCreationForm() default_language_code = get_browser_language_code(request)
if default_language_code is None:
default_language_code = "en"
initial_data = {
"realm_default_language": default_language_code,
}
form = RealmCreationForm(initial=initial_data)
context = get_realm_create_form_context() context = get_realm_create_form_context()
context.update( context.update(
@ -868,6 +906,7 @@ def new_realm_send_confirm(
email: str = REQ("email"), email: str = REQ("email"),
realm_name: str = REQ(str_validator=check_capped_string(Realm.MAX_REALM_NAME_LENGTH)), realm_name: str = REQ(str_validator=check_capped_string(Realm.MAX_REALM_NAME_LENGTH)),
realm_type: int = REQ(json_validator=check_int_in(Realm.ORG_TYPE_IDS)), realm_type: int = REQ(json_validator=check_int_in(Realm.ORG_TYPE_IDS)),
realm_default_language: str = REQ(str_validator=check_capped_string(MAX_LANGUAGE_ID_LENGTH)),
realm_subdomain: str = REQ(str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH)), realm_subdomain: str = REQ(str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH)),
) -> HttpResponse: ) -> HttpResponse:
return TemplateResponse( return TemplateResponse(
@ -880,6 +919,7 @@ def new_realm_send_confirm(
# creation. # creation.
"new_realm_name": realm_name, "new_realm_name": realm_name,
"realm_type": realm_type, "realm_type": realm_type,
"realm_default_language": realm_default_language,
"realm_subdomain": realm_subdomain, "realm_subdomain": realm_subdomain,
"realm_creation": True, "realm_creation": True,
}, },