Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create "support" stream type with topic-specific permissions #19434 #29520

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
1030e2c
is_user_in_group: Use `user_id` parameter of `UserProfile`.
sanchi-t May 14, 2024
cbaef38
migrations: Add `stream_topic_access_group` field to `stream` model.
sanchi-t Apr 17, 2024
42fbefe
migrations: Set default value for `stream_topic_access_group`.
sanchi-t May 21, 2024
e1bddc7
migrations: Alter `stream_topic_access_group` field.
sanchi-t May 21, 2024
07376dd
streams: Add new field to stream model.
sanchi-t May 27, 2024
4e51c07
do_import_realm: Update function to support `stream_topic_access_group`.
sanchi-t May 27, 2024
18370ad
streams: Add a function to check whether a stream is a support stream.
sanchi-t May 21, 2024
f04fcc5
streams: Enable the creation of support streams.
sanchi-t Apr 18, 2024
073d048
message_send: Do not create `UserMessage` rows for restricted users.
sanchi-t Apr 19, 2024
5b97892
can_access_stream_history: Deny stream history access.
sanchi-t Apr 19, 2024
d61bba5
IncludeHistoryTest: Update test to include support streams.
sanchi-t May 14, 2024
6e32bd2
update_stream_backend: Include updating `stream_topic_access_group_id`.
sanchi-t May 11, 2024
957dece
stream: Add new fields to the stream and types.
sanchi-t Apr 19, 2024
f6e8a30
stream_create: Enable the creation of support streams.
sanchi-t May 14, 2024
428f92c
echo: Avoid locally echoing a message.
sanchi-t Apr 19, 2024
86aa2ed
stream_edit: Allow changing stream settings for support streams.
sanchi-t May 14, 2024
b06c7bc
stream_creation_form: Add UI for creating support type streams.
sanchi-t May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions analytics/management/commands/populate_analytics_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,15 @@ def handle(self, *args: Any, **options: Any) -> None:
administrators_user_group = NamedUserGroup.objects.get(
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
)
everyone_user_group = NamedUserGroup.objects.get(
name=SystemGroups.EVERYONE, realm=realm, is_system_group=True
)
stream = Stream.objects.create(
name="all",
realm=realm,
date_created=installation_time,
can_remove_subscribers_group=administrators_user_group,
stream_topic_access_group=everyone_user_group,
)
recipient = Recipient.objects.create(type_id=stream.id, type=Recipient.STREAM)
stream.recipient = recipient
Expand Down
6 changes: 6 additions & 0 deletions analytics/tests/test_counts.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ def setUp(self) -> None:
realm=self.default_realm,
is_system_group=True,
)
self.everyone_user_group = NamedUserGroup.objects.get(
name=SystemGroups.EVERYONE,
realm=self.default_realm,
is_system_group=True,
)

# used to generate unique names in self.create_*
self.name_counter = 100
Expand Down Expand Up @@ -153,6 +158,7 @@ def create_stream_with_recipient(self, **kwargs: Any) -> Tuple[Stream, Recipient
"realm": self.default_realm,
"date_created": self.TIME_LAST_HOUR,
"can_remove_subscribers_group": self.administrators_user_group,
"stream_topic_access_group": self.everyone_user_group,
}
for key, value in defaults.items():
kwargs[key] = kwargs.get(key, value)
Expand Down
10 changes: 10 additions & 0 deletions api_docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ format used by the Zulip server that they are interacting with.

## Changes in Zulip 9.0

**Feature level 263**

* [`GET /streams`](/api/get-streams),
[`POST /users/me/subscriptions`](/api/subscribe),
[`PATCH /streams/{stream_id}`](/api/update-stream),
[`GET /events`](/api/get-events): Stream objects now
include the `stream_topic_access_group` integer,
which specifies the ID of the user group that has
access to all the topics in a stream.

**Feature level 262**:

* [`GET /users/{user_id}/status`](/api/get-user-status): Added a new
Expand Down
11 changes: 11 additions & 0 deletions web/src/echo.js
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,17 @@ export function try_deliver_locally(message_request, insert_new_messages) {
return undefined;
}

if (message_request.type === "stream") {
const stream = stream_data.get_sub_by_id(message_request.stream_id);
if (
stream &&
stream_data.is_support_stream(stream) &&
!stream_data.can_access_topics_in_stream(stream)
) {
return undefined;
}
}

const local_id_float = local_message.get_next_id_float();

