actions: Split out zerver.actions.create_user.

Signed-off-by: Anders Kaseorg <anders@zulip.com>
This commit is contained in:
Anders Kaseorg 2022-04-14 14:53:15 -07:00
parent 5fcbc412cf
commit cbad5739ab
31 changed files with 626 additions and 609 deletions

View File

@ -32,6 +32,11 @@ from analytics.models import (
UserCount, UserCount,
installation_epoch, installation_epoch,
) )
from zerver.actions.create_user import (
do_activate_mirror_dummy_user,
do_create_user,
do_reactivate_user,
)
from zerver.actions.invites import ( from zerver.actions.invites import (
do_invite_users, do_invite_users,
do_resend_user_invite_email, do_resend_user_invite_email,
@ -40,12 +45,9 @@ from zerver.actions.invites import (
from zerver.actions.user_activity import update_user_activity_interval from zerver.actions.user_activity import update_user_activity_interval
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_activate_mirror_dummy_user,
do_create_realm, do_create_realm,
do_create_user,
do_mark_all_as_read, do_mark_all_as_read,
do_mark_stream_messages_as_read, do_mark_stream_messages_as_read,
do_reactivate_user,
do_update_message_flags, do_update_message_flags,
) )
from zerver.lib.create_user import create_user from zerver.lib.create_user import create_user

View File

@ -5,7 +5,7 @@ from django.utils.translation import gettext as _
from corporate.lib.stripe import LicenseLimitError, get_latest_seat_count from corporate.lib.stripe import LicenseLimitError, get_latest_seat_count
from corporate.models import get_current_plan_by_realm from corporate.models import get_current_plan_by_realm
from zerver.lib.actions import send_message_to_signup_notification_stream from zerver.actions.create_user import send_message_to_signup_notification_stream
from zerver.lib.exceptions import InvitationError from zerver.lib.exceptions import InvitationError
from zerver.models import Realm, get_system_bot from zerver.models import Realm, get_system_bot

View File

@ -81,15 +81,13 @@ from corporate.models import (
get_current_plan_by_realm, get_current_plan_by_realm,
get_customer_by_realm, get_customer_by_realm,
) )
from zerver.actions.users import do_deactivate_user from zerver.actions.create_user import (
from zerver.lib.actions import (
do_activate_mirror_dummy_user, do_activate_mirror_dummy_user,
do_create_realm,
do_create_user, do_create_user,
do_deactivate_realm,
do_reactivate_realm,
do_reactivate_user, do_reactivate_user,
) )
from zerver.actions.users import do_deactivate_user
from zerver.lib.actions import do_create_realm, do_deactivate_realm, do_reactivate_realm
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
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.utils import assert_is_not_none from zerver.lib.utils import assert_is_not_none

View File

