mirror of https://github.com/zulip/zulip.git
registration: Ask user how they found Zulip.
This commit is contained in:
parent
293992fe60
commit
d21f5c9b75
|
@ -30,6 +30,7 @@ from corporate.views.support import get_plan_type_string
|
|||
from zerver.decorator import require_server_admin
|
||||
from zerver.lib.request import has_request_variables
|
||||
from zerver.models import Realm
|
||||
from zerver.models.realm_audit_logs import RealmAuditLog
|
||||
from zerver.models.realms import get_org_type_display_name
|
||||
|
||||
|
||||
|
@ -102,7 +103,8 @@ def realm_summary_table() -> str:
|
|||
coalesce(wau_table.value, 0) wau_count,
|
||||
coalesce(dau_table.value, 0) dau_count,
|
||||
coalesce(user_count_table.value, 0) user_profile_count,
|
||||
coalesce(bot_count_table.value, 0) bot_count
|
||||
coalesce(bot_count_table.value, 0) bot_count,
|
||||
coalesce(realm_audit_log_table.how_realm_creator_found_zulip, '') how_realm_creator_found_zulip
|
||||
FROM
|
||||
zerver_realm as realm
|
||||
LEFT OUTER JOIN (
|
||||
|
@ -157,6 +159,15 @@ def realm_summary_table() -> str:
|
|||
AND subgroup = 'true'
|
||||
AND end_time = %(active_users_audit_end_time)s
|
||||
) as bot_count_table ON realm.id = bot_count_table.realm_id
|
||||
LEFT OUTER JOIN (
|
||||
SELECT
|
||||
extra_data->>'how_realm_creator_found_zulip' as how_realm_creator_found_zulip,
|
||||
realm_id
|
||||
from
|
||||
zerver_realmauditlog
|
||||
WHERE
|
||||
event_type = %(realm_creation_event_type)s
|
||||
) as realm_audit_log_table ON realm.id = realm_audit_log_table.realm_id
|
||||
WHERE
|
||||
_14day_active_humans IS NOT NULL
|
||||
or realm.plan_type = 3
|
||||
|
@ -178,6 +189,7 @@ def realm_summary_table() -> str:
|
|||
"active_users_audit_end_time": COUNT_STATS[
|
||||
"active_users_audit:is_bot:day"
|
||||
].last_successful_fill(),
|
||||
"realm_creation_event_type": RealmAuditLog.REALM_CREATED,
|
||||
},
|
||||
)
|
||||
rows = dictfetchall(cursor)
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
<th>Bots</th>
|
||||
<th></th>
|
||||
<th colspan=8>Human messages sent, last 8 UTC days (today-so-far first)</th>
|
||||
<th>Referrer</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
@ -118,6 +119,10 @@
|
|||
{% else %}
|
||||
<td colspan=8></td>
|
||||
{% endif %}
|
||||
|
||||
<td>
|
||||
{{ row.how_realm_creator_found_zulip }}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -196,6 +196,26 @@ Form is validated both client-side using jquery-validation (see signup.js) and s
|
|||
<hr />
|
||||
{% endif %}
|
||||
|
||||
{% if creating_new_realm %}
|
||||
<div class="input-group input-box" id="how-realm-creator-found-zulip">
|
||||
<label for="how_realm_creator_found_zulip">
|
||||
{{ _('How did you first hear about Zulip?') }}
|
||||
{% if not corporate_enabled %}
|
||||
<i class="fa fa-question-circle-o" aria-hidden="true" data-tippy-content="{% trans %}This value is used only if you sign up for a plan, in which case it will be sent to the Zulip team.{% endtrans %}"></i>
|
||||
{% endif %}
|
||||
</label>
|
||||
<select name="how_realm_creator_found_zulip" class="required">
|
||||
<option value="" selected disabled>{{ _('Select an option') }}</option>
|
||||
{% for option_id, option_name in how_realm_creator_found_zulip_options %}
|
||||
<option value="{{ option_id }}">{{ option_name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input id="how-realm-creator-found-zulip-other" type="text" placeholder="{{ _('Please describe') }}" name="how_realm_creator_found_zulip_other_text" maxlength="100"/>
|
||||
<input id="how-realm-creator-found-zulip-where-ad" type="text" placeholder="{{ _('Where did you see the ad?') }}" name="how_realm_creator_found_zulip_where_ad" maxlength="100"/>
|
||||
<input id="how-realm-creator-found-zulip-which-organization" type="text" placeholder="{{ _('Which organization?') }}" name="how_realm_creator_found_zulip_which_organization" maxlength="100"/>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="input-group margin terms-of-service">
|
||||
{% if terms_of_service %}
|
||||
<div class="input-group">
|
||||
|
|
|
@ -59,6 +59,8 @@ async function realm_creation_tests(page: Page): Promise<void> {
|
|||
full_name: "Alice",
|
||||
password: "passwordwhichisnotreallycomplex",
|
||||
terms: true,
|
||||
how_realm_creator_found_zulip: "other",
|
||||
how_realm_creator_found_zulip_other_text: "test",
|
||||
};
|
||||
// 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.
|
||||
|
|
|
@ -302,4 +302,40 @@ $(() => {
|
|||
|
||||
$(e.target).hide();
|
||||
});
|
||||
|
||||
$("#how-realm-creator-found-zulip select").on("change", function () {
|
||||
const elements: Record<string, string> = {
|
||||
Other: "how-realm-creator-found-zulip-other",
|
||||
Advertisement: "how-realm-creator-found-zulip-where-ad",
|
||||
"At an organization that's using it":
|
||||
"how-realm-creator-found-zulip-which-organization",
|
||||
};
|
||||
|
||||
const hideElement = (element: string): void => {
|
||||
const $element = $(`#${element}`);
|
||||
$element.hide();
|
||||
$element.removeAttr("required");
|
||||
$(`#${element}-error`).hide();
|
||||
};
|
||||
|
||||
const showElement = (element: string): void => {
|
||||
const $element = $(`#${element}`);
|
||||
$element.show();
|
||||
$element.attr("required", "required");
|
||||
};
|
||||
|
||||
// Reset state
|
||||
for (const element of Object.values(elements)) {
|
||||
if (element) {
|
||||
hideElement(element);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the additional input box if needed.
|
||||
const selected_option = $("option:selected", this).text();
|
||||
const selected_element = elements[selected_option];
|
||||
if (selected_element) {
|
||||
showElement(selected_element);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1458,3 +1458,14 @@ button#register_auth_button_gitlab {
|
|||
max-width: 800px;
|
||||
}
|
||||
}
|
||||
|
||||
#registration #how-realm-creator-found-zulip label {
|
||||
top: -5px;
|
||||
}
|
||||
|
||||
#how-realm-creator-found-zulip-where-ad,
|
||||
#how-realm-creator-found-zulip-other,
|
||||
#how-realm-creator-found-zulip-which-organization {
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -115,11 +115,7 @@
|
|||
],
|
||||
"desktop-login": ["./src/bundles/portico", "./src/portico/desktop-login"],
|
||||
"desktop-redirect": ["./src/bundles/portico", "./src/portico/desktop-redirect"],
|
||||
"stats": [
|
||||
"./src/bundles/portico",
|
||||
"./styles/portico/stats.css",
|
||||
"./src/stats/stats"
|
||||
],
|
||||
"stats": ["./src/bundles/portico", "./styles/portico/stats.css", "./src/stats/stats"],
|
||||
"app": ["./src/bundles/app"],
|
||||
"digest": ["./src/bundles/portico"]
|
||||
}
|
||||
|
|
|
@ -167,6 +167,8 @@ def do_create_realm(
|
|||
enable_read_receipts: Optional[bool] = None,
|
||||
enable_spectator_access: Optional[bool] = None,
|
||||
prereg_realm: Optional[PreregistrationRealm] = None,
|
||||
how_realm_creator_found_zulip: Optional[str] = None,
|
||||
how_realm_creator_found_zulip_extra_context: Optional[str] = None,
|
||||
) -> Realm:
|
||||
if string_id in [settings.SOCIAL_AUTH_SUBDOMAIN, settings.SELF_HOSTING_MANAGEMENT_SUBDOMAIN]:
|
||||
raise AssertionError(
|
||||
|
@ -243,6 +245,10 @@ def do_create_realm(
|
|||
realm=realm,
|
||||
event_type=RealmAuditLog.REALM_CREATED,
|
||||
event_time=realm.date_created,
|
||||
extra_data={
|
||||
"how_realm_creator_found_zulip": how_realm_creator_found_zulip,
|
||||
"how_realm_creator_found_zulip_extra_context": how_realm_creator_found_zulip_extra_context,
|
||||
},
|
||||
)
|
||||
|
||||
realm_default_email_address_visibility = RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_EVERYONE
|
||||
|
|
|
@ -36,6 +36,7 @@ from zerver.lib.soft_deactivation import queue_soft_reactivation
|
|||
from zerver.lib.subdomains import get_subdomain, is_root_domain_available
|
||||
from zerver.lib.users import check_full_name
|
||||
from zerver.models import Realm, UserProfile
|
||||
from zerver.models.realm_audit_logs import RealmAuditLog
|
||||
from zerver.models.realms import (
|
||||
DisposableEmailError,
|
||||
DomainNotAllowedForRealmError,
|
||||
|
@ -140,6 +141,8 @@ class RealmDetailsForm(forms.Form):
|
|||
realm_name = forms.CharField(max_length=Realm.MAX_REALM_NAME_LENGTH)
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# Since the superclass doesn't accept random extra kwargs, we
|
||||
# remove it from the kwargs dict before initializing.
|
||||
self.realm_creation = kwargs["realm_creation"]
|
||||
del kwargs["realm_creation"]
|
||||
|
||||
|
@ -177,10 +180,6 @@ class RegistrationForm(RealmDetailsForm):
|
|||
)
|
||||
|
||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||
# Since the superclass doesn't except random extra kwargs, we
|
||||
# remove it from the kwargs dict before initializing.
|
||||
self.realm_creation = kwargs["realm_creation"]
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
if settings.TERMS_OF_SERVICE_VERSION is not None:
|
||||
self.fields["terms"] = forms.BooleanField(required=True)
|
||||
|
@ -196,6 +195,19 @@ class RegistrationForm(RealmDetailsForm):
|
|||
choices=[(lang["code"], lang["name"]) for lang in get_language_list()],
|
||||
required=self.realm_creation,
|
||||
)
|
||||
self.fields["how_realm_creator_found_zulip"] = forms.ChoiceField(
|
||||
choices=RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS.items(),
|
||||
required=self.realm_creation,
|
||||
)
|
||||
self.fields["how_realm_creator_found_zulip_other_text"] = forms.CharField(
|
||||
max_length=100, required=False
|
||||
)
|
||||
self.fields["how_realm_creator_found_zulip_where_ad"] = forms.CharField(
|
||||
max_length=100, required=False
|
||||
)
|
||||
self.fields["how_realm_creator_found_zulip_which_organization"] = forms.CharField(
|
||||
max_length=100, required=False
|
||||
)
|
||||
|
||||
def clean_full_name(self) -> str:
|
||||
try:
|
||||
|
|
|
@ -842,6 +842,8 @@ Output:
|
|||
"default_stream_group": default_stream_groups,
|
||||
"source_realm_id": source_realm_id,
|
||||
"is_demo_organization": is_demo_organization,
|
||||
"how_realm_creator_found_zulip": "other",
|
||||
"how_realm_creator_found_zulip_extra_context": "I found it on the internet.",
|
||||
}
|
||||
if enable_marketing_emails is not None:
|
||||
payload["enable_marketing_emails"] = enable_marketing_emails
|
||||
|
|
|
@ -162,6 +162,18 @@ class AbstractRealmAuditLog(models.Model):
|
|||
REALM_IMPORTED,
|
||||
]
|
||||
|
||||
HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS = {
|
||||
"existing_user": "At an organization that's using it",
|
||||
"search_engine": "Search engine",
|
||||
"review_site": "Review site",
|
||||
"personal_recommendation": "Personal recommendation",
|
||||
"hacker_news": "Hacker News",
|
||||
"ad": "Advertisement",
|
||||
"other": "Other",
|
||||
"forgot": "Don't remember",
|
||||
"refuse_to_answer": "Prefer not to say",
|
||||
}
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
|
|
@ -85,6 +85,8 @@ def register_development_realm(request: HttpRequest) -> HttpResponse:
|
|||
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)
|
||||
|
@ -120,6 +122,8 @@ def register_demo_development_realm(request: HttpRequest) -> HttpResponse:
|
|||
realm_subdomain=realm_subdomain,
|
||||
terms="true",
|
||||
is_demo_organization="true",
|
||||
how_realm_creator_found_zulip="existing_user",
|
||||
how_realm_creator_found_zulip_extra_context="test",
|
||||
)
|
||||
|
||||
return accounts_register(request)
|
||||
|
|
|
@ -82,6 +82,7 @@ from zerver.models import (
|
|||
UserProfile,
|
||||
)
|
||||
from zerver.models.constants import MAX_LANGUAGE_ID_LENGTH
|
||||
from zerver.models.realm_audit_logs import RealmAuditLog
|
||||
from zerver.models.realms import (
|
||||
DisposableEmailError,
|
||||
DomainNotAllowedForRealmError,
|
||||
|
@ -464,6 +465,32 @@ def registration_helper(
|
|||
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"]
|
||||
how_realm_creator_found_zulip = RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS[
|
||||
form.cleaned_data["how_realm_creator_found_zulip"]
|
||||
]
|
||||
how_realm_creator_found_zulip_extra_context = ""
|
||||
if (
|
||||
how_realm_creator_found_zulip
|
||||
== RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS["other"]
|
||||
):
|
||||
how_realm_creator_found_zulip_extra_context = form.cleaned_data[
|
||||
"how_realm_creator_found_zulip_other_text"
|
||||
]
|
||||
elif (
|
||||
how_realm_creator_found_zulip
|
||||
== RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS["ad"]
|
||||
):
|
||||
how_realm_creator_found_zulip_extra_context = form.cleaned_data[
|
||||
"how_realm_creator_found_zulip_where_ad"
|
||||
]
|
||||
elif (
|
||||
how_realm_creator_found_zulip
|
||||
== RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS["existing_user"]
|
||||
):
|
||||
how_realm_creator_found_zulip_extra_context = form.cleaned_data[
|
||||
"how_realm_creator_found_zulip_which_organization"
|
||||
]
|
||||
|
||||
realm = do_create_realm(
|
||||
string_id,
|
||||
realm_name,
|
||||
|
@ -471,6 +498,8 @@ def registration_helper(
|
|||
default_language=realm_default_language,
|
||||
is_demo_organization=is_demo_organization,
|
||||
prereg_realm=prereg_realm,
|
||||
how_realm_creator_found_zulip=how_realm_creator_found_zulip,
|
||||
how_realm_creator_found_zulip_extra_context=how_realm_creator_found_zulip_extra_context,
|
||||
)
|
||||
assert realm is not None
|
||||
|
||||
|
@ -686,6 +715,7 @@ def registration_helper(
|
|||
"email_address_visibility_moderators": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_MODERATORS,
|
||||
"email_address_visibility_nobody": RealmUserDefault.EMAIL_ADDRESS_VISIBILITY_NOBODY,
|
||||
"email_address_visibility_options_dict": UserProfile.EMAIL_ADDRESS_VISIBILITY_ID_TO_NAME_MAP,
|
||||
"how_realm_creator_found_zulip_options": RealmAuditLog.HOW_REALM_CREATOR_FOUND_ZULIP_OPTIONS.items(),
|
||||
}
|
||||
# Add context for realm creation part of the form.
|
||||
context.update(get_realm_create_form_context())
|
||||
|
|
Loading…
Reference in New Issue