mirror of https://github.com/zulip/zulip.git
stream_settings: Add 'Default stream' option in create stream UI.
In this commit, we introduce a new option in the stream creation UI - a 'Default stream for new users' checkbox. By default, the checkbox is set to 'off' and is only visible to admins. This allow admins to easily designate a stream as the default stream for new users during stream creation. Fixes #24048.
This commit is contained in:
parent
a81715786c
commit
63173ce1bc
|
@ -23,8 +23,11 @@ format used by the Zulip server that they are interacting with.
|
||||||
**Feature level 200**
|
**Feature level 200**
|
||||||
|
|
||||||
* [`PATCH /streams/{stream_id}`](/api/update-stream): Added
|
* [`PATCH /streams/{stream_id}`](/api/update-stream): Added
|
||||||
`is_default_stream` parameter to add or remove the stream as a default
|
`is_default_stream` parameter to change whether the stream is a
|
||||||
stream for new users.
|
default stream for new users in the organization.
|
||||||
|
* [`POST /users/me/subscriptions`](/api/subscribe): Added
|
||||||
|
`is_default_stream` parameter which determines whether any streams
|
||||||
|
created by this request will be default streams for new users.
|
||||||
|
|
||||||
**Feature level 199**
|
**Feature level 199**
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
|
||||||
# Changes should be accompanied by documentation explaining what the
|
# Changes should be accompanied by documentation explaining what the
|
||||||
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
# new level means in api_docs/changelog.md, as well as "**Changes**"
|
||||||
# entries in the endpoint's documentation in `zulip.yaml`.
|
# entries in the endpoint's documentation in `zulip.yaml`.
|
||||||
API_FEATURE_LEVEL = 199
|
API_FEATURE_LEVEL = 200
|
||||||
|
|
||||||
# Bump the minor PROVISION_VERSION to indicate that folks should provision
|
# 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
|
# only when going from an old version of the code to a newer version. Bump
|
||||||
|
|
|
@ -217,6 +217,9 @@ function create_stream() {
|
||||||
data.invite_only = JSON.stringify(invite_only);
|
data.invite_only = JSON.stringify(invite_only);
|
||||||
data.history_public_to_subscribers = JSON.stringify(history_public_to_subscribers);
|
data.history_public_to_subscribers = JSON.stringify(history_public_to_subscribers);
|
||||||
|
|
||||||
|
const default_stream = $("#stream_creation_form .is_default_stream").prop("checked");
|
||||||
|
data.is_default_stream = JSON.stringify(default_stream);
|
||||||
|
|
||||||
const stream_post_policy = Number.parseInt(
|
const stream_post_policy = Number.parseInt(
|
||||||
$("#stream_creation_form select[name=stream-post-policy]").val(),
|
$("#stream_creation_form select[name=stream-post-policy]").val(),
|
||||||
10,
|
10,
|
||||||
|
@ -370,8 +373,10 @@ export function show_new_stream_modal() {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
// set default state for "announce stream" option.
|
// set default state for "announce stream" and "default stream" option.
|
||||||
|
$("#stream_creation_form .default-stream input").prop("checked", false);
|
||||||
update_announce_stream_state();
|
update_announce_stream_state();
|
||||||
|
stream_ui_updates.update_default_stream_and_stream_privacy_state($("#stream-creation"));
|
||||||
clear_error_display();
|
clear_error_display();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +386,14 @@ export function set_up_handlers() {
|
||||||
|
|
||||||
const $container = $("#stream-creation").expectOne();
|
const $container = $("#stream-creation").expectOne();
|
||||||
|
|
||||||
$container.on("change", ".stream-privacy-values input", update_announce_stream_state);
|
$container.on("change", ".stream-privacy-values input", () => {
|
||||||
|
update_announce_stream_state();
|
||||||
|
stream_ui_updates.update_default_stream_and_stream_privacy_state($container);
|
||||||
|
});
|
||||||
|
|
||||||
|
$container.on("change", ".default-stream input", () => {
|
||||||
|
stream_ui_updates.update_default_stream_and_stream_privacy_state($container);
|
||||||
|
});
|
||||||
|
|
||||||
$container.on("click", ".finalize_create_stream", (e) => {
|
$container.on("click", ".finalize_create_stream", (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
|
@ -717,6 +717,7 @@ export function setup_page(callback) {
|
||||||
stream_privacy_policy_values: stream_data.stream_privacy_policy_values,
|
stream_privacy_policy_values: stream_data.stream_privacy_policy_values,
|
||||||
stream_privacy_policy,
|
stream_privacy_policy,
|
||||||
stream_post_policy_values: stream_data.stream_post_policy_values,
|
stream_post_policy_values: stream_data.stream_post_policy_values,
|
||||||
|
check_default_stream: false,
|
||||||
zulip_plan_is_not_limited: page_params.zulip_plan_is_not_limited,
|
zulip_plan_is_not_limited: page_params.zulip_plan_is_not_limited,
|
||||||
org_level_message_retention_setting:
|
org_level_message_retention_setting:
|
||||||
stream_edit.get_display_text_for_realm_message_retention_setting(),
|
stream_edit.get_display_text_for_realm_message_retention_setting(),
|
||||||
|
|
|
@ -115,6 +115,15 @@ export function update_regular_sub_settings(sub) {
|
||||||
|
|
||||||
export function update_default_stream_and_stream_privacy_state($container) {
|
export function update_default_stream_and_stream_privacy_state($container) {
|
||||||
const $default_stream = $container.find(".default-stream");
|
const $default_stream = $container.find(".default-stream");
|
||||||
|
const is_stream_creation = $container.attr("id") === "stream-creation";
|
||||||
|
|
||||||
|
// In the stream creation UI, if the user is a non-admin hide the
|
||||||
|
// "Default stream for new users" widget
|
||||||
|
if (is_stream_creation && !page_params.is_admin) {
|
||||||
|
$default_stream.hide();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const privacy_type = $container.find("input[type=radio][name=privacy]:checked").val();
|
const privacy_type = $container.find("input[type=radio][name=privacy]:checked").val();
|
||||||
const is_invite_only =
|
const is_invite_only =
|
||||||
privacy_type === "invite-only" || privacy_type === "invite-only-public-history";
|
privacy_type === "invite-only" || privacy_type === "invite-only-public-history";
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if is_stream_edit}}
|
<div class="default-stream">
|
||||||
<div class="default-stream">
|
|
||||||
{{> ../settings/settings_checkbox
|
{{> ../settings/settings_checkbox
|
||||||
prefix="id_"
|
prefix="id_"
|
||||||
setting_name="is_default_stream"
|
setting_name="is_default_stream"
|
||||||
|
@ -25,8 +24,7 @@
|
||||||
label="Default stream for new users"
|
label="Default stream for new users"
|
||||||
help_link="/help/set-default-streams-for-new-users"
|
help_link="/help/set-default-streams-for-new-users"
|
||||||
}}
|
}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<label class="dropdown-title">{{t 'Who can post to the stream?'}}
|
<label class="dropdown-title">{{t 'Who can post to the stream?'}}
|
||||||
|
|
|
@ -670,6 +670,7 @@ def list_to_streams(
|
||||||
user_profile: UserProfile,
|
user_profile: UserProfile,
|
||||||
autocreate: bool = False,
|
autocreate: bool = False,
|
||||||
unsubscribing_others: bool = False,
|
unsubscribing_others: bool = False,
|
||||||
|
is_default_stream: bool = False,
|
||||||
) -> Tuple[List[Stream], List[Stream]]:
|
) -> Tuple[List[Stream], List[Stream]]:
|
||||||
"""Converts list of dicts to a list of Streams, validating input in the process
|
"""Converts list of dicts to a list of Streams, validating input in the process
|
||||||
|
|
||||||
|
@ -736,6 +737,10 @@ def list_to_streams(
|
||||||
raise JsonableError(_("Insufficient permission"))
|
raise JsonableError(_("Insufficient permission"))
|
||||||
if not invite_only and not user_profile.can_create_public_streams():
|
if not invite_only and not user_profile.can_create_public_streams():
|
||||||
raise JsonableError(_("Insufficient permission"))
|
raise JsonableError(_("Insufficient permission"))
|
||||||
|
if is_default_stream and not user_profile.is_realm_admin:
|
||||||
|
raise JsonableError(_("Insufficient permission"))
|
||||||
|
if invite_only and is_default_stream:
|
||||||
|
raise JsonableError(_("A default stream cannot be private."))
|
||||||
|
|
||||||
if not autocreate:
|
if not autocreate:
|
||||||
raise JsonableError(
|
raise JsonableError(
|
||||||
|
|
|
@ -8657,6 +8657,21 @@ paths:
|
||||||
type: boolean
|
type: boolean
|
||||||
default: false
|
default: false
|
||||||
example: true
|
example: true
|
||||||
|
- name: is_default_stream
|
||||||
|
in: query
|
||||||
|
description: |
|
||||||
|
This parameter determines whether any newly created streams will be
|
||||||
|
added as [default streams][default-streams] for new users joining
|
||||||
|
the organization.
|
||||||
|
|
||||||
|
[default-streams]: /help/set-default-streams-for-new-users
|
||||||
|
|
||||||
|
**Changes**: New in Zulip 8.0 (feature level 200). Previously, default stream status
|
||||||
|
could only be changed using the [dedicated API endpoint](/api/add-default-stream).
|
||||||
|
schema:
|
||||||
|
type: boolean
|
||||||
|
default: false
|
||||||
|
example: true
|
||||||
- $ref: "#/components/parameters/HistoryPublicToSubscribers"
|
- $ref: "#/components/parameters/HistoryPublicToSubscribers"
|
||||||
- $ref: "#/components/parameters/StreamPostPolicy"
|
- $ref: "#/components/parameters/StreamPostPolicy"
|
||||||
- $ref: "#/components/parameters/MessageRetentionDays"
|
- $ref: "#/components/parameters/MessageRetentionDays"
|
||||||
|
|
|
@ -383,6 +383,47 @@ class TestCreateStreams(ZulipTestCase):
|
||||||
if stream.name == "publictrywithouthistory":
|
if stream.name == "publictrywithouthistory":
|
||||||
self.assertTrue(stream.history_public_to_subscribers)
|
self.assertTrue(stream.history_public_to_subscribers)
|
||||||
|
|
||||||
|
def test_add_stream_as_default_on_stream_creation(self) -> None:
|
||||||
|
user_profile = self.example_user("hamlet")
|
||||||
|
self.login_user(user_profile)
|
||||||
|
realm = user_profile.realm
|
||||||
|
|
||||||
|
post_data = {
|
||||||
|
"subscriptions": orjson.dumps(
|
||||||
|
[{"name": "default_stream", "description": "This stream is default for new users"}]
|
||||||
|
).decode(),
|
||||||
|
"is_default_stream": orjson.dumps(True).decode(),
|
||||||
|
}
|
||||||
|
result = self.api_post(
|
||||||
|
user_profile, "/api/v1/users/me/subscriptions", post_data, subdomain="zulip"
|
||||||
|
)
|
||||||
|
self.assert_json_error(result, "Insufficient permission")
|
||||||
|
|
||||||
|
do_change_user_role(user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None)
|
||||||
|
result = self.api_post(
|
||||||
|
user_profile, "/api/v1/users/me/subscriptions", post_data, subdomain="zulip"
|
||||||
|
)
|
||||||
|
self.assert_json_success(result)
|
||||||
|
default_stream = get_stream("default_stream", realm)
|
||||||
|
self.assertTrue(default_stream.id in get_default_stream_ids_for_realm(realm.id))
|
||||||
|
|
||||||
|
post_data = {
|
||||||
|
"subscriptions": orjson.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "private_default_stream",
|
||||||
|
"description": "This stream is private and default for new users",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
).decode(),
|
||||||
|
"invite_only": orjson.dumps(True).decode(),
|
||||||
|
"is_default_stream": orjson.dumps(True).decode(),
|
||||||
|
}
|
||||||
|
result = self.api_post(
|
||||||
|
user_profile, "/api/v1/users/me/subscriptions", post_data, subdomain="zulip"
|
||||||
|
)
|
||||||
|
self.assert_json_error(result, "A default stream cannot be private.")
|
||||||
|
|
||||||
def test_history_public_to_subscribers_zephyr_realm(self) -> None:
|
def test_history_public_to_subscribers_zephyr_realm(self) -> None:
|
||||||
realm = get_realm("zephyr")
|
realm = get_realm("zephyr")
|
||||||
|
|
||||||
|
|
|
@ -578,6 +578,7 @@ def add_subscriptions_backend(
|
||||||
),
|
),
|
||||||
invite_only: bool = REQ(json_validator=check_bool, default=False),
|
invite_only: bool = REQ(json_validator=check_bool, default=False),
|
||||||
is_web_public: bool = REQ(json_validator=check_bool, default=False),
|
is_web_public: bool = REQ(json_validator=check_bool, default=False),
|
||||||
|
is_default_stream: bool = REQ(json_validator=check_bool, default=False),
|
||||||
stream_post_policy: int = REQ(
|
stream_post_policy: int = REQ(
|
||||||
json_validator=check_int_in(Stream.STREAM_POST_POLICY_TYPES),
|
json_validator=check_int_in(Stream.STREAM_POST_POLICY_TYPES),
|
||||||
default=Stream.STREAM_POST_POLICY_EVERYONE,
|
default=Stream.STREAM_POST_POLICY_EVERYONE,
|
||||||
|
@ -660,7 +661,9 @@ def add_subscriptions_backend(
|
||||||
# Validation of the streams arguments, including enforcement of
|
# Validation of the streams arguments, including enforcement of
|
||||||
# can_create_streams policy and check_stream_name policy is inside
|
# can_create_streams policy and check_stream_name policy is inside
|
||||||
# list_to_streams.
|
# list_to_streams.
|
||||||
existing_streams, created_streams = list_to_streams(stream_dicts, user_profile, autocreate=True)
|
existing_streams, created_streams = list_to_streams(
|
||||||
|
stream_dicts, user_profile, autocreate=True, is_default_stream=is_default_stream
|
||||||
|
)
|
||||||
authorized_streams, unauthorized_streams = filter_stream_authorization(
|
authorized_streams, unauthorized_streams = filter_stream_authorization(
|
||||||
user_profile, existing_streams
|
user_profile, existing_streams
|
||||||
)
|
)
|
||||||
|
@ -682,6 +685,10 @@ def add_subscriptions_backend(
|
||||||
_("You can only invite other Zephyr mirroring users to private streams.")
|
_("You can only invite other Zephyr mirroring users to private streams.")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if is_default_stream:
|
||||||
|
for stream in created_streams:
|
||||||
|
do_add_default_stream(stream)
|
||||||
|
|
||||||
(subscribed, already_subscribed) = bulk_add_subscriptions(
|
(subscribed, already_subscribed) = bulk_add_subscriptions(
|
||||||
realm, streams, subscribers, acting_user=user_profile, color_map=color_map
|
realm, streams, subscribers, acting_user=user_profile, color_map=color_map
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue