diff --git a/zerver/actions/create_user.py b/zerver/actions/create_user.py index b201eec1eb..da521430dc 100644 --- a/zerver/actions/create_user.py +++ b/zerver/actions/create_user.py @@ -66,6 +66,7 @@ from zerver.models import ( ) from zerver.models.groups import SystemGroups from zerver.models.realm_audit_logs import AuditLogEventType +from zerver.models.streams import StreamTopicsPolicyEnum from zerver.models.users import ExternalAuthID, active_user_ids, bot_owner_user_ids, get_system_bot from zerver.tornado.django_api import send_event_on_commit @@ -82,6 +83,11 @@ def send_message_to_signup_notification_stream( with override_language(realm.default_language): topic_name = _("signups") + if ( + signup_announcements_stream.topics_policy + == StreamTopicsPolicyEnum.empty_topic_only.value + ): + topic_name = "" internal_send_stream_message(sender, signup_announcements_stream, topic_name, message) diff --git a/zerver/lib/message_report.py b/zerver/lib/message_report.py index 97428831e3..37a37e6bed 100644 --- a/zerver/lib/message_report.py +++ b/zerver/lib/message_report.py @@ -9,6 +9,7 @@ from zerver.lib.message import is_1_to_1_message, truncate_content from zerver.lib.topic_link_util import get_message_link_syntax from zerver.models import Message, Realm, UserProfile from zerver.models.recipients import Recipient +from zerver.models.streams import StreamTopicsPolicyEnum from zerver.models.users import get_system_bot # We shrink the truncate length for the reported message to ensure @@ -104,9 +105,13 @@ def send_message_report( ) content += reported_message_preview_block + topic_name = _("{fullname}'s moderation requests").format(fullname=reported_user.full_name) + if moderation_request_channel.topics_policy == StreamTopicsPolicyEnum.empty_topic_only.value: + topic_name = "" + internal_send_stream_message( sender=get_system_bot(settings.NOTIFICATION_BOT, moderation_request_channel.realm.id), stream=moderation_request_channel, - topic_name=_("{fullname}'s moderation requests").format(fullname=reported_user.full_name), + topic_name=topic_name, content=content, ) diff --git a/zerver/lib/zulip_update_announcements.py b/zerver/lib/zulip_update_announcements.py index d8aa12d5e9..5a2fcc9ea5 100644 --- a/zerver/lib/zulip_update_announcements.py +++ b/zerver/lib/zulip_update_announcements.py @@ -19,6 +19,7 @@ from zerver.lib.message import SendMessageRequest, remove_single_newlines from zerver.lib.topic import messages_for_topic from zerver.models.realm_audit_logs import AuditLogEventType, RealmAuditLog from zerver.models.realms import Realm +from zerver.models.streams import StreamTopicsPolicyEnum from zerver.models.users import UserProfile, get_system_bot @@ -483,12 +484,25 @@ def get_realms_behind_zulip_update_announcements_level(level: int) -> QuerySet[R return realms +def get_topic_name_for_zulip_update_announcements(realm: Realm) -> str: + assert realm.zulip_update_announcements_stream is not None + + with override_language(realm.default_language): + topic_name = str(realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME) + + if ( + realm.zulip_update_announcements_stream.topics_policy + == StreamTopicsPolicyEnum.empty_topic_only.value + ): + topic_name = "" + + return topic_name + + def internal_prep_group_direct_message_for_old_realm( realm: Realm, sender: UserProfile ) -> SendMessageRequest | None: administrators = list(realm.get_human_admin_users()) - with override_language(realm.default_language): - topic_name = str(realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME) if realm.zulip_update_announcements_stream is None: content = """ Zulip now supports [configuring]({organization_settings_url}) a channel where Zulip will @@ -500,6 +514,7 @@ a channel within one week, your organization will not miss any update messages. organization_settings_url="/#organization/organization-settings", ) else: + topic_name = get_topic_name_for_zulip_update_announcements(realm) content = """ Starting tomorrow, users in your organization will receive [updates]({zulip_update_announcements_help_url}) about new Zulip features in #**{zulip_update_announcements_stream}>{topic_name}**. @@ -560,8 +575,8 @@ def internal_prep_zulip_update_announcements_stream_messages( message_requests = [] stream = realm.zulip_update_announcements_stream assert stream is not None - with override_language(realm.default_language): - topic_name = str(realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME) + topic_name = get_topic_name_for_zulip_update_announcements(realm) + while current_level < latest_level: content = get_zulip_update_announcements_message_for_level(level=current_level + 1) message_requests.append( @@ -671,11 +686,9 @@ def send_zulip_update_announcements_to_realm(realm: Realm, skip_delay: bool) -> return # Send an introductory message just before the first update message. - with override_language(realm.default_language): - topic_name = str(realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME) - stream = realm.zulip_update_announcements_stream assert stream.recipient_id is not None + topic_name = get_topic_name_for_zulip_update_announcements(realm) topic_has_messages = messages_for_topic(realm.id, stream.recipient_id, topic_name).exists() if not topic_has_messages: diff --git a/zerver/tests/test_message_report.py b/zerver/tests/test_message_report.py index 008f2c351e..25fd9053fc 100644 --- a/zerver/tests/test_message_report.py +++ b/zerver/tests/test_message_report.py @@ -2,6 +2,7 @@ from django.conf import settings from typing_extensions import Any, override from zerver.actions.realm_settings import do_set_realm_moderation_request_channel +from zerver.actions.streams import do_set_stream_property from zerver.lib.markdown.fenced_code import get_unused_fence from zerver.lib.mention import silent_mention_syntax_for_user from zerver.lib.message import truncate_content @@ -15,6 +16,7 @@ from zerver.lib.topic_link_util import ( from zerver.models import UserProfile from zerver.models.messages import Message from zerver.models.recipients import get_or_create_direct_message_group +from zerver.models.streams import StreamTopicsPolicyEnum from zerver.models.users import get_system_bot @@ -373,3 +375,23 @@ class ReportMessageTest(ZulipTestCase): ) self.assert_json_success(result) + + def test_message_report_to_channel_with_topics_disabled(self) -> None: + notification_bot = get_system_bot(settings.NOTIFICATION_BOT, self.realm.id) + do_set_stream_property( + self.moderation_request_channel, + "topics_policy", + StreamTopicsPolicyEnum.empty_topic_only.value, + self.hamlet, + ) + + result = self.report_message( + self.hamlet, + self.reported_message_id, + report_type="harassment", + ) + self.assert_json_success(result) + report_msg = self.get_last_message() + self.assertEqual(report_msg.sender_id, notification_bot.id) + self.assertEqual(report_msg.topic_name(), "") + self.assertIn("reported", report_msg.content) diff --git a/zerver/tests/test_new_users.py b/zerver/tests/test_new_users.py index a7e0f68699..2e182c887b 100644 --- a/zerver/tests/test_new_users.py +++ b/zerver/tests/test_new_users.py @@ -10,6 +10,7 @@ from typing_extensions import override from corporate.lib.stripe import get_latest_seat_count from zerver.actions.create_user import notify_new_user +from zerver.actions.streams import do_set_stream_property from zerver.actions.user_settings import do_change_user_setting from zerver.lib.initial_password import initial_password from zerver.lib.test_classes import ZulipTestCase @@ -17,7 +18,7 @@ from zerver.lib.timezone import canonicalize_timezone from zerver.models import Message, Recipient, Stream, UserProfile from zerver.models.realms import get_realm from zerver.models.recipients import get_direct_message_group_user_ids -from zerver.models.streams import get_stream +from zerver.models.streams import StreamTopicsPolicyEnum, get_stream from zerver.models.users import get_system_bot from zerver.signals import JUST_CREATED_THRESHOLD, get_device_browser, get_device_os @@ -286,6 +287,7 @@ class TestNotifyNewUser(ZulipTestCase): self.assertEqual(message.recipient.type, Recipient.STREAM) actual_stream = Stream.objects.get(id=message.recipient.type_id) self.assertEqual(actual_stream.name, "core team") + self.assertEqual(message.topic_name(), "signups") self.assertIn( f"@_**Cordelia, Lear's daughter|{new_user.id}** joined this organization.", message.content, @@ -297,6 +299,28 @@ class TestNotifyNewUser(ZulipTestCase): notify_new_user(new_user) self.assertEqual(self.get_message_count(), message_count + 1) + def test_notify_realm_of_new_user_in_empty_topic_only_channel(self) -> None: + realm = get_realm("zulip") + iago = self.example_user("iago") + stream = get_stream("core team", realm) + realm.signup_announcements_stream = stream + realm.save(update_fields=["signup_announcements_stream"]) + do_set_stream_property( + stream, "topics_policy", StreamTopicsPolicyEnum.empty_topic_only.value, iago + ) + + new_user = self.example_user("cordelia") + message_count = self.get_message_count() + + notify_new_user(new_user) + self.assertEqual(self.get_message_count(), message_count + 1) + message = self.get_last_message() + self.assertEqual(message.topic_name(), "") + self.assertIn( + f"@_**Cordelia, Lear's daughter|{new_user.id}** joined this organization.", + message.content, + ) + def test_notify_realm_of_new_user_in_manual_license_management(self) -> None: realm = get_realm("zulip") admin_user_ids = set(realm.get_human_admin_users().values_list("id", flat=True)) diff --git a/zerver/tests/test_subs.py b/zerver/tests/test_subs.py index 0c240b8b1a..e389b04266 100644 --- a/zerver/tests/test_subs.py +++ b/zerver/tests/test_subs.py @@ -30,6 +30,7 @@ from zerver.actions.streams import ( deactivated_streams_by_old_name, do_change_stream_group_based_setting, do_deactivate_stream, + do_set_stream_property, do_unarchive_stream, ) from zerver.actions.user_groups import bulk_add_members_to_user_groups, check_add_user_group @@ -3431,6 +3432,7 @@ class SubscriptionAPITest(ZulipTestCase): self.assertEqual(msg.recipient.type, Recipient.STREAM) self.assertEqual(msg.recipient.type_id, new_stream_announcements_stream.id) self.assertEqual(msg.sender_id, self.notification_bot(self.test_realm).id) + self.assertEqual(msg.topic_name(), "new channels") expected_msg = f"@_**{invitee_full_name}|{invitee.id}** created a new channel #**{invite_streams[0]}**." self.assertEqual(msg.content, expected_msg) @@ -3444,6 +3446,41 @@ class SubscriptionAPITest(ZulipTestCase): ) self.assertEqual(msg.content, expected_msg) + def test_sucessful_subscription_notifies_in_empty_topic_only_stream(self) -> None: + invitee = self.example_user("iago") + invitee_full_name = "Iago" + + current_stream = self.get_streams(invitee)[0] + invite_streams = self.make_random_stream_names([current_stream])[:1] + + new_stream_announcements_stream = get_stream(current_stream, self.test_realm) + self.test_realm.new_stream_announcements_stream_id = new_stream_announcements_stream.id + self.test_realm.save() + + do_set_stream_property( + new_stream_announcements_stream, + "topics_policy", + StreamTopicsPolicyEnum.empty_topic_only.value, + invitee, + ) + + self.subscribe_via_post( + invitee, + invite_streams, + extra_post_data=dict( + announce="true", + principals=orjson.dumps([self.user_profile.id]).decode(), + ), + ) + + msg = self.get_second_to_last_message() + self.assertEqual(msg.recipient.type, Recipient.STREAM) + self.assertEqual(msg.recipient.type_id, new_stream_announcements_stream.id) + self.assertEqual(msg.sender_id, self.notification_bot(self.test_realm).id) + self.assertEqual(msg.topic_name(), "") + expected_msg = f"@_**{invitee_full_name}|{invitee.id}** created a new channel #**{invite_streams[0]}**." + self.assertEqual(msg.content, expected_msg) + def test_successful_cross_realm_notification(self) -> None: """ Calling POST /json/users/me/subscriptions in a new realm diff --git a/zerver/tests/test_zulip_update_announcements.py b/zerver/tests/test_zulip_update_announcements.py index 36e5c9707a..6973edefaa 100644 --- a/zerver/tests/test_zulip_update_announcements.py +++ b/zerver/tests/test_zulip_update_announcements.py @@ -9,7 +9,7 @@ from django.utils.timezone import now as timezone_now from typing_extensions import override from zerver.actions.create_realm import do_create_realm -from zerver.actions.streams import do_deactivate_stream +from zerver.actions.streams import do_deactivate_stream, do_set_stream_property from zerver.data_import.mattermost import do_convert_data from zerver.lib.import_realm import do_import_realm from zerver.lib.message import remove_single_newlines @@ -21,7 +21,7 @@ from zerver.lib.zulip_update_announcements import ( from zerver.models.messages import Message from zerver.models.realms import get_realm from zerver.models.recipients import Recipient, get_direct_message_group_user_ids -from zerver.models.streams import get_stream +from zerver.models.streams import StreamTopicsPolicyEnum, get_stream from zerver.models.users import get_system_bot @@ -129,6 +129,42 @@ class ZulipUpdateAnnouncementsTest(ZulipTestCase): self.assertEqual(stream_messages[1].content, "Announcement message 3.") self.assertEqual(stream_messages[2].content, "Announcement message 4.") self.assertEqual(realm.zulip_update_announcements_level, 4) + self.assertEqual( + stream_messages[0].topic_name(), realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME + ) + self.assertEqual( + stream_messages[1].topic_name(), realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME + ) + self.assertEqual( + stream_messages[2].topic_name(), realm.ZULIP_UPDATE_ANNOUNCEMENTS_TOPIC_NAME + ) + + # Test sending updates with announcements stream allowing empty topics only. + iago = self.example_user("iago") + do_set_stream_property( + verona, "topics_policy", StreamTopicsPolicyEnum.empty_topic_only.value, iago + ) + + new_updates = [ + ZulipUpdateAnnouncement( + level=5, + message="Announcement message 5.", + ), + ] + self.zulip_update_announcements.extend(new_updates) + with time_machine.travel(now + timedelta(days=15), tick=False): + send_zulip_update_announcements(skip_delay=False) + realm.refresh_from_db() + stream_messages = Message.objects.filter( + realm=realm, + sender=notification_bot, + recipient__type_id=verona.id, + date_sent__gte=now + timedelta(days=15), + ).order_by("id") + self.assert_length(stream_messages, 1) + self.assertEqual(stream_messages[0].topic_name(), "") + self.assertEqual(stream_messages[0].content, "Announcement message 5.") + self.assertEqual(realm.zulip_update_announcements_level, 5) def test_send_zulip_update_announcements_with_stream_configured(self) -> None: with mock.patch( diff --git a/zerver/views/streams.py b/zerver/views/streams.py index 94dfa5b650..aeea3c80aa 100644 --- a/zerver/views/streams.py +++ b/zerver/views/streams.py @@ -975,6 +975,11 @@ def send_messages_for_new_subscribers( else: content = _("{user_name} created a new channel {new_channels}.") topic_name = _("new channels") + if ( + new_stream_announcements_stream.topics_policy + == StreamTopicsPolicyEnum.empty_topic_only.value + ): + topic_name = "" content = content.format( user_name=silent_mention_syntax_for_user(user_profile),