mirror of https://github.com/zulip/zulip.git
actions: Split out zerver.actions.user_settings.
Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
parent
d7981dad62
commit
ec6355389a
|
@ -33,12 +33,8 @@ import zulip
|
||||||
|
|
||||||
from scripts.lib.zulip_tools import BOLDRED, ENDC
|
from scripts.lib.zulip_tools import BOLDRED, ENDC
|
||||||
from tools.lib.test_script import prepare_puppeteer_run
|
from tools.lib.test_script import prepare_puppeteer_run
|
||||||
from zerver.lib.actions import (
|
from zerver.actions.user_settings import do_change_avatar_fields
|
||||||
bulk_add_subscriptions,
|
from zerver.lib.actions import bulk_add_subscriptions, do_create_user, notify_created_bot
|
||||||
do_change_avatar_fields,
|
|
||||||
do_create_user,
|
|
||||||
notify_created_bot,
|
|
||||||
)
|
|
||||||
from zerver.lib.integrations import (
|
from zerver.lib.integrations import (
|
||||||
DOC_SCREENSHOT_CONFIG,
|
DOC_SCREENSHOT_CONFIG,
|
||||||
INTEGRATIONS,
|
INTEGRATIONS,
|
||||||
|
|
|
@ -0,0 +1,449 @@
|
||||||
|
import datetime
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
import orjson
|
||||||
|
from django.db import transaction
|
||||||
|
from django.db.models import F
|
||||||
|
from django.utils.timezone import now as timezone_now
|
||||||
|
|
||||||
|
from confirmation.models import Confirmation, create_confirmation_link
|
||||||
|
from zerver.lib.avatar import avatar_url
|
||||||
|
from zerver.lib.cache import (
|
||||||
|
cache_delete,
|
||||||
|
delete_user_profile_caches,
|
||||||
|
user_profile_by_api_key_cache_key,
|
||||||
|
)
|
||||||
|
from zerver.lib.i18n import get_language_name
|
||||||
|
from zerver.lib.queue import queue_json_publish
|
||||||
|
from zerver.lib.send_email import FromAddress, clear_scheduled_emails, send_email
|
||||||
|
from zerver.lib.timezone import canonicalize_timezone
|
||||||
|
from zerver.lib.upload import delete_avatar_image
|
||||||
|
from zerver.lib.users import check_bot_name_available, check_full_name
|
||||||
|
from zerver.lib.utils import generate_api_key
|
||||||
|
from zerver.models import (
|
||||||
|
Draft,
|
||||||
|
EmailChangeStatus,
|
||||||
|
RealmAuditLog,
|
||||||
|
ScheduledEmail,
|
||||||
|
ScheduledMessageNotificationEmail,
|
||||||
|
UserProfile,
|
||||||
|
active_user_ids,
|
||||||
|
bot_owner_user_ids,
|
||||||
|
)
|
||||||
|
from zerver.tornado.django_api import send_event
|
||||||
|
|
||||||
|
|
||||||
|
def send_user_email_update_event(user_profile: UserProfile) -> None:
|
||||||
|
payload = dict(user_id=user_profile.id, new_email=user_profile.email)
|
||||||
|
event = dict(type="realm_user", op="update", person=payload)
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
event,
|
||||||
|
active_user_ids(user_profile.realm_id),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic(savepoint=False)
|
||||||
|
def do_change_user_delivery_email(user_profile: UserProfile, new_email: str) -> None:
|
||||||
|
delete_user_profile_caches([user_profile])
|
||||||
|
|
||||||
|
user_profile.delivery_email = new_email
|
||||||
|
if user_profile.email_address_is_realm_public():
|
||||||
|
user_profile.email = new_email
|
||||||
|
user_profile.save(update_fields=["email", "delivery_email"])
|
||||||
|
else:
|
||||||
|
user_profile.save(update_fields=["delivery_email"])
|
||||||
|
|
||||||
|
# We notify just the target user (and eventually org admins, only
|
||||||
|
# when email_address_visibility=EMAIL_ADDRESS_VISIBILITY_ADMINS)
|
||||||
|
# about their new delivery email, since that field is private.
|
||||||
|
payload = dict(user_id=user_profile.id, delivery_email=new_email)
|
||||||
|
event = dict(type="realm_user", op="update", person=payload)
|
||||||
|
transaction.on_commit(lambda: send_event(user_profile.realm, event, [user_profile.id]))
|
||||||
|
|
||||||
|
if user_profile.avatar_source == UserProfile.AVATAR_FROM_GRAVATAR:
|
||||||
|
# If the user is using Gravatar to manage their email address,
|
||||||
|
# their Gravatar just changed, and we need to notify other
|
||||||
|
# clients.
|
||||||
|
notify_avatar_url_change(user_profile)
|
||||||
|
|
||||||
|
if user_profile.email_address_is_realm_public():
|
||||||
|
# Additionally, if we're also changing the publicly visible
|
||||||
|
# email, we send a new_email event as well.
|
||||||
|
send_user_email_update_event(user_profile)
|
||||||
|
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
acting_user=user_profile,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_EMAIL_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def do_start_email_change_process(user_profile: UserProfile, new_email: str) -> None:
|
||||||
|
old_email = user_profile.delivery_email
|
||||||
|
obj = EmailChangeStatus.objects.create(
|
||||||
|
new_email=new_email,
|
||||||
|
old_email=old_email,
|
||||||
|
user_profile=user_profile,
|
||||||
|
realm=user_profile.realm,
|
||||||
|
)
|
||||||
|
|
||||||
|
activation_url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE)
|
||||||
|
from zerver.context_processors import common_context
|
||||||
|
|
||||||
|
context = common_context(user_profile)
|
||||||
|
context.update(
|
||||||
|
old_email=old_email,
|
||||||
|
new_email=new_email,
|
||||||
|
activate_url=activation_url,
|
||||||
|
)
|
||||||
|
language = user_profile.default_language
|
||||||
|
send_email(
|
||||||
|
"zerver/emails/confirm_new_email",
|
||||||
|
to_emails=[new_email],
|
||||||
|
from_name=FromAddress.security_email_from_name(language=language),
|
||||||
|
from_address=FromAddress.tokenized_no_reply_address(),
|
||||||
|
language=language,
|
||||||
|
context=context,
|
||||||
|
realm=user_profile.realm,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def do_change_password(user_profile: UserProfile, password: str, commit: bool = True) -> None:
|
||||||
|
user_profile.set_password(password)
|
||||||
|
if commit:
|
||||||
|
user_profile.save(update_fields=["password"])
|
||||||
|
|
||||||
|
# Imported here to prevent import cycles
|
||||||
|
from zproject.backends import RateLimitedAuthenticationByUsername
|
||||||
|
|
||||||
|
RateLimitedAuthenticationByUsername(user_profile.delivery_email).clear_history()
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
acting_user=user_profile,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_PASSWORD_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def do_change_full_name(
|
||||||
|
user_profile: UserProfile, full_name: str, acting_user: Optional[UserProfile]
|
||||||
|
) -> None:
|
||||||
|
old_name = user_profile.full_name
|
||||||
|
user_profile.full_name = full_name
|
||||||
|
user_profile.save(update_fields=["full_name"])
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
acting_user=acting_user,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_FULL_NAME_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
extra_data=old_name,
|
||||||
|
)
|
||||||
|
payload = dict(user_id=user_profile.id, full_name=user_profile.full_name)
|
||||||
|
send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
dict(type="realm_user", op="update", person=payload),
|
||||||
|
active_user_ids(user_profile.realm_id),
|
||||||
|
)
|
||||||
|
if user_profile.is_bot:
|
||||||
|
send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
dict(type="realm_bot", op="update", bot=payload),
|
||||||
|
bot_owner_user_ids(user_profile),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def check_change_full_name(
|
||||||
|
user_profile: UserProfile, full_name_raw: str, acting_user: Optional[UserProfile]
|
||||||
|
) -> str:
|
||||||
|
"""Verifies that the user's proposed full name is valid. The caller
|
||||||
|
is responsible for checking check permissions. Returns the new
|
||||||
|
full name, which may differ from what was passed in (because this
|
||||||
|
function strips whitespace)."""
|
||||||
|
new_full_name = check_full_name(full_name_raw)
|
||||||
|
do_change_full_name(user_profile, new_full_name, acting_user)
|
||||||
|
return new_full_name
|
||||||
|
|
||||||
|
|
||||||
|
def check_change_bot_full_name(
|
||||||
|
user_profile: UserProfile, full_name_raw: str, acting_user: UserProfile
|
||||||
|
) -> None:
|
||||||
|
new_full_name = check_full_name(full_name_raw)
|
||||||
|
|
||||||
|
if new_full_name == user_profile.full_name:
|
||||||
|
# Our web app will try to patch full_name even if the user didn't
|
||||||
|
# modify the name in the form. We just silently ignore those
|
||||||
|
# situations.
|
||||||
|
return
|
||||||
|
|
||||||
|
check_bot_name_available(
|
||||||
|
realm_id=user_profile.realm_id,
|
||||||
|
full_name=new_full_name,
|
||||||
|
)
|
||||||
|
do_change_full_name(user_profile, new_full_name, acting_user)
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic(durable=True)
|
||||||
|
def do_change_tos_version(user_profile: UserProfile, tos_version: str) -> None:
|
||||||
|
user_profile.tos_version = tos_version
|
||||||
|
user_profile.save(update_fields=["tos_version"])
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
acting_user=user_profile,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_TERMS_OF_SERVICE_VERSION_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def do_regenerate_api_key(user_profile: UserProfile, acting_user: UserProfile) -> str:
|
||||||
|
old_api_key = user_profile.api_key
|
||||||
|
new_api_key = generate_api_key()
|
||||||
|
user_profile.api_key = new_api_key
|
||||||
|
user_profile.save(update_fields=["api_key"])
|
||||||
|
|
||||||
|
# We need to explicitly delete the old API key from our caches,
|
||||||
|
# because the on-save handler for flushing the UserProfile object
|
||||||
|
# in zerver/lib/cache.py only has access to the new API key.
|
||||||
|
cache_delete(user_profile_by_api_key_cache_key(old_api_key))
|
||||||
|
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
acting_user=acting_user,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_API_KEY_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
)
|
||||||
|
|
||||||
|
if user_profile.is_bot:
|
||||||
|
send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
dict(
|
||||||
|
type="realm_bot",
|
||||||
|
op="update",
|
||||||
|
bot=dict(
|
||||||
|
user_id=user_profile.id,
|
||||||
|
api_key=new_api_key,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bot_owner_user_ids(user_profile),
|
||||||
|
)
|
||||||
|
|
||||||
|
event = {"type": "clear_push_device_tokens", "user_profile_id": user_profile.id}
|
||||||
|
queue_json_publish("deferred_work", event)
|
||||||
|
|
||||||
|
return new_api_key
|
||||||
|
|
||||||
|
|
||||||
|
def notify_avatar_url_change(user_profile: UserProfile) -> None:
|
||||||
|
if user_profile.is_bot:
|
||||||
|
bot_event = dict(
|
||||||
|
type="realm_bot",
|
||||||
|
op="update",
|
||||||
|
bot=dict(
|
||||||
|
user_id=user_profile.id,
|
||||||
|
avatar_url=avatar_url(user_profile),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
bot_event,
|
||||||
|
bot_owner_user_ids(user_profile),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
payload = dict(
|
||||||
|
avatar_source=user_profile.avatar_source,
|
||||||
|
avatar_url=avatar_url(user_profile),
|
||||||
|
avatar_url_medium=avatar_url(user_profile, medium=True),
|
||||||
|
avatar_version=user_profile.avatar_version,
|
||||||
|
# Even clients using client_gravatar don't need the email,
|
||||||
|
# since we're sending the URL anyway.
|
||||||
|
user_id=user_profile.id,
|
||||||
|
)
|
||||||
|
|
||||||
|
event = dict(type="realm_user", op="update", person=payload)
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
event,
|
||||||
|
active_user_ids(user_profile.realm_id),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic(savepoint=False)
|
||||||
|
def do_change_avatar_fields(
|
||||||
|
user_profile: UserProfile,
|
||||||
|
avatar_source: str,
|
||||||
|
skip_notify: bool = False,
|
||||||
|
*,
|
||||||
|
acting_user: Optional[UserProfile],
|
||||||
|
) -> None:
|
||||||
|
user_profile.avatar_source = avatar_source
|
||||||
|
user_profile.avatar_version += 1
|
||||||
|
user_profile.save(update_fields=["avatar_source", "avatar_version"])
|
||||||
|
event_time = timezone_now()
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
modified_user=user_profile,
|
||||||
|
event_type=RealmAuditLog.USER_AVATAR_SOURCE_CHANGED,
|
||||||
|
extra_data={"avatar_source": avatar_source},
|
||||||
|
event_time=event_time,
|
||||||
|
acting_user=acting_user,
|
||||||
|
)
|
||||||
|
|
||||||
|
if not skip_notify:
|
||||||
|
notify_avatar_url_change(user_profile)
|
||||||
|
|
||||||
|
|
||||||
|
def do_delete_avatar_image(user: UserProfile, *, acting_user: Optional[UserProfile]) -> None:
|
||||||
|
do_change_avatar_fields(user, UserProfile.AVATAR_FROM_GRAVATAR, acting_user=acting_user)
|
||||||
|
delete_avatar_image(user)
|
||||||
|
|
||||||
|
|
||||||
|
def update_scheduled_email_notifications_time(
|
||||||
|
user_profile: UserProfile, old_batching_period: int, new_batching_period: int
|
||||||
|
) -> None:
|
||||||
|
existing_scheduled_emails = ScheduledMessageNotificationEmail.objects.filter(
|
||||||
|
user_profile=user_profile
|
||||||
|
)
|
||||||
|
|
||||||
|
scheduled_timestamp_change = datetime.timedelta(
|
||||||
|
seconds=new_batching_period
|
||||||
|
) - datetime.timedelta(seconds=old_batching_period)
|
||||||
|
|
||||||
|
existing_scheduled_emails.update(
|
||||||
|
scheduled_timestamp=F("scheduled_timestamp") + scheduled_timestamp_change
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@transaction.atomic(durable=True)
|
||||||
|
def do_change_user_setting(
|
||||||
|
user_profile: UserProfile,
|
||||||
|
setting_name: str,
|
||||||
|
setting_value: Union[bool, str, int],
|
||||||
|
*,
|
||||||
|
acting_user: Optional[UserProfile],
|
||||||
|
) -> None:
|
||||||
|
old_value = getattr(user_profile, setting_name)
|
||||||
|
event_time = timezone_now()
|
||||||
|
|
||||||
|
if setting_name == "timezone":
|
||||||
|
assert isinstance(setting_value, str)
|
||||||
|
setting_value = canonicalize_timezone(setting_value)
|
||||||
|
else:
|
||||||
|
property_type = UserProfile.property_types[setting_name]
|
||||||
|
assert isinstance(setting_value, property_type)
|
||||||
|
setattr(user_profile, setting_name, setting_value)
|
||||||
|
|
||||||
|
# TODO: Move these database actions into a transaction.atomic block.
|
||||||
|
user_profile.save(update_fields=[setting_name])
|
||||||
|
|
||||||
|
if setting_name in UserProfile.notification_setting_types:
|
||||||
|
# Prior to all personal settings being managed by property_types,
|
||||||
|
# these were only created for notification settings.
|
||||||
|
#
|
||||||
|
# TODO: Start creating these for all settings, and do a
|
||||||
|
# backfilled=True migration.
|
||||||
|
RealmAuditLog.objects.create(
|
||||||
|
realm=user_profile.realm,
|
||||||
|
event_type=RealmAuditLog.USER_SETTING_CHANGED,
|
||||||
|
event_time=event_time,
|
||||||
|
acting_user=acting_user,
|
||||||
|
modified_user=user_profile,
|
||||||
|
extra_data=orjson.dumps(
|
||||||
|
{
|
||||||
|
RealmAuditLog.OLD_VALUE: old_value,
|
||||||
|
RealmAuditLog.NEW_VALUE: setting_value,
|
||||||
|
"property": setting_name,
|
||||||
|
}
|
||||||
|
).decode(),
|
||||||
|
)
|
||||||
|
# Disabling digest emails should clear a user's email queue
|
||||||
|
if setting_name == "enable_digest_emails" and not setting_value:
|
||||||
|
clear_scheduled_emails(user_profile.id, ScheduledEmail.DIGEST)
|
||||||
|
|
||||||
|
if setting_name == "email_notifications_batching_period_seconds":
|
||||||
|
assert isinstance(old_value, int)
|
||||||
|
assert isinstance(setting_value, int)
|
||||||
|
update_scheduled_email_notifications_time(user_profile, old_value, setting_value)
|
||||||
|
|
||||||
|
event = {
|
||||||
|
"type": "user_settings",
|
||||||
|
"op": "update",
|
||||||
|
"property": setting_name,
|
||||||
|
"value": setting_value,
|
||||||
|
}
|
||||||
|
if setting_name == "default_language":
|
||||||
|
assert isinstance(setting_value, str)
|
||||||
|
event["language_name"] = get_language_name(setting_value)
|
||||||
|
|
||||||
|
transaction.on_commit(lambda: send_event(user_profile.realm, event, [user_profile.id]))
|
||||||
|
|
||||||
|
if setting_name in UserProfile.notification_settings_legacy:
|
||||||
|
# This legacy event format is for backwards-compatibility with
|
||||||
|
# clients that don't support the new user_settings event type.
|
||||||
|
# We only send this for settings added before Feature level 89.
|
||||||
|
legacy_event = {
|
||||||
|
"type": "update_global_notifications",
|
||||||
|
"user": user_profile.email,
|
||||||
|
"notification_name": setting_name,
|
||||||
|
"setting": setting_value,
|
||||||
|
}
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(user_profile.realm, legacy_event, [user_profile.id])
|
||||||
|
)
|
||||||
|
|
||||||
|
if setting_name in UserProfile.display_settings_legacy or setting_name == "timezone":
|
||||||
|
# This legacy event format is for backwards-compatibility with
|
||||||
|
# clients that don't support the new user_settings event type.
|
||||||
|
# We only send this for settings added before Feature level 89.
|
||||||
|
legacy_event = {
|
||||||
|
"type": "update_display_settings",
|
||||||
|
"user": user_profile.email,
|
||||||
|
"setting_name": setting_name,
|
||||||
|
"setting": setting_value,
|
||||||
|
}
|
||||||
|
if setting_name == "default_language":
|
||||||
|
assert isinstance(setting_value, str)
|
||||||
|
legacy_event["language_name"] = get_language_name(setting_value)
|
||||||
|
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(user_profile.realm, legacy_event, [user_profile.id])
|
||||||
|
)
|
||||||
|
|
||||||
|
# Updates to the time zone display setting are sent to all users
|
||||||
|
if setting_name == "timezone":
|
||||||
|
payload = dict(
|
||||||
|
email=user_profile.email,
|
||||||
|
user_id=user_profile.id,
|
||||||
|
timezone=canonicalize_timezone(user_profile.timezone),
|
||||||
|
)
|
||||||
|
timezone_event = dict(type="realm_user", op="update", person=payload)
|
||||||
|
transaction.on_commit(
|
||||||
|
lambda: send_event(
|
||||||
|
user_profile.realm,
|
||||||
|
timezone_event,
|
||||||
|
active_user_ids(user_profile.realm_id),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if setting_name == "enable_drafts_synchronization" and setting_value is False:
|
||||||
|
# Delete all of the drafts from the backend but don't send delete events
|
||||||
|
# for them since all that's happened is that we stopped syncing changes,
|
||||||
|
# not deleted every previously synced draft - to do that use the DELETE
|
||||||
|
# endpoint.
|
||||||
|
Draft.objects.filter(user_profile=user_profile).delete()
|
|
@ -19,8 +19,9 @@ from markupsafe import Markup as mark_safe
|
||||||
from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm
|
from two_factor.forms import AuthenticationTokenForm as TwoFactorAuthenticationTokenForm
|
||||||
from two_factor.utils import totp_digits
|
from two_factor.utils import totp_digits
|
||||||
|
|
||||||
|
from zerver.actions.user_settings import do_change_password
|
||||||
from zerver.decorator import rate_limit_request_by_ip
|
from zerver.decorator import rate_limit_request_by_ip
|
||||||
from zerver.lib.actions import do_change_password, email_not_system_bot
|
from zerver.lib.actions import email_not_system_bot
|
||||||
from zerver.lib.email_validation import email_allowed_for_realm
|
from zerver.lib.email_validation import email_allowed_for_realm
|
||||||
from zerver.lib.exceptions import JsonableError, RateLimited
|
from zerver.lib.exceptions import JsonableError, RateLimited
|
||||||
from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain
|
from zerver.lib.name_restrictions import is_disposable_domain, is_reserved_subdomain
|
||||||
|
|
|
@ -46,6 +46,7 @@ from zerver.actions.user_groups import (
|
||||||
do_send_user_group_members_update_event,
|
do_send_user_group_members_update_event,
|
||||||
update_users_in_full_members_system_group,
|
update_users_in_full_members_system_group,
|
||||||
)
|
)
|
||||||
|
from zerver.actions.user_settings import do_delete_avatar_image, send_user_email_update_event
|
||||||
from zerver.actions.user_topics import do_mute_topic, do_unmute_topic
|
from zerver.actions.user_topics import do_mute_topic, do_unmute_topic
|
||||||
from zerver.actions.users import change_user_is_active, get_service_dicts_for_bot
|
from zerver.actions.users import change_user_is_active, get_service_dicts_for_bot
|
||||||
from zerver.lib import retention as retention
|
from zerver.lib import retention as retention
|
||||||
|
@ -58,12 +59,10 @@ from zerver.lib.cache import (
|
||||||
cache_delete_many,
|
cache_delete_many,
|
||||||
cache_set,
|
cache_set,
|
||||||
cache_with_key,
|
cache_with_key,
|
||||||
delete_user_profile_caches,
|
|
||||||
display_recipient_cache_key,
|
display_recipient_cache_key,
|
||||||
flush_user_profile,
|
flush_user_profile,
|
||||||
get_stream_cache_key,
|
get_stream_cache_key,
|
||||||
to_dict_cache_key_id,
|
to_dict_cache_key_id,
|
||||||
user_profile_by_api_key_cache_key,
|
|
||||||
user_profile_delivery_email_cache_key,
|
user_profile_delivery_email_cache_key,
|
||||||
)
|
)
|
||||||
from zerver.lib.create_user import create_user, get_display_email_address
|
from zerver.lib.create_user import create_user, get_display_email_address
|
||||||
|
@ -78,7 +77,6 @@ from zerver.lib.exceptions import (
|
||||||
StreamWithIDDoesNotExistError,
|
StreamWithIDDoesNotExistError,
|
||||||
ZephyrMessageAlreadySentException,
|
ZephyrMessageAlreadySentException,
|
||||||
)
|
)
|
||||||
from zerver.lib.i18n import get_language_name
|
|
||||||
from zerver.lib.markdown import MessageRenderingResult, topic_links
|
from zerver.lib.markdown import MessageRenderingResult, topic_links
|
||||||
from zerver.lib.markdown import version as markdown_version
|
from zerver.lib.markdown import version as markdown_version
|
||||||
from zerver.lib.mention import MentionBackend, MentionData, silent_mention_syntax_for_user
|
from zerver.lib.mention import MentionBackend, MentionData, silent_mention_syntax_for_user
|
||||||
|
@ -103,9 +101,7 @@ from zerver.lib.recipient_users import recipient_for_user_profiles
|
||||||
from zerver.lib.retention import move_messages_to_archive
|
from zerver.lib.retention import move_messages_to_archive
|
||||||
from zerver.lib.send_email import (
|
from zerver.lib.send_email import (
|
||||||
FromAddress,
|
FromAddress,
|
||||||
clear_scheduled_emails,
|
|
||||||
clear_scheduled_invitation_emails,
|
clear_scheduled_invitation_emails,
|
||||||
send_email,
|
|
||||||
send_email_to_admins,
|
send_email_to_admins,
|
||||||
)
|
)
|
||||||
from zerver.lib.server_initialization import create_internal_realm, server_initialized
|
from zerver.lib.server_initialization import create_internal_realm, server_initialized
|
||||||
|
@ -140,7 +136,6 @@ from zerver.lib.streams import (
|
||||||
from zerver.lib.string_validation import check_stream_name, check_stream_topic
|
from zerver.lib.string_validation import check_stream_name, check_stream_topic
|
||||||
from zerver.lib.subscription_info import get_subscribers_query
|
from zerver.lib.subscription_info import get_subscribers_query
|
||||||
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
||||||
from zerver.lib.timezone import canonicalize_timezone
|
|
||||||
from zerver.lib.topic import (
|
from zerver.lib.topic import (
|
||||||
ORIG_TOPIC,
|
ORIG_TOPIC,
|
||||||
RESOLVED_TOPIC_PREFIX,
|
RESOLVED_TOPIC_PREFIX,
|
||||||
|
@ -154,7 +149,6 @@ from zerver.lib.topic import (
|
||||||
update_messages_for_topic_edit,
|
update_messages_for_topic_edit,
|
||||||
)
|
)
|
||||||
from zerver.lib.types import EditHistoryEvent
|
from zerver.lib.types import EditHistoryEvent
|
||||||
from zerver.lib.upload import delete_avatar_image
|
|
||||||
from zerver.lib.user_counts import realm_user_count, realm_user_count_by_role
|
from zerver.lib.user_counts import realm_user_count, realm_user_count_by_role
|
||||||
from zerver.lib.user_groups import (
|
from zerver.lib.user_groups import (
|
||||||
create_system_user_groups_for_realm,
|
create_system_user_groups_for_realm,
|
||||||
|
@ -163,14 +157,8 @@ from zerver.lib.user_groups import (
|
||||||
from zerver.lib.user_message import UserMessageLite, bulk_insert_ums
|
from zerver.lib.user_message import UserMessageLite, bulk_insert_ums
|
||||||
from zerver.lib.user_mutes import add_user_mute, get_muting_users, get_user_mutes
|
from zerver.lib.user_mutes import add_user_mute, get_muting_users, get_user_mutes
|
||||||
from zerver.lib.user_topics import get_users_muting_topic, remove_topic_mute
|
from zerver.lib.user_topics import get_users_muting_topic, remove_topic_mute
|
||||||
from zerver.lib.users import (
|
from zerver.lib.users import format_user_row, get_api_key, user_profile_to_user_row
|
||||||
check_bot_name_available,
|
from zerver.lib.utils import log_statsd_event
|
||||||
check_full_name,
|
|
||||||
format_user_row,
|
|
||||||
get_api_key,
|
|
||||||
user_profile_to_user_row,
|
|
||||||
)
|
|
||||||
from zerver.lib.utils import generate_api_key, log_statsd_event
|
|
||||||
from zerver.lib.validator import check_widget_content
|
from zerver.lib.validator import check_widget_content
|
||||||
from zerver.lib.widget import do_widget_post_save_actions, is_widget_message
|
from zerver.lib.widget import do_widget_post_save_actions, is_widget_message
|
||||||
from zerver.models import (
|
from zerver.models import (
|
||||||
|
@ -179,8 +167,6 @@ from zerver.models import (
|
||||||
Client,
|
Client,
|
||||||
DefaultStream,
|
DefaultStream,
|
||||||
DefaultStreamGroup,
|
DefaultStreamGroup,
|
||||||
Draft,
|
|
||||||
EmailChangeStatus,
|
|
||||||
Message,
|
Message,
|
||||||
MutedUser,
|
MutedUser,
|
||||||
PreregistrationUser,
|
PreregistrationUser,
|
||||||
|
@ -192,7 +178,6 @@ from zerver.models import (
|
||||||
Recipient,
|
Recipient,
|
||||||
ScheduledEmail,
|
ScheduledEmail,
|
||||||
ScheduledMessage,
|
ScheduledMessage,
|
||||||
ScheduledMessageNotificationEmail,
|
|
||||||
Stream,
|
Stream,
|
||||||
Subscription,
|
Subscription,
|
||||||
UserGroup,
|
UserGroup,
|
||||||
|
@ -1198,87 +1183,6 @@ def do_deactivate_stream(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def send_user_email_update_event(user_profile: UserProfile) -> None:
|
|
||||||
payload = dict(user_id=user_profile.id, new_email=user_profile.email)
|
|
||||||
event = dict(type="realm_user", op="update", person=payload)
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
event,
|
|
||||||
active_user_ids(user_profile.realm_id),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(savepoint=False)
|
|
||||||
def do_change_user_delivery_email(user_profile: UserProfile, new_email: str) -> None:
|
|
||||||
delete_user_profile_caches([user_profile])
|
|
||||||
|
|
||||||
user_profile.delivery_email = new_email
|
|
||||||
if user_profile.email_address_is_realm_public():
|
|
||||||
user_profile.email = new_email
|
|
||||||
user_profile.save(update_fields=["email", "delivery_email"])
|
|
||||||
else:
|
|
||||||
user_profile.save(update_fields=["delivery_email"])
|
|
||||||
|
|
||||||
# We notify just the target user (and eventually org admins, only
|
|
||||||
# when email_address_visibility=EMAIL_ADDRESS_VISIBILITY_ADMINS)
|
|
||||||
# about their new delivery email, since that field is private.
|
|
||||||
payload = dict(user_id=user_profile.id, delivery_email=new_email)
|
|
||||||
event = dict(type="realm_user", op="update", person=payload)
|
|
||||||
transaction.on_commit(lambda: send_event(user_profile.realm, event, [user_profile.id]))
|
|
||||||
|
|
||||||
if user_profile.avatar_source == UserProfile.AVATAR_FROM_GRAVATAR:
|
|
||||||
# If the user is using Gravatar to manage their email address,
|
|
||||||
# their Gravatar just changed, and we need to notify other
|
|
||||||
# clients.
|
|
||||||
notify_avatar_url_change(user_profile)
|
|
||||||
|
|
||||||
if user_profile.email_address_is_realm_public():
|
|
||||||
# Additionally, if we're also changing the publicly visible
|
|
||||||
# email, we send a new_email event as well.
|
|
||||||
send_user_email_update_event(user_profile)
|
|
||||||
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
acting_user=user_profile,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_EMAIL_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def do_start_email_change_process(user_profile: UserProfile, new_email: str) -> None:
|
|
||||||
old_email = user_profile.delivery_email
|
|
||||||
obj = EmailChangeStatus.objects.create(
|
|
||||||
new_email=new_email,
|
|
||||||
old_email=old_email,
|
|
||||||
user_profile=user_profile,
|
|
||||||
realm=user_profile.realm,
|
|
||||||
)
|
|
||||||
|
|
||||||
activation_url = create_confirmation_link(obj, Confirmation.EMAIL_CHANGE)
|
|
||||||
from zerver.context_processors import common_context
|
|
||||||
|
|
||||||
context = common_context(user_profile)
|
|
||||||
context.update(
|
|
||||||
old_email=old_email,
|
|
||||||
new_email=new_email,
|
|
||||||
activate_url=activation_url,
|
|
||||||
)
|
|
||||||
language = user_profile.default_language
|
|
||||||
send_email(
|
|
||||||
"zerver/emails/confirm_new_email",
|
|
||||||
to_emails=[new_email],
|
|
||||||
from_name=FromAddress.security_email_from_name(language=language),
|
|
||||||
from_address=FromAddress.tokenized_no_reply_address(),
|
|
||||||
language=language,
|
|
||||||
context=context,
|
|
||||||
realm=user_profile.realm,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def compute_irc_user_fullname(email: str) -> str:
|
def compute_irc_user_fullname(email: str) -> str:
|
||||||
return email.split("@")[0] + " (IRC)"
|
return email.split("@")[0] + " (IRC)"
|
||||||
|
|
||||||
|
@ -3708,84 +3612,6 @@ def do_change_subscription_property(
|
||||||
send_event(user_profile.realm, event, [user_profile.id])
|
send_event(user_profile.realm, event, [user_profile.id])
|
||||||
|
|
||||||
|
|
||||||
def do_change_password(user_profile: UserProfile, password: str, commit: bool = True) -> None:
|
|
||||||
user_profile.set_password(password)
|
|
||||||
if commit:
|
|
||||||
user_profile.save(update_fields=["password"])
|
|
||||||
|
|
||||||
# Imported here to prevent import cycles
|
|
||||||
from zproject.backends import RateLimitedAuthenticationByUsername
|
|
||||||
|
|
||||||
RateLimitedAuthenticationByUsername(user_profile.delivery_email).clear_history()
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
acting_user=user_profile,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_PASSWORD_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def do_change_full_name(
|
|
||||||
user_profile: UserProfile, full_name: str, acting_user: Optional[UserProfile]
|
|
||||||
) -> None:
|
|
||||||
old_name = user_profile.full_name
|
|
||||||
user_profile.full_name = full_name
|
|
||||||
user_profile.save(update_fields=["full_name"])
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
acting_user=acting_user,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_FULL_NAME_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
extra_data=old_name,
|
|
||||||
)
|
|
||||||
payload = dict(user_id=user_profile.id, full_name=user_profile.full_name)
|
|
||||||
send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
dict(type="realm_user", op="update", person=payload),
|
|
||||||
active_user_ids(user_profile.realm_id),
|
|
||||||
)
|
|
||||||
if user_profile.is_bot:
|
|
||||||
send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
dict(type="realm_bot", op="update", bot=payload),
|
|
||||||
bot_owner_user_ids(user_profile),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def check_change_full_name(
|
|
||||||
user_profile: UserProfile, full_name_raw: str, acting_user: Optional[UserProfile]
|
|
||||||
) -> str:
|
|
||||||
"""Verifies that the user's proposed full name is valid. The caller
|
|
||||||
is responsible for checking check permissions. Returns the new
|
|
||||||
full name, which may differ from what was passed in (because this
|
|
||||||
function strips whitespace)."""
|
|
||||||
new_full_name = check_full_name(full_name_raw)
|
|
||||||
do_change_full_name(user_profile, new_full_name, acting_user)
|
|
||||||
return new_full_name
|
|
||||||
|
|
||||||
|
|
||||||
def check_change_bot_full_name(
|
|
||||||
user_profile: UserProfile, full_name_raw: str, acting_user: UserProfile
|
|
||||||
) -> None:
|
|
||||||
new_full_name = check_full_name(full_name_raw)
|
|
||||||
|
|
||||||
if new_full_name == user_profile.full_name:
|
|
||||||
# Our web app will try to patch full_name even if the user didn't
|
|
||||||
# modify the name in the form. We just silently ignore those
|
|
||||||
# situations.
|
|
||||||
return
|
|
||||||
|
|
||||||
check_bot_name_available(
|
|
||||||
realm_id=user_profile.realm_id,
|
|
||||||
full_name=new_full_name,
|
|
||||||
)
|
|
||||||
do_change_full_name(user_profile, new_full_name, acting_user)
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(durable=True)
|
@transaction.atomic(durable=True)
|
||||||
def do_change_bot_owner(
|
def do_change_bot_owner(
|
||||||
user_profile: UserProfile, bot_owner: UserProfile, acting_user: UserProfile
|
user_profile: UserProfile, bot_owner: UserProfile, acting_user: UserProfile
|
||||||
|
@ -3865,128 +3691,6 @@ def do_change_bot_owner(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(durable=True)
|
|
||||||
def do_change_tos_version(user_profile: UserProfile, tos_version: str) -> None:
|
|
||||||
user_profile.tos_version = tos_version
|
|
||||||
user_profile.save(update_fields=["tos_version"])
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
acting_user=user_profile,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_TERMS_OF_SERVICE_VERSION_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def do_regenerate_api_key(user_profile: UserProfile, acting_user: UserProfile) -> str:
|
|
||||||
old_api_key = user_profile.api_key
|
|
||||||
new_api_key = generate_api_key()
|
|
||||||
user_profile.api_key = new_api_key
|
|
||||||
user_profile.save(update_fields=["api_key"])
|
|
||||||
|
|
||||||
# We need to explicitly delete the old API key from our caches,
|
|
||||||
# because the on-save handler for flushing the UserProfile object
|
|
||||||
# in zerver/lib/cache.py only has access to the new API key.
|
|
||||||
cache_delete(user_profile_by_api_key_cache_key(old_api_key))
|
|
||||||
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
acting_user=acting_user,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_API_KEY_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
)
|
|
||||||
|
|
||||||
if user_profile.is_bot:
|
|
||||||
send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
dict(
|
|
||||||
type="realm_bot",
|
|
||||||
op="update",
|
|
||||||
bot=dict(
|
|
||||||
user_id=user_profile.id,
|
|
||||||
api_key=new_api_key,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
bot_owner_user_ids(user_profile),
|
|
||||||
)
|
|
||||||
|
|
||||||
event = {"type": "clear_push_device_tokens", "user_profile_id": user_profile.id}
|
|
||||||
queue_json_publish("deferred_work", event)
|
|
||||||
|
|
||||||
return new_api_key
|
|
||||||
|
|
||||||
|
|
||||||
def notify_avatar_url_change(user_profile: UserProfile) -> None:
|
|
||||||
if user_profile.is_bot:
|
|
||||||
bot_event = dict(
|
|
||||||
type="realm_bot",
|
|
||||||
op="update",
|
|
||||||
bot=dict(
|
|
||||||
user_id=user_profile.id,
|
|
||||||
avatar_url=avatar_url(user_profile),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
bot_event,
|
|
||||||
bot_owner_user_ids(user_profile),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
payload = dict(
|
|
||||||
avatar_source=user_profile.avatar_source,
|
|
||||||
avatar_url=avatar_url(user_profile),
|
|
||||||
avatar_url_medium=avatar_url(user_profile, medium=True),
|
|
||||||
avatar_version=user_profile.avatar_version,
|
|
||||||
# Even clients using client_gravatar don't need the email,
|
|
||||||
# since we're sending the URL anyway.
|
|
||||||
user_id=user_profile.id,
|
|
||||||
)
|
|
||||||
|
|
||||||
event = dict(type="realm_user", op="update", person=payload)
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
event,
|
|
||||||
active_user_ids(user_profile.realm_id),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(savepoint=False)
|
|
||||||
def do_change_avatar_fields(
|
|
||||||
user_profile: UserProfile,
|
|
||||||
avatar_source: str,
|
|
||||||
skip_notify: bool = False,
|
|
||||||
*,
|
|
||||||
acting_user: Optional[UserProfile],
|
|
||||||
) -> None:
|
|
||||||
user_profile.avatar_source = avatar_source
|
|
||||||
user_profile.avatar_version += 1
|
|
||||||
user_profile.save(update_fields=["avatar_source", "avatar_version"])
|
|
||||||
event_time = timezone_now()
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
modified_user=user_profile,
|
|
||||||
event_type=RealmAuditLog.USER_AVATAR_SOURCE_CHANGED,
|
|
||||||
extra_data={"avatar_source": avatar_source},
|
|
||||||
event_time=event_time,
|
|
||||||
acting_user=acting_user,
|
|
||||||
)
|
|
||||||
|
|
||||||
if not skip_notify:
|
|
||||||
notify_avatar_url_change(user_profile)
|
|
||||||
|
|
||||||
|
|
||||||
def do_delete_avatar_image(user: UserProfile, *, acting_user: Optional[UserProfile]) -> None:
|
|
||||||
do_change_avatar_fields(user, UserProfile.AVATAR_FROM_GRAVATAR, acting_user=acting_user)
|
|
||||||
delete_avatar_image(user)
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(durable=True)
|
@transaction.atomic(durable=True)
|
||||||
def do_change_realm_org_type(
|
def do_change_realm_org_type(
|
||||||
realm: Realm,
|
realm: Realm,
|
||||||
|
@ -4828,141 +4532,6 @@ def do_create_realm(
|
||||||
return realm
|
return realm
|
||||||
|
|
||||||
|
|
||||||
def update_scheduled_email_notifications_time(
|
|
||||||
user_profile: UserProfile, old_batching_period: int, new_batching_period: int
|
|
||||||
) -> None:
|
|
||||||
existing_scheduled_emails = ScheduledMessageNotificationEmail.objects.filter(
|
|
||||||
user_profile=user_profile
|
|
||||||
)
|
|
||||||
|
|
||||||
scheduled_timestamp_change = datetime.timedelta(
|
|
||||||
seconds=new_batching_period
|
|
||||||
) - datetime.timedelta(seconds=old_batching_period)
|
|
||||||
|
|
||||||
existing_scheduled_emails.update(
|
|
||||||
scheduled_timestamp=F("scheduled_timestamp") + scheduled_timestamp_change
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@transaction.atomic(durable=True)
|
|
||||||
def do_change_user_setting(
|
|
||||||
user_profile: UserProfile,
|
|
||||||
setting_name: str,
|
|
||||||
setting_value: Union[bool, str, int],
|
|
||||||
*,
|
|
||||||
acting_user: Optional[UserProfile],
|
|
||||||
) -> None:
|
|
||||||
old_value = getattr(user_profile, setting_name)
|
|
||||||
event_time = timezone_now()
|
|
||||||
|
|
||||||
if setting_name == "timezone":
|
|
||||||
assert isinstance(setting_value, str)
|
|
||||||
setting_value = canonicalize_timezone(setting_value)
|
|
||||||
else:
|
|
||||||
property_type = UserProfile.property_types[setting_name]
|
|
||||||
assert isinstance(setting_value, property_type)
|
|
||||||
setattr(user_profile, setting_name, setting_value)
|
|
||||||
|
|
||||||
# TODO: Move these database actions into a transaction.atomic block.
|
|
||||||
user_profile.save(update_fields=[setting_name])
|
|
||||||
|
|
||||||
if setting_name in UserProfile.notification_setting_types:
|
|
||||||
# Prior to all personal settings being managed by property_types,
|
|
||||||
# these were only created for notification settings.
|
|
||||||
#
|
|
||||||
# TODO: Start creating these for all settings, and do a
|
|
||||||
# backfilled=True migration.
|
|
||||||
RealmAuditLog.objects.create(
|
|
||||||
realm=user_profile.realm,
|
|
||||||
event_type=RealmAuditLog.USER_SETTING_CHANGED,
|
|
||||||
event_time=event_time,
|
|
||||||
acting_user=acting_user,
|
|
||||||
modified_user=user_profile,
|
|
||||||
extra_data=orjson.dumps(
|
|
||||||
{
|
|
||||||
RealmAuditLog.OLD_VALUE: old_value,
|
|
||||||
RealmAuditLog.NEW_VALUE: setting_value,
|
|
||||||
"property": setting_name,
|
|
||||||
}
|
|
||||||
).decode(),
|
|
||||||
)
|
|
||||||
# Disabling digest emails should clear a user's email queue
|
|
||||||
if setting_name == "enable_digest_emails" and not setting_value:
|
|
||||||
clear_scheduled_emails(user_profile.id, ScheduledEmail.DIGEST)
|
|
||||||
|
|
||||||
if setting_name == "email_notifications_batching_period_seconds":
|
|
||||||
assert isinstance(old_value, int)
|
|
||||||
assert isinstance(setting_value, int)
|
|
||||||
update_scheduled_email_notifications_time(user_profile, old_value, setting_value)
|
|
||||||
|
|
||||||
event = {
|
|
||||||
"type": "user_settings",
|
|
||||||
"op": "update",
|
|
||||||
"property": setting_name,
|
|
||||||
"value": setting_value,
|
|
||||||
}
|
|
||||||
if setting_name == "default_language":
|
|
||||||
assert isinstance(setting_value, str)
|
|
||||||
event["language_name"] = get_language_name(setting_value)
|
|
||||||
|
|
||||||
transaction.on_commit(lambda: send_event(user_profile.realm, event, [user_profile.id]))
|
|
||||||
|
|
||||||
if setting_name in UserProfile.notification_settings_legacy:
|
|
||||||
# This legacy event format is for backwards-compatibility with
|
|
||||||
# clients that don't support the new user_settings event type.
|
|
||||||
# We only send this for settings added before Feature level 89.
|
|
||||||
legacy_event = {
|
|
||||||
"type": "update_global_notifications",
|
|
||||||
"user": user_profile.email,
|
|
||||||
"notification_name": setting_name,
|
|
||||||
"setting": setting_value,
|
|
||||||
}
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(user_profile.realm, legacy_event, [user_profile.id])
|
|
||||||
)
|
|
||||||
|
|
||||||
if setting_name in UserProfile.display_settings_legacy or setting_name == "timezone":
|
|
||||||
# This legacy event format is for backwards-compatibility with
|
|
||||||
# clients that don't support the new user_settings event type.
|
|
||||||
# We only send this for settings added before Feature level 89.
|
|
||||||
legacy_event = {
|
|
||||||
"type": "update_display_settings",
|
|
||||||
"user": user_profile.email,
|
|
||||||
"setting_name": setting_name,
|
|
||||||
"setting": setting_value,
|
|
||||||
}
|
|
||||||
if setting_name == "default_language":
|
|
||||||
assert isinstance(setting_value, str)
|
|
||||||
legacy_event["language_name"] = get_language_name(setting_value)
|
|
||||||
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(user_profile.realm, legacy_event, [user_profile.id])
|
|
||||||
)
|
|
||||||
|
|
||||||
# Updates to the time zone display setting are sent to all users
|
|
||||||
if setting_name == "timezone":
|
|
||||||
payload = dict(
|
|
||||||
email=user_profile.email,
|
|
||||||
user_id=user_profile.id,
|
|
||||||
timezone=canonicalize_timezone(user_profile.timezone),
|
|
||||||
)
|
|
||||||
timezone_event = dict(type="realm_user", op="update", person=payload)
|
|
||||||
transaction.on_commit(
|
|
||||||
lambda: send_event(
|
|
||||||
user_profile.realm,
|
|
||||||
timezone_event,
|
|
||||||
active_user_ids(user_profile.realm_id),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if setting_name == "enable_drafts_synchronization" and setting_value is False:
|
|
||||||
# Delete all of the drafts from the backend but don't send delete events
|
|
||||||
# for them since all that's happened is that we stopped syncing changes,
|
|
||||||
# not deleted every previously synced draft - to do that use the DELETE
|
|
||||||
# endpoint.
|
|
||||||
Draft.objects.filter(user_profile=user_profile).delete()
|
|
||||||
|
|
||||||
|
|
||||||
def get_default_subs(user_profile: UserProfile) -> List[Stream]:
|
def get_default_subs(user_profile: UserProfile) -> List[Stream]:
|
||||||
# Right now default streams are realm-wide. This wrapper gives us flexibility
|
# Right now default streams are realm-wide. This wrapper gives us flexibility
|
||||||
# to some day further customize how we set up default streams for new users.
|
# to some day further customize how we set up default streams for new users.
|
||||||
|
|
|
@ -47,7 +47,7 @@ def copy_default_settings(
|
||||||
target_profile.save()
|
target_profile.save()
|
||||||
|
|
||||||
if settings_source.avatar_source == UserProfile.AVATAR_FROM_USER:
|
if settings_source.avatar_source == UserProfile.AVATAR_FROM_USER:
|
||||||
from zerver.lib.actions import do_change_avatar_fields
|
from zerver.actions.user_settings import do_change_avatar_fields
|
||||||
|
|
||||||
do_change_avatar_fields(
|
do_change_avatar_fields(
|
||||||
target_profile,
|
target_profile,
|
||||||
|
|
|
@ -16,7 +16,8 @@ from psycopg2.extras import execute_values
|
||||||
from psycopg2.sql import SQL, Identifier
|
from psycopg2.sql import SQL, Identifier
|
||||||
|
|
||||||
from analytics.models import RealmCount, StreamCount, UserCount
|
from analytics.models import RealmCount, StreamCount, UserCount
|
||||||
from zerver.lib.actions import do_change_avatar_fields, do_change_realm_plan_type
|
from zerver.actions.user_settings import do_change_avatar_fields
|
||||||
|
from zerver.lib.actions import do_change_realm_plan_type
|
||||||
from zerver.lib.avatar_hash import user_avatar_path_from_ids
|
from zerver.lib.avatar_hash import user_avatar_path_from_ids
|
||||||
from zerver.lib.bulk_create import bulk_create_users, bulk_set_users_or_streams_recipient_fields
|
from zerver.lib.bulk_create import bulk_create_users, bulk_set_users_or_streams_recipient_fields
|
||||||
from zerver.lib.export import DATE_FIELDS, Field, Path, Record, TableData, TableName
|
from zerver.lib.export import DATE_FIELDS, Field, Path, Record, TableData, TableName
|
||||||
|
|
|
@ -9,13 +9,9 @@ from django.http import HttpRequest
|
||||||
from django_scim.adapters import SCIMUser
|
from django_scim.adapters import SCIMUser
|
||||||
from scim2_filter_parser.attr_paths import AttrPath
|
from scim2_filter_parser.attr_paths import AttrPath
|
||||||
|
|
||||||
|
from zerver.actions.user_settings import check_change_full_name, do_change_user_delivery_email
|
||||||
from zerver.actions.users import do_deactivate_user
|
from zerver.actions.users import do_deactivate_user
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import do_create_user, do_reactivate_user
|
||||||
check_change_full_name,
|
|
||||||
do_change_user_delivery_email,
|
|
||||||
do_create_user,
|
|
||||||
do_reactivate_user,
|
|
||||||
)
|
|
||||||
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
|
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
|
||||||
from zerver.lib.request import RequestNotes
|
from zerver.lib.request import RequestNotes
|
||||||
from zerver.lib.subdomains import get_subdomain
|
from zerver.lib.subdomains import get_subdomain
|
||||||
|
|
|
@ -2,7 +2,7 @@ from typing import Any, Dict
|
||||||
|
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_user_setting
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.models import UserProfile
|
from zerver.models import UserProfile
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any
|
||||||
|
|
||||||
from django.core.management.base import CommandError
|
from django.core.management.base import CommandError
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_full_name
|
from zerver.actions.user_settings import do_change_full_name
|
||||||
from zerver.lib.management import ZulipBaseCommand
|
from zerver.lib.management import ZulipBaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_user_delivery_email
|
from zerver.actions.user_settings import do_change_user_delivery_email
|
||||||
from zerver.lib.management import ZulipBaseCommand
|
from zerver.lib.management import ZulipBaseCommand
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,29 +8,31 @@ from django.utils.timezone import now as timezone_now
|
||||||
from analytics.models import StreamCount
|
from analytics.models import StreamCount
|
||||||
from zerver.actions.realm_icon import do_change_icon_source
|
from zerver.actions.realm_icon import do_change_icon_source
|
||||||
from zerver.actions.realm_playgrounds import do_add_realm_playground, do_remove_realm_playground
|
from zerver.actions.realm_playgrounds import do_add_realm_playground, do_remove_realm_playground
|
||||||
|
from zerver.actions.user_settings import (
|
||||||
|
do_change_avatar_fields,
|
||||||
|
do_change_password,
|
||||||
|
do_change_tos_version,
|
||||||
|
do_change_user_delivery_email,
|
||||||
|
do_change_user_setting,
|
||||||
|
do_regenerate_api_key,
|
||||||
|
)
|
||||||
from zerver.actions.users import do_change_user_role, do_deactivate_user
|
from zerver.actions.users import do_change_user_role, do_deactivate_user
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
bulk_add_subscriptions,
|
bulk_add_subscriptions,
|
||||||
bulk_remove_subscriptions,
|
bulk_remove_subscriptions,
|
||||||
do_activate_mirror_dummy_user,
|
do_activate_mirror_dummy_user,
|
||||||
do_add_realm_domain,
|
do_add_realm_domain,
|
||||||
do_change_avatar_fields,
|
|
||||||
do_change_bot_owner,
|
do_change_bot_owner,
|
||||||
do_change_default_all_public_streams,
|
do_change_default_all_public_streams,
|
||||||
do_change_default_events_register_stream,
|
do_change_default_events_register_stream,
|
||||||
do_change_default_sending_stream,
|
do_change_default_sending_stream,
|
||||||
do_change_password,
|
|
||||||
do_change_realm_domain,
|
do_change_realm_domain,
|
||||||
do_change_subscription_property,
|
do_change_subscription_property,
|
||||||
do_change_tos_version,
|
|
||||||
do_change_user_delivery_email,
|
|
||||||
do_change_user_setting,
|
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_deactivate_realm,
|
do_deactivate_realm,
|
||||||
do_deactivate_stream,
|
do_deactivate_stream,
|
||||||
do_reactivate_realm,
|
do_reactivate_realm,
|
||||||
do_reactivate_user,
|
do_reactivate_user,
|
||||||
do_regenerate_api_key,
|
|
||||||
do_remove_realm_domain,
|
do_remove_realm_domain,
|
||||||
do_rename_stream,
|
do_rename_stream,
|
||||||
do_set_realm_authentication_methods,
|
do_set_realm_authentication_methods,
|
||||||
|
|
|
@ -41,9 +41,9 @@ from social_django.strategy import DjangoStrategy
|
||||||
|
|
||||||
from confirmation.models import Confirmation, create_confirmation_link
|
from confirmation.models import Confirmation, create_confirmation_link
|
||||||
from zerver.actions.invites import do_invite_users
|
from zerver.actions.invites import do_invite_users
|
||||||
|
from zerver.actions.user_settings import do_change_password
|
||||||
from zerver.actions.users import change_user_is_active, do_deactivate_user
|
from zerver.actions.users import change_user_is_active, do_deactivate_user
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
do_change_password,
|
|
||||||
do_create_realm,
|
do_create_realm,
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_deactivate_realm,
|
do_deactivate_realm,
|
||||||
|
@ -6122,7 +6122,7 @@ class TestZulipLDAPUserPopulator(ZulipLDAPTestCase):
|
||||||
self.assertEqual(hamlet.full_name, "Full Name")
|
self.assertEqual(hamlet.full_name, "Full Name")
|
||||||
|
|
||||||
def test_same_full_name(self) -> None:
|
def test_same_full_name(self) -> None:
|
||||||
with mock.patch("zerver.lib.actions.do_change_full_name") as fn:
|
with mock.patch("zerver.actions.user_settings.do_change_full_name") as fn:
|
||||||
self.perform_ldap_sync(self.example_user("hamlet"))
|
self.perform_ldap_sync(self.example_user("hamlet"))
|
||||||
fn.assert_not_called()
|
fn.assert_not_called()
|
||||||
|
|
||||||
|
@ -6239,7 +6239,7 @@ class TestZulipLDAPUserPopulator(ZulipLDAPTestCase):
|
||||||
self.change_ldap_user_attr("hamlet", "cn", "Second Hamlet")
|
self.change_ldap_user_attr("hamlet", "cn", "Second Hamlet")
|
||||||
expected_call_args = [hamlet2, "Second Hamlet", None]
|
expected_call_args = [hamlet2, "Second Hamlet", None]
|
||||||
with self.settings(AUTH_LDAP_USER_ATTR_MAP={"full_name": "cn"}):
|
with self.settings(AUTH_LDAP_USER_ATTR_MAP={"full_name": "cn"}):
|
||||||
with mock.patch("zerver.lib.actions.do_change_full_name") as f:
|
with mock.patch("zerver.actions.user_settings.do_change_full_name") as f:
|
||||||
self.perform_ldap_sync(hamlet2)
|
self.perform_ldap_sync(hamlet2)
|
||||||
f.assert_called_once_with(*expected_call_args)
|
f.assert_called_once_with(*expected_call_args)
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,9 @@ from confirmation.models import (
|
||||||
create_confirmation_link,
|
create_confirmation_link,
|
||||||
generate_key,
|
generate_key,
|
||||||
)
|
)
|
||||||
|
from zerver.actions.user_settings import do_start_email_change_process
|
||||||
from zerver.actions.users import do_deactivate_user
|
from zerver.actions.users import do_deactivate_user
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import do_deactivate_realm, do_set_realm_property
|
||||||
do_deactivate_realm,
|
|
||||||
do_set_realm_property,
|
|
||||||
do_start_email_change_process,
|
|
||||||
)
|
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
from zerver.models import (
|
from zerver.models import (
|
||||||
EmailChangeStatus,
|
EmailChangeStatus,
|
||||||
|
|
|
@ -15,8 +15,8 @@ from django.test import override_settings
|
||||||
from django.utils.timezone import now as timezone_now
|
from django.utils.timezone import now as timezone_now
|
||||||
from django_auth_ldap.config import LDAPSearch
|
from django_auth_ldap.config import LDAPSearch
|
||||||
|
|
||||||
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
from zerver.actions.users import do_change_user_role
|
from zerver.actions.users import do_change_user_role
|
||||||
from zerver.lib.actions import do_change_user_setting
|
|
||||||
from zerver.lib.email_notifications import (
|
from zerver.lib.email_notifications import (
|
||||||
enqueue_welcome_emails,
|
enqueue_welcome_emails,
|
||||||
fix_emojis,
|
fix_emojis,
|
||||||
|
|
|
@ -58,6 +58,13 @@ from zerver.actions.user_groups import (
|
||||||
do_update_user_group_name,
|
do_update_user_group_name,
|
||||||
remove_members_from_user_group,
|
remove_members_from_user_group,
|
||||||
)
|
)
|
||||||
|
from zerver.actions.user_settings import (
|
||||||
|
do_change_avatar_fields,
|
||||||
|
do_change_full_name,
|
||||||
|
do_change_user_delivery_email,
|
||||||
|
do_change_user_setting,
|
||||||
|
do_regenerate_api_key,
|
||||||
|
)
|
||||||
from zerver.actions.user_topics import do_mute_topic, do_unmute_topic
|
from zerver.actions.user_topics import do_mute_topic, do_unmute_topic
|
||||||
from zerver.actions.users import (
|
from zerver.actions.users import (
|
||||||
do_change_user_role,
|
do_change_user_role,
|
||||||
|
@ -71,12 +78,10 @@ from zerver.lib.actions import (
|
||||||
bulk_remove_subscriptions,
|
bulk_remove_subscriptions,
|
||||||
do_add_reaction,
|
do_add_reaction,
|
||||||
do_add_realm_domain,
|
do_add_realm_domain,
|
||||||
do_change_avatar_fields,
|
|
||||||
do_change_bot_owner,
|
do_change_bot_owner,
|
||||||
do_change_default_all_public_streams,
|
do_change_default_all_public_streams,
|
||||||
do_change_default_events_register_stream,
|
do_change_default_events_register_stream,
|
||||||
do_change_default_sending_stream,
|
do_change_default_sending_stream,
|
||||||
do_change_full_name,
|
|
||||||
do_change_realm_domain,
|
do_change_realm_domain,
|
||||||
do_change_realm_plan_type,
|
do_change_realm_plan_type,
|
||||||
do_change_stream_description,
|
do_change_stream_description,
|
||||||
|
@ -84,15 +89,12 @@ from zerver.lib.actions import (
|
||||||
do_change_stream_permission,
|
do_change_stream_permission,
|
||||||
do_change_stream_post_policy,
|
do_change_stream_post_policy,
|
||||||
do_change_subscription_property,
|
do_change_subscription_property,
|
||||||
do_change_user_delivery_email,
|
|
||||||
do_change_user_setting,
|
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_deactivate_realm,
|
do_deactivate_realm,
|
||||||
do_deactivate_stream,
|
do_deactivate_stream,
|
||||||
do_delete_messages,
|
do_delete_messages,
|
||||||
do_mute_user,
|
do_mute_user,
|
||||||
do_reactivate_user,
|
do_reactivate_user,
|
||||||
do_regenerate_api_key,
|
|
||||||
do_remove_reaction,
|
do_remove_reaction,
|
||||||
do_remove_realm_domain,
|
do_remove_realm_domain,
|
||||||
do_rename_stream,
|
do_rename_stream,
|
||||||
|
|
|
@ -12,8 +12,9 @@ from markdown import Markdown
|
||||||
|
|
||||||
from zerver.actions.alert_words import do_add_alert_words
|
from zerver.actions.alert_words import do_add_alert_words
|
||||||
from zerver.actions.realm_emoji import do_remove_realm_emoji
|
from zerver.actions.realm_emoji import do_remove_realm_emoji
|
||||||
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
from zerver.actions.users import change_user_is_active
|
from zerver.actions.users import change_user_is_active
|
||||||
from zerver.lib.actions import do_change_user_setting, do_create_realm, do_set_realm_property
|
from zerver.lib.actions import do_create_realm, do_set_realm_property
|
||||||
from zerver.lib.alert_words import get_alert_word_automaton
|
from zerver.lib.alert_words import get_alert_word_automaton
|
||||||
from zerver.lib.camo import get_camo_url
|
from zerver.lib.camo import get_camo_url
|
||||||
from zerver.lib.create_user import create_user
|
from zerver.lib.create_user import create_user
|
||||||
|
|
|
@ -8,7 +8,8 @@ from django.core import mail
|
||||||
from django.test import override_settings
|
from django.test import override_settings
|
||||||
|
|
||||||
from corporate.lib.stripe import get_latest_seat_count
|
from corporate.lib.stripe import get_latest_seat_count
|
||||||
from zerver.lib.actions import do_change_user_setting, notify_new_user
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
|
from zerver.lib.actions import notify_new_user
|
||||||
from zerver.lib.initial_password import initial_password
|
from zerver.lib.initial_password import initial_password
|
||||||
from zerver.lib.streams import create_stream_if_needed
|
from zerver.lib.streams import create_stream_if_needed
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
|
|
|
@ -23,10 +23,10 @@ from requests.models import PreparedRequest
|
||||||
|
|
||||||
from analytics.lib.counts import CountStat, LoggingCountStat
|
from analytics.lib.counts import CountStat, LoggingCountStat
|
||||||
from analytics.models import InstallationCount, RealmCount
|
from analytics.models import InstallationCount, RealmCount
|
||||||
|
from zerver.actions.user_settings import do_regenerate_api_key
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
do_delete_messages,
|
do_delete_messages,
|
||||||
do_mark_stream_messages_as_read,
|
do_mark_stream_messages_as_read,
|
||||||
do_regenerate_api_key,
|
|
||||||
do_update_message_flags,
|
do_update_message_flags,
|
||||||
)
|
)
|
||||||
from zerver.lib.avatar import absolute_avatar_url
|
from zerver.lib.avatar import absolute_avatar_url
|
||||||
|
|
|
@ -639,7 +639,7 @@ class WorkerTest(ZulipTestCase):
|
||||||
with simulated_queue_client(fake_client):
|
with simulated_queue_client(fake_client):
|
||||||
worker = queue_processors.ConfirmationEmailWorker()
|
worker = queue_processors.ConfirmationEmailWorker()
|
||||||
worker.setup()
|
worker.setup()
|
||||||
with patch("zerver.lib.actions.send_email"), patch(
|
with patch("zerver.actions.user_settings.send_email"), patch(
|
||||||
"zerver.worker.queue_processors.send_future_email"
|
"zerver.worker.queue_processors.send_future_email"
|
||||||
) as send_mock:
|
) as send_mock:
|
||||||
worker.start()
|
worker.start()
|
||||||
|
|
|
@ -7,7 +7,7 @@ import orjson
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.http import HttpResponse
|
from django.http import HttpResponse
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_full_name
|
from zerver.actions.user_settings import do_change_full_name
|
||||||
from zerver.lib.test_classes import ZulipTestCase
|
from zerver.lib.test_classes import ZulipTestCase
|
||||||
from zerver.models import SCIMClient, UserProfile, get_realm
|
from zerver.models import SCIMClient, UserProfile, get_realm
|
||||||
|
|
||||||
|
|
|
@ -35,13 +35,13 @@ from zerver.actions.invites import (
|
||||||
do_get_invites_controlled_by_user,
|
do_get_invites_controlled_by_user,
|
||||||
do_invite_users,
|
do_invite_users,
|
||||||
)
|
)
|
||||||
|
from zerver.actions.user_settings import do_change_full_name
|
||||||
from zerver.actions.users import change_user_is_active, do_change_user_role, do_deactivate_user
|
from zerver.actions.users import change_user_is_active, do_change_user_role, do_deactivate_user
|
||||||
from zerver.context_processors import common_context
|
from zerver.context_processors import common_context
|
||||||
from zerver.decorator import do_two_factor_login
|
from zerver.decorator import do_two_factor_login
|
||||||
from zerver.forms import HomepageForm, check_subdomain_available
|
from zerver.forms import HomepageForm, check_subdomain_available
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
add_new_user_history,
|
add_new_user_history,
|
||||||
do_change_full_name,
|
|
||||||
do_change_realm_subdomain,
|
do_change_realm_subdomain,
|
||||||
do_create_realm,
|
do_create_realm,
|
||||||
do_create_user,
|
do_create_user,
|
||||||
|
|
|
@ -22,6 +22,7 @@ import zerver.lib.upload
|
||||||
from zerver.actions.realm_icon import do_change_icon_source
|
from zerver.actions.realm_icon import do_change_icon_source
|
||||||
from zerver.actions.realm_logo import do_change_logo_source
|
from zerver.actions.realm_logo import do_change_logo_source
|
||||||
from zerver.actions.uploads import do_delete_old_unclaimed_attachments
|
from zerver.actions.uploads import do_delete_old_unclaimed_attachments
|
||||||
|
from zerver.actions.user_settings import do_delete_avatar_image
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
do_change_realm_plan_type,
|
do_change_realm_plan_type,
|
||||||
do_create_realm,
|
do_create_realm,
|
||||||
|
@ -1287,7 +1288,7 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase):
|
||||||
self.assertTrue(os.path.isfile(avatar_original_path_id))
|
self.assertTrue(os.path.isfile(avatar_original_path_id))
|
||||||
self.assertTrue(os.path.isfile(avatar_medium_path_id))
|
self.assertTrue(os.path.isfile(avatar_medium_path_id))
|
||||||
|
|
||||||
zerver.lib.actions.do_delete_avatar_image(user, acting_user=user)
|
do_delete_avatar_image(user, acting_user=user)
|
||||||
|
|
||||||
self.assertEqual(user.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
self.assertEqual(user.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
||||||
self.assertFalse(os.path.isfile(avatar_path_id))
|
self.assertFalse(os.path.isfile(avatar_path_id))
|
||||||
|
@ -2124,7 +2125,7 @@ class S3Test(ZulipTestCase):
|
||||||
self.assertIsNotNone(bucket.Object(avatar_original_image_path_id))
|
self.assertIsNotNone(bucket.Object(avatar_original_image_path_id))
|
||||||
self.assertIsNotNone(bucket.Object(avatar_medium_path_id))
|
self.assertIsNotNone(bucket.Object(avatar_medium_path_id))
|
||||||
|
|
||||||
zerver.lib.actions.do_delete_avatar_image(user, acting_user=user)
|
do_delete_avatar_image(user, acting_user=user)
|
||||||
|
|
||||||
self.assertEqual(user.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
self.assertEqual(user.avatar_source, UserProfile.AVATAR_FROM_GRAVATAR)
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,9 @@ from django.shortcuts import redirect, render
|
||||||
from django.views.decorators.http import require_safe
|
from django.views.decorators.http import require_safe
|
||||||
|
|
||||||
from confirmation.models import Confirmation, confirmation_url
|
from confirmation.models import Confirmation, confirmation_url
|
||||||
|
from zerver.actions.user_settings import do_change_user_delivery_email
|
||||||
from zerver.actions.users import change_user_is_active
|
from zerver.actions.users import change_user_is_active
|
||||||
from zerver.lib.actions import do_change_user_delivery_email, do_send_realm_reactivation_email
|
from zerver.lib.actions import do_send_realm_reactivation_email
|
||||||
from zerver.lib.email_notifications import enqueue_welcome_emails
|
from zerver.lib.email_notifications import enqueue_welcome_emails
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.models import Realm, get_realm, get_realm_stream, get_user_by_delivery_email
|
from zerver.models import Realm, get_realm, get_realm_stream, get_user_by_delivery_email
|
||||||
|
|
|
@ -7,10 +7,10 @@ from django.http import HttpRequest, HttpResponse
|
||||||
from django.shortcuts import redirect, render
|
from django.shortcuts import redirect, render
|
||||||
from django.utils.cache import patch_cache_control
|
from django.utils.cache import patch_cache_control
|
||||||
|
|
||||||
|
from zerver.actions.user_settings import do_change_tos_version
|
||||||
from zerver.context_processors import get_valid_realm_from_request
|
from zerver.context_processors import get_valid_realm_from_request
|
||||||
from zerver.decorator import web_public_view, zulip_login_required
|
from zerver.decorator import web_public_view, zulip_login_required
|
||||||
from zerver.forms import ToSForm
|
from zerver.forms import ToSForm
|
||||||
from zerver.lib.actions import do_change_tos_version
|
|
||||||
from zerver.lib.compatibility import is_outdated_desktop_app, is_unsupported_browser
|
from zerver.lib.compatibility import is_outdated_desktop_app, is_unsupported_browser
|
||||||
from zerver.lib.home import build_page_params_for_home_page_load, get_user_permission_info
|
from zerver.lib.home import build_page_params_for_home_page_load, get_user_permission_info
|
||||||
from zerver.lib.request import RequestNotes
|
from zerver.lib.request import RequestNotes
|
||||||
|
|
|
@ -26,6 +26,11 @@ from confirmation.models import (
|
||||||
validate_key,
|
validate_key,
|
||||||
)
|
)
|
||||||
from zerver.actions.default_streams import lookup_default_stream_groups
|
from zerver.actions.default_streams import lookup_default_stream_groups
|
||||||
|
from zerver.actions.user_settings import (
|
||||||
|
do_change_full_name,
|
||||||
|
do_change_password,
|
||||||
|
do_change_user_setting,
|
||||||
|
)
|
||||||
from zerver.context_processors import get_realm_from_request, login_context
|
from zerver.context_processors import get_realm_from_request, login_context
|
||||||
from zerver.decorator import do_login, rate_limit_request_by_ip, require_post
|
from zerver.decorator import do_login, rate_limit_request_by_ip, require_post
|
||||||
from zerver.forms import (
|
from zerver.forms import (
|
||||||
|
@ -35,14 +40,7 @@ from zerver.forms import (
|
||||||
RealmRedirectForm,
|
RealmRedirectForm,
|
||||||
RegistrationForm,
|
RegistrationForm,
|
||||||
)
|
)
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import do_activate_mirror_dummy_user, do_create_realm, do_create_user
|
||||||
do_activate_mirror_dummy_user,
|
|
||||||
do_change_full_name,
|
|
||||||
do_change_password,
|
|
||||||
do_change_user_setting,
|
|
||||||
do_create_realm,
|
|
||||||
do_create_user,
|
|
||||||
)
|
|
||||||
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
|
from zerver.lib.email_validation import email_allowed_for_realm, validate_email_not_already_in_realm
|
||||||
from zerver.lib.exceptions import RateLimited
|
from zerver.lib.exceptions import RateLimited
|
||||||
from zerver.lib.pysa import mark_sanitized
|
from zerver.lib.pysa import mark_sanitized
|
||||||
|
|
|
@ -5,8 +5,8 @@ from django.shortcuts import render
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
|
|
||||||
from confirmation.models import Confirmation, ConfirmationKeyException, get_object_from_key
|
from confirmation.models import Confirmation, ConfirmationKeyException, get_object_from_key
|
||||||
|
from zerver.actions.user_settings import do_change_user_setting
|
||||||
from zerver.context_processors import common_context
|
from zerver.context_processors import common_context
|
||||||
from zerver.lib.actions import do_change_user_setting
|
|
||||||
from zerver.lib.send_email import clear_scheduled_emails
|
from zerver.lib.send_email import clear_scheduled_emails
|
||||||
from zerver.models import ScheduledEmail, UserProfile
|
from zerver.models import ScheduledEmail, UserProfile
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,7 @@ from confirmation.models import (
|
||||||
get_object_from_key,
|
get_object_from_key,
|
||||||
render_confirmation_key_error,
|
render_confirmation_key_error,
|
||||||
)
|
)
|
||||||
from zerver.decorator import human_users_only
|
from zerver.actions.user_settings import (
|
||||||
from zerver.lib.actions import (
|
|
||||||
check_change_full_name,
|
check_change_full_name,
|
||||||
do_change_avatar_fields,
|
do_change_avatar_fields,
|
||||||
do_change_password,
|
do_change_password,
|
||||||
|
@ -27,6 +26,7 @@ from zerver.lib.actions import (
|
||||||
do_regenerate_api_key,
|
do_regenerate_api_key,
|
||||||
do_start_email_change_process,
|
do_start_email_change_process,
|
||||||
)
|
)
|
||||||
|
from zerver.decorator import human_users_only
|
||||||
from zerver.lib.avatar import avatar_url
|
from zerver.lib.avatar import avatar_url
|
||||||
from zerver.lib.email_validation import (
|
from zerver.lib.email_validation import (
|
||||||
get_realm_email_validator,
|
get_realm_email_validator,
|
||||||
|
|
|
@ -10,6 +10,12 @@ from zerver.actions.custom_profile_fields import (
|
||||||
check_remove_custom_profile_field_value,
|
check_remove_custom_profile_field_value,
|
||||||
do_update_user_custom_profile_data_if_changed,
|
do_update_user_custom_profile_data_if_changed,
|
||||||
)
|
)
|
||||||
|
from zerver.actions.user_settings import (
|
||||||
|
check_change_bot_full_name,
|
||||||
|
check_change_full_name,
|
||||||
|
do_change_avatar_fields,
|
||||||
|
do_regenerate_api_key,
|
||||||
|
)
|
||||||
from zerver.actions.users import (
|
from zerver.actions.users import (
|
||||||
do_change_user_role,
|
do_change_user_role,
|
||||||
do_deactivate_user,
|
do_deactivate_user,
|
||||||
|
@ -20,16 +26,12 @@ from zerver.context_processors import get_valid_realm_from_request
|
||||||
from zerver.decorator import require_member_or_admin, require_realm_admin
|
from zerver.decorator import require_member_or_admin, require_realm_admin
|
||||||
from zerver.forms import PASSWORD_TOO_WEAK_ERROR, CreateUserForm
|
from zerver.forms import PASSWORD_TOO_WEAK_ERROR, CreateUserForm
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
check_change_bot_full_name,
|
|
||||||
check_change_full_name,
|
|
||||||
do_change_avatar_fields,
|
|
||||||
do_change_bot_owner,
|
do_change_bot_owner,
|
||||||
do_change_default_all_public_streams,
|
do_change_default_all_public_streams,
|
||||||
do_change_default_events_register_stream,
|
do_change_default_events_register_stream,
|
||||||
do_change_default_sending_stream,
|
do_change_default_sending_stream,
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_reactivate_user,
|
do_reactivate_user,
|
||||||
do_regenerate_api_key,
|
|
||||||
notify_created_bot,
|
notify_created_bot,
|
||||||
)
|
)
|
||||||
from zerver.lib.avatar import avatar_url, get_gravatar_url
|
from zerver.lib.avatar import avatar_url, get_gravatar_url
|
||||||
|
|
|
@ -2,10 +2,10 @@ from typing import Any, Dict, List
|
||||||
|
|
||||||
from django.core.management.base import BaseCommand
|
from django.core.management.base import BaseCommand
|
||||||
|
|
||||||
|
from zerver.actions.user_settings import do_change_avatar_fields
|
||||||
from zerver.lib.actions import (
|
from zerver.lib.actions import (
|
||||||
bulk_add_subscriptions,
|
bulk_add_subscriptions,
|
||||||
do_add_reaction,
|
do_add_reaction,
|
||||||
do_change_avatar_fields,
|
|
||||||
do_create_user,
|
do_create_user,
|
||||||
do_send_messages,
|
do_send_messages,
|
||||||
internal_prep_stream_message,
|
internal_prep_stream_message,
|
||||||
|
|
|
@ -59,9 +59,10 @@ from typing_extensions import TypedDict
|
||||||
from zxcvbn import zxcvbn
|
from zxcvbn import zxcvbn
|
||||||
|
|
||||||
from zerver.actions.custom_profile_fields import do_update_user_custom_profile_data_if_changed
|
from zerver.actions.custom_profile_fields import do_update_user_custom_profile_data_if_changed
|
||||||
|
from zerver.actions.user_settings import do_regenerate_api_key
|
||||||
from zerver.actions.users import do_deactivate_user
|
from zerver.actions.users import do_deactivate_user
|
||||||
from zerver.decorator import client_is_exempt_from_rate_limiting
|
from zerver.decorator import client_is_exempt_from_rate_limiting
|
||||||
from zerver.lib.actions import do_create_user, do_reactivate_user, do_regenerate_api_key
|
from zerver.lib.actions import do_create_user, do_reactivate_user
|
||||||
from zerver.lib.avatar import avatar_url, is_avatar_new
|
from zerver.lib.avatar import avatar_url, is_avatar_new
|
||||||
from zerver.lib.avatar_hash import user_avatar_content_hash
|
from zerver.lib.avatar_hash import user_avatar_content_hash
|
||||||
from zerver.lib.dev_ldap_directory import init_fakeldap
|
from zerver.lib.dev_ldap_directory import init_fakeldap
|
||||||
|
@ -704,7 +705,7 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend):
|
||||||
# We do local imports here to avoid import loops
|
# We do local imports here to avoid import loops
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
from zerver.lib.actions import do_change_avatar_fields
|
from zerver.actions.user_settings import do_change_avatar_fields
|
||||||
from zerver.lib.upload import upload_avatar_image
|
from zerver.lib.upload import upload_avatar_image
|
||||||
|
|
||||||
avatar_attr_name = settings.AUTH_LDAP_USER_ATTR_MAP["avatar"]
|
avatar_attr_name = settings.AUTH_LDAP_USER_ATTR_MAP["avatar"]
|
||||||
|
@ -818,7 +819,7 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend):
|
||||||
return full_name
|
return full_name
|
||||||
|
|
||||||
def sync_full_name_from_ldap(self, user_profile: UserProfile, ldap_user: _LDAPUser) -> None:
|
def sync_full_name_from_ldap(self, user_profile: UserProfile, ldap_user: _LDAPUser) -> None:
|
||||||
from zerver.lib.actions import do_change_full_name
|
from zerver.actions.user_settings import do_change_full_name
|
||||||
|
|
||||||
full_name = self.get_mapped_name(ldap_user)
|
full_name = self.get_mapped_name(ldap_user)
|
||||||
if full_name != user_profile.full_name:
|
if full_name != user_profile.full_name:
|
||||||
|
|
Loading…
Reference in New Issue