mirror of https://github.com/zulip/zulip.git
settings: Add automatically follow and unmute topics policy settings.
This commit adds two user settings, named * `automatically_follow_topics_policy` * `automatically_unmute_topics_in_muted_streams_policy` The settings control the user's preference on which topics they will automatically 'follow' or 'unmute in muted streams'. The policies offer four options: 1. Topics I participate in 2. Topics I send a message to 3. Topics I start 4. Never (default) There is no support for configuring the settings through the UI yet.
This commit is contained in:
parent
c349d1137c
commit
58568a60d6
|
@ -20,6 +20,15 @@ format used by the Zulip server that they are interacting with.
|
|||
|
||||
## Changes in Zulip 8.0
|
||||
|
||||
**Feature level 214**
|
||||
|
||||
* [`PATCH /realm/user_settings_defaults`](/api/update-realm-user-settings-defaults),
|
||||
[`POST /register`](/api/register-queue), [`PATCH /settings`](/api/update-settings):
|
||||
Added two new user settings, `automatically_follow_topics_policy` and
|
||||
`automatically_unmute_topics_in_muted_streams_policy`. The settings control the
|
||||
user's preference on which topics the user will automatically 'follow' and
|
||||
'unmute in muted streams' respectively.
|
||||
|
||||
**Feature level 213**
|
||||
|
||||
* [`POST /register`](/api/register-queue): Fixed incorrect handling of
|
||||
|
|
|
@ -230,6 +230,9 @@ python_rules = RuleList(
|
|||
"good_lines": ["topic_name"],
|
||||
"bad_lines": ['subject="foo"', " MAX_SUBJECT_LEN"],
|
||||
"exclude": FILES_WITH_LEGACY_SUBJECT,
|
||||
"exclude_line": {
|
||||
("zerver/lib/message.py", "message__subject__iexact=message.topic_name(),"),
|
||||
},
|
||||
"include_only": {
|
||||
"zerver/data_import/",
|
||||
"zerver/lib/",
|
||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
|||
# Changes should be accompanied by documentation explaining what the
|
||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||
API_FEATURE_LEVEL = 213
|
||||
API_FEATURE_LEVEL = 214
|
||||
|
||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
||||
# only when going from an old version of the code to a newer version. Bump
|
||||
|
|
|
@ -30,6 +30,7 @@ from django.utils.translation import override as override_language
|
|||
from django_stubs_ext import ValuesQuerySet
|
||||
|
||||
from zerver.actions.uploads import do_claim_attachments
|
||||
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.addressee import Addressee
|
||||
from zerver.lib.alert_words import get_alert_word_automaton
|
||||
from zerver.lib.cache import cache_with_key, user_profile_delivery_email_cache_key
|
||||
|
@ -50,7 +51,9 @@ from zerver.lib.message import (
|
|||
check_user_group_mention_allowed,
|
||||
normalize_body,
|
||||
render_markdown,
|
||||
set_visibility_policy_possible,
|
||||
truncate_topic,
|
||||
visibility_policy_for_send_message,
|
||||
wildcard_mention_allowed,
|
||||
)
|
||||
from zerver.lib.muted_users import get_muting_users
|
||||
|
@ -180,6 +183,7 @@ class RecipientInfoResult:
|
|||
service_bot_tuples: List[Tuple[int, int]]
|
||||
all_bot_user_ids: Set[int]
|
||||
topic_participant_user_ids: Set[int]
|
||||
sender_muted_stream: Optional[bool]
|
||||
|
||||
|
||||
class ActiveUserDict(TypedDict):
|
||||
|
@ -212,6 +216,7 @@ def get_recipient_info(
|
|||
stream_wildcard_mention_in_followed_topic_user_ids: Set[int] = set()
|
||||
muted_sender_user_ids: Set[int] = get_muting_users(sender_id)
|
||||
topic_participant_user_ids: Set[int] = set()
|
||||
sender_muted_stream: Optional[bool] = None
|
||||
|
||||
if recipient.type == Recipient.PERSONAL:
|
||||
# The sender and recipient may be the same id, so
|
||||
|
@ -275,7 +280,14 @@ def get_recipient_info(
|
|||
.order_by("user_profile_id")
|
||||
)
|
||||
|
||||
message_to_user_ids = [row["user_profile_id"] for row in subscription_rows]
|
||||
message_to_user_ids = list()
|
||||
for row in subscription_rows:
|
||||
message_to_user_ids.append(row["user_profile_id"])
|
||||
# We store the 'sender_muted_stream' information here to avoid db query at
|
||||
# a later stage when we perform automatically unmute topic in muted stream operation.
|
||||
if row["user_profile_id"] == sender_id:
|
||||
sender_muted_stream = row["is_muted"]
|
||||
|
||||
user_id_to_visibility_policy = stream_topic.user_id_to_visibility_policy_dict()
|
||||
|
||||
def notification_recipients(setting: str) -> Set[int]:
|
||||
|
@ -466,6 +478,7 @@ def get_recipient_info(
|
|||
service_bot_tuples=service_bot_tuples,
|
||||
all_bot_user_ids=all_bot_user_ids,
|
||||
topic_participant_user_ids=topic_participant_user_ids,
|
||||
sender_muted_stream=sender_muted_stream,
|
||||
)
|
||||
|
||||
|
||||
|
@ -642,6 +655,7 @@ def build_message_send_dict(
|
|||
|
||||
message_send_dict = SendMessageRequest(
|
||||
stream=stream,
|
||||
sender_muted_stream=info.sender_muted_stream,
|
||||
local_id=local_id,
|
||||
sender_queue_id=sender_queue_id,
|
||||
realm=realm,
|
||||
|
@ -896,6 +910,8 @@ def do_send_messages(
|
|||
|
||||
# This next loop is responsible for notifying other parts of the
|
||||
# Zulip system about the messages we just committed to the database:
|
||||
# * Sender automatically follows or unmutes the topic depending on 'automatically_follow_topics_policy'
|
||||
# and 'automatically_unmute_topics_in_muted_streams_policy' user settings.
|
||||
# * Notifying clients via send_event
|
||||
# * Triggering outgoing webhooks via the service event queue.
|
||||
# * Updating the `first_message_id` field for streams without any message history.
|
||||
|
@ -910,6 +926,39 @@ def do_send_messages(
|
|||
# assert needed because stubs for django are missing
|
||||
assert send_request.stream is not None
|
||||
realm_id = send_request.stream.realm_id
|
||||
sender = send_request.message.sender
|
||||
|
||||
# Determine and set the visibility_policy depending on 'automatically_follow_topics_policy'
|
||||
# and 'automatically_unmute_topics_in_muted_streams_policy'.
|
||||
if set_visibility_policy_possible(sender, send_request.message) and not (
|
||||
sender.automatically_follow_topics_policy
|
||||
== sender.automatically_unmute_topics_in_muted_streams_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER
|
||||
):
|
||||
try:
|
||||
user_topic = UserTopic.objects.get(
|
||||
user_profile=sender,
|
||||
stream_id=send_request.stream.id,
|
||||
topic_name__iexact=send_request.message.topic_name(),
|
||||
)
|
||||
visibility_policy = user_topic.visibility_policy
|
||||
except UserTopic.DoesNotExist:
|
||||
visibility_policy = UserTopic.VisibilityPolicy.INHERIT
|
||||
|
||||
new_visibility_policy = visibility_policy_for_send_message(
|
||||
sender,
|
||||
send_request.message,
|
||||
send_request.stream,
|
||||
send_request.sender_muted_stream,
|
||||
visibility_policy,
|
||||
)
|
||||
if new_visibility_policy:
|
||||
do_set_user_topic_visibility_policy(
|
||||
user_profile=sender,
|
||||
stream=send_request.stream,
|
||||
topic=send_request.message.topic_name(),
|
||||
visibility_policy=new_visibility_policy,
|
||||
)
|
||||
|
||||
# Deliver events to the real-time push system, as well as
|
||||
# enqueuing any additional processing triggered by the message.
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
from typing import Any, Dict, Optional
|
||||
|
||||
from zerver.actions.create_user import create_historical_user_messages
|
||||
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.emoji import check_emoji_request, get_emoji_data
|
||||
from zerver.lib.exceptions import ReactionExistsError
|
||||
from zerver.lib.message import access_message, update_to_dict_cache
|
||||
from zerver.lib.message import (
|
||||
access_message,
|
||||
set_visibility_policy_possible,
|
||||
should_change_visibility_policy,
|
||||
update_to_dict_cache,
|
||||
visibility_policy_for_participation,
|
||||
)
|
||||
from zerver.lib.stream_subscription import subscriber_ids_with_stream_history_access
|
||||
from zerver.lib.streams import access_stream_by_id
|
||||
from zerver.models import Message, Reaction, Recipient, Stream, UserMessage, UserProfile
|
||||
from zerver.tornado.django_api import send_event_on_commit
|
||||
|
||||
|
@ -82,6 +90,32 @@ def do_add_reaction(
|
|||
|
||||
reaction.save()
|
||||
|
||||
# Determine and set the visibility_policy depending on 'automatically_follow_topics_policy'
|
||||
# and 'automatically_unmute_topics_in_muted_streams_policy'.
|
||||
if set_visibility_policy_possible(
|
||||
user_profile, message
|
||||
) and UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION in [
|
||||
user_profile.automatically_follow_topics_policy,
|
||||
user_profile.automatically_unmute_topics_in_muted_streams_policy,
|
||||
]:
|
||||
stream_id = message.recipient.type_id
|
||||
(stream, sub) = access_stream_by_id(user_profile, stream_id)
|
||||
assert stream is not None
|
||||
if sub:
|
||||
new_visibility_policy = visibility_policy_for_participation(user_profile, sub.is_muted)
|
||||
if new_visibility_policy and should_change_visibility_policy(
|
||||
new_visibility_policy,
|
||||
user_profile,
|
||||
stream_id,
|
||||
topic_name=message.topic_name(),
|
||||
):
|
||||
do_set_user_topic_visibility_policy(
|
||||
user_profile=user_profile,
|
||||
stream=stream,
|
||||
topic=message.topic_name(),
|
||||
visibility_policy=new_visibility_policy,
|
||||
)
|
||||
|
||||
notify_reaction_update(user_profile, message, reaction, "add")
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
from django.utils.translation import gettext as _
|
||||
|
||||
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.models import Realm, SubMessage, UserMessage
|
||||
from zerver.lib.message import (
|
||||
set_visibility_policy_possible,
|
||||
should_change_visibility_policy,
|
||||
visibility_policy_for_participation,
|
||||
)
|
||||
from zerver.lib.streams import access_stream_by_id
|
||||
from zerver.models import Realm, SubMessage, UserMessage, UserProfile
|
||||
from zerver.tornado.django_api import send_event_on_commit
|
||||
|
||||
|
||||
|
@ -48,6 +55,33 @@ def do_add_submessage(
|
|||
)
|
||||
submessage.save()
|
||||
|
||||
# Determine and set the visibility_policy depending on 'automatically_follow_topics_policy'
|
||||
# and 'automatically_unmute_topics_policy'.
|
||||
sender = submessage.sender
|
||||
if set_visibility_policy_possible(
|
||||
sender, submessage.message
|
||||
) and UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION in [
|
||||
sender.automatically_follow_topics_policy,
|
||||
sender.automatically_unmute_topics_in_muted_streams_policy,
|
||||
]:
|
||||
stream_id = submessage.message.recipient.type_id
|
||||
(stream, sub) = access_stream_by_id(sender, stream_id)
|
||||
assert stream is not None
|
||||
if sub:
|
||||
new_visibility_policy = visibility_policy_for_participation(sender, sub.is_muted)
|
||||
if new_visibility_policy and should_change_visibility_policy(
|
||||
new_visibility_policy,
|
||||
sender,
|
||||
stream_id,
|
||||
topic_name=submessage.message.topic_name(),
|
||||
):
|
||||
do_set_user_topic_visibility_policy(
|
||||
user_profile=sender,
|
||||
stream=stream,
|
||||
topic=submessage.message.topic_name(),
|
||||
visibility_policy=new_visibility_policy,
|
||||
)
|
||||
|
||||
event = dict(
|
||||
type="submessage",
|
||||
msg_type=msg_type,
|
||||
|
|
|
@ -20,7 +20,7 @@ import ahocorasick
|
|||
import orjson
|
||||
from django.conf import settings
|
||||
from django.db import connection
|
||||
from django.db.models import Max, Sum
|
||||
from django.db.models import Max, QuerySet, Sum
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext as _
|
||||
from django_stubs_ext import ValuesQuerySet
|
||||
|
@ -47,9 +47,15 @@ from zerver.lib.stream_subscription import (
|
|||
get_subscribed_stream_recipient_ids_for_user,
|
||||
num_subscribers_for_stream_id,
|
||||
)
|
||||
from zerver.lib.streams import get_web_public_streams_queryset
|
||||
from zerver.lib.streams import can_access_stream_history, get_web_public_streams_queryset
|
||||
from zerver.lib.timestamp import datetime_to_timestamp
|
||||
from zerver.lib.topic import DB_TOPIC_NAME, MESSAGE__TOPIC, TOPIC_LINKS, TOPIC_NAME
|
||||
from zerver.lib.topic import (
|
||||
DB_TOPIC_NAME,
|
||||
MESSAGE__TOPIC,
|
||||
TOPIC_LINKS,
|
||||
TOPIC_NAME,
|
||||
messages_for_topic,
|
||||
)
|
||||
from zerver.lib.types import DisplayRecipientT, EditHistoryEvent, UserDisplayRecipient
|
||||
from zerver.lib.url_preview.types import UrlEmbedData
|
||||
from zerver.lib.user_groups import is_user_in_group
|
||||
|
@ -147,6 +153,7 @@ class SendMessageRequest:
|
|||
message: Message
|
||||
rendering_result: MessageRenderingResult
|
||||
stream: Optional[Stream]
|
||||
sender_muted_stream: Optional[bool]
|
||||
local_id: Optional[str]
|
||||
sender_queue_id: Optional[str]
|
||||
realm: Realm
|
||||
|
@ -1713,3 +1720,180 @@ def update_to_dict_cache(
|
|||
|
||||
cache_set_many(items_for_remote_cache)
|
||||
return message_ids
|
||||
|
||||
|
||||
def visibility_policy_for_participation(
|
||||
sender: UserProfile,
|
||||
is_stream_muted: Optional[bool],
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
This function determines the visibility policy to set when a user
|
||||
participates in a topic, depending on the 'automatically_follow_topics_policy'
|
||||
and 'automatically_unmute_topics_in_muted_streams_policy' settings.
|
||||
"""
|
||||
if (
|
||||
sender.automatically_follow_topics_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.FOLLOWED
|
||||
|
||||
if (
|
||||
is_stream_muted
|
||||
and sender.automatically_unmute_topics_in_muted_streams_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.UNMUTED
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def visibility_policy_for_send(
|
||||
sender: UserProfile,
|
||||
is_stream_muted: Optional[bool],
|
||||
) -> Optional[int]:
|
||||
if (
|
||||
sender.automatically_follow_topics_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.FOLLOWED
|
||||
|
||||
if (
|
||||
is_stream_muted
|
||||
and sender.automatically_unmute_topics_in_muted_streams_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.UNMUTED
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def visibility_policy_for_send_message(
|
||||
sender: UserProfile,
|
||||
message: Message,
|
||||
stream: Stream,
|
||||
is_stream_muted: Optional[bool],
|
||||
current_visibility_policy: int,
|
||||
) -> Optional[int]:
|
||||
"""
|
||||
This function determines the visibility policy to set when a message
|
||||
is sent to a topic, depending on the 'automatically_follow_topics_policy'
|
||||
and 'automatically_unmute_topics_in_muted_streams_policy' settings.
|
||||
|
||||
It returns None when the policies can't make it more visible than the
|
||||
current visibility policy.
|
||||
"""
|
||||
# We prioritize 'FOLLOW' over 'UNMUTE' in muted streams.
|
||||
# We need to carefully handle the following two cases:
|
||||
#
|
||||
# 1. When an action qualifies for multiple values. Example:
|
||||
# - starting a topic is INITIATION, PARTICIPATION as well as SEND
|
||||
# - sending a non-first message is PARTICIPATION as well as SEND
|
||||
# action | 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# start | ON_PARTICIPATION / ON_SEND | ON_INITIATION | FOLLOWED
|
||||
# send | ON_SEND / ON_PARTICIPATION | ON_PARTICIPATION / ON_SEND | FOLLOWED
|
||||
#
|
||||
# 2. When both the policies have the same values.
|
||||
# action | 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# start | ON_INITIATION | ON_INITIATION | FOLLOWED
|
||||
# partc | ON_PARTICIPATION | ON_PARTICIPATION | FOLLOWED
|
||||
# send | ON_SEND | ON_SEND | FOLLOWED
|
||||
visibility_policy = None
|
||||
|
||||
if current_visibility_policy == UserTopic.VisibilityPolicy.FOLLOWED:
|
||||
return visibility_policy
|
||||
|
||||
visibility_policy_participation = visibility_policy_for_participation(sender, is_stream_muted)
|
||||
visibility_policy_send = visibility_policy_for_send(sender, is_stream_muted)
|
||||
|
||||
if UserTopic.VisibilityPolicy.FOLLOWED in (
|
||||
visibility_policy_participation,
|
||||
visibility_policy_send,
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.FOLLOWED
|
||||
|
||||
if UserTopic.VisibilityPolicy.UNMUTED in (
|
||||
visibility_policy_participation,
|
||||
visibility_policy_send,
|
||||
):
|
||||
visibility_policy = UserTopic.VisibilityPolicy.UNMUTED
|
||||
|
||||
# If a topic has a visibility policy set, it can't be the case
|
||||
# of initiation. We return early, thus saving a DB query.
|
||||
if current_visibility_policy != UserTopic.VisibilityPolicy.INHERIT:
|
||||
if visibility_policy and current_visibility_policy == visibility_policy:
|
||||
return None
|
||||
return visibility_policy
|
||||
|
||||
# Now we need to check if the user initiated the topic.
|
||||
old_accessible_messages_in_topic: Union[QuerySet[Message], QuerySet[UserMessage]]
|
||||
if can_access_stream_history(sender, stream):
|
||||
old_accessible_messages_in_topic = messages_for_topic(
|
||||
realm_id=sender.realm_id,
|
||||
stream_recipient_id=message.recipient_id,
|
||||
topic_name=message.topic_name(),
|
||||
).exclude(id=message.id)
|
||||
else:
|
||||
# We use the user's own message access to avoid leaking information in
|
||||
# private streams with protected history.
|
||||
old_accessible_messages_in_topic = UserMessage.objects.filter(
|
||||
user_profile=sender,
|
||||
message__recipient_id=message.recipient_id,
|
||||
message__subject__iexact=message.topic_name(),
|
||||
).exclude(message_id=message.id)
|
||||
|
||||
if (
|
||||
sender.automatically_follow_topics_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION
|
||||
and not old_accessible_messages_in_topic.exists()
|
||||
):
|
||||
return UserTopic.VisibilityPolicy.FOLLOWED
|
||||
|
||||
if (
|
||||
is_stream_muted
|
||||
and sender.automatically_unmute_topics_in_muted_streams_policy
|
||||
== UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION
|
||||
and not old_accessible_messages_in_topic.exists()
|
||||
):
|
||||
visibility_policy = UserTopic.VisibilityPolicy.UNMUTED
|
||||
|
||||
return visibility_policy
|
||||
|
||||
|
||||
def should_change_visibility_policy(
|
||||
new_visibility_policy: int,
|
||||
sender: UserProfile,
|
||||
stream_id: int,
|
||||
topic_name: str,
|
||||
) -> bool:
|
||||
try:
|
||||
user_topic = UserTopic.objects.get(
|
||||
user_profile=sender, stream_id=stream_id, topic_name__iexact=topic_name
|
||||
)
|
||||
except UserTopic.DoesNotExist:
|
||||
return True
|
||||
current_visibility_policy = user_topic.visibility_policy
|
||||
|
||||
if new_visibility_policy == current_visibility_policy:
|
||||
return False
|
||||
|
||||
# The intent of these "automatically follow or unmute" policies is that they
|
||||
# can only increase the user's visibility policy for the topic. If a topic is
|
||||
# already FOLLOWED, we don't change the state to UNMUTED due to these policies.
|
||||
if current_visibility_policy == UserTopic.VisibilityPolicy.FOLLOWED:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def set_visibility_policy_possible(user_profile: UserProfile, message: Message) -> bool:
|
||||
"""If the user can set a visibility policy."""
|
||||
if not message.is_stream_message():
|
||||
return False
|
||||
|
||||
if user_profile.is_bot:
|
||||
return False
|
||||
|
||||
if user_profile.realm != message.get_realm():
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.5 on 2023-09-19 10:52
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0475_realm_jitsi_server_url"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="realmuserdefault",
|
||||
name="automatically_follow_topics_policy",
|
||||
field=models.PositiveSmallIntegerField(default=3),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="realmuserdefault",
|
||||
name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
field=models.PositiveSmallIntegerField(default=3),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="userprofile",
|
||||
name="automatically_follow_topics_policy",
|
||||
field=models.PositiveSmallIntegerField(default=3),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="userprofile",
|
||||
name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
field=models.PositiveSmallIntegerField(default=3),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,32 @@
|
|||
# Generated by Django 4.2.5 on 2023-10-02 05:53
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0476_realmuserdefault_automatically_follow_topics_policy_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="realmuserdefault",
|
||||
name="automatically_follow_topics_policy",
|
||||
field=models.PositiveSmallIntegerField(default=4),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="realmuserdefault",
|
||||
name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
field=models.PositiveSmallIntegerField(default=4),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userprofile",
|
||||
name="automatically_follow_topics_policy",
|
||||
field=models.PositiveSmallIntegerField(default=4),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userprofile",
|
||||
name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
field=models.PositiveSmallIntegerField(default=4),
|
||||
),
|
||||
]
|
|
@ -1642,6 +1642,30 @@ class UserBaseSettings(models.Model):
|
|||
default=REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_AUTOMATIC
|
||||
)
|
||||
|
||||
# The following two settings control which topics to automatically
|
||||
# 'follow' or 'unmute in a muted stream', respectively.
|
||||
# Follow or unmute a topic automatically on:
|
||||
# - PARTICIPATION: Send a message, React to a message, Participate in a poll or Edit a TO-DO list.
|
||||
# - SEND: Send a message.
|
||||
# - INITIATION: Send the first message in the topic.
|
||||
# - NEVER: Never automatically follow or unmute a topic.
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION = 1
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND = 2
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION = 3
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER = 4
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES = [
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
]
|
||||
automatically_follow_topics_policy = models.PositiveSmallIntegerField(
|
||||
default=AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER
|
||||
)
|
||||
automatically_unmute_topics_in_muted_streams_policy = models.PositiveSmallIntegerField(
|
||||
default=AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER
|
||||
)
|
||||
|
||||
# Whether or not the user wants to sync their drafts.
|
||||
enable_drafts_synchronization = models.BooleanField(default=True)
|
||||
|
||||
|
@ -1737,6 +1761,8 @@ class UserBaseSettings(models.Model):
|
|||
enable_followed_topic_push_notifications=bool,
|
||||
enable_followed_topic_audible_notifications=bool,
|
||||
enable_followed_topic_wildcard_mentions_notify=bool,
|
||||
automatically_follow_topics_policy=int,
|
||||
automatically_unmute_topics_in_muted_streams_policy=int,
|
||||
)
|
||||
|
||||
notification_setting_types = {
|
||||
|
|
|
@ -10206,6 +10206,44 @@ paths:
|
|||
- 2
|
||||
- 3
|
||||
example: 1
|
||||
- name: automatically_follow_topics_policy
|
||||
in: query
|
||||
description: |
|
||||
Which [topics to follow automatically](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
schema:
|
||||
type: integer
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
example: 1
|
||||
- name: automatically_unmute_topics_in_muted_streams_policy
|
||||
in: query
|
||||
description: |
|
||||
Which [topics to unmute automatically in muted streams](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
schema:
|
||||
type: integer
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
example: 1
|
||||
- name: presence_enabled
|
||||
in: query
|
||||
description: |
|
||||
|
@ -12422,6 +12460,28 @@ paths:
|
|||
**Changes**: New in Zulip 7.0 (feature level 168), replacing the
|
||||
previous `realm_name_in_notifications` boolean;
|
||||
`true` corresponded to `Always`, and `false` to `Never`.
|
||||
automatically_follow_topics_policy:
|
||||
type: integer
|
||||
description: |
|
||||
Which [topics to follow automatically](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
automatically_unmute_topics_in_muted_streams_policy:
|
||||
type: integer
|
||||
description: |
|
||||
Which [topics to unmute automatically in muted streams](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
presence_enabled:
|
||||
type: boolean
|
||||
description: |
|
||||
|
@ -14561,6 +14621,28 @@ paths:
|
|||
**Changes**: New in Zulip 7.0 (feature level 168), replacing the
|
||||
previous `realm_name_in_notifications` boolean;
|
||||
`true` corresponded to `Always`, and `false` to `Never`.
|
||||
automatically_follow_topics_policy:
|
||||
type: integer
|
||||
description: |
|
||||
Which [topics to follow automatically](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
automatically_unmute_topics_in_muted_streams_policy:
|
||||
type: integer
|
||||
description: |
|
||||
Which [topics to unmute automatically in muted streams](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
presence_enabled:
|
||||
type: boolean
|
||||
description: |
|
||||
|
@ -15808,6 +15890,44 @@ paths:
|
|||
- 2
|
||||
- 3
|
||||
example: 1
|
||||
- name: automatically_follow_topics_policy
|
||||
in: query
|
||||
description: |
|
||||
Which [topics to follow automatically](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
schema:
|
||||
type: integer
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
example: 1
|
||||
- name: automatically_unmute_topics_in_muted_streams_policy
|
||||
in: query
|
||||
description: |
|
||||
Which [topics to unmute automatically in muted streams](/help/mute-a-topic).
|
||||
|
||||
- 1 - Topics the user participates in
|
||||
- 2 - Topics the user sends a message to
|
||||
- 3 - Topics the user starts
|
||||
- 4 - Never
|
||||
|
||||
**Changes**: New in Zulip 8.0 (feature level 214).
|
||||
schema:
|
||||
type: integer
|
||||
enum:
|
||||
- 1
|
||||
- 2
|
||||
- 3
|
||||
- 4
|
||||
example: 1
|
||||
- name: presence_enabled
|
||||
in: query
|
||||
description: |
|
||||
|
|
|
@ -535,11 +535,195 @@ class NormalActionsTest(BaseAction):
|
|||
)
|
||||
|
||||
def test_stream_send_message_events(self) -> None:
|
||||
user_profile = self.example_user("hamlet")
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(user_profile, "Verona", "hello"),
|
||||
client_gravatar=False,
|
||||
hamlet = self.example_user("hamlet")
|
||||
for stream_name in ["Verona", "Denmark", "core team"]:
|
||||
stream = get_stream(stream_name, hamlet.realm)
|
||||
sub = get_subscription(stream.name, hamlet)
|
||||
do_change_subscription_property(hamlet, sub, stream, "is_muted", True, acting_user=None)
|
||||
|
||||
def verify_events_generated_and_reset_visibility_policy(
|
||||
events: List[Dict[str, Any]], stream_name: str, topic: str
|
||||
) -> None:
|
||||
# event-type: muted_topics
|
||||
check_muted_topics("events[0]", events[0])
|
||||
# event-type: user_topic
|
||||
check_user_topic("events[1]", events[1])
|
||||
|
||||
if events[2]["type"] == "message":
|
||||
check_message("events[2]", events[2])
|
||||
else:
|
||||
# event-type: reaction
|
||||
check_reaction_add("events[2]", events[2])
|
||||
|
||||
# Reset visibility policy
|
||||
do_set_user_topic_visibility_policy(
|
||||
hamlet,
|
||||
get_stream(stream_name, hamlet.realm),
|
||||
topic,
|
||||
visibility_policy=UserTopic.VisibilityPolicy.INHERIT,
|
||||
)
|
||||
|
||||
# Events generated during send message action depends on the 'automatically_follow_topics_policy'
|
||||
# and 'automatically_unmute_topics_in_muted_streams_policy' settings. Here we test all the
|
||||
# possible combinations.
|
||||
|
||||
# action: participation
|
||||
# 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# ON_PARTICIPATION | ON_INITIATION | FOLLOWED
|
||||
# ON_PARTICIPATION | ON_PARTICIPATION | FOLLOWED
|
||||
# ON_PARTICIPATION | ON_SEND | FOLLOWED
|
||||
# ON_PARTICIPATION | NEVER | FOLLOWED
|
||||
message_id = self.send_stream_message(hamlet, "Verona", "hello", "topic")
|
||||
message = Message.objects.get(id=message_id)
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
for setting_value in UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES:
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=setting_value,
|
||||
acting_user=None,
|
||||
)
|
||||
# Three events are generated:
|
||||
# 2 for following the topic and 1 for adding reaction.
|
||||
events = self.verify_action(
|
||||
lambda: do_add_reaction(hamlet, message, "tada", "1f389", "unicode_emoji"),
|
||||
client_gravatar=False,
|
||||
num_events=3,
|
||||
)
|
||||
verify_events_generated_and_reset_visibility_policy(events, "Verona", "topic")
|
||||
do_remove_reaction(hamlet, message, "1f389", "unicode_emoji")
|
||||
|
||||
# action: send
|
||||
# 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# ON_SEND | ON_INITIATION | FOLLOWED
|
||||
# ON_SEND | ON_PARTICIPATION | FOLLOWED
|
||||
# ON_SEND | ON_SEND | FOLLOWED
|
||||
# ON_SEND | NEVER | FOLLOWED
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
||||
acting_user=None,
|
||||
)
|
||||
for setting_value in UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES:
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=setting_value,
|
||||
acting_user=None,
|
||||
)
|
||||
# Three events are generated:
|
||||
# 2 for following the topic and 1 for the message sent.
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(hamlet, "Verona", "hello", "topic"),
|
||||
client_gravatar=False,
|
||||
num_events=3,
|
||||
)
|
||||
verify_events_generated_and_reset_visibility_policy(events, "Verona", "topic")
|
||||
|
||||
# action: initiation
|
||||
# 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# ON_INITIATION | ON_INITIATION | FOLLOWED
|
||||
# ON_INITIATION | ON_PARTICIPATION | FOLLOWED
|
||||
# ON_INITIATION | ON_SEND | FOLLOWED
|
||||
# ON_INITIATION | NEVER | FOLLOWED
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
for index, setting_value in enumerate(
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES
|
||||
):
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=setting_value,
|
||||
acting_user=None,
|
||||
)
|
||||
# Three events are generated:
|
||||
# 2 for following the topic and 1 for the message sent.
|
||||
send_message = lambda index=index: self.send_stream_message(
|
||||
hamlet, "Denmark", "hello", f"new topic {index}"
|
||||
)
|
||||
events = self.verify_action(
|
||||
send_message,
|
||||
client_gravatar=False,
|
||||
num_events=3,
|
||||
)
|
||||
verify_events_generated_and_reset_visibility_policy(
|
||||
events, "Denmark", f"new topic {index}"
|
||||
)
|
||||
|
||||
# 'automatically_follow_topics_policy' | 'automatically_unmute_topics_in_muted_streams_policy' | visibility_policy
|
||||
# NEVER | ON_INITIATION | UNMUTED
|
||||
# NEVER | ON_PARTICIPATION | UNMUTED
|
||||
# NEVER | ON_SEND | UNMUTED
|
||||
# NEVER | NEVER | NA
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
for setting_value in [
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
||||
]:
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=setting_value,
|
||||
acting_user=None,
|
||||
)
|
||||
# Three events are generated:
|
||||
# 2 for unmuting the topic and 1 for the message sent.
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(hamlet, "core team", "hello", "topic"),
|
||||
client_gravatar=False,
|
||||
num_events=3,
|
||||
)
|
||||
verify_events_generated_and_reset_visibility_policy(events, "core team", "topic")
|
||||
|
||||
# If current_visibility_policy is already set to the value the policies would set.
|
||||
do_set_user_topic_visibility_policy(
|
||||
hamlet,
|
||||
get_stream("core team", hamlet.realm),
|
||||
"new Topic",
|
||||
visibility_policy=UserTopic.VisibilityPolicy.UNMUTED,
|
||||
)
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# 1 event for the message sent
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(hamlet, "core team", "hello", "new Topic"),
|
||||
client_gravatar=False,
|
||||
num_events=1,
|
||||
)
|
||||
|
||||
do_change_user_setting(
|
||||
user_profile=hamlet,
|
||||
setting_name="automatically_unmute_topics_in_muted_streams_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
# Only one message event is generated
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(hamlet, "core team", "hello"),
|
||||
client_gravatar=True,
|
||||
)
|
||||
# event-type: message
|
||||
check_message("events[0]", events[0])
|
||||
assert isinstance(events[0]["message"]["avatar_url"], str)
|
||||
|
||||
|
@ -551,7 +735,7 @@ class NormalActionsTest(BaseAction):
|
|||
)
|
||||
|
||||
events = self.verify_action(
|
||||
lambda: self.send_stream_message(user_profile, "Verona", "hello"),
|
||||
lambda: self.send_stream_message(hamlet, "core team", "hello"),
|
||||
client_gravatar=True,
|
||||
)
|
||||
check_message("events[0]", events[0])
|
||||
|
@ -560,13 +744,9 @@ class NormalActionsTest(BaseAction):
|
|||
# Here we add coverage for the case where 'apply_unread_message_event'
|
||||
# should be called and unread messages in unmuted or followed topic in
|
||||
# muted stream is treated as unmuted stream message, thus added to 'unmuted_stream_msgs'.
|
||||
stream = get_stream("Verona", user_profile.realm)
|
||||
sub = get_subscription(stream.name, user_profile)
|
||||
do_change_subscription_property(
|
||||
user_profile, sub, stream, "is_muted", True, acting_user=None
|
||||
)
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
do_set_user_topic_visibility_policy(
|
||||
user_profile,
|
||||
hamlet,
|
||||
stream,
|
||||
"test",
|
||||
visibility_policy=UserTopic.VisibilityPolicy.UNMUTED,
|
||||
|
@ -2063,6 +2243,8 @@ class NormalActionsTest(BaseAction):
|
|||
"desktop_icon_count_display",
|
||||
"presence_enabled",
|
||||
"realm_name_in_email_notifications_policy",
|
||||
"automatically_follow_topics_policy",
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
]:
|
||||
# These settings are tested in their own tests.
|
||||
continue
|
||||
|
@ -2201,6 +2383,38 @@ class NormalActionsTest(BaseAction):
|
|||
check_user_settings_update("events[0]", events[0])
|
||||
check_update_global_notifications("events[1]", events[1], 2)
|
||||
|
||||
def test_change_automatically_follow_topics_policy(self) -> None:
|
||||
notification_setting = "automatically_follow_topics_policy"
|
||||
|
||||
for setting_value in UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES:
|
||||
events = self.verify_action(
|
||||
partial(
|
||||
do_change_user_setting,
|
||||
self.user_profile,
|
||||
notification_setting,
|
||||
setting_value,
|
||||
acting_user=self.user_profile,
|
||||
),
|
||||
num_events=1,
|
||||
)
|
||||
check_user_settings_update("events[0]", events[0])
|
||||
|
||||
def test_change_automatically_unmute_topics_in_muted_streams_policy(self) -> None:
|
||||
notification_setting = "automatically_unmute_topics_in_muted_streams_policy"
|
||||
|
||||
for setting_value in UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES:
|
||||
events = self.verify_action(
|
||||
partial(
|
||||
do_change_user_setting,
|
||||
self.user_profile,
|
||||
notification_setting,
|
||||
setting_value,
|
||||
acting_user=self.user_profile,
|
||||
),
|
||||
num_events=1,
|
||||
)
|
||||
check_user_settings_update("events[0]", events[0])
|
||||
|
||||
def test_realm_update_org_type(self) -> None:
|
||||
realm = self.user_profile.realm
|
||||
|
||||
|
@ -3129,6 +3343,8 @@ class RealmPropertyActionTest(BaseAction):
|
|||
email_notifications_batching_period_seconds=[120, 300],
|
||||
email_address_visibility=UserProfile.EMAIL_ADDRESS_VISIBILITY_TYPES,
|
||||
realm_name_in_email_notifications_policy=UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES,
|
||||
automatically_follow_topics_policy=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES,
|
||||
automatically_unmute_topics_in_muted_streams_policy=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES,
|
||||
)
|
||||
|
||||
vals = test_values.get(name)
|
||||
|
|
|
@ -1454,12 +1454,24 @@ class StreamMessagesTest(ZulipTestCase):
|
|||
topic_name = "foo"
|
||||
content = "whatever"
|
||||
|
||||
# Note: We don't need to assert the db query count for each possible
|
||||
# combination of 'automatically_follow_topics_policy' and 'automatically_unmute_topics_in_muted_streams_policy',
|
||||
# as the query count depends only on the actions, i.e., 'ON_INITIATION',
|
||||
# 'ON_PARTICIPATION', and 'NEVER', and is independent of the final visibility_policy set.
|
||||
# Asserting query count using one of the above-mentioned settings fulfils our purpose.
|
||||
|
||||
# To get accurate count of the queries, we should make sure that
|
||||
# caches don't come into play. If we count queries while caches are
|
||||
# filled, we will get a lower count. Caches are not supposed to be
|
||||
# persistent, so our test can also fail if cache is invalidated
|
||||
# during the course of the unit test.
|
||||
flush_per_request_caches()
|
||||
do_change_user_setting(
|
||||
user_profile=sender,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
with self.assert_database_query_count(13):
|
||||
check_send_stream_message(
|
||||
sender=sender,
|
||||
|
@ -1469,6 +1481,57 @@ class StreamMessagesTest(ZulipTestCase):
|
|||
body=content,
|
||||
)
|
||||
|
||||
do_change_user_setting(
|
||||
user_profile=sender,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# There will be an increase in the query count of 5 while sending
|
||||
# the first message to a topic.
|
||||
# 5 queries: 1 to check if it is the first message in the topic +
|
||||
# 1 to check if the topic is already followed + 3 to follow the topic.
|
||||
flush_per_request_caches()
|
||||
with self.assert_database_query_count(18):
|
||||
check_send_stream_message(
|
||||
sender=sender,
|
||||
client=sending_client,
|
||||
stream_name=stream_name,
|
||||
topic="new topic",
|
||||
body=content,
|
||||
)
|
||||
|
||||
do_change_user_setting(
|
||||
user_profile=sender,
|
||||
setting_name="automatically_follow_topics_policy",
|
||||
setting_value=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(self.example_user("iago"), stream_name, "Hello", "topic 2")
|
||||
# There will be an increase in the query count of 4 while sending
|
||||
# a message to a topic with visibility policy other than FOLLOWED.
|
||||
# 1 to check if the topic is already followed + 3 queries to follow the topic.
|
||||
flush_per_request_caches()
|
||||
with self.assert_database_query_count(17):
|
||||
check_send_stream_message(
|
||||
sender=sender,
|
||||
client=sending_client,
|
||||
stream_name=stream_name,
|
||||
topic="topic 2",
|
||||
body=content,
|
||||
)
|
||||
# If the topic is already FOLLOWED, there will be an increase in the query
|
||||
# count of 1 to check if the topic is already followed.
|
||||
flush_per_request_caches()
|
||||
with self.assert_database_query_count(14):
|
||||
check_send_stream_message(
|
||||
sender=sender,
|
||||
client=sending_client,
|
||||
stream_name=stream_name,
|
||||
topic="topic 2",
|
||||
body=content,
|
||||
)
|
||||
|
||||
def test_stream_message_dict(self) -> None:
|
||||
user_profile = self.example_user("iago")
|
||||
self.subscribe(user_profile, "Denmark")
|
||||
|
|
|
@ -1345,6 +1345,8 @@ class RealmAPITest(ZulipTestCase):
|
|||
email_notifications_batching_period_seconds=[120, 300],
|
||||
email_address_visibility=UserProfile.EMAIL_ADDRESS_VISIBILITY_TYPES,
|
||||
realm_name_in_email_notifications_policy=UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES,
|
||||
automatically_follow_topics_policy=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES,
|
||||
automatically_unmute_topics_in_muted_streams_policy=UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES,
|
||||
)
|
||||
|
||||
vals = test_values.get(name)
|
||||
|
|
|
@ -362,6 +362,8 @@ class ChangeSettingsTest(ZulipTestCase):
|
|||
desktop_icon_count_display=2,
|
||||
email_address_visibility=3,
|
||||
realm_name_in_email_notifications_policy=2,
|
||||
automatically_follow_topics_policy=1,
|
||||
automatically_unmute_topics_in_muted_streams_policy=1,
|
||||
)
|
||||
|
||||
self.login("hamlet")
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import Any, Dict, List
|
||||
|
||||
import orjson
|
||||
import time_machine
|
||||
from django.utils.timezone import now as timezone_now
|
||||
|
||||
from zerver.actions.reactions import check_add_reaction
|
||||
from zerver.actions.user_settings import do_change_user_setting
|
||||
from zerver.actions.user_topics import do_set_user_topic_visibility_policy
|
||||
from zerver.lib.stream_topic import StreamTopicTarget
|
||||
from zerver.lib.test_classes import ZulipTestCase
|
||||
from zerver.lib.test_helpers import get_subscription
|
||||
from zerver.lib.user_topics import get_topic_mutes, topic_has_visibility_policy
|
||||
from zerver.models import UserProfile, UserTopic, get_stream
|
||||
|
||||
|
@ -638,3 +642,895 @@ class UnmutedTopicsTests(ZulipTestCase):
|
|||
|
||||
result = self.api_post(user, url, data)
|
||||
self.assert_json_error(result, "Invalid stream ID")
|
||||
|
||||
|
||||
class AutomaticallyFollowTopicsTests(ZulipTestCase):
|
||||
def test_automatically_follow_topic_on_initiation(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
cordelia = self.example_user("cordelia")
|
||||
iago = self.example_user("iago")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# For hamlet & cordelia,
|
||||
# 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION'.
|
||||
for user in [hamlet, cordelia]:
|
||||
do_change_user_setting(
|
||||
user,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# Hamlet starts a topic. DO automatically follow the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
# Cordelia sends a message to the topic which hamlet started. DON'T automatically follow the topic.
|
||||
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
# Iago has 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER'.
|
||||
# DON'T automatically follow the topic, even if he starts the topic.
|
||||
do_change_user_setting(
|
||||
iago,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(iago, stream_name=stream.name, topic_name="New Topic")
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name="New Topic",
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# When a user sends the first message to a topic with protected history,
|
||||
# the user starts that topic from their perspective. So, the user
|
||||
# should follow the topic if 'automatically_follow_topics_policy' is set
|
||||
# to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION', even if the message
|
||||
# is not the first message in the topic.
|
||||
private_stream = self.make_stream(stream_name="private stream", invite_only=True)
|
||||
self.subscribe(iago, private_stream.name)
|
||||
self.send_stream_message(iago, private_stream.name)
|
||||
|
||||
# Hamlet should automatically follow the topic, even if it already has messages.
|
||||
self.subscribe(hamlet, private_stream.name)
|
||||
self.send_stream_message(hamlet, private_stream.name)
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=private_stream.id,
|
||||
topic_name="test",
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_follow_topic_on_send(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
self.send_stream_message(aaron, stream.name, "hello", topic_name)
|
||||
|
||||
# For hamlet, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
||||
do_change_user_setting(
|
||||
hamlet,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet sends a message. DO automatically follow the topic.
|
||||
# Aaron sends a message. DON'T automatically follow the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_follow_topic_on_participation_send_message(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
# For hamlet, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
hamlet,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet sends a message. DO automatically follow the topic.
|
||||
# Aaron sends a message. DON'T automatically follow the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_follow_topic_on_participation_add_reaction(self) -> None:
|
||||
cordelia = self.example_user("cordelia")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
# For cordelia, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
cordelia,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
message_id = self.send_stream_message(
|
||||
hamlet, stream_name=stream.name, topic_name=topic_name
|
||||
)
|
||||
# Cordelia reacts to a message. DO automatically follow the topic.
|
||||
# Aaron reacts to a message. DON'T automatically follow the topic.
|
||||
check_add_reaction(
|
||||
user_profile=cordelia,
|
||||
message_id=message_id,
|
||||
emoji_name="smile",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
check_add_reaction(
|
||||
user_profile=aaron,
|
||||
message_id=message_id,
|
||||
emoji_name="smile",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {cordelia.id})
|
||||
|
||||
# We don't decrease visibility policy
|
||||
sub = get_subscription(stream.name, cordelia)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
do_change_user_setting(
|
||||
cordelia,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
do_change_user_setting(
|
||||
cordelia,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
check_add_reaction(
|
||||
user_profile=cordelia,
|
||||
message_id=message_id,
|
||||
emoji_name="plus",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {cordelia.id})
|
||||
|
||||
# increase visibility policy
|
||||
do_set_user_topic_visibility_policy(
|
||||
cordelia,
|
||||
stream,
|
||||
topic_name,
|
||||
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
||||
)
|
||||
check_add_reaction(
|
||||
user_profile=cordelia,
|
||||
message_id=message_id,
|
||||
emoji_name="heart",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {cordelia.id})
|
||||
|
||||
# Add test coverage for 'should_change_visibility_policy' when
|
||||
# new_visibility_policy == current_visibility_policy
|
||||
check_add_reaction(
|
||||
user_profile=cordelia,
|
||||
message_id=message_id,
|
||||
emoji_name="tada",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {cordelia.id})
|
||||
|
||||
def test_automatically_follow_topic_on_participation_participate_in_poll(self) -> None:
|
||||
iago = self.example_user("iago")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
# For iago, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
iago,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet creates a poll.
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=orjson.dumps(stream.name).decode(),
|
||||
topic=topic_name,
|
||||
content="/poll Preference?\n\nyes\nno",
|
||||
)
|
||||
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
# Iago participates in the poll. DO automatically follow the topic.
|
||||
# Aaron participates in the poll. DON'T automatically follow the topic.
|
||||
message = self.get_last_message()
|
||||
|
||||
def participate_in_poll(user: UserProfile, data: Dict[str, object]) -> None:
|
||||
content = orjson.dumps(data).decode()
|
||||
payload = dict(
|
||||
message_id=message.id,
|
||||
msg_type="widget",
|
||||
content=content,
|
||||
)
|
||||
result = self.api_post(user, "/api/v1/submessage", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
participate_in_poll(iago, dict(type="vote", key="1,1", vote=1))
|
||||
participate_in_poll(aaron, dict(type="new_option", idx=7, option="maybe"))
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {iago.id})
|
||||
|
||||
def test_automatically_follow_topic_on_participation_edit_todo_list(self) -> None:
|
||||
othello = self.example_user("othello")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
# For othello, 'automatically_follow_topics_policy' set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
othello,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_follow_topics_policy' NOT set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet creates a todo list.
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=orjson.dumps(stream.name).decode(),
|
||||
topic=topic_name,
|
||||
content="/todo",
|
||||
)
|
||||
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
# Othello edits the todo list. DO automatically follow the topic.
|
||||
# Aaron edits the todo list. DON'T automatically follow the topic.
|
||||
message = self.get_last_message()
|
||||
|
||||
def edit_todo_list(user: UserProfile, data: Dict[str, object]) -> None:
|
||||
content = orjson.dumps(data).decode()
|
||||
payload = dict(
|
||||
message_id=message.id,
|
||||
msg_type="widget",
|
||||
content=content,
|
||||
)
|
||||
result = self.api_post(user, "/api/v1/submessage", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
edit_todo_list(othello, dict(type="new_task", key=7, task="eat", desc="", completed=False))
|
||||
edit_todo_list(aaron, dict(type="strike", key="5,9"))
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.FOLLOWED
|
||||
)
|
||||
self.assertEqual(user_ids, {othello.id})
|
||||
|
||||
|
||||
class AutomaticallyUnmuteTopicsTests(ZulipTestCase):
|
||||
def test_automatically_unmute_topic_on_initiation(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
cordelia = self.example_user("cordelia")
|
||||
iago = self.example_user("iago")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [hamlet, cordelia, iago]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# For hamlet & cordelia, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION'.
|
||||
for user in [hamlet, cordelia]:
|
||||
do_change_user_setting(
|
||||
user,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# Hamlet starts a topic. DO automatically unmute the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
# Cordelia sends a message to the topic which hamlet started. DON'T automatically unmute the topic.
|
||||
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
# Iago has 'automatically_unmute_topics_in_muted_streams_policy' set to
|
||||
# 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER'.
|
||||
# DON'T automatically unmute the topic, even if he starts the topic.
|
||||
do_change_user_setting(
|
||||
iago,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(iago, stream_name=stream.name, topic_name="New Topic")
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name="New Topic",
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# When a user sends the first message to a topic with protected history,
|
||||
# the user starts that topic from their perspective. So, the user
|
||||
# should unmute the topic if 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# is set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION', even if
|
||||
# the message is not the first message in the topic.
|
||||
private_stream = self.make_stream(stream_name="private stream", invite_only=True)
|
||||
self.subscribe(iago, private_stream.name)
|
||||
self.send_stream_message(iago, private_stream.name)
|
||||
|
||||
# Hamlet should automatically unmute the topic, even if it already has messages.
|
||||
self.subscribe(hamlet, private_stream.name)
|
||||
sub = get_subscription(private_stream.name, hamlet)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
self.send_stream_message(hamlet, private_stream.name)
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=private_stream.id,
|
||||
topic_name="test",
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_unmute_topic_on_send(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
self.send_stream_message(aaron, stream.name, "hello", topic_name)
|
||||
for user in [hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
# For hamlet, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
||||
do_change_user_setting(
|
||||
hamlet,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_SEND'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet sends a message. DO automatically unmute the topic.
|
||||
# Aaron sends a message. DON'T automatically unmute the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_unmute_topic_on_participation_send_message(self) -> None:
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", hamlet.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
# For hamlet, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
hamlet,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet sends a message. DO automatically unmute the topic.
|
||||
# Aaron sends a message. DON'T automatically unmute the topic.
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_unmute_topic_on_participation_add_reaction(self) -> None:
|
||||
cordelia = self.example_user("cordelia")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [cordelia, hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
# For cordelia, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
cordelia,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
message_id = self.send_stream_message(
|
||||
hamlet, stream_name=stream.name, topic_name=topic_name
|
||||
)
|
||||
# Cordelia reacts to a message. DO automatically unmute the topic.
|
||||
# Aaron reacts to a message. DON'T automatically unmute the topic.
|
||||
check_add_reaction(
|
||||
user_profile=cordelia,
|
||||
message_id=message_id,
|
||||
emoji_name="smile",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
check_add_reaction(
|
||||
user_profile=aaron,
|
||||
message_id=message_id,
|
||||
emoji_name="smile",
|
||||
emoji_code=None,
|
||||
reaction_type=None,
|
||||
)
|
||||
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {cordelia.id})
|
||||
|
||||
def test_automatically_unmute_topic_on_participation_participate_in_poll(self) -> None:
|
||||
iago = self.example_user("iago")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [iago, hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
# For iago, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
iago,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet creates a poll.
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=orjson.dumps(stream.name).decode(),
|
||||
topic=topic_name,
|
||||
content="/poll Preference?\n\nyes\nno",
|
||||
)
|
||||
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
# Iago participates in the poll. DO automatically unmute the topic.
|
||||
# Aaron participates in the poll. DON'T automatically unmute the topic.
|
||||
message = self.get_last_message()
|
||||
|
||||
def participate_in_poll(user: UserProfile, data: Dict[str, object]) -> None:
|
||||
content = orjson.dumps(data).decode()
|
||||
payload = dict(
|
||||
message_id=message.id,
|
||||
msg_type="widget",
|
||||
content=content,
|
||||
)
|
||||
result = self.api_post(user, "/api/v1/submessage", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
participate_in_poll(iago, dict(type="vote", key="1,1", vote=1))
|
||||
participate_in_poll(aaron, dict(type="new_option", idx=7, option="maybe"))
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {iago.id})
|
||||
|
||||
def test_automatically_unmute_topic_on_participation_edit_todo_list(self) -> None:
|
||||
othello = self.example_user("othello")
|
||||
hamlet = self.example_user("hamlet")
|
||||
aaron = self.example_user("aaron")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [othello, hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
# For othello, 'automatically_unmute_topics_in_muted_streams_policy'
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
othello,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
# For aaron, 'automatically_unmute_topics_in_muted_streams_policy' NOT
|
||||
# set to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION'.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# Hamlet creates a todo list.
|
||||
payload = dict(
|
||||
type="stream",
|
||||
to=orjson.dumps(stream.name).decode(),
|
||||
topic=topic_name,
|
||||
content="/todo",
|
||||
)
|
||||
result = self.api_post(hamlet, "/api/v1/messages", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
# Othello edits the todo list. DO automatically unmute the topic.
|
||||
# Aaron edits the todo list. DON'T automatically unmute the topic.
|
||||
message = self.get_last_message()
|
||||
|
||||
def edit_todo_list(user: UserProfile, data: Dict[str, object]) -> None:
|
||||
content = orjson.dumps(data).decode()
|
||||
payload = dict(
|
||||
message_id=message.id,
|
||||
msg_type="widget",
|
||||
content=content,
|
||||
)
|
||||
result = self.api_post(user, "/api/v1/submessage", payload)
|
||||
self.assert_json_success(result)
|
||||
|
||||
edit_todo_list(othello, dict(type="new_task", key=7, task="eat", desc="", completed=False))
|
||||
edit_todo_list(aaron, dict(type="strike", key="5,9"))
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {othello.id})
|
||||
|
||||
def test_only_automatically_increase_visibility_policy(self) -> None:
|
||||
aaron = self.example_user("aaron")
|
||||
hamlet = self.example_user("hamlet")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
for user in [hamlet, aaron]:
|
||||
sub = get_subscription(stream.name, user)
|
||||
sub.is_muted = True
|
||||
sub.save()
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
# If a topic is already FOLLOWED, we don't change the state to UNMUTED as the
|
||||
# intent of these "automatically follow or unmute" policies is that they can only
|
||||
# increase the user's visibility policy for the topic.
|
||||
do_set_user_topic_visibility_policy(
|
||||
aaron,
|
||||
stream,
|
||||
topic_name,
|
||||
visibility_policy=UserTopic.VisibilityPolicy.FOLLOWED,
|
||||
)
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
# increase visibility from MUTED to UNMUTED
|
||||
topic_name = "new Topic"
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
do_set_user_topic_visibility_policy(
|
||||
hamlet,
|
||||
stream,
|
||||
topic_name,
|
||||
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
||||
)
|
||||
do_change_user_setting(
|
||||
hamlet,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(hamlet, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, {hamlet.id})
|
||||
|
||||
def test_automatically_unmute_policy_unmuted_stream(self) -> None:
|
||||
aaron = self.example_user("aaron")
|
||||
cordelia = self.example_user("cordelia")
|
||||
stream = get_stream("Verona", aaron.realm)
|
||||
topic_name = "teST topic"
|
||||
|
||||
stream_topic_target = StreamTopicTarget(
|
||||
stream_id=stream.id,
|
||||
topic_name=topic_name,
|
||||
)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
# The 'automatically_unmute_topics_in_muted_streams_policy' setting has
|
||||
# NO effect in unmuted streams.
|
||||
do_change_user_setting(
|
||||
aaron,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_INITIATION,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(aaron, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
||||
do_set_user_topic_visibility_policy(
|
||||
cordelia,
|
||||
stream,
|
||||
topic_name,
|
||||
visibility_policy=UserTopic.VisibilityPolicy.MUTED,
|
||||
)
|
||||
do_change_user_setting(
|
||||
cordelia,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_ON_PARTICIPATION,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_stream_message(cordelia, stream_name=stream.name, topic_name=topic_name)
|
||||
user_ids = stream_topic_target.user_ids_with_visibility_policy(
|
||||
UserTopic.VisibilityPolicy.UNMUTED
|
||||
)
|
||||
self.assertEqual(user_ids, set())
|
||||
|
|
|
@ -1910,6 +1910,7 @@ class RecipientInfoTest(ZulipTestCase):
|
|||
service_bot_tuples=[],
|
||||
all_bot_user_ids=set(),
|
||||
topic_participant_user_ids=set(),
|
||||
sender_muted_stream=False,
|
||||
)
|
||||
|
||||
self.assertEqual(info, expected_info)
|
||||
|
|
|
@ -556,6 +556,14 @@ def update_realm_user_settings_defaults(
|
|||
json_validator=check_int_in(UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_follow_topics_policy: Optional[int] = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_unmute_topics_in_muted_streams_policy: Optional[int] = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
presence_enabled: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||
enter_sends: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||
enable_drafts_synchronization: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||
|
|
|
@ -258,6 +258,14 @@ def json_change_settings(
|
|||
json_validator=check_int_in(UserProfile.REALM_NAME_IN_EMAIL_NOTIFICATIONS_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_follow_topics_policy: Optional[int] = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
automatically_unmute_topics_in_muted_streams_policy: Optional[int] = REQ(
|
||||
json_validator=check_int_in(UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_CHOICES),
|
||||
default=None,
|
||||
),
|
||||
presence_enabled: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||
enter_sends: Optional[bool] = REQ(json_validator=check_bool, default=None),
|
||||
send_private_typing_notifications: Optional[bool] = REQ(
|
||||
|
|
|
@ -30,6 +30,7 @@ from zerver.actions.realm_linkifiers import do_add_linkifier
|
|||
from zerver.actions.scheduled_messages import check_schedule_message
|
||||
from zerver.actions.streams import bulk_add_subscriptions
|
||||
from zerver.actions.user_groups import create_user_group_in_database
|
||||
from zerver.actions.user_settings import do_change_user_setting
|
||||
from zerver.actions.users import do_change_user_role
|
||||
from zerver.lib.bulk_create import bulk_create_streams
|
||||
from zerver.lib.generate_test_data import create_test_data, generate_topics
|
||||
|
@ -825,6 +826,29 @@ class Command(BaseCommand):
|
|||
UserProfile.objects.filter(is_bot=False, realm=zulip_realm)
|
||||
)
|
||||
|
||||
# As we plan to change the default values for 'automatically_follow_topics_policy' and
|
||||
# 'automatically_unmute_topics_in_muted_streams_policy' in the future, it will lead to
|
||||
# skewing a lot of our tests, which now need to take into account extra events and database queries.
|
||||
#
|
||||
# We explicitly set the values for both settings to 'AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER'
|
||||
# to make the tests independent of the default values.
|
||||
#
|
||||
# We have separate tests to verify events generated, database query counts,
|
||||
# and other important details related to the above-mentioned settings.
|
||||
for user in user_profiles:
|
||||
do_change_user_setting(
|
||||
user,
|
||||
"automatically_follow_topics_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
do_change_user_setting(
|
||||
user,
|
||||
"automatically_unmute_topics_in_muted_streams_policy",
|
||||
UserProfile.AUTOMATICALLY_CHANGE_VISIBILITY_POLICY_NEVER,
|
||||
acting_user=None,
|
||||
)
|
||||
|
||||
# Create a test realm emoji.
|
||||
IMAGE_FILE_PATH = static_path("images/test-images/checkbox.png")
|
||||
with open(IMAGE_FILE_PATH, "rb") as fp:
|
||||
|
|
Loading…
Reference in New Issue