diff --git a/docs/subsystems/sending-messages.md b/docs/subsystems/sending-messages.md index ff79f8ef1a..94aa8107f8 100644 --- a/docs/subsystems/sending-messages.md +++ b/docs/subsystems/sending-messages.md @@ -387,3 +387,9 @@ There are a few details that require special care with this system: determine what has happened in streams the user can see. We can use the user's subscriptions to construct what messages they should have access to for this feature. +- Soft-deactivated users experience high loading latency when + returning after being idle for months. We optimize this by + triggering a soft reactivation for users who receive email or push + notification for private messages or personal mentions, or who + request a password reset, since these are good leading indicators + that a user is likely to return to Zulip. diff --git a/zerver/forms.py b/zerver/forms.py index 86e7917f82..335ec54aa3 100644 --- a/zerver/forms.py +++ b/zerver/forms.py @@ -29,6 +29,7 @@ from zerver.lib.exceptions import JsonableError, RateLimited from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain from zerver.lib.rate_limiter import RateLimitedObject from zerver.lib.send_email import FromAddress, send_email +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 ( @@ -360,6 +361,7 @@ class ZulipPasswordResetForm(PasswordResetForm): if user is not None: context["active_account_in_realm"] = True context["reset_url"] = generate_password_reset_url(user, token_generator) + queue_soft_reactivation(user.id) send_email( "zerver/emails/password_reset", to_user_ids=[user.id], diff --git a/zerver/tests/test_signup.py b/zerver/tests/test_signup.py index 8107654707..96ebdba8e7 100644 --- a/zerver/tests/test_signup.py +++ b/zerver/tests/test_signup.py @@ -731,6 +731,17 @@ class PasswordResetTest(ZulipTestCase): result = self.client_get("/accounts/new/send_confirm/alice@example.com") self.assert_in_success_response(["/new/"], result) + def test_password_reset_for_soft_deactivated_user(self) -> None: + user_profile = self.example_user("hamlet") + email = user_profile.delivery_email + with self.soft_deactivate_and_check_long_term_idle(user_profile, False): + # start the password reset process by supplying an email address + result = self.client_post("/accounts/password/reset/", {"email": email}) + + # check the redirect link telling you to check mail for password reset link + self.assertEqual(result.status_code, 302) + self.assertTrue(result["Location"].endswith("/accounts/password/reset/done/")) + class LoginTest(ZulipTestCase): """