templates: Add realm information fields in create_realm.html.

We now show inputs for realm details like name, type and URL
in the create_realm.html template opened for "/new" url and
these information will be stored in PreregistrationRealm
objects in further commits.

We add a new class RealmDetailsForm in forms.py for this
such that it is used as a base class for RealmCreationForm
and we define RealmDetailsForm such that we can use it as
a subclass for RegistrationForm as well to avoid duplication.
This commit is contained in:
Sahil Batra 2023-03-01 15:17:38 +05:30 committed by Tim Abbott
parent 7f1bf9d6ab
commit 80b00933b1
9 changed files with 87 additions and 23 deletions

View File

@ -11,7 +11,7 @@
<div class="app register-page"> <div class="app register-page">
<div class="app-main register-page-container new-style flex full-page center"> <div class="app-main register-page-container new-style flex full-page center">
<div class="register-form left"> <div class="register-form left" id="new-realm-creation">
<div class="lead"> <div class="lead">
<h1 class="get-started">{{ _("Create a new Zulip organization") }}</h1> <h1 class="get-started">{{ _("Create a new Zulip organization") }}</h1>
</div> </div>
@ -19,23 +19,26 @@
<form class="form-inline" id="create_realm" name="email_form" <form class="form-inline" id="create_realm" name="email_form"
action="{{ current_url() }}" method="post"> action="{{ current_url() }}" method="post">
{{ csrf_input }} {{ csrf_input }}
{% include 'zerver/realm_creation_form.html' %}
<div class="input-box horizontal"> <div class="input-box horizontal">
<div class="inline-block relative"> <div class="inline-block relative">
<input type="text" class="email required" placeholder="{{ _("Enter your email address") }}" <input type="text" class="email required" placeholder="{{ _("Enter your email address") }}"
id="email" name="email" autofocus required /> id="email" name="email" required />
<label for="email">{{ _('Email') }}</label> <label for="email">{{ _('Your email') }}</label>
</div> </div>
{% if form.email.errors %}
{% for error in form.email.errors %}
<div class="alert alert-error">{{ error }}</div>
{% endfor %}
{% endif %}
</div>
<div class="input-box">
<button type="submit" class="new-organization-button register-button">{{ _("Create organization") }}</button> <button type="submit" class="new-organization-button register-button">{{ _("Create organization") }}</button>
</div> </div>
</form> </form>
</div> </div>
<div class="alert alert-error email-frontend-error"></div>
{% if form.email.errors %}
{% for error in form.email.errors %}
<div class="alert alert-error">{{ error }}</div>
{% endfor %}
{% endif %}
<div class="bottom-text"> <div class="bottom-text">
{% trans %} {% trans %}
Or import Or import

View File

@ -3,7 +3,7 @@
<input id="id_team_name" class="required" type="text" <input id="id_team_name" class="required" type="text"
placeholder="Acme or Ακμή" placeholder="Acme or Ακμή"
value="{% if form.realm_name.value() %}{{ form.realm_name.value() }}{% endif %}" value="{% if form.realm_name.value() %}{{ form.realm_name.value() }}{% endif %}"
name="realm_name" maxlength="{{ MAX_REALM_NAME_LENGTH }}" required /> name="realm_name" maxlength="{{ MAX_REALM_NAME_LENGTH }}" required {% if not user_registration_form %}autofocus{% endif %} />
</div> </div>
<label for="id_team_name" class="inline-block label-title">{{ _('Organization name') }}</label> <label for="id_team_name" class="inline-block label-title">{{ _('Organization name') }}</label>
{% if form.realm_name.errors %} {% if form.realm_name.errors %}

View File

@ -34,7 +34,10 @@ Form is validated both client-side using jquery-validation (see signup.js) and s
<fieldset class="org-registration"> <fieldset class="org-registration">
{% if creating_new_realm %} {% if creating_new_realm %}
<legend>{{ _('Your organization') }}</legend> <legend>{{ _('Your organization') }}</legend>
{% include 'zerver/realm_creation_form.html' %} {% with %}
{% set user_registration_form = "true" %}
{% include 'zerver/realm_creation_form.html' %}
{% endwith %}
{% endif %} {% endif %}
</fieldset> </fieldset>

