stream: Add do_change_can_remove_subscribers_group and field to objects.

This commit adds do_change_can_remove_subscriber_group function for
changing can_remove_subscribers_group field of a stream. We also add
can_remove_subscribers_group_id field to stream and subscription
objects.

This function will be helpful for writing tests in next commit.
We would add API and UI support to change this setting in further
commits.
This commit is contained in:
Sahil Batra 2022-06-27 22:09:33 +05:30 committed by Tim Abbott
parent 86c2f6881e
commit b9248c75f4
10 changed files with 102 additions and 1 deletions

View File

@ -50,6 +50,7 @@ exports.test_streams = {
is_web_public: false,
message_retention_days: null,
stream_post_policy: 1,
can_remove_subscribers_group_id: 2,
},
test: {
name: "test",
@ -64,6 +65,7 @@ exports.test_streams = {
is_announcement_only: false,
message_retention_days: null,
stream_post_policy: 1,
can_remove_subscribers_group_id: 2,
},
};

View File

@ -20,6 +20,13 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 6.0
**Feature level 142**
* [`GET users/me/subscriptions`](/api/get-subscriptions), [`GET
/streams`](/api/get-streams), [`POST /register`](/api/register-queue),
[`GET /events`](/api/get-events): Added `can_remove_subscribers_group_id`
field to Stream and Subscription objects.
**Feature level 141**
* [`POST /register`](/api/register-queue), [`PATCH

View File

@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.4.3"
# Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md, as well as
# "**Changes**" entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 141
API_FEATURE_LEVEL = 142
# 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

View File

@ -68,6 +68,7 @@ from zerver.models import (
Recipient,
Stream,
Subscription,
UserGroup,
UserProfile,
active_non_guest_user_ids,
get_system_bot,
@ -289,6 +290,7 @@ def send_subscription_add_events(
stream_weekly_traffic=stream_info.stream_weekly_traffic,
subscribers=stream_info.subscribers,
# Fields from Stream.API_FIELDS
can_remove_subscribers_group_id=stream_dict["can_remove_subscribers_group_id"],
date_created=stream_dict["date_created"],
description=stream_dict["description"],
first_message_id=stream_dict["first_message_id"],
@ -1317,3 +1319,39 @@ def do_change_stream_message_retention_days(
old_value=old_message_retention_days_value,
new_value=message_retention_days,
)
def do_change_can_remove_subscribers_group(
stream: Stream, user_group: UserGroup, *, acting_user: Optional[UserProfile] = None
) -> None:
old_user_group = stream.can_remove_subscribers_group
old_user_group_id = None
if old_user_group is not None:
old_user_group_id = old_user_group.id
stream.can_remove_subscribers_group = user_group
stream.save()
RealmAuditLog.objects.create(
realm=stream.realm,
acting_user=acting_user,
modified_stream=stream,
event_type=RealmAuditLog.STREAM_CAN_REMOVE_SUBSCRIBERS_GROUP_CHANGED,
event_time=timezone_now(),
extra_data=orjson.dumps(
{
RealmAuditLog.OLD_VALUE: old_user_group_id,
RealmAuditLog.NEW_VALUE: user_group.id,
}
).decode(),
)
event = dict(
op="update",
type="stream",
property="can_remove_subscribers_group_id",
value=user_group.id,
stream_id=stream.id,
name=stream.name,
)
transaction.on_commit(
lambda: send_event(stream.realm, event, can_access_stream_user_ids(stream))
)

View File

@ -50,6 +50,7 @@ from zerver.models import Realm, RealmUserDefault, Stream, UserProfile
# These fields are used for "stream" events, and are included in the
# larger "subscription" events that also contain personal settings.
basic_stream_fields = [
("can_remove_subscribers_group_id", int),
("date_created", int),
("description", str),
("first_message_id", OptionalType(int)),
@ -1287,6 +1288,9 @@ def check_stream_update(
elif prop == "stream_post_policy":
assert extra_keys == set()
assert value in Stream.STREAM_POST_POLICY_TYPES
elif prop == "can_remove_subscribers_group_id":
assert extra_keys == set()
assert isinstance(value, int)
else:
raise AssertionError(f"Unknown property: {prop}")

View File

@ -40,6 +40,7 @@ def get_web_public_subs(realm: Realm) -> SubscriptionInfo:
subscribed = []
for stream in get_web_public_streams_queryset(realm):
# Add Stream fields.
can_remove_subscribers_group_id = stream.can_remove_subscribers_group_id
date_created = datetime_to_timestamp(stream.date_created)
description = stream.description
first_message_id = stream.first_message_id
@ -71,6 +72,7 @@ def get_web_public_subs(realm: Realm) -> SubscriptionInfo:
sub = SubscriptionStreamDict(
audible_notifications=audible_notifications,
can_remove_subscribers_group_id=can_remove_subscribers_group_id,
color=color,
date_created=date_created,
description=description,
@ -110,6 +112,7 @@ def build_stream_dict_for_sub(
recent_traffic: Dict[int, int],
) -> SubscriptionStreamDict:
# Handle Stream.API_FIELDS
can_remove_subscribers_group_id = raw_stream_dict["can_remove_subscribers_group_id"]
date_created = datetime_to_timestamp(raw_stream_dict["date_created"])
description = raw_stream_dict["description"]
first_message_id = raw_stream_dict["first_message_id"]
@ -153,6 +156,7 @@ def build_stream_dict_for_sub(
# Our caller may add a subscribers field.
return SubscriptionStreamDict(
audible_notifications=audible_notifications,
can_remove_subscribers_group_id=can_remove_subscribers_group_id,
color=color,
date_created=date_created,
description=description,
@ -182,6 +186,7 @@ def build_stream_dict_for_never_sub(
raw_stream_dict: RawStreamDict,
recent_traffic: Dict[int, int],
) -> NeverSubscribedStreamDict:
can_remove_subscribers_group_id = raw_stream_dict["can_remove_subscribers_group_id"]
date_created = datetime_to_timestamp(raw_stream_dict["date_created"])
description = raw_stream_dict["description"]
first_message_id = raw_stream_dict["first_message_id"]
@ -202,6 +207,7 @@ def build_stream_dict_for_never_sub(
# Our caller may add a subscribers field.
return NeverSubscribedStreamDict(
can_remove_subscribers_group_id=can_remove_subscribers_group_id,
date_created=date_created,
description=description,
first_message_id=first_message_id,

View File

@ -177,6 +177,7 @@ class RawStreamDict(TypedDict):
are needed to encode the stream for the API.
"""
can_remove_subscribers_group_id: int
date_created: datetime.datetime
description: str
email_token: str
@ -216,6 +217,7 @@ class SubscriptionStreamDict(TypedDict):
"""
audible_notifications: Optional[bool]
can_remove_subscribers_group_id: int
color: str
date_created: int
description: str
@ -242,6 +244,7 @@ class SubscriptionStreamDict(TypedDict):
class NeverSubscribedStreamDict(TypedDict):
can_remove_subscribers_group_id: int
date_created: int
description: str
first_message_id: Optional[int]
@ -264,6 +267,7 @@ class APIStreamDict(TypedDict):
with few exceptions and possible additional fields.
"""
can_remove_subscribers_group_id: int
date_created: int
description: str
first_message_id: Optional[int]

View File

@ -2552,6 +2552,7 @@ class Stream(models.Model):
"name",
"rendered_description",
"stream_post_policy",
"can_remove_subscribers_group_id",
]
@staticmethod
@ -2561,6 +2562,7 @@ class Stream(models.Model):
def to_dict(self) -> APIStreamDict:
return APIStreamDict(
can_remove_subscribers_group_id=self.can_remove_subscribers_group_id,
date_created=datetime_to_timestamp(self.date_created),
description=self.description,
first_message_id=self.first_message_id,
@ -4364,6 +4366,7 @@ class AbstractRealmAuditLog(models.Model):
STREAM_REACTIVATED = 604
STREAM_MESSAGE_RETENTION_DAYS_CHANGED = 605
STREAM_PROPERTY_CHANGED = 607
STREAM_CAN_REMOVE_SUBSCRIBERS_GROUP_CHANGED = 608
# The following values are only for RemoteZulipServerAuditLog
# Values should be exactly 10000 greater than the corresponding

View File

@ -644,6 +644,7 @@ paths:
"in_home_view": true,
"email_address": "test_stream.af64447e9e39374841063747ade8e6b0.show-sender@testserver",
"stream_weekly_traffic": null,
"can_remove_subscribers_group_id": 2,
"subscribers": [10],
},
],
@ -1143,6 +1144,7 @@ paths:
"first_message_id": null,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
],
"id": 0,
@ -1188,6 +1190,7 @@ paths:
"first_message_id": null,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
],
"id": 0,
@ -1745,6 +1748,7 @@ paths:
"first_message_id": 1,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
{
"name": "Denmark",
@ -1758,6 +1762,7 @@ paths:
"first_message_id": 4,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
{
"name": "Verona",
@ -1771,6 +1776,7 @@ paths:
"first_message_id": 6,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
],
},
@ -1815,6 +1821,7 @@ paths:
"first_message_id": 1,
"message_retention_days": null,
"is_announcement_only": false,
"can_remove_subscribers_group_id": 2,
},
],
"id": 0,
@ -10089,6 +10096,7 @@ paths:
first_message_id:
nullable: true
is_announcement_only: {}
can_remove_subscribers_group_id: {}
stream_weekly_traffic:
type: integer
nullable: true
@ -13879,6 +13887,7 @@ paths:
first_message_id:
nullable: true
is_announcement_only: {}
can_remove_subscribers_group_id: {}
is_default:
type: boolean
description: |
@ -13989,6 +13998,7 @@ paths:
"rendered_description": "<p>A Scandinavian country</p>",
"stream_id": 7,
"stream_post_policy": 1,
"can_remove_subscribers_group_id": 2,
},
}
"400":
@ -15075,6 +15085,7 @@ components:
first_message_id:
nullable: true
is_announcement_only: {}
can_remove_subscribers_group_id: {}
BasicStreamBase:
type: object
description: |
@ -15176,6 +15187,13 @@ components:
**Changes**: Deprecated in Zulip 3.0 (feature level 1). Clients
should use `stream_post_policy` instead.
can_remove_subscribers_group_id:
type: integer
description: |
ID of the user group whose members are allowed to unsubscribe others
from the stream.
**Changes**: New in Zulip 6.0 (feature level 142).
BasicBot:
allOf:
- $ref: "#/components/schemas/BasicBotBase"
@ -15792,6 +15810,13 @@ components:
Null means the stream was recently created and there is
insufficient data to estimate the average traffic.
can_remove_subscribers_group_id:
type: integer
description: |
ID of the user group whose members are allowed to unsubscribe others
from the stream.
**Changes**: New in Zulip 6.0 (feature level 142).
DefaultStreamGroup:
type: object
description: |

View File

@ -79,6 +79,7 @@ from zerver.actions.realm_settings import (
from zerver.actions.streams import (
bulk_add_subscriptions,
bulk_remove_subscriptions,
do_change_can_remove_subscribers_group,
do_change_stream_description,
do_change_stream_message_retention_days,
do_change_stream_permission,
@ -2885,6 +2886,17 @@ class SubscribeActionTest(BaseAction):
events = self.verify_action(action, include_subscribers=include_subscribers, num_events=2)
check_stream_update("events[0]", events[0])
moderators_group = UserGroup.objects.get(
name=UserGroup.MODERATORS_GROUP_NAME,
is_system_group=True,
realm=self.user_profile.realm,
)
action = lambda: do_change_can_remove_subscribers_group(
stream, moderators_group, acting_user=self.example_user("hamlet")
)
events = self.verify_action(action, include_subscribers=include_subscribers, num_events=1)
check_stream_update("events[0]", events[0])
# Subscribe to a totally new invite-only stream, so it's just Hamlet on it
stream = self.make_stream("private", self.user_profile.realm, invite_only=True)
stream.message_retention_days = 10