if (!local_id_float) {
Expand Down
8 changes: 8 additions & 0 deletions web/src/settings_components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ export let can_remove_subscribers_group_widget: DropdownWidget | null = null;
export let can_access_all_users_group_widget: DropdownWidget | null = null;
export let can_mention_group_widget: DropdownWidget | null = null;
export let new_group_can_mention_group_widget: DropdownWidget | null = null;
export let stream_topic_access_group_widget: DropdownWidget | null = null;

export function get_widget_for_dropdown_list_settings(
property_name: string,
Expand All @@ -514,6 +515,8 @@ export function get_widget_for_dropdown_list_settings(
return can_access_all_users_group_widget;
case "can_mention_group":
return can_mention_group_widget;
case "stream_topic_access_group":
return stream_topic_access_group_widget;
default:
blueslip.error("No dropdown list widget for property", {property_name});
return null;
Expand Down Expand Up @@ -556,6 +559,10 @@ export function set_new_group_can_mention_group_widget(widget: DropdownWidget):
new_group_can_mention_group_widget = widget;
}

export function set_stream_topic_access_group_widget(widget: DropdownWidget): void {
stream_topic_access_group_widget = widget;
}

export function set_dropdown_list_widget_setting_value(
property_name: string,
value: number | string,
Expand Down Expand Up @@ -858,6 +865,7 @@ export function check_property_changed(
case "realm_create_multiuse_invite_group":
case "can_mention_group":
case "realm_can_access_all_users_group":
case "stream_topic_access_group":
proposed_val = get_dropdown_list_widget_setting_value($elem);
break;
case "email_notifications_batching_period_seconds":
Expand Down
1 change: 1 addition & 0 deletions web/src/settings_org.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,7 @@ export function discard_property_element_changes(elem, for_realm_default_setting
case "realm_create_multiuse_invite_group":
case "realm_can_access_all_users_group":
case "can_mention_group":
case "stream_topic_access_group":
settings_components.set_dropdown_list_widget_setting_value(
property_name,
property_value,
Expand Down
8 changes: 8 additions & 0 deletions web/src/stream_create.js
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,13 @@ function create_stream() {
);
data.can_remove_subscribers_group = can_remove_subscribers_group_id;

const stream_topic_access_group_id = Number.parseInt(
stream_settings_components.new_stream_stream_topic_access_group_widget.value(),
10,
);

data.stream_topic_access_group = stream_topic_access_group_id;

loading.make_indicator($("#stream_creating_indicator"), {
text: $t({defaultMessage: "Creating channel..."}),
});
Expand Down Expand Up @@ -460,4 +467,5 @@ export function set_up_handlers() {
});

stream_settings_components.new_stream_can_remove_subscribers_group_widget.setup();
stream_settings_components.new_stream_stream_topic_access_group_widget.setup();
}
28 changes: 28 additions & 0 deletions web/src/stream_data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,13 @@ export function update_can_remove_subscribers_group_id(
sub.can_remove_subscribers_group = can_remove_subscribers_group_id;
}

export function update_stream_topic_access_group_id(
sub: StreamSubscription,
stream_topic_access_group_id: number,
): void {
sub.stream_topic_access_group = stream_topic_access_group_id;
}

export function receives_notifications(
stream_id: number,
notification_name: keyof StreamSpecificNotificationSettings,
Expand Down Expand Up @@ -513,6 +520,15 @@ export function can_toggle_subscription(sub: StreamSubscription): boolean {
);
}

export function is_support_stream(sub: StreamSubscription): boolean {
const stream_topic_access_group_id = sub.stream_topic_access_group;
const stream_topic_access_group = user_groups.get_user_group_from_id(
stream_topic_access_group_id,
);

return stream_topic_access_group.name !== "role:everyone";
}

export function can_access_stream_email(sub: StreamSubscription): boolean {
return (
(sub.subscribed || sub.is_web_public || (!current_user.is_guest && !sub.invite_only)) &&
Expand Down Expand Up @@ -630,6 +646,18 @@ export function can_post_messages_in_stream(stream: StreamSubscription): boolean
return true;
}

export function can_access_topics_in_stream(stream: StreamSubscription): boolean {
if (current_user.is_admin) {
return true;
}

const group_allowed_to_access_topics = stream.stream_topic_access_group;
return user_groups.is_user_in_group(
group_allowed_to_access_topics,
people.my_current_user_id(),
);
}

export function is_subscribed_by_name(stream_name: string): boolean {
const sub = get_sub(stream_name);
return sub ? sub.subscribed : false;
Expand Down
31 changes: 31 additions & 0 deletions web/src/stream_edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,37 @@ function setup_dropdown(sub, slim_sub) {
can_remove_subscribers_group_widget,
);
can_remove_subscribers_group_widget.setup();

const stream_topic_access_group_widget = new dropdown_widget.DropdownWidget({
widget_name: "stream_topic_access_group",
get_options: () =>
user_groups.get_realm_user_groups_for_dropdown_list_widget(
"stream_topic_access_group",
"stream",
),
item_click_callback(event, dropdown) {
dropdown.hide();
event.preventDefault();
event.stopPropagation();
stream_topic_access_group_widget.render();
settings_components.save_discard_widget_status_handler(
$("#stream_permission_settings"),
false,
slim_sub,
);
},
$events_container: $("#subscription_overlay .subscription_settings"),
tippy_props: {
placement: "bottom-start",
},
default_id: sub.stream_topic_access_group,
unique_id_type: dropdown_widget.DataTypes.NUMBER,
on_mount_callback(dropdown) {
$(dropdown.popper).css("min-width", "300px");
},
});
settings_components.set_stream_topic_access_group_widget(stream_topic_access_group_widget);
stream_topic_access_group_widget.setup();
}

export function show_settings_for(node) {
Expand Down
3 changes: 3 additions & 0 deletions web/src/stream_events.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ export function update_property(stream_id, property, value, other_values) {
update_stream_setting(sub, value, property);
stream_list.refresh_pinned_or_unpinned_stream(sub);
break;
case "stream_topic_access_group":
stream_settings_ui.update_stream_topic_access_group_id(sub, value);
break;
case "invite_only":
stream_settings_ui.update_stream_privacy(sub, {
invite_only: value,
Expand Down
26 changes: 26 additions & 0 deletions web/src/stream_settings_components.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export function get_active_data() {
}

export let new_stream_can_remove_subscribers_group_widget = null;
export let new_stream_stream_topic_access_group_widget = null;

export function dropdown_setup() {
new_stream_can_remove_subscribers_group_widget = new dropdown_widget.DropdownWidget({
Expand All @@ -90,6 +91,31 @@ export function dropdown_setup() {
default_id: user_groups.get_user_group_from_name("role:administrators").id,
unique_id_type: dropdown_widget.DataTypes.NUMBER,
});

new_stream_stream_topic_access_group_widget = new dropdown_widget.DropdownWidget({
widget_name: "new_stream_stream_topic_access_group",
get_options: () =>
user_groups.get_realm_user_groups_for_dropdown_list_widget(
"stream_topic_access_group",
"stream",
),
item_click_callback(event, dropdown) {
dropdown.hide();
event.preventDefault();
event.stopPropagation();
new_stream_stream_topic_access_group_widget.render();
},
$events_container: $("#subscription_overlay"),
tippy_props: {
placement: "bottom-start",
},
on_mount_callback(dropdown) {
$(dropdown.popper).css("min-width", "300px");
},
default_text: $t({defaultMessage: "No user groups"}),
default_id: user_groups.get_user_group_from_name("role:everyone").id,
unique_id_type: dropdown_widget.DataTypes.NUMBER,
});
}

/* For the given stream_row, remove the tick and replace by a spinner. */
Expand Down
6 changes: 6 additions & 0 deletions web/src/stream_settings_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,12 @@ export function update_can_remove_subscribers_group_id(sub, new_value) {
stream_edit_subscribers.rerender_subscribers_list(sub);
}

export function update_stream_topic_access_group_id(sub, new_value) {
stream_data.update_stream_topic_access_group_id(sub, new_value);
stream_ui_updates.update_setting_element(sub, "stream_topic_access_group");
stream_edit_subscribers.rerender_subscribers_list(sub);
}

export function update_is_default_stream() {
const active_stream_id = stream_settings_components.get_active_data().id;
if (active_stream_id) {
Expand Down
1 change: 1 addition & 0 deletions web/src/sub_store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export type Stream = {
stream_id: number;
stream_post_policy: StreamPostPolicy;
can_remove_subscribers_group: number;
stream_topic_access_group: number;
};

export type StreamSpecificNotificationSettings = {
Expand Down
3 changes: 2 additions & 1 deletion web/templates/stream_settings/stream_creation_form.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
{{> stream_types
stream_post_policy=stream_post_policy_values.everyone.code
is_stream_edit=false
can_remove_subscribers_setting_widget_name="new_stream_can_remove_subscribers_group" }}
can_remove_subscribers_setting_widget_name="new_stream_can_remove_subscribers_group"
stream_topic_access_setting_widget_name="new_stream_stream_topic_access_group" }}
</div>
</section>
<section class="block">
Expand Down
3 changes: 2 additions & 1 deletion web/templates/stream_settings/stream_settings.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@
is_business_type_org=../is_business_type_org
org_level_message_retention_setting=../org_level_message_retention_setting
is_stream_edit=true
can_remove_subscribers_setting_widget_name="can_remove_subscribers_group" }}
can_remove_subscribers_setting_widget_name="can_remove_subscribers_group"
stream_topic_access_setting_widget_name="stream_topic_access_group" }}
</div>
{{/with}}
<div class="stream_details_box">
Expand Down
5 changes: 5 additions & 0 deletions web/templates/stream_settings/stream_types.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@
label=(t 'Who can unsubscribe others from this channel?')
value_type="number"}}

{{> ../dropdown_widget_with_label
widget_name=stream_topic_access_setting_widget_name
label=(t 'Who can see all the topics?')
value_type="number"}}

{{#if (or is_owner is_stream_edit)}}
<div>
<div class="input-group inline-block message-retention-setting-group time-limit-setting">
Expand Down
2 changes: 2 additions & 0 deletions web/tests/lib/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ exports.test_streams = {
message_retention_days: null,
stream_post_policy: 1,
can_remove_subscribers_group: 2,
stream_topic_access_group: 14,
},
test: {
name: "test",
Expand All @@ -70,6 +71,7 @@ exports.test_streams = {
message_retention_days: null,
stream_post_policy: 1,
can_remove_subscribers_group: 2,
stream_topic_access_group: 14,
},
};

Expand Down