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 tools.lib.test_script import prepare_puppeteer_run
|
||||
from zerver.lib.actions import (
|
||||
bulk_add_subscriptions,
|
||||
do_change_avatar_fields,
|
||||
do_create_user,
|
||||
notify_created_bot,
|
||||
)
|
||||
from zerver.actions.user_settings import do_change_avatar_fields
|
||||
from zerver.lib.actions import bulk_add_subscriptions, do_create_user, notify_created_bot
|
||||
from zerver.lib.integrations import (
|
||||
DOC_SCREENSHOT_CONFIG,
|
||||
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.utils import totp_digits
|
||||
|
||||
from zerver.actions.user_settings import do_change_password
|
||||
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.exceptions import JsonableError, RateLimited
|
||||
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,
|
||||
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.users import change_user_is_active, get_service_dicts_for_bot
|
||||
from zerver.lib import retention as retention
|
||||
|
@ -58,12 +59,10 @@ from zerver.lib.cache import (
|
|||
cache_delete_many,
|
||||
cache_set,
|
||||
cache_with_key,
|
||||
delete_user_profile_caches,
|
||||
display_recipient_cache_key,
|
||||
flush_user_profile,
|
||||
get_stream_cache_key,
|
||||
to_dict_cache_key_id,
|
||||
user_profile_by_api_key_cache_key,
|
||||
user_profile_delivery_email_cache_key,
|
||||
)
|
||||
from zerver.lib.create_user import create_user, get_display_email_address
|
||||
|
@ -78,7 +77,6 @@ from zerver.lib.exceptions import (
|
|||
StreamWithIDDoesNotExistError,
|
||||
ZephyrMessageAlreadySentException,
|
||||
)
|
||||
from zerver.lib.i18n import get_language_name
|
||||
from zerver.lib.markdown import MessageRenderingResult, topic_links
|
||||
from zerver.lib.markdown import version as markdown_version
|
||||
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.send_email import (
|
||||
FromAddress,
|
||||
clear_scheduled_emails,
|
||||
clear_scheduled_invitation_emails,
|
||||
send_email,
|
||||
send_email_to_admins,
|
||||
)
|
||||
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.subscription_info import get_subscribers_query
|
||||
from zerver.lib.timestamp import datetime_to_timestamp, timestamp_to_datetime
|
||||
from zerver.lib.timezone import canonicalize_timezone
|
||||
from zerver.lib.topic import (
|
||||
ORIG_TOPIC,
|
||||
RESOLVED_TOPIC_PREFIX,
|
||||
|
@ -154,7 +149,6 @@ from zerver.lib.topic import (
|
|||
update_messages_for_topic_edit,
|
||||
)
|
||||
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_groups import (
|
||||
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_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.users import (
|
||||
check_bot_name_available,
|
||||
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.users import format_user_row, get_api_key, user_profile_to_user_row
|
||||
from zerver.lib.utils import log_statsd_event
|
||||
from zerver.lib.validator import check_widget_content
|
||||
from zerver.lib.widget import do_widget_post_save_actions, is_widget_message
|
||||
from zerver.models import (
|
||||
|
@ -179,8 +167,6 @@ from zerver.models import (
|
|||
Client,
|
||||
DefaultStream,
|
||||
DefaultStreamGroup,
|
||||
Draft,
|
||||
EmailChangeStatus,
|
||||
Message,
|
||||
MutedUser,
|
||||
PreregistrationUser,
|
||||
|
@ -192,7 +178,6 @@ from zerver.models import (
|
|||
Recipient,
|
||||
ScheduledEmail,
|
||||
ScheduledMessage,
|
||||
ScheduledMessageNotificationEmail,
|
||||
Stream,
|
||||
Subscription,
|
||||
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:
|
||||
return email.split("@")[0] + " (IRC)"
|
||||
|
||||
|
@ -3708,84 +3612,6 @@ def do_change_subscription_property(
|
|||
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)
|
||||
def do_change_bot_owner(
|
||||
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)
|
||||
def do_change_realm_org_type(
|
||||
realm: Realm,
|
||||
|
@ -4828,141 +4532,6 @@ def do_create_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]:
|
||||
# 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.
|
||||
|
|
|
@ -47,7 +47,7 @@ def copy_default_settings(
|
|||
target_profile.save()
|
||||
|
||||
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(
|
||||
target_profile,
|
||||
|
|
|
@ -16,7 +16,8 @@ from psycopg2.extras import execute_values
|
|||
from psycopg2.sql import SQL, Identifier
|
||||
|
||||
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.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
|
||||
|
|
|
@ -9,13 +9,9 @@ from django.http import HttpRequest
|
|||
from django_scim.adapters import SCIMUser
|
||||
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.lib.actions import (
|
||||
check_change_full_name,
|
||||
do_change_user_delivery_email,
|
||||
do_create_user,
|
||||
do_reactivate_user,
|
||||
)
|
||||
from zerver.lib.actions import 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.request import RequestNotes
|
||||
from zerver.lib.subdomains import get_subdomain
|
||||
|
|
|
@ -2,7 +2,7 @@ from typing import Any, Dict
|
|||
|
||||
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.models import UserProfile
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from typing import Any
|
|||
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from argparse import ArgumentParser
|
||||
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
|
||||
|
||||
|
||||
|
|
|
@ -8,29 +8,31 @@ from django.utils.timezone import now as timezone_now
|
|||
from analytics.models import StreamCount
|
||||
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.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.lib.actions import (
|
||||
bulk_add_subscriptions,
|
||||
bulk_remove_subscriptions,
|
||||
do_activate_mirror_dummy_user,
|
||||
do_add_realm_domain,
|
||||
do_change_avatar_fields,
|
||||
do_change_bot_owner,
|
||||
do_change_default_all_public_streams,
|
||||
do_change_default_events_register_stream,
|
||||
do_change_default_sending_stream,
|
||||
do_change_password,
|
||||
do_change_realm_domain,
|
||||
do_change_subscription_property,
|
||||
do_change_tos_version,
|
||||
do_change_user_delivery_email,
|
||||
do_change_user_setting,
|
||||
do_create_user,
|
||||
do_deactivate_realm,
|
||||
do_deactivate_stream,
|
||||
do_reactivate_realm,
|
||||
do_reactivate_user,
|
||||
do_regenerate_api_key,
|
||||
do_remove_realm_domain,
|
||||
do_rename_stream,
|
||||
do_set_realm_authentication_methods,
|
||||
|
|
|
@ -41,9 +41,9 @@ from social_django.strategy import DjangoStrategy
|
|||
|
||||
from confirmation.models import Confirmation, create_confirmation_link
|
||||
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.lib.actions import (
|
||||
do_change_password,
|
||||
do_create_realm,
|
||||
do_create_user,
|
||||
do_deactivate_realm,
|
||||
|
@ -6122,7 +6122,7 @@ class TestZulipLDAPUserPopulator(ZulipLDAPTestCase):
|
|||
self.assertEqual(hamlet.full_name, "Full Name")
|
||||
|
||||
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"))
|
||||
fn.assert_not_called()
|
||||
|
||||
|
@ -6239,7 +6239,7 @@ class TestZulipLDAPUserPopulator(ZulipLDAPTestCase):
|
|||
self.change_ldap_user_attr("hamlet", "cn", "Second Hamlet")
|
||||
expected_call_args = [hamlet2, "Second Hamlet", None]
|
||||
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)
|
||||
f.assert_called_once_with(*expected_call_args)
|
||||
|
||||
|
|
|
@ -11,12 +11,9 @@ from confirmation.models import (
|
|||
create_confirmation_link,
|
||||
generate_key,
|
||||
)
|
||||
from zerver.actions.user_settings import do_start_email_change_process
|
||||
from zerver.actions.users import do_deactivate_user
|
||||
from zerver.lib.actions import (
|
||||
do_deactivate_realm,
|
||||
do_set_realm_property,
|
||||
do_start_email_change_process,
|
||||
)
|
||||
from zerver.lib.actions import do_deactivate_realm, do_set_realm_property
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.models import (
|
||||
EmailChangeStatus,
|
||||
|
|
|
@ -15,8 +15,8 @@ from django.test import override_settings
|
|||
from django.utils.timezone import now as timezone_now
|
||||
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.lib.actions import do_change_user_setting
|
||||
from zerver.lib.email_notifications import (
|
||||
enqueue_welcome_emails,
|
||||
fix_emojis,
|
||||
|
|
|
@ -58,6 +58,13 @@ from zerver.actions.user_groups import (
|
|||
do_update_user_group_name,
|
||||
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.users import (
|
||||
do_change_user_role,
|
||||
|
@ -71,12 +78,10 @@ from zerver.lib.actions import (
|
|||
bulk_remove_subscriptions,
|
||||
do_add_reaction,
|
||||
do_add_realm_domain,
|
||||
do_change_avatar_fields,
|
||||
do_change_bot_owner,
|
||||
do_change_default_all_public_streams,
|
||||
do_change_default_events_register_stream,
|
||||
do_change_default_sending_stream,
|
||||
do_change_full_name,
|
||||
do_change_realm_domain,
|
||||
do_change_realm_plan_type,
|
||||
do_change_stream_description,
|
||||
|
@ -84,15 +89,12 @@ from zerver.lib.actions import (
|
|||
do_change_stream_permission,
|
||||
do_change_stream_post_policy,
|
||||
do_change_subscription_property,
|
||||
do_change_user_delivery_email,
|
||||
do_change_user_setting,
|
||||
do_create_user,
|
||||
do_deactivate_realm,
|
||||
do_deactivate_stream,
|
||||
do_delete_messages,
|
||||
do_mute_user,
|
||||
do_reactivate_user,
|
||||
do_regenerate_api_key,
|
||||
do_remove_reaction,
|
||||
do_remove_realm_domain,
|
||||
do_rename_stream,
|
||||
|
|
|
@ -12,8 +12,9 @@ from markdown import Markdown
|
|||
|
||||
from zerver.actions.alert_words import do_add_alert_words
|
||||
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.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.camo import get_camo_url
|
||||
from zerver.lib.create_user import create_user
|
||||
|
|
|
@ -8,7 +8,8 @@ from django.core import mail
|
|||
from django.test import override_settings
|
||||
|
||||
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.streams import create_stream_if_needed
|
||||
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.models import InstallationCount, RealmCount
|
||||
from zerver.actions.user_settings import do_regenerate_api_key
|
||||
from zerver.lib.actions import (
|
||||
do_delete_messages,
|
||||
do_mark_stream_messages_as_read,
|
||||
do_regenerate_api_key,
|
||||
do_update_message_flags,
|
||||
)
|
||||
from zerver.lib.avatar import absolute_avatar_url
|
||||
|
|
|
@ -639,7 +639,7 @@ class WorkerTest(ZulipTestCase):
|
|||
with simulated_queue_client(fake_client):
|
||||
worker = queue_processors.ConfirmationEmailWorker()
|
||||
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"
|
||||
) as send_mock:
|
||||
worker.start()
|
||||
|
|
|
@ -7,7 +7,7 @@ import orjson
|
|||
from django.conf import settings
|
||||
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.models import SCIMClient, UserProfile, get_realm
|
||||
|
||||
|
|
|
@ -35,13 +35,13 @@ from zerver.actions.invites import (
|
|||
do_get_invites_controlled_by_user,
|
||||
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.context_processors import common_context
|
||||
from zerver.decorator import do_two_factor_login
|
||||
from zerver.forms import HomepageForm, check_subdomain_available
|
||||
from zerver.lib.actions import (
|
||||
add_new_user_history,
|
||||
do_change_full_name,
|
||||
do_change_realm_subdomain,
|
||||
do_create_realm,
|
||||
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_logo import do_change_logo_source
|
||||
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 (
|
||||
do_change_realm_plan_type,
|
||||
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_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.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_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)
|
||||
|
||||
|
|
|
@ -9,8 +9,9 @@ from django.shortcuts import redirect, render
|
|||
from django.views.decorators.http import require_safe
|
||||
|
||||
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.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.response import json_success
|
||||
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.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.decorator import web_public_view, zulip_login_required
|
||||
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.home import build_page_params_for_home_page_load, get_user_permission_info
|
||||
from zerver.lib.request import RequestNotes
|
||||
|
|
|
@ -26,6 +26,11 @@ from confirmation.models import (
|
|||
validate_key,
|
||||
)
|
||||
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.decorator import do_login, rate_limit_request_by_ip, require_post
|
||||
from zerver.forms import (
|
||||
|
@ -35,14 +40,7 @@ from zerver.forms import (
|
|||
RealmRedirectForm,
|
||||
RegistrationForm,
|
||||
)
|
||||
from zerver.lib.actions import (
|
||||
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.actions import do_activate_mirror_dummy_user, 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.exceptions import RateLimited
|
||||
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 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.lib.actions import do_change_user_setting
|
||||
from zerver.lib.send_email import clear_scheduled_emails
|
||||
from zerver.models import ScheduledEmail, UserProfile
|
||||
|
||||
|
|
|
@ -17,8 +17,7 @@ from confirmation.models import (
|
|||
get_object_from_key,
|
||||
render_confirmation_key_error,
|
||||
)
|
||||
from zerver.decorator import human_users_only
|
||||
from zerver.lib.actions import (
|
||||
from zerver.actions.user_settings import (
|
||||
check_change_full_name,
|
||||
do_change_avatar_fields,
|
||||
do_change_password,
|
||||
|
@ -27,6 +26,7 @@ from zerver.lib.actions import (
|
|||
do_regenerate_api_key,
|
||||
do_start_email_change_process,
|
||||
)
|
||||
from zerver.decorator import human_users_only
|
||||
from zerver.lib.avatar import avatar_url
|
||||
from zerver.lib.email_validation import (
|
||||
get_realm_email_validator,
|
||||
|
|
|
@ -10,6 +10,12 @@ from zerver.actions.custom_profile_fields import (
|
|||
check_remove_custom_profile_field_value,
|
||||
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 (
|
||||
do_change_user_role,
|
||||
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.forms import PASSWORD_TOO_WEAK_ERROR, CreateUserForm
|
||||
from zerver.lib.actions import (
|
||||
check_change_bot_full_name,
|
||||
check_change_full_name,
|
||||
do_change_avatar_fields,
|
||||
do_change_bot_owner,
|
||||
do_change_default_all_public_streams,
|
||||
do_change_default_events_register_stream,
|
||||
do_change_default_sending_stream,
|
||||
do_create_user,
|
||||
do_reactivate_user,
|
||||
do_regenerate_api_key,
|
||||
notify_created_bot,
|
||||
)
|
||||
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 zerver.actions.user_settings import do_change_avatar_fields
|
||||
from zerver.lib.actions import (
|
||||
bulk_add_subscriptions,
|
||||
do_add_reaction,
|
||||
do_change_avatar_fields,
|
||||
do_create_user,
|
||||
do_send_messages,
|
||||
internal_prep_stream_message,
|
||||
|
|
|
@ -59,9 +59,10 @@ from typing_extensions import TypedDict
|
|||
from zxcvbn import zxcvbn
|
||||
|
||||
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.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_hash import user_avatar_content_hash
|
||||
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
|
||||
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
|
||||
|
||||
avatar_attr_name = settings.AUTH_LDAP_USER_ATTR_MAP["avatar"]
|
||||
|
@ -818,7 +819,7 @@ class ZulipLDAPAuthBackendBase(ZulipAuthMixin, LDAPBackend):
|
|||
return full_name
|
||||
|
||||
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)
|
||||
if full_name != user_profile.full_name:
|
||||
|
|
Loading…
Reference in New Issue