@ -33,9 +33,9 @@ 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.actions.create_user import do_create_user, notify_created_bot
from zerver.actions.streams import bulk_add_subscriptions from zerver.actions.streams import bulk_add_subscriptions
from zerver.actions.user_settings import do_change_avatar_fields from zerver.actions.user_settings import do_change_avatar_fields
from zerver.lib.actions import do_create_user, notify_created_bot
from zerver.lib.integrations import ( from zerver.lib.integrations import (
DOC_SCREENSHOT_CONFIG, DOC_SCREENSHOT_CONFIG,
INTEGRATIONS, INTEGRATIONS,

View File

@ -31,13 +31,9 @@ with test_server_running(
): ):
# zerver imports should happen after `django.setup()` is run # zerver imports should happen after `django.setup()` is run
# by the test_server_running decorator. # by the test_server_running decorator.
from zerver.actions.create_user import do_create_user, do_reactivate_user
from zerver.actions.users import change_user_is_active from zerver.actions.users import change_user_is_active
from zerver.lib.actions import ( from zerver.lib.actions import do_deactivate_realm, do_reactivate_realm
do_create_user,
do_deactivate_realm,
do_reactivate_realm,
do_reactivate_user,
)
from zerver.lib.test_helpers import reset_emails_in_zulip_realm from zerver.lib.test_helpers import reset_emails_in_zulip_realm
from zerver.lib.users import get_api_key from zerver.lib.users import get_api_key
from zerver.models import get_realm, get_user from zerver.models import get_realm, get_user

View File

@ -0,0 +1,566 @@
import datetime
from collections import defaultdict
from typing import Any, Dict, Iterable, List, Optional, Sequence, Set
import orjson
from django.conf import settings
from django.db import transaction
from django.utils.timezone import now as timezone_now
from django.utils.translation import gettext as _
from django.utils.translation import override as override_language
from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat
from confirmation import settings as confirmation_settings
from zerver.actions.default_streams import get_default_streams_for_realm
from zerver.actions.invites import notify_invites_changed
from zerver.actions.message_send import internal_send_private_message, internal_send_stream_message
from zerver.actions.streams import bulk_add_subscriptions, send_peer_subscriber_events
from zerver.actions.user_groups import do_send_user_group_members_update_event
from zerver.actions.users import change_user_is_active, get_service_dicts_for_bot
from zerver.lib.avatar import avatar_url
from zerver.lib.create_user import create_user
from zerver.lib.email_notifications import enqueue_welcome_emails
from zerver.lib.mention import silent_mention_syntax_for_user
from zerver.lib.send_email import clear_scheduled_invitation_emails
from zerver.lib.stream_subscription import bulk_get_subscriber_peer_info
from zerver.lib.streams import get_signups_stream
from zerver.lib.user_counts import realm_user_count, realm_user_count_by_role
from zerver.lib.user_groups import get_system_user_group_for_user
from zerver.lib.users import format_user_row, get_api_key, user_profile_to_user_row
from zerver.models import (
DefaultStreamGroup,
Message,
PreregistrationUser,
Realm,
RealmAuditLog,
Recipient,
Stream,
Subscription,
UserGroup,
UserGroupMembership,
UserMessage,
UserProfile,
active_user_ids,
bot_owner_user_ids,
get_realm,
get_system_bot,
)
from zerver.tornado.django_api import send_event
if settings.BILLING_ENABLED:
from corporate.lib.stripe import update_license_ledger_if_needed
ONBOARDING_TOTAL_MESSAGES = 1000
ONBOARDING_UNREAD_MESSAGES = 20
ONBOARDING_RECENT_TIMEDELTA = datetime.timedelta(weeks=1)
def create_historical_user_messages(*, user_id: int, message_ids: List[int]) -> None:
# Users can see and interact with messages sent to streams with
# public history for which they do not have a UserMessage because
# they were not a subscriber at the time the message was sent.
# In order to add emoji reactions or mutate message flags for
# those messages, we create UserMessage objects for those messages;
# these have the special historical flag which keeps track of the
# fact that the user did not receive the message at the time it was sent.
for message_id in message_ids:
UserMessage.objects.create(
user_profile_id=user_id,
message_id=message_id,
flags=UserMessage.flags.historical | UserMessage.flags.read,
)
def send_message_to_signup_notification_stream(
sender: UserProfile, realm: Realm, message: str, topic_name: str = _("signups")
) -> None:
signup_notifications_stream = realm.get_signup_notifications_stream()
if signup_notifications_stream is None:
return
with override_language(realm.default_language):
internal_send_stream_message(sender, signup_notifications_stream, topic_name, message)
def notify_new_user(user_profile: UserProfile) -> None:
user_count = realm_user_count(user_profile.realm)
sender_email = settings.NOTIFICATION_BOT
sender = get_system_bot(sender_email, user_profile.realm_id)
is_first_user = user_count == 1
if not is_first_user:
message = _("{user} just signed up for Zulip. (total: {user_count})").format(
user=silent_mention_syntax_for_user(user_profile), user_count=user_count
)
if settings.BILLING_ENABLED:
from corporate.lib.registration import generate_licenses_low_warning_message_if_required
licenses_low_warning_message = generate_licenses_low_warning_message_if_required(
user_profile.realm
)
if licenses_low_warning_message is not None:
message += "\n"
message += licenses_low_warning_message
send_message_to_signup_notification_stream(sender, user_profile.realm, message)
# We also send a notification to the Zulip administrative realm
admin_realm = get_realm(settings.SYSTEM_BOT_REALM)
admin_realm_sender = get_system_bot(sender_email, admin_realm.id)
try:
# Check whether the stream exists
signups_stream = get_signups_stream(admin_realm)
# We intentionally use the same strings as above to avoid translation burden.
message = _("{user} just signed up for Zulip. (total: {user_count})").format(
user=f"{user_profile.full_name} <`{user_profile.email}`>", user_count=user_count
)
internal_send_stream_message(
admin_realm_sender, signups_stream, user_profile.realm.display_subdomain, message
)
except Stream.DoesNotExist:
# If the signups stream hasn't been created in the admin
# realm, don't auto-create it to send to it; just do nothing.
pass
def add_new_user_history(user_profile: UserProfile, streams: Iterable[Stream]) -> None:
"""Give you the last ONBOARDING_TOTAL_MESSAGES messages on your public
streams, so you have something to look at in your home view once
you finish the tutorial. The most recent ONBOARDING_UNREAD_MESSAGES
are marked unread.
"""
one_week_ago = timezone_now() - ONBOARDING_RECENT_TIMEDELTA
recipient_ids = [stream.recipient_id for stream in streams if not stream.invite_only]
recent_messages = Message.objects.filter(
recipient_id__in=recipient_ids, date_sent__gt=one_week_ago
).order_by("-id")
message_ids_to_use = list(
reversed(recent_messages.values_list("id", flat=True)[0:ONBOARDING_TOTAL_MESSAGES])
)
if len(message_ids_to_use) == 0:
return
# Handle the race condition where a message arrives between
# bulk_add_subscriptions above and the Message query just above
already_ids = set(
UserMessage.objects.filter(
message_id__in=message_ids_to_use, user_profile=user_profile
).values_list("message_id", flat=True)
)
# Mark the newest ONBOARDING_UNREAD_MESSAGES as unread.
marked_unread = 0
ums_to_create = []
for message_id in reversed(message_ids_to_use):
if message_id in already_ids:
continue
um = UserMessage(user_profile=user_profile, message_id=message_id)
if marked_unread < ONBOARDING_UNREAD_MESSAGES:
marked_unread += 1
else:
um.flags = UserMessage.flags.read
ums_to_create.append(um)
UserMessage.objects.bulk_create(reversed(ums_to_create))
# Does the processing for a new user account:
# * Subscribes to default/invitation streams
# * Fills in some recent historical messages
# * Notifies other users in realm and Zulip about the signup
# * Deactivates PreregistrationUser objects
def process_new_human_user(
user_profile: UserProfile,
prereg_user: Optional[PreregistrationUser] = None,
default_stream_groups: Sequence[DefaultStreamGroup] = [],
realm_creation: bool = False,
) -> None:
realm = user_profile.realm
mit_beta_user = realm.is_zephyr_mirror_realm
if prereg_user is not None:
streams: List[Stream] = list(prereg_user.streams.all())
acting_user: Optional[UserProfile] = prereg_user.referred_by
# A PregistrationUser should not be used for another UserProfile
assert prereg_user.created_user is None, "PregistrationUser should not be reused"
else:
streams = []
acting_user = None
# If the user's invitation didn't explicitly list some streams, we
# add the default streams
if len(streams) == 0:
streams = get_default_subs(user_profile)
for default_stream_group in default_stream_groups:
default_stream_group_streams = default_stream_group.streams.all()
for stream in default_stream_group_streams:
if stream not in streams:
streams.append(stream)
bulk_add_subscriptions(
realm,
streams,
[user_profile],
from_user_creation=True,
acting_user=acting_user,
)
add_new_user_history(user_profile, streams)
# mit_beta_users don't have a referred_by field
if (
not mit_beta_user
and prereg_user is not None
and prereg_user.referred_by is not None
and prereg_user.referred_by.is_active
):
# This is a cross-realm private message.
with override_language(prereg_user.referred_by.default_language):
internal_send_private_message(
get_system_bot(settings.NOTIFICATION_BOT, prereg_user.referred_by.realm_id),
prereg_user.referred_by,
_("{user} accepted your invitation to join Zulip!").format(
user=f"{user_profile.full_name} <`{user_profile.email}`>"
),
)
# Revoke all preregistration users except prereg_user, and link prereg_user to
# the created user
if prereg_user is None:
assert not realm_creation, "realm_creation should only happen with a PreregistrationUser"
if prereg_user is not None:
prereg_user.status = confirmation_settings.STATUS_ACTIVE
prereg_user.created_user = user_profile
prereg_user.save(update_fields=["status", "created_user"])
# In the special case of realm creation, there can be no additional PreregistrationUser
# for us to want to modify - because other realm_creation PreregistrationUsers should be
# left usable for creating different realms.
if not realm_creation:
# Mark any other PreregistrationUsers in the realm that are STATUS_ACTIVE as
# inactive so we can keep track of the PreregistrationUser we
# actually used for analytics.
if prereg_user is not None:
PreregistrationUser.objects.filter(
email__iexact=user_profile.delivery_email, realm=user_profile.realm
).exclude(id=prereg_user.id).update(status=confirmation_settings.STATUS_REVOKED)
else:
PreregistrationUser.objects.filter(
email__iexact=user_profile.delivery_email, realm=user_profile.realm
).update(status=confirmation_settings.STATUS_REVOKED)
if prereg_user is not None and prereg_user.referred_by is not None:
notify_invites_changed(user_profile.realm)
notify_new_user(user_profile)
# Clear any scheduled invitation emails to prevent them
# from being sent after the user is created.
clear_scheduled_invitation_emails(user_profile.delivery_email)
if realm.send_welcome_emails:
enqueue_welcome_emails(user_profile, realm_creation)
# We have an import loop here; it's intentional, because we want
# to keep all the onboarding code in zerver/lib/onboarding.py.
from zerver.lib.onboarding import send_initial_pms
send_initial_pms(user_profile)
def notify_created_user(user_profile: UserProfile) -> None:
user_row = user_profile_to_user_row(user_profile)
person = format_user_row(
user_profile.realm,
user_profile,
user_row,
# Since we don't know what the client
# supports at this point in the code, we
# just assume client_gravatar and
# user_avatar_url_field_optional = False :(
client_gravatar=False,
user_avatar_url_field_optional=False,
# We assume there's no custom profile
# field data for a new user; initial
# values are expected to be added in a
# later event.
custom_profile_field_data={},
)
event: Dict[str, Any] = dict(type="realm_user", op="add", person=person)
send_event(user_profile.realm, event, active_user_ids(user_profile.realm_id))
def created_bot_event(user_profile: UserProfile) -> Dict[str, Any]:
def stream_name(stream: Optional[Stream]) -> Optional[str]:
if not stream:
return None
return stream.name
default_sending_stream_name = stream_name(user_profile.default_sending_stream)
default_events_register_stream_name = stream_name(user_profile.default_events_register_stream)
bot = dict(
email=user_profile.email,
user_id=user_profile.id,
full_name=user_profile.full_name,
bot_type=user_profile.bot_type,
is_active=user_profile.is_active,
api_key=get_api_key(user_profile),
default_sending_stream=default_sending_stream_name,
default_events_register_stream=default_events_register_stream_name,
default_all_public_streams=user_profile.default_all_public_streams,
avatar_url=avatar_url(user_profile),
services=get_service_dicts_for_bot(user_profile.id),
)
# Set the owner key only when the bot has an owner.
# The default bots don't have an owner. So don't
# set the owner key while reactivating them.
if user_profile.bot_owner is not None:
bot["owner_id"] = user_profile.bot_owner.id
return dict(type="realm_bot", op="add", bot=bot)
def notify_created_bot(user_profile: UserProfile) -> None:
event = created_bot_event(user_profile)
send_event(user_profile.realm, event, bot_owner_user_ids(user_profile))
def do_create_user(
email: str,
password: Optional[str],
realm: Realm,
full_name: str,
bot_type: Optional[int] = None,
role: Optional[int] = None,
bot_owner: Optional[UserProfile] = None,
tos_version: Optional[str] = None,
timezone: str = "",
avatar_source: str = UserProfile.AVATAR_FROM_GRAVATAR,
default_sending_stream: Optional[Stream] = None,
default_events_register_stream: Optional[Stream] = None,
default_all_public_streams: Optional[bool] = None,
prereg_user: Optional[PreregistrationUser] = None,
default_stream_groups: Sequence[DefaultStreamGroup] = [],
source_profile: Optional[UserProfile] = None,
realm_creation: bool = False,
*,
acting_user: Optional[UserProfile],
enable_marketing_emails: bool = True,
) -> UserProfile:
with transaction.atomic():
user_profile = create_user(
email=email,
password=password,
realm=realm,
full_name=full_name,
role=role,
bot_type=bot_type,
bot_owner=bot_owner,
tos_version=tos_version,
timezone=timezone,
avatar_source=avatar_source,
default_sending_stream=default_sending_stream,
default_events_register_stream=default_events_register_stream,
default_all_public_streams=default_all_public_streams,
source_profile=source_profile,
enable_marketing_emails=enable_marketing_emails,
)
event_time = user_profile.date_joined
if not acting_user:
acting_user = user_profile
RealmAuditLog.objects.create(
realm=user_profile.realm,
acting_user=acting_user,
modified_user=user_profile,
event_type=RealmAuditLog.USER_CREATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
if realm_creation:
# If this user just created a realm, make sure they are
# properly tagged as the creator of the realm.
realm_creation_audit_log = (
RealmAuditLog.objects.filter(event_type=RealmAuditLog.REALM_CREATED, realm=realm)
.order_by("id")
.last()
)
assert realm_creation_audit_log is not None
realm_creation_audit_log.acting_user = user_profile
realm_creation_audit_log.save(update_fields=["acting_user"])
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
system_user_group = get_system_user_group_for_user(user_profile)
UserGroupMembership.objects.create(user_profile=user_profile, user_group=system_user_group)
if user_profile.role == UserProfile.ROLE_MEMBER and not user_profile.is_provisional_member:
full_members_system_group = UserGroup.objects.get(
name="@role:fullmembers", realm=user_profile.realm, is_system_group=True
)
UserGroupMembership.objects.create(
user_profile=user_profile, user_group=full_members_system_group
)
# Note that for bots, the caller will send an additional event
# with bot-specific info like services.
notify_created_user(user_profile)
do_send_user_group_members_update_event("add_members", system_user_group, [user_profile.id])
if user_profile.role == UserProfile.ROLE_MEMBER and not user_profile.is_provisional_member:
do_send_user_group_members_update_event(
"add_members", full_members_system_group, [user_profile.id]
)
if bot_type is None:
process_new_human_user(
user_profile,
prereg_user=prereg_user,
default_stream_groups=default_stream_groups,
realm_creation=realm_creation,
)
if realm_creation:
assert realm.signup_notifications_stream is not None
bulk_add_subscriptions(
realm, [realm.signup_notifications_stream], [user_profile], acting_user=None
)
from zerver.lib.onboarding import send_initial_realm_messages
send_initial_realm_messages(realm)
return user_profile
def do_activate_mirror_dummy_user(
user_profile: UserProfile, *, acting_user: Optional[UserProfile]
) -> None:
"""Called to have a user "take over" a "mirror dummy" user
(i.e. is_mirror_dummy=True) account when they sign up with the
same email address.
Essentially, the result should be as though we had created the
UserProfile just now with do_create_user, except that the mirror
dummy user may appear as the recipient or sender of messages from
before their account was fully created.
TODO: This function likely has bugs resulting from this being a
parallel code path to do_create_user; e.g. it likely does not
handle preferences or default streams properly.
"""
with transaction.atomic():
change_user_is_active(user_profile, True)
user_profile.is_mirror_dummy = False
user_profile.set_unusable_password()
user_profile.date_joined = timezone_now()
user_profile.tos_version = settings.TERMS_OF_SERVICE_VERSION
user_profile.save(
update_fields=["date_joined", "password", "is_mirror_dummy", "tos_version"]
)
event_time = user_profile.date_joined
RealmAuditLog.objects.create(
realm=user_profile.realm,
modified_user=user_profile,
acting_user=acting_user,
event_type=RealmAuditLog.USER_ACTIVATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
notify_created_user(user_profile)
def do_reactivate_user(user_profile: UserProfile, *, acting_user: Optional[UserProfile]) -> None:
"""Reactivate a user that had previously been deactivated"""
with transaction.atomic():
change_user_is_active(user_profile, True)
event_time = timezone_now()
RealmAuditLog.objects.create(
realm=user_profile.realm,
modified_user=user_profile,
acting_user=acting_user,
event_type=RealmAuditLog.USER_REACTIVATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
notify_created_user(user_profile)
if user_profile.is_bot:
notify_created_bot(user_profile)
subscribed_recipient_ids = Subscription.objects.filter(
user_profile_id=user_profile.id, active=True, recipient__type=Recipient.STREAM
).values_list("recipient__type_id", flat=True)
subscribed_streams = Stream.objects.filter(id__in=subscribed_recipient_ids, deactivated=False)
subscriber_peer_info = bulk_get_subscriber_peer_info(
realm=user_profile.realm,
streams=subscribed_streams,
)
altered_user_dict: Dict[int, Set[int]] = defaultdict(set)
for stream in subscribed_streams:
altered_user_dict[stream.id] = {user_profile.id}
stream_dict = {stream.id: stream for stream in subscribed_streams}
send_peer_subscriber_events(
op="peer_add",
realm=user_profile.realm,
altered_user_dict=altered_user_dict,
stream_dict=stream_dict,
private_peer_dict=subscriber_peer_info.private_peer_dict,
)
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.
return get_default_streams_for_realm(user_profile.realm_id)

View File

@ -16,33 +16,23 @@ from django.utils.translation import override as override_language
from typing_extensions import TypedDict from typing_extensions import TypedDict
from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat from analytics.lib.counts import COUNT_STATS, do_increment_logging_stat
from confirmation import settings as confirmation_settings
from confirmation.models import Confirmation, create_confirmation_link, generate_key from confirmation.models import Confirmation, create_confirmation_link, generate_key
from zerver.actions.create_user import create_historical_user_messages, created_bot_event
from zerver.actions.custom_profile_fields import do_remove_realm_custom_profile_fields from zerver.actions.custom_profile_fields import do_remove_realm_custom_profile_fields
from zerver.actions.default_streams import get_default_streams_for_realm
from zerver.actions.invites import notify_invites_changed
from zerver.actions.message_send import ( from zerver.actions.message_send import (
filter_presence_idle_user_ids, filter_presence_idle_user_ids,
get_recipient_info, get_recipient_info,
internal_send_private_message,
internal_send_stream_message, internal_send_stream_message,
render_incoming_message, render_incoming_message,
) )
from zerver.actions.streams import bulk_add_subscriptions, send_peer_subscriber_events
from zerver.actions.uploads import check_attachment_reference_change from zerver.actions.uploads import check_attachment_reference_change
from zerver.actions.user_groups import ( from zerver.actions.user_groups import update_users_in_full_members_system_group
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_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.lib import retention as retention from zerver.lib import retention as retention
from zerver.lib.avatar import avatar_url
from zerver.lib.bulk_create import create_users from zerver.lib.bulk_create import create_users
from zerver.lib.cache import flush_user_profile from zerver.lib.cache import flush_user_profile
from zerver.lib.create_user import create_user, get_display_email_address from zerver.lib.create_user import get_display_email_address
from zerver.lib.email_notifications import enqueue_welcome_emails
from zerver.lib.email_validation import email_reserved_for_system_bots_error from zerver.lib.email_validation import email_reserved_for_system_bots_error
from zerver.lib.emoji import check_emoji_request, emoji_name_to_emoji_code from zerver.lib.emoji import check_emoji_request, emoji_name_to_emoji_code
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
@ -62,15 +52,10 @@ from zerver.lib.message import (
) )
from zerver.lib.queue import queue_json_publish from zerver.lib.queue import queue_json_publish
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, send_email_to_admins
FromAddress,
clear_scheduled_invitation_emails,
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
from zerver.lib.sessions import delete_user_sessions from zerver.lib.sessions import delete_user_sessions
from zerver.lib.stream_subscription import ( from zerver.lib.stream_subscription import (
bulk_get_subscriber_peer_info,
get_active_subscriptions_for_stream_id, get_active_subscriptions_for_stream_id,
subscriber_ids_with_stream_history_access, subscriber_ids_with_stream_history_access,
) )
@ -95,25 +80,19 @@ 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.user_counts import realm_user_count, realm_user_count_by_role from zerver.lib.user_counts import 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,
get_system_user_group_for_user,
)
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_user_mutes from zerver.lib.user_mutes import add_user_mute, 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 format_user_row, get_api_key, user_profile_to_user_row
from zerver.lib.utils import log_statsd_event from zerver.lib.utils import log_statsd_event
from zerver.lib.widget import is_widget_message from zerver.lib.widget import is_widget_message
from zerver.models import ( from zerver.models import (
ArchivedAttachment, ArchivedAttachment,
Attachment, Attachment,
DefaultStream, DefaultStream,
DefaultStreamGroup,
Message, Message,
MutedUser, MutedUser,
PreregistrationUser,
Reaction, Reaction,
Realm, Realm,
RealmAuditLog, RealmAuditLog,
@ -122,9 +101,6 @@ from zerver.models import (
Recipient, Recipient,
ScheduledEmail, ScheduledEmail,
Stream, Stream,
Subscription,
UserGroup,
UserGroupMembership,
UserMessage, UserMessage,
UserProfile, UserProfile,
active_user_ids, active_user_ids,
@ -138,525 +114,13 @@ from zerver.models import (
from zerver.tornado.django_api import send_event from zerver.tornado.django_api import send_event
if settings.BILLING_ENABLED: if settings.BILLING_ENABLED:
from corporate.lib.stripe import ( from corporate.lib.stripe import downgrade_now_without_creating_additional_invoices
downgrade_now_without_creating_additional_invoices,
update_license_ledger_if_needed,
)
ONBOARDING_TOTAL_MESSAGES = 1000
ONBOARDING_UNREAD_MESSAGES = 20
ONBOARDING_RECENT_TIMEDELTA = datetime.timedelta(weeks=1)
def create_historical_user_messages(*, user_id: int, message_ids: List[int]) -> None:
# Users can see and interact with messages sent to streams with
# public history for which they do not have a UserMessage because
# they were not a subscriber at the time the message was sent.
# In order to add emoji reactions or mutate message flags for
# those messages, we create UserMessage objects for those messages;
# these have the special historical flag which keeps track of the
# fact that the user did not receive the message at the time it was sent.
for message_id in message_ids:
UserMessage.objects.create(
user_profile_id=user_id,
message_id=message_id,
flags=UserMessage.flags.historical | UserMessage.flags.read,
)
def subscriber_info(user_id: int) -> Dict[str, Any]: def subscriber_info(user_id: int) -> Dict[str, Any]:
return {"id": user_id, "flags": ["read"]} return {"id": user_id, "flags": ["read"]}
def send_message_to_signup_notification_stream(
sender: UserProfile, realm: Realm, message: str, topic_name: str = _("signups")
) -> None:
signup_notifications_stream = realm.get_signup_notifications_stream()
if signup_notifications_stream is None:
return
with override_language(realm.default_language):
internal_send_stream_message(sender, signup_notifications_stream, topic_name, message)
def notify_new_user(user_profile: UserProfile) -> None:
user_count = realm_user_count(user_profile.realm)
sender_email = settings.NOTIFICATION_BOT
sender = get_system_bot(sender_email, user_profile.realm_id)
is_first_user = user_count == 1
if not is_first_user:
message = _("{user} just signed up for Zulip. (total: {user_count})").format(
user=silent_mention_syntax_for_user(user_profile), user_count=user_count
)
if settings.BILLING_ENABLED:
from corporate.lib.registration import generate_licenses_low_warning_message_if_required
licenses_low_warning_message = generate_licenses_low_warning_message_if_required(
user_profile.realm
)
if licenses_low_warning_message is not None:
message += "\n"
message += licenses_low_warning_message
send_message_to_signup_notification_stream(sender, user_profile.realm, message)
# We also send a notification to the Zulip administrative realm
admin_realm = get_realm(settings.SYSTEM_BOT_REALM)
admin_realm_sender = get_system_bot(sender_email, admin_realm.id)
try:
# Check whether the stream exists
signups_stream = get_signups_stream(admin_realm)
# We intentionally use the same strings as above to avoid translation burden.
message = _("{user} just signed up for Zulip. (total: {user_count})").format(
user=f"{user_profile.full_name} <`{user_profile.email}`>", user_count=user_count
)
internal_send_stream_message(
admin_realm_sender, signups_stream, user_profile.realm.display_subdomain, message
)
except Stream.DoesNotExist:
# If the signups stream hasn't been created in the admin
# realm, don't auto-create it to send to it; just do nothing.
pass
def add_new_user_history(user_profile: UserProfile, streams: Iterable[Stream]) -> None:
"""Give you the last ONBOARDING_TOTAL_MESSAGES messages on your public
streams, so you have something to look at in your home view once
you finish the tutorial. The most recent ONBOARDING_UNREAD_MESSAGES
are marked unread.
"""
one_week_ago = timezone_now() - ONBOARDING_RECENT_TIMEDELTA
recipient_ids = [stream.recipient_id for stream in streams if not stream.invite_only]
recent_messages = Message.objects.filter(
recipient_id__in=recipient_ids, date_sent__gt=one_week_ago
).order_by("-id")
message_ids_to_use = list(
reversed(recent_messages.values_list("id", flat=True)[0:ONBOARDING_TOTAL_MESSAGES])
)
if len(message_ids_to_use) == 0:
return
# Handle the race condition where a message arrives between
# bulk_add_subscriptions above and the Message query just above
already_ids = set(
UserMessage.objects.filter(
message_id__in=message_ids_to_use, user_profile=user_profile
).values_list("message_id", flat=True)
)
# Mark the newest ONBOARDING_UNREAD_MESSAGES as unread.
marked_unread = 0
ums_to_create = []
for message_id in reversed(message_ids_to_use):
if message_id in already_ids:
continue
um = UserMessage(user_profile=user_profile, message_id=message_id)
if marked_unread < ONBOARDING_UNREAD_MESSAGES:
marked_unread += 1
else:
um.flags = UserMessage.flags.read
ums_to_create.append(um)
UserMessage.objects.bulk_create(reversed(ums_to_create))
# Does the processing for a new user account:
# * Subscribes to default/invitation streams
# * Fills in some recent historical messages
# * Notifies other users in realm and Zulip about the signup
# * Deactivates PreregistrationUser objects
def process_new_human_user(
user_profile: UserProfile,
prereg_user: Optional[PreregistrationUser] = None,
default_stream_groups: Sequence[DefaultStreamGroup] = [],
realm_creation: bool = False,
) -> None:
realm = user_profile.realm
mit_beta_user = realm.is_zephyr_mirror_realm
if prereg_user is not None:
streams: List[Stream] = list(prereg_user.streams.all())
acting_user: Optional[UserProfile] = prereg_user.referred_by
# A PregistrationUser should not be used for another UserProfile
assert prereg_user.created_user is None, "PregistrationUser should not be reused"
else:
streams = []
acting_user = None
# If the user's invitation didn't explicitly list some streams, we
# add the default streams
if len(streams) == 0:
streams = get_default_subs(user_profile)
for default_stream_group in default_stream_groups:
default_stream_group_streams = default_stream_group.streams.all()
for stream in default_stream_group_streams:
if stream not in streams:
streams.append(stream)
bulk_add_subscriptions(
realm,
streams,
[user_profile],
from_user_creation=True,
acting_user=acting_user,
)
add_new_user_history(user_profile, streams)
# mit_beta_users don't have a referred_by field
if (
not mit_beta_user
and prereg_user is not None
and prereg_user.referred_by is not None
and prereg_user.referred_by.is_active
):
# This is a cross-realm private message.
with override_language(prereg_user.referred_by.default_language):
internal_send_private_message(
get_system_bot(settings.NOTIFICATION_BOT, prereg_user.referred_by.realm_id),
prereg_user.referred_by,
_("{user} accepted your invitation to join Zulip!").format(
user=f"{user_profile.full_name} <`{user_profile.email}`>"
),
)
# Revoke all preregistration users except prereg_user, and link prereg_user to
# the created user
if prereg_user is None:
assert not realm_creation, "realm_creation should only happen with a PreregistrationUser"
if prereg_user is not None:
prereg_user.status = confirmation_settings.STATUS_ACTIVE
prereg_user.created_user = user_profile
prereg_user.save(update_fields=["status", "created_user"])
# In the special case of realm creation, there can be no additional PreregistrationUser
# for us to want to modify - because other realm_creation PreregistrationUsers should be
# left usable for creating different realms.
if not realm_creation:
# Mark any other PreregistrationUsers in the realm that are STATUS_ACTIVE as
# inactive so we can keep track of the PreregistrationUser we
# actually used for analytics.
if prereg_user is not None:
PreregistrationUser.objects.filter(
email__iexact=user_profile.delivery_email, realm=user_profile.realm
).exclude(id=prereg_user.id).update(status=confirmation_settings.STATUS_REVOKED)
else:
PreregistrationUser.objects.filter(
email__iexact=user_profile.delivery_email, realm=user_profile.realm
).update(status=confirmation_settings.STATUS_REVOKED)
if prereg_user is not None and prereg_user.referred_by is not None:
notify_invites_changed(user_profile.realm)
notify_new_user(user_profile)
# Clear any scheduled invitation emails to prevent them
# from being sent after the user is created.
clear_scheduled_invitation_emails(user_profile.delivery_email)
if realm.send_welcome_emails:
enqueue_welcome_emails(user_profile, realm_creation)
# We have an import loop here; it's intentional, because we want
# to keep all the onboarding code in zerver/lib/onboarding.py.
from zerver.lib.onboarding import send_initial_pms
send_initial_pms(user_profile)
def notify_created_user(user_profile: UserProfile) -> None:
user_row = user_profile_to_user_row(user_profile)
person = format_user_row(
user_profile.realm,
user_profile,
user_row,
# Since we don't know what the client
# supports at this point in the code, we
# just assume client_gravatar and
# user_avatar_url_field_optional = False :(
client_gravatar=False,
user_avatar_url_field_optional=False,
# We assume there's no custom profile
# field data for a new user; initial
# values are expected to be added in a
# later event.
custom_profile_field_data={},
)
event: Dict[str, Any] = dict(type="realm_user", op="add", person=person)
send_event(user_profile.realm, event, active_user_ids(user_profile.realm_id))
def created_bot_event(user_profile: UserProfile) -> Dict[str, Any]:
def stream_name(stream: Optional[Stream]) -> Optional[str]:
if not stream:
return None
return stream.name
default_sending_stream_name = stream_name(user_profile.default_sending_stream)
default_events_register_stream_name = stream_name(user_profile.default_events_register_stream)
bot = dict(
email=user_profile.email,
user_id=user_profile.id,
full_name=user_profile.full_name,
bot_type=user_profile.bot_type,
is_active=user_profile.is_active,
api_key=get_api_key(user_profile),
default_sending_stream=default_sending_stream_name,
default_events_register_stream=default_events_register_stream_name,
default_all_public_streams=user_profile.default_all_public_streams,
avatar_url=avatar_url(user_profile),
services=get_service_dicts_for_bot(user_profile.id),
)
# Set the owner key only when the bot has an owner.
# The default bots don't have an owner. So don't
# set the owner key while reactivating them.
if user_profile.bot_owner is not None:
bot["owner_id"] = user_profile.bot_owner.id
return dict(type="realm_bot", op="add", bot=bot)
def notify_created_bot(user_profile: UserProfile) -> None:
event = created_bot_event(user_profile)
send_event(user_profile.realm, event, bot_owner_user_ids(user_profile))
def do_create_user(
email: str,
password: Optional[str],
realm: Realm,
full_name: str,
bot_type: Optional[int] = None,
role: Optional[int] = None,
bot_owner: Optional[UserProfile] = None,
tos_version: Optional[str] = None,
timezone: str = "",
avatar_source: str = UserProfile.AVATAR_FROM_GRAVATAR,
default_sending_stream: Optional[Stream] = None,
default_events_register_stream: Optional[Stream] = None,
default_all_public_streams: Optional[bool] = None,
prereg_user: Optional[PreregistrationUser] = None,
default_stream_groups: Sequence[DefaultStreamGroup] = [],
source_profile: Optional[UserProfile] = None,
realm_creation: bool = False,
*,
acting_user: Optional[UserProfile],
enable_marketing_emails: bool = True,
) -> UserProfile:
with transaction.atomic():
user_profile = create_user(
email=email,
password=password,
realm=realm,
full_name=full_name,
role=role,
bot_type=bot_type,
bot_owner=bot_owner,
tos_version=tos_version,
timezone=timezone,
avatar_source=avatar_source,
default_sending_stream=default_sending_stream,
default_events_register_stream=default_events_register_stream,
default_all_public_streams=default_all_public_streams,
source_profile=source_profile,
enable_marketing_emails=enable_marketing_emails,
)
event_time = user_profile.date_joined
if not acting_user:
acting_user = user_profile
RealmAuditLog.objects.create(
realm=user_profile.realm,
acting_user=acting_user,
modified_user=user_profile,
event_type=RealmAuditLog.USER_CREATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
if realm_creation:
# If this user just created a realm, make sure they are
# properly tagged as the creator of the realm.
realm_creation_audit_log = (
RealmAuditLog.objects.filter(event_type=RealmAuditLog.REALM_CREATED, realm=realm)
.order_by("id")
.last()
)
assert realm_creation_audit_log is not None
realm_creation_audit_log.acting_user = user_profile
realm_creation_audit_log.save(update_fields=["acting_user"])
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
system_user_group = get_system_user_group_for_user(user_profile)
UserGroupMembership.objects.create(user_profile=user_profile, user_group=system_user_group)
if user_profile.role == UserProfile.ROLE_MEMBER and not user_profile.is_provisional_member:
full_members_system_group = UserGroup.objects.get(
name="@role:fullmembers", realm=user_profile.realm, is_system_group=True
)
UserGroupMembership.objects.create(
user_profile=user_profile, user_group=full_members_system_group
)
# Note that for bots, the caller will send an additional event
# with bot-specific info like services.
notify_created_user(user_profile)
do_send_user_group_members_update_event("add_members", system_user_group, [user_profile.id])
if user_profile.role == UserProfile.ROLE_MEMBER and not user_profile.is_provisional_member:
do_send_user_group_members_update_event(
"add_members", full_members_system_group, [user_profile.id]
)
if bot_type is None:
process_new_human_user(
user_profile,
prereg_user=prereg_user,
default_stream_groups=default_stream_groups,
realm_creation=realm_creation,
)
if realm_creation:
assert realm.signup_notifications_stream is not None
bulk_add_subscriptions(
realm, [realm.signup_notifications_stream], [user_profile], acting_user=None
)
from zerver.lib.onboarding import send_initial_realm_messages
send_initial_realm_messages(realm)
return user_profile
def do_activate_mirror_dummy_user(
user_profile: UserProfile, *, acting_user: Optional[UserProfile]
) -> None:
"""Called to have a user "take over" a "mirror dummy" user
(i.e. is_mirror_dummy=True) account when they sign up with the
same email address.
Essentially, the result should be as though we had created the
UserProfile just now with do_create_user, except that the mirror
dummy user may appear as the recipient or sender of messages from
before their account was fully created.
TODO: This function likely has bugs resulting from this being a
parallel code path to do_create_user; e.g. it likely does not
handle preferences or default streams properly.
"""
with transaction.atomic():
change_user_is_active(user_profile, True)
user_profile.is_mirror_dummy = False
user_profile.set_unusable_password()
user_profile.date_joined = timezone_now()
user_profile.tos_version = settings.TERMS_OF_SERVICE_VERSION
user_profile.save(
update_fields=["date_joined", "password", "is_mirror_dummy", "tos_version"]
)
event_time = user_profile.date_joined
RealmAuditLog.objects.create(
realm=user_profile.realm,
modified_user=user_profile,
acting_user=acting_user,
event_type=RealmAuditLog.USER_ACTIVATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
notify_created_user(user_profile)
def do_reactivate_user(user_profile: UserProfile, *, acting_user: Optional[UserProfile]) -> None:
"""Reactivate a user that had previously been deactivated"""
with transaction.atomic():
change_user_is_active(user_profile, True)
event_time = timezone_now()
RealmAuditLog.objects.create(
realm=user_profile.realm,
modified_user=user_profile,
acting_user=acting_user,
event_type=RealmAuditLog.USER_REACTIVATED,
event_time=event_time,
extra_data=orjson.dumps(
{
RealmAuditLog.ROLE_COUNT: realm_user_count_by_role(user_profile.realm),
}
).decode(),
)
do_increment_logging_stat(
user_profile.realm,
COUNT_STATS["active_users_log:is_bot:day"],
user_profile.is_bot,
event_time,
)
if settings.BILLING_ENABLED:
update_license_ledger_if_needed(user_profile.realm, event_time)
notify_created_user(user_profile)
if user_profile.is_bot:
notify_created_bot(user_profile)
subscribed_recipient_ids = Subscription.objects.filter(
user_profile_id=user_profile.id, active=True, recipient__type=Recipient.STREAM
).values_list("recipient__type_id", flat=True)
subscribed_streams = Stream.objects.filter(id__in=subscribed_recipient_ids, deactivated=False)
subscriber_peer_info = bulk_get_subscriber_peer_info(
realm=user_profile.realm,
streams=subscribed_streams,
)
altered_user_dict: Dict[int, Set[int]] = defaultdict(set)
for stream in subscribed_streams:
altered_user_dict[stream.id] = {user_profile.id}
stream_dict = {stream.id: stream for stream in subscribed_streams}
send_peer_subscriber_events(
op="peer_add",
realm=user_profile.realm,
altered_user_dict=altered_user_dict,
stream_dict=stream_dict,
private_peer_dict=subscriber_peer_info.private_peer_dict,
)
def active_humans_in_realm(realm: Realm) -> Sequence[UserProfile]: def active_humans_in_realm(realm: Realm) -> Sequence[UserProfile]:
return UserProfile.objects.filter(realm=realm, is_active=True, is_bot=False) return UserProfile.objects.filter(realm=realm, is_active=True, is_bot=False)
@ -1870,12 +1334,6 @@ def do_create_realm(
return realm return realm
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.
return get_default_streams_for_realm(user_profile.realm_id)
@dataclass @dataclass
class ReadMessagesEvent: class ReadMessagesEvent:
messages: List[int] messages: List[int]

View File

@ -9,9 +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.create_user import do_create_user, do_reactivate_user
from zerver.actions.user_settings import check_change_full_name, do_change_user_delivery_email 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 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

View File

@ -3,7 +3,8 @@ from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from zerver.lib.actions import do_create_realm, do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.actions import do_create_realm
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand
from zerver.models import UserProfile from zerver.models import UserProfile

View File

@ -4,7 +4,7 @@ from typing import Any
from django.core.management.base import CommandError from django.core.management.base import CommandError
from django.db.utils import IntegrityError from django.db.utils import IntegrityError
from zerver.lib.actions import do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.management import ZulipBaseCommand from zerver.lib.management import ZulipBaseCommand

View File

@ -10,10 +10,11 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from zerver.actions.create_user import do_create_user
from zerver.actions.presence import update_user_presence from zerver.actions.presence import update_user_presence
from zerver.actions.realm_linkifiers import do_add_linkifier from zerver.actions.realm_linkifiers import do_add_linkifier
from zerver.actions.realm_playgrounds import do_add_realm_playground from zerver.actions.realm_playgrounds import do_add_realm_playground
from zerver.lib.actions import do_add_reaction, do_create_user from zerver.lib.actions import do_add_reaction
from zerver.lib.events import do_events_register from zerver.lib.events import do_events_register
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase

View File

@ -6,6 +6,11 @@ from django.contrib.auth.password_validation import validate_password
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from analytics.models import StreamCount from analytics.models import StreamCount
from zerver.actions.create_user import (
do_activate_mirror_dummy_user,
do_create_user,
do_reactivate_user,
)
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.streams import ( from zerver.actions.streams import (
@ -25,17 +30,14 @@ from zerver.actions.user_settings import (
) )
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 (
do_activate_mirror_dummy_user,
do_add_realm_domain, do_add_realm_domain,
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_realm_domain, do_change_realm_domain,
do_create_user,
do_deactivate_realm, do_deactivate_realm,
do_reactivate_realm, do_reactivate_realm,
do_reactivate_user,
do_remove_realm_domain, do_remove_realm_domain,
do_set_realm_authentication_methods, do_set_realm_authentication_methods,
do_set_realm_message_editing, do_set_realm_message_editing,

View File

@ -40,15 +40,14 @@ from social_django.storage import BaseDjangoStorage
from social_django.strategy import DjangoStrategy 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.create_user import do_create_user, do_reactivate_user
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.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_create_realm, do_create_realm,
do_create_user,
do_deactivate_realm, do_deactivate_realm,
do_reactivate_realm, do_reactivate_realm,
do_reactivate_user,
do_set_realm_property, do_set_realm_property,
) )
from zerver.lib.avatar import avatar_url from zerver.lib.avatar import avatar_url

View File

@ -14,6 +14,7 @@ from django.core.exceptions import ValidationError
from django.http import HttpRequest, HttpResponse from django.http import HttpRequest, HttpResponse
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from zerver.actions.create_user import do_reactivate_user
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.decorator import ( from zerver.decorator import (
authenticate_notify, authenticate_notify,
@ -33,7 +34,6 @@ from zerver.lib.actions import (
do_create_realm, do_create_realm,
do_deactivate_realm, do_deactivate_realm,
do_reactivate_realm, do_reactivate_realm,
do_reactivate_user,
do_set_realm_property, do_set_realm_property,
) )
from zerver.lib.cache import dict_to_items_tuple, ignore_unhashable_lru_cache, items_tuple_to_dict from zerver.lib.cache import dict_to_items_tuple, ignore_unhashable_lru_cache, items_tuple_to_dict

View File

@ -7,7 +7,7 @@ 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 confirmation.models import one_click_unsubscribe_link from confirmation.models import one_click_unsubscribe_link
from zerver.lib.actions import do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.digest import ( from zerver.lib.digest import (
DigestTopic, DigestTopic,
_enqueue_emails_for_realm, _enqueue_emails_for_realm,

View File

@ -14,6 +14,7 @@ import orjson
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from zerver.actions.alert_words import do_add_alert_words, do_remove_alert_words from zerver.actions.alert_words import do_add_alert_words, do_remove_alert_words
from zerver.actions.create_user import do_create_user, do_reactivate_user
from zerver.actions.custom_profile_fields import ( from zerver.actions.custom_profile_fields import (
do_remove_realm_custom_profile_field, do_remove_realm_custom_profile_field,
do_update_user_custom_profile_data_if_changed, do_update_user_custom_profile_data_if_changed,
@ -93,11 +94,9 @@ from zerver.lib.actions import (
do_change_default_sending_stream, do_change_default_sending_stream,
do_change_realm_domain, do_change_realm_domain,
do_change_realm_plan_type, do_change_realm_plan_type,
do_create_user,
do_deactivate_realm, do_deactivate_realm,
do_delete_messages, do_delete_messages,
do_mute_user, do_mute_user,
do_reactivate_user,
do_remove_reaction, do_remove_reaction,
do_remove_realm_domain, do_remove_realm_domain,
do_set_realm_authentication_methods, do_set_realm_authentication_methods,

View File

@ -13,8 +13,9 @@ 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 corporate.models import Customer, CustomerPlan from corporate.models import Customer, CustomerPlan
from zerver.actions.create_user import do_create_user
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_realm_plan_type, do_create_user from zerver.lib.actions import do_change_realm_plan_type
from zerver.lib.compatibility import LAST_SERVER_UPGRADE_TIME, is_outdated_server from zerver.lib.compatibility import LAST_SERVER_UPGRADE_TIME, is_outdated_server
from zerver.lib.home import ( from zerver.lib.home import (
get_billing_info, get_billing_info,

View File

@ -1,5 +1,5 @@
from zerver.actions.create_user import do_create_user
from zerver.actions.hotspots import do_mark_hotspot_as_read from zerver.actions.hotspots import do_mark_hotspot_as_read
from zerver.lib.actions import do_create_user
from zerver.lib.hotspots import ALL_HOTSPOTS, INTRO_HOTSPOTS, get_next_hotspots from zerver.lib.hotspots import ALL_HOTSPOTS, INTRO_HOTSPOTS, get_next_hotspots
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.models import UserHotspot, UserProfile, get_realm from zerver.models import UserHotspot, UserProfile, get_realm

View File

@ -11,6 +11,7 @@ from django.utils.timezone import now as timezone_now
from analytics.models import UserCount from analytics.models import UserCount
from zerver.actions.alert_words import do_add_alert_words from zerver.actions.alert_words import do_add_alert_words
from zerver.actions.create_user import do_create_user
from zerver.actions.custom_profile_fields import ( from zerver.actions.custom_profile_fields import (
do_update_user_custom_profile_data_if_changed, do_update_user_custom_profile_data_if_changed,
try_add_realm_custom_profile_field, try_add_realm_custom_profile_field,
@ -27,7 +28,6 @@ from zerver.lib.actions import (
check_add_reaction, check_add_reaction,
do_add_reaction, do_add_reaction,
do_change_realm_plan_type, do_change_realm_plan_type,
do_create_user,
do_mute_user, do_mute_user,
) )
from zerver.lib.avatar_hash import user_avatar_path from zerver.lib.avatar_hash import user_avatar_path

View File

@ -13,7 +13,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 confirmation.models import RealmCreationKey, generate_realm_creation_url from confirmation.models import RealmCreationKey, generate_realm_creation_url
from zerver.lib.actions import do_add_reaction, do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.actions import do_add_reaction
from zerver.lib.management import ZulipBaseCommand, check_config from zerver.lib.management import ZulipBaseCommand, check_config
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import most_recent_message, stdout_suppressed from zerver.lib.test_helpers import most_recent_message, stdout_suppressed

View File

@ -10,6 +10,7 @@ from django.http import HttpResponse
from django.test import override_settings 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 zerver.actions.create_user import do_create_user
from zerver.actions.message_send import ( from zerver.actions.message_send import (
build_message_send_dict, build_message_send_dict,
check_message, check_message,
@ -27,12 +28,7 @@ from zerver.actions.message_send import (
) )
from zerver.actions.streams import do_change_stream_post_policy from zerver.actions.streams import do_change_stream_post_policy
from zerver.actions.users import do_change_can_forge_sender, do_deactivate_user from zerver.actions.users import do_change_can_forge_sender, do_deactivate_user
from zerver.lib.actions import ( from zerver.lib.actions import do_add_realm_domain, do_create_realm, do_set_realm_property
do_add_realm_domain,
do_create_realm,
do_create_user,
do_set_realm_property,
)
from zerver.lib.addressee import Addressee from zerver.lib.addressee import Addressee
from zerver.lib.cache import cache_delete, get_stream_cache_key from zerver.lib.cache import cache_delete, get_stream_cache_key
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError

View File

@ -8,8 +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.actions.create_user import notify_new_user
from zerver.actions.user_settings import do_change_user_setting 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

View File

@ -6,7 +6,7 @@ import requests
import responses import responses
from version import ZULIP_VERSION from version import ZULIP_VERSION
from zerver.lib.actions import do_create_user from zerver.actions.create_user import do_create_user
from zerver.lib.outgoing_webhook import ( from zerver.lib.outgoing_webhook import (
GenericOutgoingWebhookService, GenericOutgoingWebhookService,
SlackOutgoingWebhookService, SlackOutgoingWebhookService,

View File

@ -1,8 +1,9 @@
from unittest import mock from unittest import mock
from zerver.actions.create_user import do_create_user
from zerver.actions.realm_emoji import check_add_realm_emoji from zerver.actions.realm_emoji import check_add_realm_emoji
from zerver.actions.users import do_change_user_role from zerver.actions.users import do_change_user_role
from zerver.lib.actions import do_create_realm, do_create_user, do_set_realm_property from zerver.lib.actions import do_create_realm, do_set_realm_property
from zerver.lib.exceptions import JsonableError from zerver.lib.exceptions import JsonableError
from zerver.lib.test_classes import ZulipTestCase from zerver.lib.test_classes import ZulipTestCase
from zerver.lib.test_helpers import get_test_image_file from zerver.lib.test_helpers import get_test_image_file

View File

@ -6,8 +6,8 @@ import orjson
from django.conf import settings from django.conf import settings
from django.test import override_settings from django.test import override_settings
from zerver.actions.create_user import do_create_user
from zerver.actions.message_send import get_service_bot_events from zerver.actions.message_send import get_service_bot_events
from zerver.lib.actions import do_create_user
from zerver.lib.bot_config import ConfigError, load_bot_config_template, set_bot_config from zerver.lib.bot_config import ConfigError, load_bot_config_template, set_bot_config
from zerver.lib.bot_lib import EmbeddedBotEmptyRecipientsList, EmbeddedBotHandler, StateHandler from zerver.lib.bot_lib import EmbeddedBotEmptyRecipientsList, EmbeddedBotHandler, StateHandler
from zerver.lib.bot_storage import StateError from zerver.lib.bot_storage import StateError

View File

@ -25,6 +25,7 @@ from confirmation.models import (
one_click_unsubscribe_link, one_click_unsubscribe_link,
) )
from corporate.lib.stripe import get_latest_seat_count from corporate.lib.stripe import get_latest_seat_count
from zerver.actions.create_user import add_new_user_history, do_create_user, process_new_human_user
from zerver.actions.default_streams import ( from zerver.actions.default_streams import (
do_add_default_stream, do_add_default_stream,
do_create_default_stream_group, do_create_default_stream_group,
@ -41,14 +42,11 @@ 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,
do_change_realm_subdomain, do_change_realm_subdomain,
do_create_realm, do_create_realm,
do_create_user,
do_deactivate_realm, do_deactivate_realm,
do_set_realm_property, do_set_realm_property,
do_set_realm_user_default_setting, do_set_realm_user_default_setting,
process_new_human_user,
) )
from zerver.lib.email_notifications import enqueue_welcome_emails, followup_day2_email_delay from zerver.lib.email_notifications import enqueue_welcome_emails, followup_day2_email_delay
from zerver.lib.initial_password import initial_password from zerver.lib.initial_password import initial_password
@ -253,7 +251,7 @@ class AddNewUserHistoryTest(ZulipTestCase):
self.send_stream_message(self.example_user("hamlet"), stream.name, "test 2") self.send_stream_message(self.example_user("hamlet"), stream.name, "test 2")
self.send_stream_message(self.example_user("hamlet"), stream.name, "test 3") self.send_stream_message(self.example_user("hamlet"), stream.name, "test 3")
with patch("zerver.lib.actions.add_new_user_history"): with patch("zerver.actions.create_user.add_new_user_history"):
self.register(self.nonreg_email("test"), "test") self.register(self.nonreg_email("test"), "test")
user_profile = self.nonreg_user("test") user_profile = self.nonreg_user("test")
subs = Subscription.objects.select_related("recipient").filter( subs = Subscription.objects.select_related("recipient").filter(
@ -269,7 +267,9 @@ class AddNewUserHistoryTest(ZulipTestCase):
# Overwrite ONBOARDING_UNREAD_MESSAGES to 2 # Overwrite ONBOARDING_UNREAD_MESSAGES to 2
ONBOARDING_UNREAD_MESSAGES = 2 ONBOARDING_UNREAD_MESSAGES = 2
with patch("zerver.lib.actions.ONBOARDING_UNREAD_MESSAGES", ONBOARDING_UNREAD_MESSAGES): with patch(
"zerver.actions.create_user.ONBOARDING_UNREAD_MESSAGES", ONBOARDING_UNREAD_MESSAGES
):
add_new_user_history(user_profile, streams) add_new_user_history(user_profile, streams)
# Our first message is in the user's history # Our first message is in the user's history

View File

@ -12,6 +12,7 @@ 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 confirmation.models import Confirmation from confirmation.models import Confirmation
from zerver.actions.create_user import do_create_user, do_reactivate_user
from zerver.actions.invites import do_create_multiuse_invite_link, do_invite_users from zerver.actions.invites import do_create_multiuse_invite_link, do_invite_users
from zerver.actions.message_send import get_recipient_info from zerver.actions.message_send import get_recipient_info
from zerver.actions.users import ( from zerver.actions.users import (
@ -21,12 +22,7 @@ from zerver.actions.users import (
do_deactivate_user, do_deactivate_user,
do_delete_user, do_delete_user,
) )
from zerver.lib.actions import ( from zerver.lib.actions import do_mute_user, do_set_realm_property
do_create_user,
do_mute_user,
do_reactivate_user,
do_set_realm_property,
)
from zerver.lib.avatar import avatar_url, get_gravatar_url from zerver.lib.avatar import avatar_url, get_gravatar_url
from zerver.lib.bulk_create import create_users from zerver.lib.bulk_create import create_users
from zerver.lib.create_user import copy_default_settings from zerver.lib.create_user import copy_default_settings

View File

@ -25,6 +25,7 @@ from confirmation.models import (
render_confirmation_key_error, render_confirmation_key_error,
validate_key, validate_key,
) )
from zerver.actions.create_user import do_activate_mirror_dummy_user, do_create_user
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 ( from zerver.actions.user_settings import (
do_change_full_name, do_change_full_name,
@ -40,7 +41,7 @@ from zerver.forms import (
RealmRedirectForm, RealmRedirectForm,
RegistrationForm, RegistrationForm,
) )
from zerver.lib.actions import do_activate_mirror_dummy_user, do_create_realm, do_create_user from zerver.lib.actions import do_create_realm
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

View File

@ -6,6 +6,7 @@ from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from zerver.actions.create_user import do_create_user, do_reactivate_user, notify_created_bot
from zerver.actions.custom_profile_fields import ( 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,
@ -30,9 +31,6 @@ from zerver.lib.actions import (
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_reactivate_user,
notify_created_bot,
) )
from zerver.lib.avatar import avatar_url, get_gravatar_url from zerver.lib.avatar import avatar_url, get_gravatar_url
from zerver.lib.bot_config import set_bot_config from zerver.lib.bot_config import set_bot_config

View File

@ -2,10 +2,11 @@ from typing import Any, Dict, List
from django.core.management.base import BaseCommand from django.core.management.base import BaseCommand
from zerver.actions.create_user import do_create_user
from zerver.actions.message_send import do_send_messages, internal_prep_stream_message from zerver.actions.message_send import do_send_messages, internal_prep_stream_message
from zerver.actions.streams import bulk_add_subscriptions from zerver.actions.streams import bulk_add_subscriptions
from zerver.actions.user_settings import do_change_avatar_fields from zerver.actions.user_settings import do_change_avatar_fields
from zerver.lib.actions import do_add_reaction, do_create_user from zerver.lib.actions import do_add_reaction
from zerver.lib.emoji import emoji_name_to_emoji_code from zerver.lib.emoji import emoji_name_to_emoji_code
from zerver.lib.streams import ensure_stream from zerver.lib.streams import ensure_stream
from zerver.lib.upload import upload_avatar_image from zerver.lib.upload import upload_avatar_image

View File

@ -58,11 +58,11 @@ from social_core.pipeline.partial import partial
from typing_extensions import TypedDict from typing_extensions import TypedDict
from zxcvbn import zxcvbn from zxcvbn import zxcvbn
from zerver.actions.create_user import do_create_user, do_reactivate_user
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.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
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