mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 04:53:36 +00:00
do_deactivate_stream: Remove unnecessary mutations.
Streams should not be marked as private, and subscribers of the deactivated stream should not be removed. Update the confirmation message when archiving a stream.
This commit is contained in:
committed by
Tim Abbott
parent
f04fb937a3
commit
795b2ba14e
@@ -1682,6 +1682,7 @@ def check_message(
|
||||
mention_backend: MentionBackend | None = None,
|
||||
limit_unread_user_ids: set[int] | None = None,
|
||||
disable_external_notifications: bool = False,
|
||||
archived_channel_notice: bool = False,
|
||||
) -> SendMessageRequest:
|
||||
"""See
|
||||
https://zulip.readthedocs.io/en/latest/subsystems/sending-messages.html
|
||||
@@ -1727,7 +1728,10 @@ def check_message(
|
||||
|
||||
if not skip_stream_access_check:
|
||||
access_stream_for_send_message(
|
||||
sender=sender, stream=stream, forwarder_user_profile=forwarder_user_profile
|
||||
sender=sender,
|
||||
stream=stream,
|
||||
forwarder_user_profile=forwarder_user_profile,
|
||||
archived_channel_notice=archived_channel_notice,
|
||||
)
|
||||
else:
|
||||
# Defensive assertion - the only currently supported use case
|
||||
@@ -1859,6 +1863,7 @@ def _internal_prep_message(
|
||||
disable_external_notifications: bool = False,
|
||||
forged: bool = False,
|
||||
forged_timestamp: float | None = None,
|
||||
archived_channel_notice: bool = False,
|
||||
) -> SendMessageRequest | None:
|
||||
"""
|
||||
Create a message object and checks it, but doesn't send it or save it to the database.
|
||||
@@ -1890,6 +1895,7 @@ def _internal_prep_message(
|
||||
disable_external_notifications=disable_external_notifications,
|
||||
forged=forged,
|
||||
forged_timestamp=forged_timestamp,
|
||||
archived_channel_notice=archived_channel_notice,
|
||||
)
|
||||
except JsonableError as e:
|
||||
logging.exception(
|
||||
@@ -1913,6 +1919,7 @@ def internal_prep_stream_message(
|
||||
limit_unread_user_ids: set[int] | None = None,
|
||||
forged: bool = False,
|
||||
forged_timestamp: float | None = None,
|
||||
archived_channel_notice: bool = False,
|
||||
) -> SendMessageRequest | None:
|
||||
"""
|
||||
See _internal_prep_message for details of how this works.
|
||||
@@ -1930,6 +1937,7 @@ def internal_prep_stream_message(
|
||||
limit_unread_user_ids=limit_unread_user_ids,
|
||||
forged=forged,
|
||||
forged_timestamp=forged_timestamp,
|
||||
archived_channel_notice=archived_channel_notice,
|
||||
)
|
||||
|
||||
|
||||
@@ -2008,6 +2016,7 @@ def internal_send_stream_message(
|
||||
email_gateway: bool = False,
|
||||
message_type: int = Message.MessageType.NORMAL,
|
||||
limit_unread_user_ids: set[int] | None = None,
|
||||
archived_channel_notice: bool = False,
|
||||
) -> int | None:
|
||||
message = internal_prep_stream_message(
|
||||
sender,
|
||||
@@ -2017,6 +2026,7 @@ def internal_send_stream_message(
|
||||
email_gateway=email_gateway,
|
||||
message_type=message_type,
|
||||
limit_unread_user_ids=limit_unread_user_ids,
|
||||
archived_channel_notice=archived_channel_notice,
|
||||
)
|
||||
|
||||
if message is None:
|
||||
|
||||
@@ -23,7 +23,7 @@ from zerver.lib.cache import (
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.mention import silent_mention_syntax_for_user
|
||||
from zerver.lib.message import get_last_message_id
|
||||
from zerver.lib.queue import queue_event_on_commit, queue_json_publish
|
||||
from zerver.lib.queue import queue_event_on_commit
|
||||
from zerver.lib.stream_color import pick_colors
|
||||
from zerver.lib.stream_subscription import (
|
||||
SubInfo,
|
||||
@@ -71,88 +71,25 @@ from zerver.models.users import active_non_guest_user_ids, active_user_ids, get_
|
||||
from zerver.tornado.django_api import send_event_on_commit
|
||||
|
||||
|
||||
def send_user_remove_events_on_stream_deactivation(
|
||||
stream: Stream, subscribed_users: list[UserProfile]
|
||||
) -> None:
|
||||
guest_subscribed_users = [user for user in subscribed_users if user.is_guest]
|
||||
|
||||
if len(guest_subscribed_users) == 0: # nocoverage
|
||||
# Save a few database queries in the case that the stream
|
||||
# didn't contain any guest users.
|
||||
return
|
||||
|
||||
other_subscriptions_of_subscribed_users = get_subscribers_of_target_user_subscriptions(
|
||||
guest_subscribed_users
|
||||
)
|
||||
users_involved_in_dms_dict = get_users_involved_in_dms_with_target_users(
|
||||
guest_subscribed_users, stream.realm
|
||||
)
|
||||
|
||||
subscriber_ids = {user.id for user in subscribed_users}
|
||||
inaccessible_user_dict: dict[int, set[int]] = defaultdict(set)
|
||||
for guest_user in guest_subscribed_users:
|
||||
users_accessible_by_guest = (
|
||||
{guest_user.id}
|
||||
| other_subscriptions_of_subscribed_users[guest_user.id]
|
||||
| users_involved_in_dms_dict[guest_user.id]
|
||||
)
|
||||
users_inaccessible_to_guest = subscriber_ids - users_accessible_by_guest
|
||||
for user_id in users_inaccessible_to_guest:
|
||||
inaccessible_user_dict[user_id].add(guest_user.id)
|
||||
|
||||
for user_id, notify_user_ids in inaccessible_user_dict.items():
|
||||
event_remove_user = dict(
|
||||
type="realm_user",
|
||||
op="remove",
|
||||
person=dict(user_id=user_id, full_name=str(UserProfile.INACCESSIBLE_USER_NAME)),
|
||||
)
|
||||
send_event_on_commit(stream.realm, event_remove_user, list(notify_user_ids))
|
||||
|
||||
|
||||
@transaction.atomic(savepoint=False)
|
||||
def do_deactivate_stream(stream: Stream, *, acting_user: UserProfile | None) -> None:
|
||||
# If the stream is already deactivated, this is a no-op
|
||||
if stream.deactivated is True:
|
||||
raise JsonableError(_("Channel is already deactivated"))
|
||||
|
||||
# We want to mark all messages in the to-be-deactivated stream as
|
||||
# read for all users; otherwise they will pollute queries like
|
||||
# "Get the user's first unread message". Since this can be an
|
||||
# expensive operation, we do it via the deferred_work queue
|
||||
# processor.
|
||||
deferred_work_event = {
|
||||
"type": "mark_stream_messages_as_read_for_everyone",
|
||||
"stream_recipient_id": stream.recipient_id,
|
||||
}
|
||||
transaction.on_commit(lambda: queue_json_publish("deferred_work", deferred_work_event))
|
||||
|
||||
# Get the affected user ids *before* we deactivate everybody.
|
||||
affected_user_ids = can_access_stream_user_ids(stream)
|
||||
|
||||
stream_subscribers = get_active_subscriptions_for_stream_id(
|
||||
stream.id, include_deactivated_users=True
|
||||
).select_related("user_profile")
|
||||
subscribed_users = [sub.user_profile for sub in stream_subscribers]
|
||||
stream_subscribers.update(active=False)
|
||||
|
||||
was_invite_only = stream.invite_only
|
||||
was_public = stream.is_public()
|
||||
was_web_public = stream.is_web_public
|
||||
|
||||
# We do not use do_change_stream_permission because no users need to
|
||||
# be notified, and we do not want to create audit log entries for
|
||||
# changing stream privacy. And due to this we also need to duplicate
|
||||
# the code to unset is_web_public field on attachments below.
|
||||
stream.deactivated = True
|
||||
stream.invite_only = True
|
||||
|
||||
stream.save(update_fields=["deactivated", "invite_only"])
|
||||
stream.save(update_fields=["deactivated"])
|
||||
|
||||
assert stream.recipient_id is not None
|
||||
if was_web_public:
|
||||
assert was_public
|
||||
# Unset the is_web_public and is_realm_public cache on attachments,
|
||||
# since the stream is now private.
|
||||
# since the stream is now archived.
|
||||
Attachment.objects.filter(messages__recipient_id=stream.recipient_id).update(
|
||||
is_web_public=None, is_realm_public=None
|
||||
)
|
||||
@@ -160,7 +97,7 @@ def do_deactivate_stream(stream: Stream, *, acting_user: UserProfile | None) ->
|
||||
is_web_public=None, is_realm_public=None
|
||||
)
|
||||
elif was_public:
|
||||
# Unset the is_realm_public cache on attachments, since the stream is now private.
|
||||
# Unset the is_realm_public cache on attachments, since the stream is now archived.
|
||||
Attachment.objects.filter(messages__recipient_id=stream.recipient_id).update(
|
||||
is_realm_public=None
|
||||
)
|
||||
@@ -178,13 +115,9 @@ def do_deactivate_stream(stream: Stream, *, acting_user: UserProfile | None) ->
|
||||
do_remove_streams_from_default_stream_group(stream.realm, group, [stream])
|
||||
|
||||
stream_dict = stream_to_dict(stream)
|
||||
stream_dict.update(dict(invite_only=was_invite_only))
|
||||
event = dict(type="stream", op="delete", streams=[stream_dict])
|
||||
send_event_on_commit(stream.realm, event, affected_user_ids)
|
||||
|
||||
if stream.realm.can_access_all_users_group.named_user_group.name != SystemGroups.EVERYONE:
|
||||
send_user_remove_events_on_stream_deactivation(stream, subscribed_users)
|
||||
|
||||
event_time = timezone_now()
|
||||
RealmAuditLog.objects.create(
|
||||
realm=stream.realm,
|
||||
@@ -194,6 +127,16 @@ def do_deactivate_stream(stream: Stream, *, acting_user: UserProfile | None) ->
|
||||
event_time=event_time,
|
||||
)
|
||||
|
||||
sender = get_system_bot(settings.NOTIFICATION_BOT, stream.realm_id)
|
||||
with override_language(stream.realm.default_language):
|
||||
internal_send_stream_message(
|
||||
sender,
|
||||
stream,
|
||||
topic_name=str(Realm.STREAM_EVENTS_NOTIFICATION_TOPIC_NAME),
|
||||
content=_("Channel {channel_name} has been archived.").format(channel_name=stream.name),
|
||||
archived_channel_notice=True,
|
||||
)
|
||||
|
||||
|
||||
def deactivated_streams_by_old_name(realm: Realm, stream_name: str) -> QuerySet[Stream]:
|
||||
fixed_length_prefix = ".......!DEACTIVATED:"
|
||||
@@ -223,32 +166,33 @@ def deactivated_streams_by_old_name(realm: Realm, stream_name: str) -> QuerySet[
|
||||
@transaction.atomic(savepoint=False)
|
||||
def do_unarchive_stream(stream: Stream, new_name: str, *, acting_user: UserProfile | None) -> None:
|
||||
realm = stream.realm
|
||||
stream_subscribers = get_active_subscriptions_for_stream_id(
|
||||
stream.id, include_deactivated_users=True
|
||||
).select_related("user_profile")
|
||||
|
||||
if not stream.deactivated:
|
||||
raise JsonableError(_("Channel is not currently deactivated"))
|
||||
if stream.name != new_name and Stream.objects.filter(realm=realm, name=new_name).exists():
|
||||
raise JsonableError(
|
||||
_("Channel named {channel_name} already exists").format(channel_name=new_name)
|
||||
)
|
||||
if stream.invite_only and not stream_subscribers:
|
||||
raise JsonableError(_("Channel is private and have no subscribers"))
|
||||
assert stream.recipient_id is not None
|
||||
|
||||
stream.deactivated = False
|
||||
stream.name = new_name
|
||||
if stream.invite_only and stream.is_web_public:
|
||||
# Previously, because archiving a channel set invite_only=True
|
||||
# without mutating is_web_public, it was possible for archived
|
||||
# channels to have this invalid state. Fix that.
|
||||
stream.is_web_public = False
|
||||
|
||||
# We only set invite_only=True during deactivation, which can lead
|
||||
# to the invalid state of to invite-only but also web-public
|
||||
# streams. Explicitly reset the access; we do not use
|
||||
# do_change_stream_permission because no users need be notified,
|
||||
# and it cannot handle the broken state that may currently exist.
|
||||
stream.is_web_public = False
|
||||
stream.invite_only = True
|
||||
stream.history_public_to_subscribers = True
|
||||
stream.save(
|
||||
update_fields=[
|
||||
"name",
|
||||
"deactivated",
|
||||
"is_web_public",
|
||||
"invite_only",
|
||||
"history_public_to_subscribers",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -280,17 +224,12 @@ def do_unarchive_stream(stream: Stream, new_name: str, *, acting_user: UserProfi
|
||||
|
||||
recent_traffic = get_streams_traffic({stream.id}, realm)
|
||||
|
||||
# All admins always get to know about private streams' existence,
|
||||
# but we only subscribe the realm owners.
|
||||
send_stream_creation_event(
|
||||
realm, stream, [user.id for user in realm.get_admin_users_and_bots()], recent_traffic
|
||||
)
|
||||
bulk_add_subscriptions(
|
||||
realm=realm,
|
||||
streams=[stream],
|
||||
users=realm.get_human_owner_users(),
|
||||
acting_user=acting_user,
|
||||
)
|
||||
subscribed_users = {sub.user_profile for sub in stream_subscribers}
|
||||
admin_users_and_bots = set(realm.get_admin_users_and_bots())
|
||||
|
||||
notify_users = admin_users_and_bots | subscribed_users
|
||||
|
||||
send_stream_creation_event(realm, stream, [user.id for user in notify_users], recent_traffic)
|
||||
|
||||
sender = get_system_bot(settings.NOTIFICATION_BOT, stream.realm_id)
|
||||
with override_language(stream.realm.default_language):
|
||||
|
||||
Reference in New Issue
Block a user