View File

@ -5,7 +5,6 @@ import type {Page} from "puppeteer";
import * as common from "./lib/common"; import * as common from "./lib/common";
const email = "alice@test.example.com"; const email = "alice@test.example.com";
const subdomain = "testsubdomain";
const organization_name = "Awesome Organization"; const organization_name = "Awesome Organization";
const host = "zulipdev.com:9981"; const host = "zulipdev.com:9981";
@ -15,6 +14,9 @@ async function realm_creation_tests(page: Page): Promise<void> {
// submit the email for realm creation. // submit the email for realm creation.
await page.waitForSelector("#email"); await page.waitForSelector("#email");
await page.type("#email", email); await page.type("#email", email);
await page.type("#id_team_name", organization_name);
await page.$eval("#realm_in_root_domain", (el) => (el as HTMLInputElement).click());
await Promise.all([ await Promise.all([
page.waitForNavigation(), page.waitForNavigation(),
page.$eval("#create_realm", (form) => (form as HTMLFormElement).submit()), page.$eval("#create_realm", (form) => (form as HTMLFormElement).submit()),
@ -50,15 +52,12 @@ async function realm_creation_tests(page: Page): Promise<void> {
// fill the form. // fill the form.
const params = { const params = {
realm_name: organization_name,
realm_subdomain: subdomain,
full_name: "Alice", full_name: "Alice",
password: "passwordwhichisnotreallycomplex", password: "passwordwhichisnotreallycomplex",
terms: true, terms: true,
}; };
// For some reason, page.click() does not work this for particular checkbox // For some reason, page.click() does not work this for particular checkbox
// so use page.$eval here to call the .click method in the browser. // so use page.$eval here to call the .click method in the browser.
await page.$eval("#realm_in_root_domain", (el) => (el as HTMLInputElement).click());
await common.fill_form(page, "#registration", params); await common.fill_form(page, "#registration", params);
await page.$eval("#registration", (form) => (form as HTMLFormElement).submit()); await page.$eval("#registration", (form) => (form as HTMLFormElement).submit());

View File

@ -45,7 +45,7 @@ $(() => {
"#id_new_password2 ~ .password_visibility_toggle", "#id_new_password2 ~ .password_visibility_toggle",
); );
$("#registration, #password_reset").validate({ $("#registration, #password_reset", "#create_realm").validate({
rules: { rules: {
password: "password_strength", password: "password_strength",
new_password1: "password_strength", new_password1: "password_strength",
@ -133,7 +133,7 @@ $(() => {
$("input[name='next']").attr("value", next_value + window.location.hash); $("input[name='next']").attr("value", next_value + window.location.hash);
} }
$("#send_confirm", "#create_realm").validate({ $("#send_confirm").validate({
errorElement: "div", errorElement: "div",
errorPlacement($error) { errorPlacement($error) {
$(".email-frontend-error").empty(); $(".email-frontend-error").empty();

View File

@ -46,6 +46,12 @@ html {
} }
} }
#new-realm-creation {
.get-started {
font-size: 2rem;
}
}
.bottom-text-large { .bottom-text-large {
text-align: center; text-align: center;
margin-top: 20px; margin-top: 20px;
@ -877,7 +883,8 @@ button#register_auth_button_gitlab {
} }
} }
#registration { #registration,
#new-realm-creation {
width: auto; width: auto;
padding: 0; padding: 0;
margin: 30px; margin: 30px;

View File

@ -19,7 +19,7 @@ from zerver.lib.realm_description import get_realm_rendered_description, get_rea
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
from zerver.lib.send_email import FromAddress from zerver.lib.send_email import FromAddress
from zerver.lib.subdomains import get_subdomain from zerver.lib.subdomains import get_subdomain, is_root_domain_available
from zerver.models import Realm, UserProfile, get_realm from zerver.models import Realm, UserProfile, get_realm
from zproject.backends import ( from zproject.backends import (
AUTH_BACKEND_NAME_MAP, AUTH_BACKEND_NAME_MAP,
@ -256,3 +256,13 @@ def latest_info_context() -> Dict[str, str]:
"latest_release_announcement": LATEST_RELEASE_ANNOUNCEMENT, "latest_release_announcement": LATEST_RELEASE_ANNOUNCEMENT,
} }
return context return context
def get_realm_create_form_context() -> Dict[str, Any]:
context = {
"MAX_REALM_NAME_LENGTH": str(Realm.MAX_REALM_NAME_LENGTH),
"MAX_REALM_SUBDOMAIN_LENGTH": str(Realm.MAX_REALM_SUBDOMAIN_LENGTH),
"root_domain_available": is_root_domain_available(),
"sorted_realm_types": sorted(Realm.ORG_TYPES.values(), key=lambda d: d["display_order"]),
}
return context

View File

@ -259,10 +259,40 @@ def email_is_not_disposable(email: str) -> None:
raise ValidationError(_("Please use your real email address.")) raise ValidationError(_("Please use your real email address."))
class RealmCreationForm(forms.Form): class RealmDetailsForm(forms.Form):
realm_subdomain = forms.CharField(max_length=Realm.MAX_REALM_SUBDOMAIN_LENGTH, required=False)
realm_type = forms.TypedChoiceField(
coerce=int, choices=[(t["id"], t["name"]) for t in Realm.ORG_TYPES.values()]
)
realm_name = forms.CharField(max_length=Realm.MAX_REALM_NAME_LENGTH)
def __init__(self, *args: Any, **kwargs: Any) -> None:
self.realm_creation = kwargs["realm_creation"]
del kwargs["realm_creation"]
super().__init__(*args, **kwargs)
def clean_realm_subdomain(self) -> str:
if not self.realm_creation:
# This field is only used if realm_creation
return ""
subdomain = self.cleaned_data["realm_subdomain"]
if "realm_in_root_domain" in self.data:
subdomain = Realm.SUBDOMAIN_FOR_ROOT_DOMAIN
check_subdomain_available(subdomain)
return subdomain
class RealmCreationForm(RealmDetailsForm):
# This form determines whether users can create a new realm. # This form determines whether users can create a new realm.
email = forms.EmailField(validators=[email_not_system_bot, email_is_not_disposable]) email = forms.EmailField(validators=[email_not_system_bot, email_is_not_disposable])
def __init__(self, *args: Any, **kwargs: Any) -> None:
kwargs["realm_creation"] = True
super().__init__(*args, **kwargs)
class LoggingSetPasswordForm(SetPasswordForm): class LoggingSetPasswordForm(SetPasswordForm):
new_password1 = forms.CharField( new_password1 = forms.CharField(

View File

@ -34,7 +34,11 @@ from zerver.actions.user_settings import (
do_change_password, do_change_password,
do_change_user_setting, do_change_user_setting,
) )
from zerver.context_processors import get_realm_from_request, login_context from zerver.context_processors import (
get_realm_create_form_context,
get_realm_from_request,
login_context,
)
from zerver.decorator import add_google_analytics, do_login, require_post from zerver.decorator import add_google_analytics, do_login, require_post
from zerver.forms import ( from zerver.forms import (
FindMyTeamForm, FindMyTeamForm,
@ -698,10 +702,18 @@ def create_realm(request: HttpRequest, creation_key: Optional[str] = None) -> Ht
return HttpResponseRedirect(url) return HttpResponseRedirect(url)
else: else:
form = RealmCreationForm() form = RealmCreationForm()
context = get_realm_create_form_context()
context.update(
{
"form": form,
"current_url": request.get_full_path,
}
)
return TemplateResponse( return TemplateResponse(
request, request,
"zerver/create_realm.html", "zerver/create_realm.html",
context={"form": form, "current_url": request.get_full_path}, context=context,
) )