Some checks failed
Code scanning / CodeQL (push) Failing after 15s
Zulip production suite / Ubuntu 22.04 production build (push) Failing after 26s
Zulip production suite / Debian 12 production install with custom db name and user (push) Has been skipped
Zulip production suite / Ubuntu 22.04 production install and PostgreSQL upgrade with pgroonga (push) Has been skipped
Zulip production suite / Ubuntu 24.04 production install (push) Has been skipped
Zulip production suite / Ubuntu 26.04 production install (push) Has been skipped
Zulip production suite / Debian 13 production install (push) Has been skipped
Zulip production suite / 7.0 Version Upgrade (push) Has been skipped
Zulip production suite / 8.0 Version Upgrade (push) Has been skipped
Zulip production suite / 6.0 Version Upgrade (push) Has been skipped
Zulip production suite / 10.0 Version Upgrade (push) Has been skipped
Zulip production suite / 9.0 Version Upgrade (push) Has been skipped
Zulip production suite / 12.0 Version Upgrade (push) Has been skipped
Zulip production suite / 11.0 Version Upgrade (push) Has been skipped
GitHub Actions Security Analysis with zizmor / zizmor (push) Failing after 3s
Zulip CI / Debian 12 (Python 3.11, backend + documentation) (push) Failing after 26s
Zulip CI / Ubuntu 22.04 (Python 3.10, backend + frontend) (push) Failing after 3s
Zulip CI / Ubuntu 24.04 (Python 3.12, backend) (push) Failing after 25s
Zulip CI / Ubuntu 26.04 (Python 3.14, backend) (push) Failing after 25s
Zulip CI / Debian 13 (Python 3.13, backend) (push) Failing after 28s
Zulip production suite / Required jobs (push) Has been cancelled
Zulip CI / Required jobs (push) Has been cancelled
API Documentation Update Check / check-feature-level-updated (push) Has been cancelled
API Documentation Update Check / notify-if-api-docs-changed (push) Has been cancelled
81 lines
3.0 KiB
Python
81 lines
3.0 KiB
Python
from collections.abc import Callable
|
|
|
|
from django.http import HttpRequest, HttpResponse
|
|
from django.shortcuts import render
|
|
from django.views.decorators.csrf import csrf_exempt
|
|
|
|
from confirmation.models import Confirmation, ConfirmationKeyError, get_object_from_key
|
|
from zerver.actions.user_settings import do_change_user_setting
|
|
from zerver.context_processors import common_context
|
|
from zerver.lib.send_email import clear_scheduled_emails
|
|
from zerver.models import ScheduledEmail, UserProfile
|
|
|
|
|
|
def process_unsubscribe(
|
|
request: HttpRequest,
|
|
confirmation_key: str,
|
|
subscription_type: str,
|
|
unsubscribe_function: Callable[[UserProfile], None],
|
|
) -> HttpResponse:
|
|
try:
|
|
user_profile = get_object_from_key(
|
|
confirmation_key, [Confirmation.UNSUBSCRIBE], mark_object_used=False
|
|
)
|
|
except ConfirmationKeyError:
|
|
return render(request, "zerver/unsubscribe_link_error.html")
|
|
|
|
assert isinstance(user_profile, UserProfile)
|
|
unsubscribe_function(user_profile)
|
|
context = common_context(user_profile)
|
|
context.update(subscription_type=subscription_type)
|
|
return render(request, "zerver/unsubscribe_success.html", context=context)
|
|
|
|
|
|
# Email unsubscribe functions. All have the function signature
|
|
# processor(user_profile).
|
|
|
|
|
|
def do_missedmessage_unsubscribe(user_profile: UserProfile) -> None:
|
|
do_change_user_setting(
|
|
user_profile, "enable_offline_email_notifications", False, acting_user=user_profile
|
|
)
|
|
|
|
|
|
def do_welcome_unsubscribe(user_profile: UserProfile) -> None:
|
|
clear_scheduled_emails([user_profile.id], ScheduledEmail.WELCOME)
|
|
|
|
|
|
def do_digest_unsubscribe(user_profile: UserProfile) -> None:
|
|
do_change_user_setting(user_profile, "enable_digest_emails", False, acting_user=user_profile)
|
|
|
|
|
|
def do_login_unsubscribe(user_profile: UserProfile) -> None:
|
|
do_change_user_setting(user_profile, "enable_login_emails", False, acting_user=user_profile)
|
|
|
|
|
|
def do_marketing_unsubscribe(user_profile: UserProfile) -> None:
|
|
do_change_user_setting(user_profile, "enable_marketing_emails", False, acting_user=user_profile)
|
|
|
|
|
|
# The keys are part of the URL for the unsubscribe link and must be valid
|
|
# without encoding.
|
|
# The values are a tuple of (display name, unsubscribe function), where the
|
|
# display name is what we call this class of email in user-visible text.
|
|
email_unsubscribers = {
|
|
"missed_messages": ("missed messages", do_missedmessage_unsubscribe),
|
|
"welcome": ("welcome", do_welcome_unsubscribe),
|
|
"digest": ("digest", do_digest_unsubscribe),
|
|
"login": ("login", do_login_unsubscribe),
|
|
"marketing": ("marketing", do_marketing_unsubscribe),
|
|
}
|
|
|
|
|
|
# Login NOT required. These are for one-click unsubscribes.
|
|
@csrf_exempt
|
|
def email_unsubscribe(request: HttpRequest, email_type: str, confirmation_key: str) -> HttpResponse:
|
|
if email_type in email_unsubscribers:
|
|
display_name, unsubscribe_function = email_unsubscribers[email_type]
|
|
return process_unsubscribe(request, confirmation_key, display_name, unsubscribe_function)
|
|
|
|
return render(request, "zerver/unsubscribe_link_error.html")
|