diff --git a/corporate/tests/test_stripe.py b/corporate/tests/test_stripe.py index 14b101eeea..4f695eb7f6 100644 --- a/corporate/tests/test_stripe.py +++ b/corporate/tests/test_stripe.py @@ -2344,6 +2344,37 @@ class StripeTest(StripeTestCase): response, ) + def test_demo_request(self) -> None: + result = self.client_get("/request-demo/") + self.assertEqual(result.status_code, 200) + self.assert_in_success_response(["Request a demo"], result) + + data = { + "full_name": "King Hamlet", + "email": "test@zulip.com", + "role": "Manager", + "organization_name": "Zulip", + "organization_type": "Business", + "organization_website": "https://example.com", + "expected_user_count": "10 (2 unpaid members)", + "message": "Need help!", + } + result = self.client_post("/request-demo/", data) + self.assert_in_success_response(["Thanks for contacting us!"], result) + + from django.core.mail import outbox + + self.assert_length(outbox, 1) + + for message in outbox: + self.assert_length(message.to, 1) + self.assertEqual(message.to[0], "desdemona+admin@zulip.com") + self.assertEqual(message.subject, "Demo request for Zulip") + self.assertEqual(message.reply_to, ["test@zulip.com"]) + self.assertEqual(self.email_envelope_from(message), settings.NOREPLY_EMAIL_ADDRESS) + self.assertIn("Zulip demo request None: user = self.example_user("hamlet") self.assertIsNone(get_customer_by_realm(user.realm)) diff --git a/corporate/urls.py b/corporate/urls.py index 6bf9598cc5..3d6f359bd9 100644 --- a/corporate/urls.py +++ b/corporate/urls.py @@ -68,7 +68,7 @@ from corporate.views.sponsorship import ( sponsorship, sponsorship_page, ) -from corporate.views.support import remote_servers_support, support, support_request +from corporate.views.support import demo_request, remote_servers_support, support, support_request from corporate.views.upgrade import ( remote_realm_upgrade, remote_realm_upgrade_page, @@ -94,6 +94,7 @@ i18n_urlpatterns: Any = [ path("sponsorship/", sponsorship_page, name="sponsorship_request"), path("upgrade/", upgrade_page, name="upgrade_page"), path("support/", support_request), + path("request-demo/", demo_request), path("billing/event_status/", event_status_page, name="event_status_page"), path("stripe/webhook/", stripe_webhook, name="stripe_webhook"), # Server admin (user_profile.is_staff) visible stats pages diff --git a/corporate/views/support.py b/corporate/views/support.py index c49e8de2a6..5b31ef1dae 100644 --- a/corporate/views/support.py +++ b/corporate/views/support.py @@ -95,6 +95,21 @@ class SupportRequestForm(forms.Form): request_message = forms.CharField(widget=forms.Textarea) +class DemoRequestForm(forms.Form): + MAX_INPUT_LENGTH = 50 + SORTED_ORG_TYPE_NAMES = sorted( + ([org_type["name"] for org_type in Realm.ORG_TYPES.values() if not org_type["hidden"]]), + ) + full_name = forms.CharField(max_length=MAX_INPUT_LENGTH) + email = forms.EmailField() + role = forms.CharField(max_length=MAX_INPUT_LENGTH) + organization_name = forms.CharField(max_length=MAX_INPUT_LENGTH) + organization_type = forms.CharField() + organization_website = forms.URLField(required=True) + expected_user_count = forms.CharField(max_length=MAX_INPUT_LENGTH) + message = forms.CharField(widget=forms.Textarea) + + @zulip_login_required @has_request_variables def support_request(request: HttpRequest) -> HttpResponse: @@ -139,6 +154,47 @@ def support_request(request: HttpRequest) -> HttpResponse: return response +@has_request_variables +def demo_request(request: HttpRequest) -> HttpResponse: + context = { + "MAX_INPUT_LENGTH": DemoRequestForm.MAX_INPUT_LENGTH, + "SORTED_ORG_TYPE_NAMES": DemoRequestForm.SORTED_ORG_TYPE_NAMES, + } + + if request.POST: + post_data = request.POST.copy() + form = DemoRequestForm(post_data) + + if form.is_valid(): + email_context = { + "full_name": form.cleaned_data["full_name"], + "email": form.cleaned_data["email"], + "role": form.cleaned_data["role"], + "organization_name": form.cleaned_data["organization_name"], + "organization_type": form.cleaned_data["organization_type"], + "organization_website": form.cleaned_data["organization_website"], + "expected_user_count": form.cleaned_data["expected_user_count"], + "message": form.cleaned_data["message"], + } + # Sent to the server's support team, so this email is not user-facing. + send_email( + "zerver/emails/demo_request", + to_emails=[FromAddress.SUPPORT], + from_name="Zulip demo request", + from_address=FromAddress.tokenized_no_reply_address(), + reply_to_email=email_context["email"], + context=email_context, + ) + + response = render( + request, "corporate/support/support_request_thanks.html", context=context + ) + return response + + response = render(request, "corporate/support/demo_request.html", context=context) + return response + + def get_plan_type_string(plan_type: int) -> str: return { Realm.PLAN_TYPE_SELF_HOSTED: "Self-hosted", diff --git a/templates/corporate/support/demo_request.html b/templates/corporate/support/demo_request.html new file mode 100644 index 0000000000..492a63634c --- /dev/null +++ b/templates/corporate/support/demo_request.html @@ -0,0 +1,65 @@ +{% extends "zerver/portico_signup.html" %} + +{% block portico_content %} +
+
+
+

Request a demo

+
+ +
+ {{ csrf_input }} + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
+
+
+ +{% endblock %} diff --git a/templates/corporate/support/support_request_thanks.html b/templates/corporate/support/support_request_thanks.html index 1afbc931be..140ac2d84a 100644 --- a/templates/corporate/support/support_request_thanks.html +++ b/templates/corporate/support/support_request_thanks.html @@ -6,7 +6,7 @@ {% block portico_content %}
-
+

{{ _("Thanks for contacting us!") }}

{{ _("We will be in touch with you soon.") }}

diff --git a/templates/zerver/emails/demo_request.html b/templates/zerver/emails/demo_request.html new file mode 100644 index 0000000000..0ecc4890f9 --- /dev/null +++ b/templates/zerver/emails/demo_request.html @@ -0,0 +1,21 @@ +{% extends "zerver/emails/email_base_messages.html" %} + +{% block content %} +Subject: Demo request for {{ organization_name }} +
+Full name: {{ full_name }} +
+Email: {{ email }} +
+Role: {{ role }} +
+Organization type: {{ organization_type }} +
+Organization website: {{ organization_website }} +
+Expected user count: {{ expected_user_count }} +
+Message: {{ message }} +
+ +{% endblock %} diff --git a/templates/zerver/emails/demo_request.subject.txt b/templates/zerver/emails/demo_request.subject.txt new file mode 100644 index 0000000000..4e280c732f --- /dev/null +++ b/templates/zerver/emails/demo_request.subject.txt @@ -0,0 +1 @@ +Demo request for {{organization_name}} diff --git a/templates/zerver/emails/demo_request.txt b/templates/zerver/emails/demo_request.txt new file mode 100644 index 0000000000..9d26158589 --- /dev/null +++ b/templates/zerver/emails/demo_request.txt @@ -0,0 +1,15 @@ +Subject: Demo request for {{ organization_name }} + +Full name: {{ full_name }} + +Email: {{ email }} + +Role: {{ role }} + +Organization type: {{ organization_type }} + +Organization website: {{ organization_website }} + +Expected user count: {{ expected_user_count }} + +Message: {{ message }} diff --git a/web/styles/portico/portico_signin.css b/web/styles/portico/portico_signin.css index fd9a72c2ba..94b11244ef 100644 --- a/web/styles/portico/portico_signin.css +++ b/web/styles/portico/portico_signin.css @@ -527,6 +527,7 @@ html { input[type="email"], input[type="password"], input[type="number"], + input[type="url"], textarea, select { padding: 10px 32px 10px 12px;