diff --git a/templates/zerver/create_realm.html b/templates/zerver/create_realm.html index 27523d2fe3..1c80e8c763 100644 --- a/templates/zerver/create_realm.html +++ b/templates/zerver/create_realm.html @@ -39,13 +39,6 @@ -
- {% trans %} - Or import - from Slack, Mattermost, - or Rocket.Chat. - {% endtrans %} -
diff --git a/templates/zerver/realm_creation_form.html b/templates/zerver/realm_creation_form.html index 84115a7d33..71790b898b 100644 --- a/templates/zerver/realm_creation_form.html +++ b/templates/zerver/realm_creation_form.html @@ -85,4 +85,26 @@ {% endif %} + + {% if not user_registration_form %} +
+
+ +
+ {% trans %} + You can also import from + Mattermost or + Rocket.Chat. + {% endtrans %} +
+
+ +
+ {% endif %} diff --git a/templates/zerver/slack_import.html b/templates/zerver/slack_import.html index 1ef0569c9e..b67c2568f2 100644 --- a/templates/zerver/slack_import.html +++ b/templates/zerver/slack_import.html @@ -14,7 +14,7 @@
{% if poll_for_import_completion %} - +
@@ -25,7 +25,7 @@
{{ csrf_input }}
- +
diff --git a/web/src/portico/signup.ts b/web/src/portico/signup.ts index 5a4b23f0bb..b404b66b4a 100644 --- a/web/src/portico/signup.ts +++ b/web/src/portico/signup.ts @@ -348,7 +348,7 @@ $(() => { }); if ($("#slack-import-drag-and-drop").length > 0) { - const key = $("#auth_key").val(); + const key = $("#auth_key_for_file_upload").val(); const uppy = new Uppy({ debug: true, autoProceed: true, @@ -370,6 +370,7 @@ $(() => { "Drag and drop your Slack export file here, or click to browse.", }), }, + pluralize: () => 0, }, }); uppy.use(Tus, {endpoint: "/api/v1/tus/", removeFingerprintOnSuccess: true}); @@ -384,7 +385,7 @@ $(() => { } if ($("#slack-import-poll-status").length > 0) { - const key = $("#auth_key").val(); + const key = $("#auth_key_for_polling").val(); const pollInterval = 2000; // Poll every 2 seconds let poll_id: ReturnType | undefined; diff --git a/zerver/context_processors.py b/zerver/context_processors.py index a4f48e1cc8..ea6ae0a362 100644 --- a/zerver/context_processors.py +++ b/zerver/context_processors.py @@ -22,7 +22,7 @@ from zerver.lib.realm_icon import get_realm_icon_url from zerver.lib.request import RequestNotes from zerver.lib.send_email import FromAddress from zerver.lib.subdomains import get_subdomain, is_root_domain_available -from zerver.models import Realm, UserProfile +from zerver.models import PreregistrationRealm, Realm, UserProfile from zerver.models.realms import get_realm from zproject.backends import ( AUTH_BACKEND_NAME_MAP, @@ -293,5 +293,6 @@ def get_realm_create_form_context() -> dict[str, Any]: "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"]), + "import_from_choices": PreregistrationRealm.IMPORT_FROM_CHOICES, } return context diff --git a/zerver/forms.py b/zerver/forms.py index a0541fcb9e..943a08ea2e 100644 --- a/zerver/forms.py +++ b/zerver/forms.py @@ -32,7 +32,7 @@ from zerver.lib.name_restrictions import is_reserved_subdomain from zerver.lib.rate_limiter import RateLimitedObject, rate_limit_request_by_ip 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 import PreregistrationRealm, Realm, UserProfile from zerver.models.realm_audit_logs import RealmAuditLog from zerver.models.realms import ( DisposableEmailError, @@ -325,6 +325,9 @@ class ImportRealmOwnerSelectionForm(forms.Form): class RealmCreationForm(RealmDetailsForm): # This form determines whether users can create a new realm. email = forms.EmailField(validators=[email_not_system_bot, email_is_not_disposable]) + import_from = forms.ChoiceField( + choices=PreregistrationRealm.IMPORT_FROM_CHOICES, + ) def __init__(self, *args: Any, **kwargs: Any) -> None: kwargs["realm_creation"] = True diff --git a/zerver/lib/test_classes.py b/zerver/lib/test_classes.py index fe935b2082..89b6bc11d8 100644 --- a/zerver/lib/test_classes.py +++ b/zerver/lib/test_classes.py @@ -896,6 +896,7 @@ Output: "realm_type": realm_type, "realm_default_language": realm_default_language, "realm_subdomain": realm_subdomain, + "import_from": "none", } if realm_in_root_domain is not None: payload["realm_in_root_domain"] = realm_in_root_domain diff --git a/zerver/models/prereg_users.py b/zerver/models/prereg_users.py index 741d487488..839f333f73 100644 --- a/zerver/models/prereg_users.py +++ b/zerver/models/prereg_users.py @@ -50,6 +50,10 @@ class PreregistrationRealm(models.Model): UserProfile, null=True, related_name="+", on_delete=models.SET_NULL ) + IMPORT_FROM_CHOICES = [ + ("none", "Don't import"), + ("slack", "Import from Slack"), + ] data_import_metadata = models.JSONField(default=dict, encoder=DjangoJSONEncoder) diff --git a/zerver/tests/test_management_commands.py b/zerver/tests/test_management_commands.py index 8e37df70df..ffbc249490 100644 --- a/zerver/tests/test_management_commands.py +++ b/zerver/tests/test_management_commands.py @@ -306,6 +306,7 @@ class TestGenerateRealmCreationLink(ZulipTestCase): "realm_type": Realm.ORG_TYPES["business"]["id"], "realm_default_language": "en", "realm_subdomain": "custom-test", + "import_from": "none", }, ) self.assertEqual(result.status_code, 302) @@ -334,6 +335,7 @@ class TestGenerateRealmCreationLink(ZulipTestCase): "realm_type": Realm.ORG_TYPES["business"]["id"], "realm_default_language": "en", "realm_subdomain": string_id, + "import_from": "none", }, ) self.assertEqual(result.status_code, 302) diff --git a/zerver/views/auth.py b/zerver/views/auth.py index f46f617fc6..c79b62422e 100644 --- a/zerver/views/auth.py +++ b/zerver/views/auth.py @@ -145,6 +145,7 @@ def create_preregistration_realm( string_id: str, org_type: int, default_language: str, + import_from: str | None = None, ) -> PreregistrationRealm: return PreregistrationRealm.objects.create( email=email, @@ -152,6 +153,7 @@ def create_preregistration_realm( string_id=string_id, org_type=org_type, default_language=default_language, + data_import_metadata={"import_from": import_from}, ) diff --git a/zerver/views/registration.py b/zerver/views/registration.py index 90e016e97d..7e2ca0dee0 100644 --- a/zerver/views/registration.py +++ b/zerver/views/registration.py @@ -157,7 +157,11 @@ def get_prereg_key_and_redirect( registration_url = reverse("accounts_register") if realm_creation: - registration_url = reverse("realm_register") + assert isinstance(prereg_object, PreregistrationRealm) + if prereg_object.data_import_metadata.get("import_from") == "slack": + registration_url = reverse("import_realm_from_slack") + else: + registration_url = reverse("realm_register") return render( request, @@ -235,6 +239,11 @@ def accounts_register(*args: Any, **kwargs: Any) -> HttpResponse: return registration_helper(*args, **kwargs) +@require_post +def import_realm_from_slack(*args: Any, **kwargs: Any) -> HttpResponse: + return registration_helper(*args, **kwargs) + + @typed_endpoint def registration_helper( request: HttpRequest, @@ -298,6 +307,35 @@ def registration_helper( "key": key, }, ) + + elif prereg_realm.data_import_metadata.get("import_from") == "slack": + context: dict[str, Any] = { + "key": key, + } + + saved_slack_access_token = prereg_realm.data_import_metadata.get("slack_access_token") + if saved_slack_access_token or slack_access_token: + if slack_access_token and slack_access_token != saved_slack_access_token: + # Verify slack token access. + from zerver.data_import.slack import check_token_access + + check_token_access(slack_access_token) + + saved_slack_access_token = slack_access_token + prereg_realm.data_import_metadata["slack_access_token"] = slack_access_token + prereg_realm.save(update_fields=["data_import_metadata"]) + + context["slack_access_token"] = saved_slack_access_token + context["uploaded_import_file_name"] = prereg_realm.data_import_metadata.get( + "uploaded_import_file_name" + ) + + return TemplateResponse( + request, + "zerver/slack_import.html", + context, + ) + password_required = True role = UserProfile.ROLE_REALM_OWNER else: @@ -874,9 +912,15 @@ def prepare_realm_activation_url( string_id: str, org_type: int, default_language: str, + import_form: str, ) -> str: prereg_realm = create_preregistration_realm( - email, realm_name, string_id, org_type, default_language + email, + realm_name, + string_id, + org_type, + default_language, + import_form, ) activation_url = create_confirmation_link( prereg_realm, Confirmation.REALM_CREATION, no_associated_realm_object=True @@ -1092,6 +1136,7 @@ def create_realm(request: HttpRequest, creation_key: str | None = None) -> HttpR realm_type = form.cleaned_data["realm_type"] realm_default_language = form.cleaned_data["realm_default_language"] realm_subdomain = form.cleaned_data["realm_subdomain"] + import_from = form.cleaned_data["import_from"] activation_url = prepare_realm_activation_url( email, request.session, @@ -1099,6 +1144,7 @@ def create_realm(request: HttpRequest, creation_key: str | None = None) -> HttpR realm_subdomain, realm_type, realm_default_language, + import_from, ) if key_record is not None and key_record.presume_email_valid: # The user has a token created from the server command line; diff --git a/zproject/urls.py b/zproject/urls.py index 8f87f6b9be..b35b594188 100644 --- a/zproject/urls.py +++ b/zproject/urls.py @@ -142,6 +142,7 @@ from zerver.views.registration import ( create_realm, find_account, get_prereg_key_and_redirect, + import_realm_from_slack, new_realm_send_confirm, realm_import_post_process, realm_import_status, @@ -604,6 +605,7 @@ i18n_urls = [ realm_import_post_process, name="realm_import_post_process", ), + path("new/import/slack/", import_realm_from_slack, name="import_realm_from_slack"), path( "accounts/do_confirm/", get_prereg_key_and_redirect,