mirror of https://github.com/zulip/zulip.git
settings: Add two realm settings to restrict direct messages.
Fixes #24467.
This commit is contained in:
parent
318d3e3cca
commit
6098c2cebe
|
@ -20,6 +20,18 @@ format used by the Zulip server that they are interacting with.
|
|||
|
||||
## Changes in Zulip 9.0
|
||||
|
||||
**Feature level 270**
|
||||
|
||||
* `PATCH /realm`, [`POST /register`](/api/register-queue),
|
||||
[`GET /events`](/api/get-events): Added two new realm settings,
|
||||
`direct_message_initiator_group`, which is a
|
||||
[group-setting value](/api/group-setting-values) describing the
|
||||
set of users with permission to initiate direct message thread, and
|
||||
`direct_message_permission_group`, which is a
|
||||
[group-setting value](/api/group-setting-values) describing the
|
||||
set of users of which at least one member must be included as sender
|
||||
or recipient in all personal and group direct messages.
|
||||
|
||||
**Feature level 269**
|
||||
|
||||
* [`POST /register`](/api/register-queue), [`PATCH
|
||||
|
|
|
@ -33,7 +33,9 @@ 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 = 269
|
||||
|
||||
API_FEATURE_LEVEL = 270 # Last bumped for direct_message_permission_group
|
||||
|
||||
|
||||
# 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
|
||||
|
|
|
@ -142,6 +142,8 @@ export function build_page() {
|
|||
language_list,
|
||||
realm_default_language_name: get_language_name(realm.realm_default_language),
|
||||
realm_default_language_code: realm.realm_default_language,
|
||||
realm_direct_message_initiator_group_id: realm.realm_direct_message_initiator_group,
|
||||
realm_direct_message_permission_group_id: realm.realm_direct_message_permission_group,
|
||||
realm_waiting_period_threshold: realm.realm_waiting_period_threshold,
|
||||
realm_new_stream_announcements_stream_id: realm.realm_new_stream_announcements_stream_id,
|
||||
realm_signup_announcements_stream_id: realm.realm_signup_announcements_stream_id,
|
||||
|
@ -276,6 +278,10 @@ export function build_page() {
|
|||
|
||||
tippy.default($("#realm_can_access_all_users_group_widget_container")[0], opts);
|
||||
}
|
||||
|
||||
settings_org.check_disable_direct_message_initiator_group_dropdown(
|
||||
realm.realm_direct_message_permission_group,
|
||||
);
|
||||
}
|
||||
|
||||
export function launch(section, user_settings_tab) {
|
||||
|
|
|
@ -15,6 +15,7 @@ import * as compose_validate from "./compose_validate";
|
|||
import * as drafts from "./drafts";
|
||||
import * as message_lists from "./message_lists";
|
||||
import type {Message} from "./message_store";
|
||||
import * as message_util from "./message_util";
|
||||
import * as message_viewport from "./message_viewport";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import {page_params} from "./page_params";
|
||||
|
@ -22,9 +23,7 @@ import * as people from "./people";
|
|||
import * as popovers from "./popovers";
|
||||
import * as reload_state from "./reload_state";
|
||||
import * as resize from "./resize";
|
||||
import * as settings_config from "./settings_config";
|
||||
import * as spectators from "./spectators";
|
||||
import {realm} from "./state_data";
|
||||
import * as stream_data from "./stream_data";
|
||||
|
||||
// Opts sent to `compose_actions.start`.
|
||||
|
@ -530,26 +529,21 @@ export function on_narrow(opts: NarrowActivateOpts): void {
|
|||
}
|
||||
return;
|
||||
}
|
||||
// Do not open compose box if organization has disabled sending
|
||||
// direct messages and recipient is not a bot.
|
||||
// Do not open compose box if sender is not allowed to send direct message.
|
||||
const recipient_ids_string = people.emails_strings_to_user_ids_string(
|
||||
opts.private_message_recipient,
|
||||
);
|
||||
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.disabled.code &&
|
||||
opts.private_message_recipient
|
||||
recipient_ids_string &&
|
||||
!message_util.user_can_send_direct_message(recipient_ids_string)
|
||||
) {
|
||||
const emails = opts.private_message_recipient.split(",");
|
||||
if (
|
||||
emails.length !== 1 ||
|
||||
emails[0] === undefined ||
|
||||
!people.get_by_email(emails[0])!.is_bot
|
||||
) {
|
||||
// If we are navigating between direct message conversations,
|
||||
// we want the compose box to close for non-bot users.
|
||||
if (compose_state.composing()) {
|
||||
cancel();
|
||||
}
|
||||
return;
|
||||
// If we are navigating between direct message conversation,
|
||||
// we want the compose box to close for non-bot users.
|
||||
if (compose_state.composing()) {
|
||||
cancel();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Open the compose box, passing the option to skip attempting
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import $ from "jquery";
|
||||
|
||||
import render_cannot_send_direct_message_error from "../templates/compose_banner/cannot_send_direct_message_error.hbs";
|
||||
import render_compose_banner from "../templates/compose_banner/compose_banner.hbs";
|
||||
import render_stream_does_not_exist_error from "../templates/compose_banner/stream_does_not_exist_error.hbs";
|
||||
|
||||
|
@ -48,7 +49,7 @@ export const CLASSNAMES = {
|
|||
stream_does_not_exist: "stream_does_not_exist",
|
||||
missing_stream: "missing_stream",
|
||||
no_post_permissions: "no_post_permissions",
|
||||
private_messages_disabled: "private_messages_disabled",
|
||||
cannot_send_direct_message: "cannot_send_direct_message",
|
||||
missing_private_message_recipient: "missing_private_message_recipient",
|
||||
invalid_recipient: "invalid_recipient",
|
||||
invalid_recipients: "invalid_recipients",
|
||||
|
@ -183,6 +184,21 @@ export function show_error_message(
|
|||
}
|
||||
}
|
||||
|
||||
export function cannot_send_direct_message_error(error_message: string): void {
|
||||
// Remove any existing banners with this warning.
|
||||
$(`#compose_banners .${CSS.escape(CLASSNAMES.cannot_send_direct_message)}`).remove();
|
||||
|
||||
const new_row_html = render_cannot_send_direct_message_error({
|
||||
banner_type: ERROR,
|
||||
error_message,
|
||||
classname: CLASSNAMES.cannot_send_direct_message,
|
||||
});
|
||||
append_compose_banner_to_banner_list($(new_row_html), $("#compose_banners"));
|
||||
hide_compose_spinner();
|
||||
|
||||
$("#private_message_recipient").trigger("focus").trigger("select");
|
||||
}
|
||||
|
||||
export function show_stream_does_not_exist_error(stream_name: string): void {
|
||||
// Remove any existing banners with this warning.
|
||||
$(`#compose_banners .${CSS.escape(CLASSNAMES.stream_does_not_exist)}`).remove();
|
||||
|
|
|
@ -4,6 +4,7 @@ import * as compose_actions from "./compose_actions";
|
|||
import {$t} from "./i18n";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as message_store from "./message_store";
|
||||
import * as message_util from "./message_util";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import * as people from "./people";
|
||||
import * as stream_data from "./stream_data";
|
||||
|
@ -132,7 +133,7 @@ export function update_buttons_for_private(): void {
|
|||
|
||||
let disable_reply;
|
||||
|
||||
if (!pm_ids_string || people.user_can_direct_message(pm_ids_string)) {
|
||||
if (!pm_ids_string || message_util.user_can_send_direct_message(pm_ids_string)) {
|
||||
disable_reply = false;
|
||||
} else {
|
||||
// disable the [Message X] button when in a private narrow
|
||||
|
|
|
@ -19,12 +19,11 @@ import * as dropdown_widget from "./dropdown_widget";
|
|||
import type {Option} from "./dropdown_widget";
|
||||
import {$t} from "./i18n";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import * as people from "./people";
|
||||
import * as settings_config from "./settings_config";
|
||||
import {realm} from "./state_data";
|
||||
import * as stream_data from "./stream_data";
|
||||
import * as sub_store from "./sub_store";
|
||||
import * as ui_util from "./ui_util";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as util from "./util";
|
||||
|
||||
type MessageType = "stream" | "private";
|
||||
|
@ -116,12 +115,7 @@ export function update_on_recipient_change(): void {
|
|||
export function get_posting_policy_error_message(): string {
|
||||
if (compose_state.selected_recipient_id === "direct") {
|
||||
const recipients = compose_pm_pill.get_user_ids_string();
|
||||
if (!people.user_can_direct_message(recipients)) {
|
||||
return $t({
|
||||
defaultMessage: "You are not allowed to send direct messages in this organization.",
|
||||
});
|
||||
}
|
||||
return "";
|
||||
return compose_validate.check_dm_permissions_and_get_error_string(recipients);
|
||||
}
|
||||
|
||||
if (!isNumber(compose_state.selected_recipient_id)) {
|
||||
|
@ -146,11 +140,13 @@ export function check_posting_policy_for_compose_box(): void {
|
|||
}
|
||||
|
||||
let banner_classname = compose_banner.CLASSNAMES.no_post_permissions;
|
||||
if (compose_state.selected_recipient_id === "direct") {
|
||||
banner_classname = compose_banner.CLASSNAMES.private_messages_disabled;
|
||||
}
|
||||
compose_validate.set_recipient_disallowed(true);
|
||||
compose_banner.show_error_message(banner_text, banner_classname, $("#compose_banners"));
|
||||
if (compose_state.selected_recipient_id === "direct") {
|
||||
banner_classname = compose_banner.CLASSNAMES.cannot_send_direct_message;
|
||||
compose_banner.cannot_send_direct_message_error(banner_text);
|
||||
} else {
|
||||
compose_banner.show_error_message(banner_text, banner_classname, $("#compose_banners"));
|
||||
}
|
||||
}
|
||||
|
||||
function switch_message_type(message_type: MessageType): void {
|
||||
|
@ -263,10 +259,8 @@ function get_options_for_recipient_widget(): Option[] {
|
|||
name: $t({defaultMessage: "Direct message"}),
|
||||
};
|
||||
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.by_anyone.code
|
||||
) {
|
||||
const {name} = user_groups.get_user_group_from_id(realm.realm_direct_message_permission_group);
|
||||
if (name !== "role:nobody") {
|
||||
options.unshift(direct_messages_option);
|
||||
} else {
|
||||
options.push(direct_messages_option);
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as compose_recipient from "./compose_recipient";
|
|||
import * as compose_state from "./compose_state";
|
||||
import * as compose_validate from "./compose_validate";
|
||||
import {$t} from "./i18n";
|
||||
import {pick_empty_narrow_banner} from "./narrow_banner";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import * as popover_menus from "./popover_menus";
|
||||
import {EXTRA_LONG_HOVER_DELAY, INSTANT_HOVER_DELAY, LONG_HOVER_DELAY} from "./tippyjs";
|
||||
|
@ -47,11 +48,7 @@ export function initialize(): void {
|
|||
const button_type = $elem.attr("data-reply-button-type");
|
||||
switch (button_type) {
|
||||
case "direct_disabled": {
|
||||
instance.setContent(
|
||||
parse_html(
|
||||
$("#compose_reply_direct_disabled_button_tooltip_template").html(),
|
||||
),
|
||||
);
|
||||
instance.setContent(pick_empty_narrow_banner().title);
|
||||
return;
|
||||
}
|
||||
case "selected_message": {
|
||||
|
|
|
@ -14,6 +14,7 @@ import * as compose_state from "./compose_state";
|
|||
import * as compose_ui from "./compose_ui";
|
||||
import {$t} from "./i18n";
|
||||
import * as message_store from "./message_store";
|
||||
import * as message_util from "./message_util";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import * as peer_data from "./peer_data";
|
||||
import * as people from "./people";
|
||||
|
@ -26,6 +27,7 @@ import * as stream_data from "./stream_data";
|
|||
import * as sub_store from "./sub_store";
|
||||
import type {StreamSubscription} from "./sub_store";
|
||||
import type {UserOrMention} from "./typeahead_helper";
|
||||
import * as user_groups from "./user_groups";
|
||||
import * as util from "./util";
|
||||
|
||||
let user_acknowledged_stream_wildcard = false;
|
||||
|
@ -110,6 +112,32 @@ export function needs_subscribe_warning(user_id: number, stream_id: number): boo
|
|||
return true;
|
||||
}
|
||||
|
||||
export function check_dm_permissions_and_get_error_string(user_ids_string: string): string {
|
||||
if (!people.user_can_direct_message(user_ids_string)) {
|
||||
const {name} = user_groups.get_user_group_from_id(
|
||||
realm.realm_direct_message_permission_group,
|
||||
);
|
||||
if (name === "role:nobody") {
|
||||
return $t({
|
||||
defaultMessage: "Direct messages are disabled in this organization.",
|
||||
});
|
||||
}
|
||||
return $t({
|
||||
defaultMessage: "This conversation does not include any users who can authorize it.",
|
||||
});
|
||||
}
|
||||
if (
|
||||
message_util.get_direct_message_permission_hints(user_ids_string)
|
||||
.is_known_empty_conversation &&
|
||||
!people.user_can_initiate_direct_message_thread(user_ids_string)
|
||||
) {
|
||||
return $t({
|
||||
defaultMessage: "You are not allowed to start direct message conversations.",
|
||||
});
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function get_stream_id_for_textarea($textarea: JQuery<HTMLTextAreaElement>): number | undefined {
|
||||
// Returns the stream ID, if any, associated with the textarea:
|
||||
// The recipient of a message being edited, or the target
|
||||
|
@ -619,20 +647,9 @@ function validate_stream_message(scheduling_message: boolean): boolean {
|
|||
// for now)
|
||||
function validate_private_message(): boolean {
|
||||
const user_ids = compose_pm_pill.get_user_ids();
|
||||
const user_ids_string = util.sorted_ids(user_ids).join(",");
|
||||
const $banner_container = $("#compose_banners");
|
||||
|
||||
const user_ids_string = user_ids.join(",");
|
||||
|
||||
if (!people.user_can_direct_message(user_ids_string)) {
|
||||
compose_banner.show_error_message(
|
||||
$t({defaultMessage: "Direct messages are disabled in this organization."}),
|
||||
compose_banner.CLASSNAMES.private_messages_disabled,
|
||||
$banner_container,
|
||||
$("#private_message_recipient"),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compose_state.private_message_recipient().length === 0) {
|
||||
compose_banner.show_error_message(
|
||||
$t({defaultMessage: "Please specify at least one valid recipient."}),
|
||||
|
@ -646,6 +663,12 @@ function validate_private_message(): boolean {
|
|||
return true;
|
||||
}
|
||||
|
||||
const direct_message_error_string = check_dm_permissions_and_get_error_string(user_ids_string);
|
||||
if (direct_message_error_string) {
|
||||
compose_banner.cannot_send_direct_message_error(direct_message_error_string);
|
||||
return false;
|
||||
}
|
||||
|
||||
const invalid_recipients = get_invalid_recipient_emails();
|
||||
|
||||
let context = {};
|
||||
|
|
|
@ -10,6 +10,7 @@ import * as markdown from "./markdown";
|
|||
import * as message_lists from "./message_lists";
|
||||
import * as message_live_update from "./message_live_update";
|
||||
import * as message_store from "./message_store";
|
||||
import * as message_util from "./message_util";
|
||||
import * as people from "./people";
|
||||
import * as pm_list from "./pm_list";
|
||||
import * as recent_view_data from "./recent_view_data";
|
||||
|
@ -224,6 +225,14 @@ export function try_deliver_locally(message_request, insert_new_messages) {
|
|||
// view; this is useful to ensure it will be visible in other
|
||||
// views that we might navigate to before we get a response from
|
||||
// the server.
|
||||
if (
|
||||
message_request.to_user_ids &&
|
||||
!people.user_can_initiate_direct_message_thread(message_request.to_user_ids) &&
|
||||
!message_util.get_direct_message_permission_hints(message_request.to_user_ids)
|
||||
.is_local_echo_safe
|
||||
) {
|
||||
return undefined;
|
||||
}
|
||||
if (markdown.contains_backend_only_syntax(message_request.content)) {
|
||||
return undefined;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@ import $ from "jquery";
|
|||
import {all_messages_data} from "./all_messages_data";
|
||||
import * as blueslip from "./blueslip";
|
||||
import * as channel from "./channel";
|
||||
import * as compose_closed_ui from "./compose_closed_ui";
|
||||
import * as compose_recipient from "./compose_recipient";
|
||||
import * as direct_message_group_data from "./direct_message_group_data";
|
||||
import * as message_feed_loading from "./message_feed_loading";
|
||||
import * as message_feed_top_notices from "./message_feed_top_notices";
|
||||
|
@ -106,6 +108,8 @@ function process_result(data, opts) {
|
|||
// Even after loading more messages, we have
|
||||
// no messages to display in this narrow.
|
||||
narrow_banner.show_empty_narrow_message();
|
||||
compose_closed_ui.update_buttons_for_private();
|
||||
compose_recipient.check_posting_policy_for_compose_box();
|
||||
}
|
||||
|
||||
if (opts.num_before > 0 && !has_found_oldest) {
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import assert from "minimalistic-assert";
|
||||
|
||||
import {all_messages_data} from "./all_messages_data";
|
||||
import type {MessageListData} from "./message_list_data";
|
||||
import type {MessageList, RenderInfo} from "./message_lists";
|
||||
import {type MessageList, type RenderInfo} from "./message_lists";
|
||||
import * as message_lists from "./message_lists";
|
||||
import * as message_store from "./message_store";
|
||||
import type {Message} from "./message_store";
|
||||
import * as people from "./people";
|
||||
import * as pm_conversations from "./pm_conversations";
|
||||
import * as unread from "./unread";
|
||||
import * as unread_ui from "./unread_ui";
|
||||
|
||||
type DirectMessagePermissionHints = {
|
||||
is_known_empty_conversation: boolean;
|
||||
is_local_echo_safe: boolean;
|
||||
};
|
||||
|
||||
export function do_unread_count_updates(messages: Message[], expect_no_new_unreads = false): void {
|
||||
const any_new_unreads = unread.process_loaded_messages(messages, expect_no_new_unreads);
|
||||
|
||||
|
@ -112,3 +122,45 @@ export function get_topics_for_message_ids(message_ids: number[]): Map<string, [
|
|||
}
|
||||
return topics;
|
||||
}
|
||||
|
||||
export function get_direct_message_permission_hints(
|
||||
recipient_ids_string: string,
|
||||
): DirectMessagePermissionHints {
|
||||
// Check if there are any previous messages in the DM conversation.
|
||||
const have_conversation_in_cache =
|
||||
pm_conversations.recent.has_conversation(recipient_ids_string);
|
||||
if (have_conversation_in_cache) {
|
||||
return {is_known_empty_conversation: false, is_local_echo_safe: true};
|
||||
}
|
||||
|
||||
// If not, we need to check if the current filter matches the DM view we
|
||||
// are composing to.
|
||||
const dm_conversation = message_lists.current?.data?.filter.operands("dm")[0];
|
||||
if (dm_conversation) {
|
||||
const current_user_ids_string = people.emails_strings_to_user_ids_string(dm_conversation);
|
||||
assert(current_user_ids_string !== undefined);
|
||||
// If it matches and the messages for the current filter are fetched,
|
||||
// then there are certainly no messages in the conversation.
|
||||
if (
|
||||
people.pm_lookup_key(recipient_ids_string) ===
|
||||
people.pm_lookup_key(current_user_ids_string) &&
|
||||
message_lists.current?.data?.fetch_status.has_found_newest()
|
||||
) {
|
||||
return {is_known_empty_conversation: true, is_local_echo_safe: true};
|
||||
}
|
||||
}
|
||||
|
||||
// If it does not match, then there can be messages in the DM conversation
|
||||
// which are not fetched locally and hence we disable local echo for clean
|
||||
// error handling in case there are no messages in the conversation and
|
||||
// user is not allowed to initiate DM conversations.
|
||||
return {is_known_empty_conversation: false, is_local_echo_safe: false};
|
||||
}
|
||||
|
||||
export function user_can_send_direct_message(user_ids_string: string): boolean {
|
||||
return (
|
||||
(!get_direct_message_permission_hints(user_ids_string).is_known_empty_conversation ||
|
||||
people.user_can_initiate_direct_message_thread(user_ids_string)) &&
|
||||
people.user_can_direct_message(user_ids_string)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,16 +2,17 @@ import $ from "jquery";
|
|||
import _ from "lodash";
|
||||
import assert from "minimalistic-assert";
|
||||
|
||||
import * as compose_validate from "./compose_validate";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import type {NarrowBannerData, SearchData} from "./narrow_error";
|
||||
import {narrow_error} from "./narrow_error";
|
||||
import * as narrow_state from "./narrow_state";
|
||||
import {page_params} from "./page_params";
|
||||
import * as people from "./people";
|
||||
import * as settings_config from "./settings_config";
|
||||
import * as spectators from "./spectators";
|
||||
import {realm} from "./state_data";
|
||||
import * as stream_data from "./stream_data";
|
||||
import * as util from "./util";
|
||||
|
||||
const SPECTATOR_STREAM_NARROW_BANNER = {
|
||||
title: "",
|
||||
|
@ -104,7 +105,7 @@ function retrieve_search_query_data(): SearchData {
|
|||
return search_string_result;
|
||||
}
|
||||
|
||||
function pick_empty_narrow_banner(): NarrowBannerData {
|
||||
export function pick_empty_narrow_banner(): NarrowBannerData {
|
||||
const default_banner = {
|
||||
title: $t({defaultMessage: "There are no messages here."}),
|
||||
// Spectators cannot start a conversation.
|
||||
|
@ -230,17 +231,6 @@ function pick_empty_narrow_banner(): NarrowBannerData {
|
|||
return MENTIONS_VIEW_EMPTY_BANNER;
|
||||
case "dm":
|
||||
// You have no direct messages.
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.disabled.code
|
||||
) {
|
||||
return {
|
||||
title: $t({
|
||||
defaultMessage:
|
||||
"You are not allowed to send direct messages in this organization.",
|
||||
}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
title: $t({defaultMessage: "You have no direct messages yet!"}),
|
||||
html: $t_html(
|
||||
|
@ -324,16 +314,21 @@ function pick_empty_narrow_banner(): NarrowBannerData {
|
|||
}
|
||||
const user_ids = people.emails_strings_to_user_ids_array(first_operand);
|
||||
assert(user_ids?.[0] !== undefined);
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.disabled.code &&
|
||||
(user_ids.length !== 1 || !people.get_by_user_id(user_ids[0]).is_bot)
|
||||
) {
|
||||
const user_ids_string = util.sorted_ids(user_ids).join(",");
|
||||
const direct_message_error_string =
|
||||
compose_validate.check_dm_permissions_and_get_error_string(user_ids_string);
|
||||
if (direct_message_error_string) {
|
||||
return {
|
||||
title: $t({
|
||||
defaultMessage:
|
||||
"You are not allowed to send direct messages in this organization.",
|
||||
}),
|
||||
title: direct_message_error_string,
|
||||
html: $t_html(
|
||||
{
|
||||
defaultMessage: "<z-link>Learn more.</z-link>",
|
||||
},
|
||||
{
|
||||
"z-link": (content_html) =>
|
||||
`<a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">${content_html.join("")}</a>`,
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
if (!first_operand.includes(",")) {
|
||||
|
@ -409,16 +404,21 @@ function pick_empty_narrow_banner(): NarrowBannerData {
|
|||
title: $t({defaultMessage: "This user does not exist!"}),
|
||||
};
|
||||
}
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.disabled.code &&
|
||||
!person_in_dms.is_bot
|
||||
) {
|
||||
const person_id_string = person_in_dms.user_id.toString();
|
||||
const direct_message_error_string =
|
||||
compose_validate.check_dm_permissions_and_get_error_string(person_id_string);
|
||||
if (direct_message_error_string) {
|
||||
return {
|
||||
title: $t({
|
||||
defaultMessage:
|
||||
"You are not allowed to send direct messages in this organization.",
|
||||
}),
|
||||
title: direct_message_error_string,
|
||||
html: $t_html(
|
||||
{
|
||||
defaultMessage: "<z-link>Learn more.</z-link>",
|
||||
},
|
||||
{
|
||||
"z-link": (content_html) =>
|
||||
`<a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">${content_html.join("")}</a>`,
|
||||
},
|
||||
),
|
||||
};
|
||||
}
|
||||
if (people.is_current_user(first_operand)) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import type {
|
|||
} from "./state_data";
|
||||
import {current_user, realm} from "./state_data";
|
||||
import * as timerender from "./timerender";
|
||||
import {is_user_in_group} from "./user_groups";
|
||||
import {user_settings} from "./user_settings";
|
||||
import * as util from "./util";
|
||||
|
||||
|
@ -760,28 +761,38 @@ export function should_add_guest_user_indicator(user_id: number): boolean {
|
|||
return user.is_guest;
|
||||
}
|
||||
|
||||
export function user_can_direct_message(recipient_ids_string: string): boolean {
|
||||
// Common function for checking if a user can send a direct
|
||||
// message to the target user (or group of users) represented by a
|
||||
// user ids string.
|
||||
|
||||
// Regardless of policy, we allow sending direct messages to bots and to self.
|
||||
export function user_can_initiate_direct_message_thread(recipient_ids_string: string): boolean {
|
||||
const direct_message_initiator_group_id = realm.realm_direct_message_initiator_group;
|
||||
const recipient_ids = user_ids_string_to_ids_array(recipient_ids_string);
|
||||
if (
|
||||
recipient_ids.length === 1 &&
|
||||
recipient_ids[0] !== undefined &&
|
||||
(is_valid_bot_user(recipient_ids[0]) || is_my_user_id(recipient_ids[0]))
|
||||
) {
|
||||
if (is_user_in_group(direct_message_initiator_group_id, my_user_id)) {
|
||||
return true;
|
||||
}
|
||||
for (const recipient of recipient_ids) {
|
||||
if (!is_valid_bot_user(recipient) && recipient !== my_user_id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function user_can_direct_message(recipient_ids_string: string): boolean {
|
||||
const direct_message_permission_group_id = realm.realm_direct_message_permission_group;
|
||||
const recipient_ids = user_ids_string_to_ids_array(recipient_ids_string);
|
||||
if (is_user_in_group(direct_message_permission_group_id, my_user_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
realm.realm_private_message_policy ===
|
||||
settings_config.private_message_policy_values.disabled.code
|
||||
) {
|
||||
return false;
|
||||
let other_human_recipients_exist = false;
|
||||
for (const recipient_id of recipient_ids) {
|
||||
if (is_valid_bot_user(recipient_id) || recipient_id === my_user_id) {
|
||||
continue;
|
||||
}
|
||||
if (is_user_in_group(direct_message_permission_group_id, recipient_id)) {
|
||||
return true;
|
||||
}
|
||||
other_human_recipients_exist = true;
|
||||
}
|
||||
return true;
|
||||
return !other_human_recipients_exist;
|
||||
}
|
||||
|
||||
function gravatar_url_for_email(email: string): string {
|
||||
|
|
|
@ -12,6 +12,7 @@ import * as browser_history from "./browser_history";
|
|||
import {buddy_list} from "./buddy_list";
|
||||
import * as compose_call from "./compose_call";
|
||||
import * as compose_call_ui from "./compose_call_ui";
|
||||
import * as compose_closed_ui from "./compose_closed_ui";
|
||||
import * as compose_pm_pill from "./compose_pm_pill";
|
||||
import * as compose_recipient from "./compose_recipient";
|
||||
import * as compose_state from "./compose_state";
|
||||
|
@ -212,6 +213,8 @@ export function dispatch_normal_event(event) {
|
|||
description: noop,
|
||||
digest_emails_enabled: noop,
|
||||
digest_weekday: noop,
|
||||
direct_message_initiator_group: noop,
|
||||
direct_message_permission_group: noop,
|
||||
email_changes_disabled: settings_account.update_email_change_display,
|
||||
disallow_disposable_email_addresses: noop,
|
||||
inline_image_preview: noop,
|
||||
|
@ -229,7 +232,6 @@ export function dispatch_normal_event(event) {
|
|||
name_changes_disabled: settings_account.update_name_change_display,
|
||||
new_stream_announcements_stream_id: stream_ui_updates.update_announce_stream_option,
|
||||
org_type: noop,
|
||||
private_message_policy: compose_recipient.check_posting_policy_for_compose_box,
|
||||
push_notifications_enabled: noop,
|
||||
require_unique_names: noop,
|
||||
send_welcome_emails: noop,
|
||||
|
@ -296,6 +298,17 @@ export function dispatch_normal_event(event) {
|
|||
stream_settings_ui.update_stream_privacy_choices(key);
|
||||
}
|
||||
|
||||
if (
|
||||
key === "direct_message_initiator_group" ||
|
||||
key === "direct_message_permission_group"
|
||||
) {
|
||||
settings_org.check_disable_direct_message_initiator_group_dropdown(
|
||||
realm.realm_direct_message_permission_group,
|
||||
);
|
||||
compose_closed_ui.update_buttons_for_private();
|
||||
compose_recipient.check_posting_policy_for_compose_box();
|
||||
}
|
||||
|
||||
if (key === "edit_topic_policy") {
|
||||
message_live_update.rerender_messages_view();
|
||||
}
|
||||
|
|
|
@ -207,7 +207,6 @@ type simple_dropdown_realm_settings = Pick<
|
|||
| "realm_create_web_public_stream_policy"
|
||||
| "realm_invite_to_stream_policy"
|
||||
| "realm_user_group_edit_policy"
|
||||
| "realm_private_message_policy"
|
||||
| "realm_add_custom_emoji_policy"
|
||||
| "realm_invite_to_realm_policy"
|
||||
| "realm_wildcard_mention_policy"
|
||||
|
@ -474,6 +473,8 @@ const dropdown_widget_map = new Map<string, DropdownWidget | null>([
|
|||
["can_mention_group", null],
|
||||
["realm_can_create_public_channel_group", null],
|
||||
["realm_can_create_private_channel_group", null],
|
||||
["realm_direct_message_initiator_group", null],
|
||||
["realm_direct_message_permission_group", null],
|
||||
]);
|
||||
|
||||
export function get_widget_for_dropdown_list_settings(
|
||||
|
@ -781,6 +782,8 @@ export function check_realm_settings_property_changed(elem: HTMLElement): boolea
|
|||
case "realm_can_access_all_users_group":
|
||||
case "realm_can_create_public_channel_group":
|
||||
case "realm_can_create_private_channel_group":
|
||||
case "realm_direct_message_initiator_group":
|
||||
case "realm_direct_message_permission_group":
|
||||
proposed_val = get_dropdown_list_widget_setting_value($elem);
|
||||
break;
|
||||
case "realm_message_content_edit_limit_seconds":
|
||||
|
@ -974,6 +977,8 @@ export function populate_data_for_realm_settings_request(
|
|||
const realm_group_settings_using_new_api_format = new Set([
|
||||
"can_create_private_channel_group",
|
||||
"can_create_public_channel_group",
|
||||
"direct_message_initiator_group",
|
||||
"direct_message_permission_group",
|
||||
]);
|
||||
if (realm_group_settings_using_new_api_format.has(property_name)) {
|
||||
const old_value = get_realm_settings_property_value(
|
||||
|
|
|
@ -256,19 +256,6 @@ export const email_invite_to_realm_policy_values = {
|
|||
},
|
||||
};
|
||||
|
||||
export const private_message_policy_values = {
|
||||
by_anyone: {
|
||||
order: 1,
|
||||
code: 1,
|
||||
description: $t({defaultMessage: "Admins, moderators, members and guests"}),
|
||||
},
|
||||
disabled: {
|
||||
order: 2,
|
||||
code: 2,
|
||||
description: $t({defaultMessage: "Direct messages disabled"}),
|
||||
},
|
||||
};
|
||||
|
||||
export const wildcard_mention_policy_values = {
|
||||
by_everyone: {
|
||||
order: 1,
|
||||
|
|
|
@ -95,9 +95,6 @@ export function get_organization_settings_options() {
|
|||
options.common_policy_values = settings_components.get_sorted_options_list(
|
||||
settings_config.common_policy_values,
|
||||
);
|
||||
options.private_message_policy_values = settings_components.get_sorted_options_list(
|
||||
settings_config.private_message_policy_values,
|
||||
);
|
||||
options.wildcard_mention_policy_values = settings_components.get_sorted_options_list(
|
||||
settings_config.wildcard_mention_policy_values,
|
||||
);
|
||||
|
@ -129,7 +126,6 @@ const simple_dropdown_properties = [
|
|||
"realm_create_web_public_stream_policy",
|
||||
"realm_invite_to_stream_policy",
|
||||
"realm_user_group_edit_policy",
|
||||
"realm_private_message_policy",
|
||||
"realm_add_custom_emoji_policy",
|
||||
"realm_invite_to_realm_policy",
|
||||
"realm_wildcard_mention_policy",
|
||||
|
@ -373,6 +369,14 @@ function set_create_web_public_stream_dropdown_visibility() {
|
|||
);
|
||||
}
|
||||
|
||||
export function check_disable_direct_message_initiator_group_dropdown(current_value) {
|
||||
if (current_value === user_groups.get_user_group_from_name("role:nobody").id) {
|
||||
$("#realm_direct_message_initiator_group_widget").prop("disabled", true);
|
||||
} else {
|
||||
$("#realm_direct_message_initiator_group_widget").prop("disabled", false);
|
||||
}
|
||||
}
|
||||
|
||||
export function populate_realm_domains_label(realm_domains) {
|
||||
if (!meta.loaded) {
|
||||
return;
|
||||
|
@ -472,6 +476,11 @@ function update_dependent_subsettings(property_name) {
|
|||
case "realm_enable_spectator_access":
|
||||
set_create_web_public_stream_dropdown_visibility();
|
||||
break;
|
||||
case "realm_direct_message_permission_group":
|
||||
check_disable_direct_message_initiator_group_dropdown(
|
||||
realm.realm_direct_message_permission_group,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -491,6 +500,8 @@ export function discard_realm_property_element_changes(elem) {
|
|||
case "realm_zulip_update_announcements_stream_id":
|
||||
case "realm_default_code_block_language":
|
||||
case "realm_create_multiuse_invite_group":
|
||||
case "realm_direct_message_initiator_group":
|
||||
case "realm_direct_message_permission_group":
|
||||
case "realm_can_access_all_users_group":
|
||||
case "realm_can_create_public_channel_group":
|
||||
case "realm_can_create_private_channel_group":
|
||||
|
@ -744,7 +755,12 @@ export function set_up() {
|
|||
maybe_disable_widgets();
|
||||
}
|
||||
|
||||
function set_up_dropdown_widget(setting_name, setting_options, setting_type) {
|
||||
function set_up_dropdown_widget(
|
||||
setting_name,
|
||||
setting_options,
|
||||
setting_type,
|
||||
custom_dropdown_widget_callback,
|
||||
) {
|
||||
const $save_discard_widget_container = $(`#id_${CSS.escape(setting_name)}`).closest(
|
||||
".settings-subsection-parent",
|
||||
);
|
||||
|
@ -774,6 +790,9 @@ function set_up_dropdown_widget(setting_name, setting_options, setting_type) {
|
|||
settings_components.save_discard_realm_settings_widget_status_handler(
|
||||
$save_discard_widget_container,
|
||||
);
|
||||
if (custom_dropdown_widget_callback !== undefined) {
|
||||
custom_dropdown_widget_callback(this.current_value);
|
||||
}
|
||||
},
|
||||
tippy_props: {
|
||||
placement: "bottom-start",
|
||||
|
@ -799,7 +818,17 @@ export function set_up_dropdown_widget_for_realm_group_settings() {
|
|||
for (const setting_name of realm_group_permission_settings) {
|
||||
const get_setting_options = () =>
|
||||
user_groups.get_realm_user_groups_for_dropdown_list_widget(setting_name, "realm");
|
||||
set_up_dropdown_widget("realm_" + setting_name, get_setting_options, "group");
|
||||
let dropdown_list_item_click_callback;
|
||||
if (setting_name === "direct_message_permission_group") {
|
||||
dropdown_list_item_click_callback =
|
||||
check_disable_direct_message_initiator_group_dropdown;
|
||||
}
|
||||
set_up_dropdown_widget(
|
||||
"realm_" + setting_name,
|
||||
get_setting_options,
|
||||
"group",
|
||||
dropdown_list_item_click_callback,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -278,6 +278,8 @@ const realm_schema = z.object({
|
|||
realm_description: z.string(),
|
||||
realm_digest_emails_enabled: NOT_TYPED_YET,
|
||||
realm_digest_weekday: NOT_TYPED_YET,
|
||||
realm_direct_message_initiator_group: z.number(),
|
||||
realm_direct_message_permission_group: z.number(),
|
||||
realm_disallow_disposable_email_addresses: z.boolean(),
|
||||
realm_domains: z.array(
|
||||
z.object({
|
||||
|
|
|
@ -568,7 +568,10 @@ export function initialize_everything(state_data) {
|
|||
user_status.initialize(state_data.user_status);
|
||||
compose_recipient.initialize();
|
||||
compose_pm_pill.initialize({
|
||||
on_pill_create_or_remove: compose_recipient.update_placeholder_text,
|
||||
on_pill_create_or_remove() {
|
||||
compose_recipient.update_placeholder_text();
|
||||
compose_recipient.check_posting_policy_for_compose_box();
|
||||
},
|
||||
});
|
||||
compose_closed_ui.initialize();
|
||||
compose_reply.initialize();
|
||||
|
|
|
@ -24,6 +24,7 @@ import * as dialog_widget from "./dialog_widget";
|
|||
import * as hash_util from "./hash_util";
|
||||
import {$t, $t_html} from "./i18n";
|
||||
import * as message_lists from "./message_lists";
|
||||
import {user_can_send_direct_message} from "./message_util";
|
||||
import * as message_view from "./message_view";
|
||||
import * as muted_users from "./muted_users";
|
||||
import * as overlays from "./overlays";
|
||||
|
@ -32,7 +33,6 @@ import * as people from "./people";
|
|||
import * as popover_menus from "./popover_menus";
|
||||
import {hide_all} from "./popovers";
|
||||
import * as rows from "./rows";
|
||||
import * as settings_config from "./settings_config";
|
||||
import * as sidebar_ui from "./sidebar_ui";
|
||||
import {current_user, realm} from "./state_data";
|
||||
import * as timerender from "./timerender";
|
||||
|
@ -263,13 +263,13 @@ function get_user_card_popover_data(
|
|||
.map((f) => user_profile.get_custom_profile_field_data(user, f, field_types))
|
||||
.filter((f) => f.display_in_profile_summary && f.value !== undefined && f.value !== null);
|
||||
|
||||
const user_id_string = user.user_id.toString();
|
||||
const can_send_private_message =
|
||||
user_can_send_direct_message(user_id_string) && is_active && !is_me;
|
||||
|
||||
const args = {
|
||||
invisible_mode,
|
||||
can_send_private_message:
|
||||
is_active &&
|
||||
!is_me &&
|
||||
realm.realm_private_message_policy !==
|
||||
settings_config.private_message_policy_values.disabled.code,
|
||||
can_send_private_message,
|
||||
display_profile_fields,
|
||||
has_message_context,
|
||||
is_active,
|
||||
|
|
|
@ -3,6 +3,7 @@ import type {z} from "zod";
|
|||
import * as blueslip from "./blueslip";
|
||||
import {FoldDict} from "./fold_dict";
|
||||
import * as group_permission_settings from "./group_permission_settings";
|
||||
import {$t} from "./i18n";
|
||||
import * as settings_config from "./settings_config";
|
||||
import type {StateData, user_group_schema} from "./state_data";
|
||||
import {current_user} from "./state_data";
|
||||
|
@ -225,6 +226,14 @@ export function is_user_in_group(user_group_id: number, user_id: number): boolea
|
|||
return false;
|
||||
}
|
||||
|
||||
function get_display_name_for_system_group_option(setting_name: string, name: string): string {
|
||||
// We use a special label for the "Nobody" system group for clarity.
|
||||
if (setting_name === "direct_message_permission_group" && name === "Nobody") {
|
||||
return $t({defaultMessage: "Direct messages disabled"});
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
export function get_realm_user_groups_for_dropdown_list_widget(
|
||||
setting_name: string,
|
||||
setting_type: "realm" | "stream" | "group",
|
||||
|
@ -277,7 +286,7 @@ export function get_realm_user_groups_for_dropdown_list_widget(
|
|||
throw new Error(`Unknown group name: ${group.name}`);
|
||||
}
|
||||
return {
|
||||
name: group.display_name,
|
||||
name: get_display_name_for_system_group_option(setting_name, group.display_name),
|
||||
unique_id: user_group.id,
|
||||
};
|
||||
});
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
{{#> compose_banner }}
|
||||
<p class="banner_message">
|
||||
{{error_message}}
|
||||
{{#tr}}
|
||||
<z-link>Learn more.</z-link>
|
||||
{{#*inline "z-link"}}<a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">{{> @partial-block}}</a>{{/inline}}
|
||||
{{/tr}}
|
||||
</p>
|
||||
{{/compose_banner}}
|
|
@ -110,6 +110,24 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div id="org-direct-message-permissions" class="settings-subsection-parent">
|
||||
<div class="subsection-header">
|
||||
<h3>{{t "Direct message permissions" }}
|
||||
{{> ../help_link_widget link="/help/restrict-direct-messages" }}
|
||||
</h3>
|
||||
{{> settings_save_discard_widget section_name="direct-message-permissions" }}
|
||||
</div>
|
||||
{{> ../dropdown_widget_with_label
|
||||
widget_name="realm_direct_message_permission_group"
|
||||
label=(t 'Who can authorize a direct message conversation')
|
||||
value_type="number" }}
|
||||
|
||||
{{> ../dropdown_widget_with_label
|
||||
widget_name="realm_direct_message_initiator_group"
|
||||
label=(t 'Who can start a direct message conversation')
|
||||
value_type="number" }}
|
||||
</div>
|
||||
|
||||
<div id="org-msg-editing" class="settings-subsection-parent">
|
||||
<div class="subsection-header">
|
||||
<h3>{{t "Message editing" }}
|
||||
|
@ -336,15 +354,6 @@
|
|||
{{> dropdown_options_widget option_values=common_policy_values}}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="realm_private_message_policy" class="settings-field-label">{{t "Who can use direct messages" }} ({{t "beta" }})
|
||||
{{> ../help_link_widget link="/help/restrict-direct-messages" }}
|
||||
</label>
|
||||
<select name="realm_private_message_policy" class="setting-widget prop-element settings_select bootstrap-focus-style" id="id_realm_private_message_policy" data-setting-widget-type="number">
|
||||
{{> dropdown_options_widget option_values=private_message_policy_values}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
@ -18,9 +18,6 @@
|
|||
{{t 'Reply to selected conversation' }}
|
||||
{{tooltip_hotkey_hints "R"}}
|
||||
</template>
|
||||
<template id="compose_reply_direct_disabled_button_tooltip_template">
|
||||
{{t 'You are not allowed to send direct messages in this organization.' }}
|
||||
</template>
|
||||
<template id="left_bar_compose_mobile_button_tooltip_template">
|
||||
{{t 'New message' }}
|
||||
{{tooltip_hotkey_hints "C"}}
|
||||
|
|
|
@ -11,7 +11,7 @@ const {run_test, noop} = require("./lib/test");
|
|||
const $ = require("./lib/zjquery");
|
||||
const {current_user, page_params, realm, user_settings} = require("./lib/zpage_params");
|
||||
|
||||
const settings_config = zrequire("settings_config");
|
||||
const user_groups = zrequire("user_groups");
|
||||
|
||||
set_global("document", {
|
||||
querySelector() {},
|
||||
|
@ -115,6 +115,23 @@ const social = {
|
|||
};
|
||||
stream_data.add_sub(social);
|
||||
|
||||
const nobody = {
|
||||
name: "role:nobody",
|
||||
id: 1,
|
||||
members: new Set([]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const everyone = {
|
||||
name: "role:everyone",
|
||||
id: 2,
|
||||
members: new Set([30]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone]});
|
||||
|
||||
function test_ui(label, f) {
|
||||
// TODO: initialize data more aggressively.
|
||||
run_test(label, f);
|
||||
|
@ -462,6 +479,8 @@ test_ui("finish", ({override, override_rewire}) => {
|
|||
compose_state.set_message_type("private");
|
||||
override(compose_pm_pill, "get_emails", () => "bob@example.com");
|
||||
override(compose_pm_pill, "get_user_ids", () => []);
|
||||
override(realm, "realm_direct_message_permission_group", nobody.id);
|
||||
override(realm, "realm_direct_message_initiator_group", everyone.id);
|
||||
|
||||
let compose_finished_event_checked = false;
|
||||
$(document).on("compose_finished.zulip", () => {
|
||||
|
@ -817,11 +836,8 @@ test_ui("create_message_object", ({override, override_rewire}) => {
|
|||
|
||||
test_ui("DM policy disabled", ({override, override_rewire}) => {
|
||||
// Disable dms in the organisation
|
||||
override(
|
||||
realm,
|
||||
"realm_private_message_policy",
|
||||
settings_config.private_message_policy_values.disabled.code,
|
||||
);
|
||||
override(realm, "realm_direct_message_permission_group", nobody.id);
|
||||
override(realm, "realm_direct_message_initiator_group", everyone.id);
|
||||
let reply_disabled = false;
|
||||
override_rewire(compose_closed_ui, "update_reply_button_state", (disabled = false) => {
|
||||
reply_disabled = disabled;
|
||||
|
@ -839,6 +855,8 @@ test_ui("DM policy disabled", ({override, override_rewire}) => {
|
|||
test_ui("narrow_button_titles", ({override}) => {
|
||||
override(narrow_state, "pm_ids_string", () => "31");
|
||||
override(narrow_state, "is_message_feed_visible", () => true);
|
||||
override(realm, "realm_direct_message_permission_group", everyone.id);
|
||||
override(realm, "realm_direct_message_initiator_group", everyone.id);
|
||||
compose_closed_ui.update_buttons_for_private();
|
||||
assert.equal(
|
||||
$("#new_conversation_button").text(),
|
||||
|
|
|
@ -5,10 +5,27 @@ const {strict: assert} = require("assert");
|
|||
const {mock_banners} = require("./lib/compose_banner");
|
||||
const {mock_esm, set_global, zrequire} = require("./lib/namespace");
|
||||
const {run_test, noop} = require("./lib/test");
|
||||
const blueslip = require("./lib/zblueslip");
|
||||
const $ = require("./lib/zjquery");
|
||||
const {realm} = require("./lib/zpage_params");
|
||||
|
||||
const settings_config = zrequire("settings_config");
|
||||
const user_groups = zrequire("user_groups");
|
||||
|
||||
const nobody = {
|
||||
name: "role:nobody",
|
||||
id: 1,
|
||||
members: new Set([]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const everyone = {
|
||||
name: "role:everyone",
|
||||
id: 2,
|
||||
members: new Set([30]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone]});
|
||||
|
||||
set_global("document", {
|
||||
to_$: () => $("document-stub"),
|
||||
|
@ -365,6 +382,8 @@ test("reply_with_mention", ({override, override_rewire, mock_template}) => {
|
|||
test("quote_and_reply", ({disallow, override, override_rewire}) => {
|
||||
override_rewire(compose_recipient, "on_compose_select_recipient_update", noop);
|
||||
override_rewire(compose_reply, "selection_within_message_id", () => undefined);
|
||||
override(realm, "realm_direct_message_permission_group", nobody.id);
|
||||
override(realm, "realm_direct_message_initiator_group", everyone.id);
|
||||
|
||||
mock_banners();
|
||||
compose_state.set_message_type("stream");
|
||||
|
@ -508,6 +527,7 @@ test("on_narrow", ({override, override_rewire}) => {
|
|||
};
|
||||
people.add_active_user(bot);
|
||||
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone]});
|
||||
let cancel_called = false;
|
||||
override_rewire(compose_actions, "cancel", () => {
|
||||
cancel_called = true;
|
||||
|
@ -544,8 +564,8 @@ test("on_narrow", ({override, override_rewire}) => {
|
|||
start_called = true;
|
||||
});
|
||||
narrowed_by_pm_reply = true;
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.disabled.code;
|
||||
realm.realm_direct_message_permission_group = nobody.id;
|
||||
realm.realm_direct_message_initiator_group = everyone.id;
|
||||
compose_actions.on_narrow({
|
||||
force_close: false,
|
||||
trigger: "not-search",
|
||||
|
@ -560,8 +580,8 @@ test("on_narrow", ({override, override_rewire}) => {
|
|||
});
|
||||
assert.ok(start_called);
|
||||
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.by_anyone.code;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
blueslip.expect("warn", "Unknown emails");
|
||||
compose_actions.on_narrow({
|
||||
force_close: false,
|
||||
trigger: "not-search",
|
||||
|
|
|
@ -21,6 +21,7 @@ const settings_config = zrequire("settings_config");
|
|||
const settings_data = mock_esm("../src/settings_data");
|
||||
const stream_data = zrequire("stream_data");
|
||||
const compose_recipient = zrequire("/compose_recipient");
|
||||
const user_groups = zrequire("user_groups");
|
||||
|
||||
const me = {
|
||||
email: "me@example.com",
|
||||
|
@ -39,6 +40,7 @@ const bob = {
|
|||
email: "bob@example.com",
|
||||
user_id: 32,
|
||||
full_name: "Bob",
|
||||
is_admin: true,
|
||||
};
|
||||
|
||||
const social_sub = {
|
||||
|
@ -64,6 +66,29 @@ const welcome_bot = {
|
|||
|
||||
people.add_cross_realm_user(welcome_bot);
|
||||
|
||||
const nobody = {
|
||||
name: "role:nobody",
|
||||
id: 1,
|
||||
members: new Set([]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const everyone = {
|
||||
name: "role:everyone",
|
||||
id: 2,
|
||||
members: new Set([30]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const admin = {
|
||||
name: "role:administrators",
|
||||
id: 3,
|
||||
members: new Set([32]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone, admin]});
|
||||
function test_ui(label, f) {
|
||||
// The sloppy_$ flag lets us reuse setup from prior tests.
|
||||
run_test(label, (helpers) => {
|
||||
|
@ -148,6 +173,8 @@ test_ui("validate", ({mock_template}) => {
|
|||
add_content_to_compose_box();
|
||||
compose_state.private_message_recipient("");
|
||||
let pm_recipient_error_rendered = false;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
realm.realm_direct_message_initiator_group = everyone.id;
|
||||
mock_template("compose_banner/compose_banner.hbs", false, (data) => {
|
||||
assert.equal(data.classname, compose_banner.CLASSNAMES.missing_private_message_recipient);
|
||||
assert.equal(
|
||||
|
@ -167,6 +194,16 @@ test_ui("validate", ({mock_template}) => {
|
|||
assert.ok(compose_validate.validate());
|
||||
assert.ok(!pm_recipient_error_rendered);
|
||||
|
||||
realm.realm_direct_message_initiator_group = admin.id;
|
||||
assert.ok(compose_validate.validate());
|
||||
assert.ok(!pm_recipient_error_rendered);
|
||||
|
||||
realm.realm_direct_message_permission_group = admin.id;
|
||||
assert.ok(compose_validate.validate());
|
||||
assert.ok(!pm_recipient_error_rendered);
|
||||
|
||||
realm.realm_direct_message_initiator_group = everyone.id;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
people.deactivate(bob);
|
||||
let deactivated_user_error_rendered = false;
|
||||
mock_template("compose_banner/compose_banner.hbs", false, (data) => {
|
||||
|
|
|
@ -28,6 +28,7 @@ const alert_words_ui = mock_esm("../src/alert_words_ui");
|
|||
const attachments_ui = mock_esm("../src/attachments_ui");
|
||||
const audible_notifications = mock_esm("../src/audible_notifications");
|
||||
const bot_data = mock_esm("../src/bot_data");
|
||||
const compose_banner = mock_esm("../src/compose_banner");
|
||||
const compose_pm_pill = mock_esm("../src/compose_pm_pill");
|
||||
const theme = mock_esm("../src/theme");
|
||||
const emoji_picker = mock_esm("../src/emoji_picker");
|
||||
|
@ -441,6 +442,7 @@ run_test("realm settings", ({override}) => {
|
|||
current_user.is_admin = true;
|
||||
realm.realm_date_created = new Date("2023-01-01Z");
|
||||
|
||||
override(settings_org, "check_disable_direct_message_initiator_group_dropdown", noop);
|
||||
override(settings_org, "sync_realm_settings", noop);
|
||||
override(settings_bots, "update_bot_permissions_ui", noop);
|
||||
override(settings_invites, "update_invite_user_panel", noop);
|
||||
|
@ -449,6 +451,7 @@ run_test("realm settings", ({override}) => {
|
|||
override(narrow_title, "redraw_title", noop);
|
||||
override(navbar_alerts, "check_profile_incomplete", noop);
|
||||
override(navbar_alerts, "show_profile_incomplete", noop);
|
||||
override(compose_banner, "clear_errors", noop);
|
||||
|
||||
function test_electron_dispatch(event, fake_send_event) {
|
||||
with_overrides(({override}) => {
|
||||
|
@ -573,6 +576,7 @@ run_test("realm settings", ({override}) => {
|
|||
realm.realm_edit_topic_policy = 3;
|
||||
realm.realm_authentication_methods = {Google: {enabled: false, available: true}};
|
||||
realm.realm_can_create_public_channel_group = 1;
|
||||
realm.realm_direct_message_permission_group = 1;
|
||||
override(settings_org, "populate_auth_methods", noop);
|
||||
dispatch(event);
|
||||
assert_same(realm.realm_create_multiuse_invite_group, 3);
|
||||
|
@ -583,6 +587,7 @@ run_test("realm settings", ({override}) => {
|
|||
Google: {enabled: true, available: true},
|
||||
});
|
||||
assert_same(realm.realm_can_create_public_channel_group, 3);
|
||||
assert_same(realm.realm_direct_message_permission_group, 3);
|
||||
assert_same(update_stream_privacy_choices_called, true);
|
||||
|
||||
event = event_fixtures.realm__update_dict__icon;
|
||||
|
|
|
@ -374,6 +374,7 @@ exports.fixtures = {
|
|||
Google: {enabled: true, available: true},
|
||||
},
|
||||
can_create_public_channel_group: 3,
|
||||
direct_message_permission_group: 3,
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
@ -16,12 +16,13 @@ const stream_data = zrequire("stream_data");
|
|||
const {Filter} = zrequire("../src/filter");
|
||||
const message_view = zrequire("message_view");
|
||||
const narrow_title = zrequire("narrow_title");
|
||||
const settings_config = zrequire("settings_config");
|
||||
const recent_view_util = zrequire("recent_view_util");
|
||||
const inbox_util = zrequire("inbox_util");
|
||||
const message_lists = zrequire("message_lists");
|
||||
const user_groups = zrequire("user_groups");
|
||||
|
||||
mock_esm("../src/compose_banner", {
|
||||
clear_errors() {},
|
||||
clear_search_view_banner() {},
|
||||
});
|
||||
const compose_pm_pill = mock_esm("../src/compose_pm_pill");
|
||||
|
@ -49,6 +50,9 @@ function set_filter(terms) {
|
|||
message_lists.set_current({
|
||||
data: {
|
||||
filter: new Filter(terms),
|
||||
fetch_status: {
|
||||
has_found_newest: () => true,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -78,6 +82,23 @@ const bot = {
|
|||
is_bot: true,
|
||||
};
|
||||
|
||||
const nobody = {
|
||||
name: "role:nobody",
|
||||
id: 1,
|
||||
members: new Set([]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const everyone = {
|
||||
name: "role:everyone",
|
||||
id: 2,
|
||||
members: new Set([5]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone]});
|
||||
|
||||
run_test("empty_narrow_html", ({mock_template}) => {
|
||||
mock_template("empty_feed_notice.hbs", true, (_data, html) => html);
|
||||
|
||||
|
@ -298,21 +319,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
),
|
||||
);
|
||||
|
||||
// organization has disabled sending direct messages
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.disabled.code;
|
||||
set_filter([["is", "dm"]]);
|
||||
narrow_banner.show_empty_narrow_message();
|
||||
assert.equal(
|
||||
$(".empty_feed_notice_main").html(),
|
||||
empty_narrow_html(
|
||||
"translated: You are not allowed to send direct messages in this organization.",
|
||||
),
|
||||
);
|
||||
|
||||
// sending direct messages enabled
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.by_anyone.code;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
realm.realm_direct_message_initiator_group = everyone.id;
|
||||
set_filter([["is", "dm"]]);
|
||||
narrow_banner.show_empty_narrow_message();
|
||||
assert.equal(
|
||||
|
@ -345,8 +353,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
);
|
||||
|
||||
// organization has disabled sending direct messages
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.disabled.code;
|
||||
realm.realm_direct_message_permission_group = nobody.id;
|
||||
|
||||
// prioritize information about invalid user(s) in narrow/search
|
||||
set_filter([["dm", ["Yo"]]]);
|
||||
|
@ -369,7 +376,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
assert.equal(
|
||||
$(".empty_feed_notice_main").html(),
|
||||
empty_narrow_html(
|
||||
"translated: You are not allowed to send direct messages in this organization.",
|
||||
"translated: Direct messages are disabled in this organization.",
|
||||
'translated HTML: <a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">Learn more.</a>',
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -393,13 +401,13 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
assert.equal(
|
||||
$(".empty_feed_notice_main").html(),
|
||||
empty_narrow_html(
|
||||
"translated: You are not allowed to send direct messages in this organization.",
|
||||
"translated: Direct messages are disabled in this organization.",
|
||||
'translated HTML: <a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">Learn more.</a>',
|
||||
),
|
||||
);
|
||||
|
||||
// sending direct messages enabled
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.by_anyone.code;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
set_filter([["dm", "alice@example.com"]]);
|
||||
narrow_banner.show_empty_narrow_message();
|
||||
assert.equal(
|
||||
|
@ -433,8 +441,7 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
);
|
||||
|
||||
// organization has disabled sending direct messages
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.disabled.code;
|
||||
realm.realm_direct_message_permission_group = nobody.id;
|
||||
|
||||
// prioritize information about invalid user in narrow/search
|
||||
set_filter([["dm-including", ["Yo"]]]);
|
||||
|
@ -449,7 +456,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
assert.equal(
|
||||
$(".empty_feed_notice_main").html(),
|
||||
empty_narrow_html(
|
||||
"translated: You are not allowed to send direct messages in this organization.",
|
||||
"translated: Direct messages are disabled in this organization.",
|
||||
'translated HTML: <a target="_blank" rel="noopener noreferrer" href="/help/restrict-direct-messages">Learn more.</a>',
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -463,8 +471,8 @@ run_test("show_empty_narrow_message", ({mock_template}) => {
|
|||
);
|
||||
|
||||
// sending direct messages enabled
|
||||
realm.realm_private_message_policy =
|
||||
settings_config.private_message_policy_values.by_anyone.code;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
realm.realm_direct_message_permission_group = everyone.id;
|
||||
set_filter([["dm-including", "alice@example.com"]]);
|
||||
narrow_banner.show_empty_narrow_message();
|
||||
assert.equal(
|
||||
|
|
|
@ -19,6 +19,7 @@ const settings_data = mock_esm("../src/settings_data", {
|
|||
|
||||
const muted_users = zrequire("muted_users");
|
||||
const people = zrequire("people");
|
||||
const user_groups = zrequire("user_groups");
|
||||
|
||||
const welcome_bot = {
|
||||
email: "welcome-bot@example.com",
|
||||
|
@ -59,6 +60,23 @@ function initialize() {
|
|||
people._add_user(unknown_user);
|
||||
}
|
||||
|
||||
const nobody = {
|
||||
name: "role:nobody",
|
||||
id: 1,
|
||||
members: new Set([]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
const everyone = {
|
||||
name: "role:everyone",
|
||||
id: 2,
|
||||
members: new Set([30]),
|
||||
is_system_group: true,
|
||||
direct_subgroup_ids: new Set([]),
|
||||
};
|
||||
|
||||
user_groups.initialize({realm_user_groups: [nobody, everyone]});
|
||||
|
||||
function test_people(label, f) {
|
||||
run_test(label, (helpers) => {
|
||||
initialize();
|
||||
|
@ -1485,6 +1503,17 @@ test_people("get_user_by_id_assert_valid", ({override}) => {
|
|||
assert.equal(user.email, charles.email);
|
||||
});
|
||||
|
||||
test_people("user_can_initiate_direct_message_thread", () => {
|
||||
people.add_active_user(welcome_bot);
|
||||
realm.realm_direct_message_initiator_group = nobody.id;
|
||||
assert.ok(!people.user_can_initiate_direct_message_thread("32"));
|
||||
// Can send if only bots and self are present.
|
||||
assert.ok(people.user_can_initiate_direct_message_thread("4,30"));
|
||||
|
||||
realm.realm_direct_message_initiator_group = everyone.id;
|
||||
assert.ok(people.user_can_initiate_direct_message_thread("32"));
|
||||
});
|
||||
|
||||
// reset to native Date()
|
||||
run_test("reset MockDate", () => {
|
||||
MockDate.reset();
|
||||
|
|
|
@ -22,7 +22,7 @@ import orjson
|
|||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import IntegrityError, transaction
|
||||
from django.db.models import F
|
||||
from django.db.models import F, Q
|
||||
from django.utils.html import escape
|
||||
from django.utils.timezone import now as timezone_now
|
||||
from django.utils.translation import gettext as _
|
||||
|
@ -39,6 +39,8 @@ from zerver.lib.alert_words import get_alert_word_automaton
|
|||
from zerver.lib.cache import cache_with_key, user_profile_delivery_email_cache_key
|
||||
from zerver.lib.create_user import create_user
|
||||
from zerver.lib.exceptions import (
|
||||
DirectMessageInitiationError,
|
||||
DirectMessagePermissionError,
|
||||
JsonableError,
|
||||
MarkdownRenderingError,
|
||||
StreamDoesNotExistError,
|
||||
|
@ -80,6 +82,7 @@ from zerver.lib.string_validation import check_stream_name
|
|||
from zerver.lib.timestamp import timestamp_to_datetime
|
||||
from zerver.lib.topic import participants_for_topic
|
||||
from zerver.lib.url_preview.types import UrlEmbedData
|
||||
from zerver.lib.user_groups import is_any_user_in_group, is_user_in_group
|
||||
from zerver.lib.user_message import UserMessageLite, bulk_insert_ums
|
||||
from zerver.lib.users import (
|
||||
check_can_access_user,
|
||||
|
@ -104,7 +107,6 @@ from zerver.models import (
|
|||
)
|
||||
from zerver.models.clients import get_client
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import PrivateMessagePolicyEnum
|
||||
from zerver.models.recipients import get_direct_message_group_user_ids
|
||||
from zerver.models.scheduled_jobs import NotificationTriggers
|
||||
from zerver.models.streams import get_stream, get_stream_by_id_in_realm
|
||||
|
@ -1525,19 +1527,61 @@ def validate_stream_id_with_pm_notification(
|
|||
return stream
|
||||
|
||||
|
||||
def check_private_message_policy(
|
||||
realm: Realm, sender: UserProfile, user_profiles: Sequence[UserProfile]
|
||||
def check_can_send_direct_message(
|
||||
realm: Realm, sender: UserProfile, recipient_users: Sequence[UserProfile], recipient: Recipient
|
||||
) -> None:
|
||||
if realm.private_message_policy == PrivateMessagePolicyEnum.DISABLED:
|
||||
if sender.is_bot or (
|
||||
len(user_profiles) == 1 and (user_profiles[0].is_bot or user_profiles[0] == sender)
|
||||
):
|
||||
# We allow direct messages only between users and bots or to oneself,
|
||||
# to avoid breaking the tutorial as well as automated
|
||||
# notifications from system bots to users.
|
||||
if sender.is_bot:
|
||||
return
|
||||
|
||||
if all(user_profile.is_bot or user_profile.id == sender.id for user_profile in recipient_users):
|
||||
return
|
||||
|
||||
direct_message_permission_group = realm.direct_message_permission_group
|
||||
|
||||
if (
|
||||
not hasattr(direct_message_permission_group, "named_user_group")
|
||||
or direct_message_permission_group.named_user_group.name != SystemGroups.EVERYONE
|
||||
):
|
||||
user_ids = [recipient_user.id for recipient_user in recipient_users] + [sender.id]
|
||||
if not is_any_user_in_group(direct_message_permission_group, user_ids):
|
||||
is_nobody_group = (
|
||||
direct_message_permission_group.named_user_group.name == SystemGroups.NOBODY
|
||||
)
|
||||
raise DirectMessagePermissionError(is_nobody_group)
|
||||
|
||||
direct_message_initiator_group = realm.direct_message_initiator_group
|
||||
if (
|
||||
not hasattr(direct_message_initiator_group, "named_user_group")
|
||||
or direct_message_initiator_group.named_user_group.name != SystemGroups.EVERYONE
|
||||
):
|
||||
if is_user_in_group(direct_message_initiator_group, sender):
|
||||
return
|
||||
|
||||
raise JsonableError(_("Direct messages are disabled in this organization."))
|
||||
# TODO: This check is inefficient; we should in the future be able to cache
|
||||
# on the Huddle object whether the conversation already exists, likely in the
|
||||
# form of a `first_message_id` field, and be able to save doing this check in the
|
||||
# common case that this is not the first message in a conversation.
|
||||
if recipient.type == Recipient.PERSONAL:
|
||||
recipient_user_profile = recipient_users[0]
|
||||
previous_messages_exist = (
|
||||
Message.objects.filter(
|
||||
realm=realm,
|
||||
recipient__type=Recipient.PERSONAL,
|
||||
)
|
||||
.filter(
|
||||
Q(sender=sender, recipient=recipient)
|
||||
| Q(sender=recipient_user_profile, recipient_id=sender.recipient_id)
|
||||
)
|
||||
.exists()
|
||||
)
|
||||
else:
|
||||
assert recipient.type == Recipient.DIRECT_MESSAGE_GROUP
|
||||
previous_messages_exist = Message.objects.filter(
|
||||
realm=realm,
|
||||
recipient=recipient,
|
||||
).exists()
|
||||
if not previous_messages_exist:
|
||||
raise DirectMessageInitiationError
|
||||
|
||||
|
||||
def check_sender_can_access_recipients(
|
||||
|
@ -1692,8 +1736,6 @@ def check_message(
|
|||
|
||||
check_sender_can_access_recipients(realm, sender, user_profiles)
|
||||
|
||||
check_private_message_policy(realm, sender, user_profiles)
|
||||
|
||||
recipients_for_user_creation_events = get_recipients_for_user_creation_events(
|
||||
realm, sender, user_profiles
|
||||
)
|
||||
|
@ -1709,6 +1751,8 @@ def check_message(
|
|||
except ValidationError as e:
|
||||
assert isinstance(e.messages[0], str)
|
||||
raise JsonableError(e.messages[0])
|
||||
|
||||
check_can_send_direct_message(realm, sender, user_profiles, recipient)
|
||||
else:
|
||||
# This is defensive code--Addressee already validates
|
||||
# the message type.
|
||||
|
|
|
@ -113,8 +113,14 @@ def get_usable_missed_message_address(address: str) -> MissedMessageEmailAddress
|
|||
mm_address = MissedMessageEmailAddress.objects.select_related(
|
||||
"user_profile",
|
||||
"user_profile__realm",
|
||||
# Fetch group settings that are needed to determine whether a user
|
||||
# can send a direct message to a given recipient.
|
||||
"user_profile__realm__can_access_all_users_group",
|
||||
"user_profile__realm__can_access_all_users_group__named_user_group",
|
||||
"user_profile__realm__direct_message_initiator_group",
|
||||
"user_profile__realm__direct_message_initiator_group__named_user_group",
|
||||
"user_profile__realm__direct_message_permission_group",
|
||||
"user_profile__realm__direct_message_permission_group__named_user_group",
|
||||
"message",
|
||||
"message__sender",
|
||||
"message__recipient",
|
||||
|
|
|
@ -1049,6 +1049,8 @@ group_setting_update_data_type = DictType(
|
|||
("can_access_all_users_group", int),
|
||||
("can_create_public_channel_group", group_setting_type),
|
||||
("can_create_private_channel_group", group_setting_type),
|
||||
("direct_message_initiator_group", group_setting_type),
|
||||
("direct_message_permission_group", group_setting_type),
|
||||
],
|
||||
)
|
||||
|
||||
|
|
|
@ -519,6 +519,25 @@ class InvitationError(JsonableError):
|
|||
self.daily_limit_reached: bool = daily_limit_reached
|
||||
|
||||
|
||||
class DirectMessageInitiationError(JsonableError):
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
@override
|
||||
def msg_format() -> str:
|
||||
return _("You do not have permission to initiate direct message conversations.")
|
||||
|
||||
|
||||
class DirectMessagePermissionError(JsonableError):
|
||||
def __init__(self, is_nobody_group: bool) -> None:
|
||||
if is_nobody_group:
|
||||
msg = _("Direct messages are disabled in this organization.")
|
||||
else:
|
||||
msg = _("You do not have permission to send direct messages to this recipient.")
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class AccessDeniedError(JsonableError):
|
||||
http_status_code = 403
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
# Generated by Django 4.2.8 on 2024-01-01 10:02
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0548_realmuserdefault_web_channel_default_view_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="realm",
|
||||
name="direct_message_initiator_group",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.RESTRICT,
|
||||
related_name="+",
|
||||
to="zerver.usergroup",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="realm",
|
||||
name="direct_message_permission_group",
|
||||
field=models.ForeignKey(
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.RESTRICT,
|
||||
related_name="+",
|
||||
to="zerver.usergroup",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -0,0 +1,56 @@
|
|||
# Generated by Django 4.2.8 on 2024-01-01 10:03
|
||||
|
||||
|
||||
from django.db import migrations
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
from django.db.models import OuterRef
|
||||
|
||||
|
||||
def set_default_value_for_direct_message_initiator_group_and_more(
|
||||
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||
) -> None:
|
||||
Realm = apps.get_model("zerver", "Realm")
|
||||
NamedUserGroup = apps.get_model("zerver", "NamedUserGroup")
|
||||
|
||||
EVERYONE_GROUP_NAME = "role:everyone"
|
||||
NOBODY_GROUP_NAME = "role:nobody"
|
||||
PRIVATE_MESSAGE_POLICY_DISABLED = 2
|
||||
|
||||
Realm.objects.filter(
|
||||
direct_message_initiator_group=None,
|
||||
).update(
|
||||
direct_message_initiator_group=NamedUserGroup.objects.filter(
|
||||
name=EVERYONE_GROUP_NAME, realm=OuterRef("id"), is_system_group=True
|
||||
).values("pk")
|
||||
)
|
||||
Realm.objects.filter(
|
||||
direct_message_permission_group=None, private_message_policy=PRIVATE_MESSAGE_POLICY_DISABLED
|
||||
).update(
|
||||
direct_message_permission_group=NamedUserGroup.objects.filter(
|
||||
name=NOBODY_GROUP_NAME, realm=OuterRef("id"), is_system_group=True
|
||||
).values("pk")
|
||||
)
|
||||
Realm.objects.filter(
|
||||
direct_message_permission_group=None,
|
||||
).exclude(private_message_policy=PRIVATE_MESSAGE_POLICY_DISABLED).update(
|
||||
direct_message_permission_group=NamedUserGroup.objects.filter(
|
||||
name=EVERYONE_GROUP_NAME, realm=OuterRef("id"), is_system_group=True
|
||||
).values("pk")
|
||||
)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
("zerver", "0549_realm_direct_message_initiator_group_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
set_default_value_for_direct_message_initiator_group_and_more,
|
||||
elidable=True,
|
||||
reverse_code=migrations.RunPython.noop,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,31 @@
|
|||
# Generated by Django 4.2.8 on 2024-01-01 11:28
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0550_set_default_value_for_realm_direct_message_initiator_group_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name="realm",
|
||||
name="direct_message_initiator_group",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.RESTRICT,
|
||||
related_name="+",
|
||||
to="zerver.usergroup",
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="realm",
|
||||
name="direct_message_permission_group",
|
||||
field=models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.RESTRICT,
|
||||
related_name="+",
|
||||
to="zerver.usergroup",
|
||||
),
|
||||
),
|
||||
]
|
|
@ -332,6 +332,18 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||
)
|
||||
|
||||
# UserGroup of which at least one member must be included as sender
|
||||
# or recipient in all personal and group direct messages.
|
||||
direct_message_initiator_group = models.ForeignKey(
|
||||
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||
)
|
||||
|
||||
# UserGroup whose members must be included as sender or recipient in all
|
||||
# direct messages.
|
||||
direct_message_permission_group = models.ForeignKey(
|
||||
"UserGroup", on_delete=models.RESTRICT, related_name="+"
|
||||
)
|
||||
|
||||
# on_delete field here is set to RESTRICT because we don't want to allow
|
||||
# deleting a user group in case it is referenced by this setting.
|
||||
# We are not using PROTECT since we want to allow deletion of user groups
|
||||
|
@ -724,11 +736,31 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
default_group_name=SystemGroups.MEMBERS,
|
||||
id_field_name="can_create_private_channel_group_id",
|
||||
),
|
||||
direct_message_initiator_group=GroupPermissionSetting(
|
||||
require_system_group=False,
|
||||
allow_internet_group=False,
|
||||
allow_owners_group=True,
|
||||
allow_nobody_group=True,
|
||||
allow_everyone_group=True,
|
||||
default_group_name=SystemGroups.EVERYONE,
|
||||
id_field_name="direct_message_initiator_group_id",
|
||||
),
|
||||
direct_message_permission_group=GroupPermissionSetting(
|
||||
require_system_group=False,
|
||||
allow_internet_group=False,
|
||||
allow_owners_group=True,
|
||||
allow_nobody_group=True,
|
||||
allow_everyone_group=True,
|
||||
default_group_name=SystemGroups.EVERYONE,
|
||||
id_field_name="direct_message_permission_group_id",
|
||||
),
|
||||
)
|
||||
|
||||
REALM_PERMISSION_GROUP_SETTINGS_WITH_NEW_API_FORMAT = [
|
||||
"can_create_private_channel_group",
|
||||
"can_create_public_channel_group",
|
||||
"direct_message_initiator_group",
|
||||
"direct_message_permission_group",
|
||||
]
|
||||
|
||||
DIGEST_WEEKDAY_VALUES = [0, 1, 2, 3, 4, 5, 6]
|
||||
|
@ -1092,6 +1124,10 @@ def get_realm_with_settings(realm_id: int) -> Realm:
|
|||
"can_create_public_channel_group__named_user_group",
|
||||
"can_create_private_channel_group",
|
||||
"can_create_private_channel_group__named_user_group",
|
||||
"direct_message_initiator_group",
|
||||
"direct_message_initiator_group__named_user_group",
|
||||
"direct_message_permission_group",
|
||||
"direct_message_permission_group__named_user_group",
|
||||
).get(id=realm_id)
|
||||
|
||||
|
||||
|
|
|
@ -780,6 +780,8 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings):
|
|||
"create_multiuse_invite_group",
|
||||
"create_web_public_stream_policy",
|
||||
"delete_own_message_policy",
|
||||
"direct_message_initiator_group",
|
||||
"direct_message_permission_group",
|
||||
"edit_topic_policy",
|
||||
"invite_to_stream_policy",
|
||||
"invite_to_realm_policy",
|
||||
|
@ -913,6 +915,10 @@ def get_user_profile_by_id(user_profile_id: int) -> UserProfile:
|
|||
"realm",
|
||||
"realm__can_access_all_users_group",
|
||||
"realm__can_access_all_users_group__named_user_group",
|
||||
"realm__direct_message_initiator_group",
|
||||
"realm__direct_message_initiator_group__named_user_group",
|
||||
"realm__direct_message_permission_group",
|
||||
"realm__direct_message_permission_group__named_user_group",
|
||||
"bot_owner",
|
||||
).get(id=user_profile_id)
|
||||
|
||||
|
@ -963,6 +969,10 @@ def get_user_by_delivery_email(email: str, realm: "Realm") -> UserProfile:
|
|||
"realm",
|
||||
"realm__can_access_all_users_group",
|
||||
"realm__can_access_all_users_group__named_user_group",
|
||||
"realm__direct_message_initiator_group",
|
||||
"realm__direct_message_initiator_group__named_user_group",
|
||||
"realm__direct_message_permission_group",
|
||||
"realm__direct_message_permission_group__named_user_group",
|
||||
"bot_owner",
|
||||
).get(delivery_email__iexact=email.strip(), realm=realm)
|
||||
|
||||
|
@ -1003,6 +1013,10 @@ def get_user(email: str, realm: "Realm") -> UserProfile:
|
|||
"realm",
|
||||
"realm__can_access_all_users_group",
|
||||
"realm__can_access_all_users_group__named_user_group",
|
||||
"realm__direct_message_initiator_group",
|
||||
"realm__direct_message_initiator_group__named_user_group",
|
||||
"realm__direct_message_permission_group",
|
||||
"realm__direct_message_permission_group__named_user_group",
|
||||
"bot_owner",
|
||||
).get(email__iexact=email.strip(), realm=realm)
|
||||
|
||||
|
|
|
@ -4306,6 +4306,37 @@ paths:
|
|||
description: |
|
||||
The day of the week when the organization will send
|
||||
its weekly digest email to inactive users.
|
||||
direct_message_initiator_group:
|
||||
allOf:
|
||||
- description: |
|
||||
A [group-setting value](/api/group-setting-values) defining the set of
|
||||
users who have permission to start a new direct message conversation
|
||||
involving other non-bot users. Users who are outside this group and attempt
|
||||
to send the first direct message to a given collection of recipient users
|
||||
will receive an error, unless all other recipients are bots or the sender.
|
||||
|
||||
**Changes**: New in Zulip 9.0 (feature level 270).
|
||||
|
||||
Previously, access to send direct messages was controlled by the
|
||||
`private_message_policy` realm setting, which supported values of
|
||||
1 (enabled) and 2 (disabled).
|
||||
- $ref: "#/components/schemas/GroupSettingValue"
|
||||
direct_message_permission_group:
|
||||
allOf:
|
||||
- description: |
|
||||
A [group-setting value](/api/group-setting-values) defining the set of
|
||||
users who have permission to fully use direct messages. Users outside
|
||||
this group can only send direct messages to conversations where all the
|
||||
recipients are in this group, are bots, or are the sender, ensuring that
|
||||
every direct message conversation will be visible to at least one user in
|
||||
this group.
|
||||
|
||||
**Changes**: New in Zulip 9.0 (feature level 270).
|
||||
|
||||
Previously, access to send direct messages was controlled by the
|
||||
`private_message_policy` realm setting, which supported values of
|
||||
1 (enabled) and 2 (disabled).
|
||||
- $ref: "#/components/schemas/GroupSettingValue"
|
||||
disallow_disposable_email_addresses:
|
||||
type: boolean
|
||||
description: |
|
||||
|
@ -15764,6 +15795,37 @@ paths:
|
|||
- 2 = Nobody
|
||||
|
||||
**Changes**: New in Zulip 3.0 (feature level 1).
|
||||
realm_direct_message_initiator_group:
|
||||
allOf:
|
||||
- description: |
|
||||
A [group-setting value](/api/group-setting-values) defining the set of
|
||||
users who have permission to start a new direct message conversation
|
||||
involving other non-bot users. Users who are outside this group and attempt
|
||||
to send the first direct message to a given collection of recipient users
|
||||
will receive an error, unless all other recipients are bots or the sender.
|
||||
|
||||
**Changes**: New in Zulip 9.0 (feature level 270).
|
||||
|
||||
Previously, access to send direct messages was controlled by the
|
||||
`private_message_policy` realm setting, which supported values of
|
||||
1 (enabled) and 2 (disabled).
|
||||
- $ref: "#/components/schemas/GroupSettingValue"
|
||||
realm_direct_message_permission_group:
|
||||
allOf:
|
||||
- description: |
|
||||
A [group-setting value](/api/group-setting-values) defining the set of
|
||||
users who have permission to fully use direct messages. Users outside
|
||||
this group can only send direct messages to conversations where all the
|
||||
recipients are in this group, are bots, or are the sender, ensuring that
|
||||
every direct message conversation will be visible to at least one user in
|
||||
this group.
|
||||
|
||||
**Changes**: New in Zulip 9.0 (feature level 270).
|
||||
|
||||
Previously, access to send direct messages was controlled by the
|
||||
`private_message_policy` realm setting, which supported values of
|
||||
1 (enabled) and 2 (disabled).
|
||||
- $ref: "#/components/schemas/GroupSettingValue"
|
||||
realm_user_group_edit_policy:
|
||||
type: integer
|
||||
description: |
|
||||
|
|
|
@ -142,6 +142,8 @@ class HomeTest(ZulipTestCase):
|
|||
"realm_description",
|
||||
"realm_digest_emails_enabled",
|
||||
"realm_digest_weekday",
|
||||
"realm_direct_message_initiator_group",
|
||||
"realm_direct_message_permission_group",
|
||||
"realm_disallow_disposable_email_addresses",
|
||||
"realm_domains",
|
||||
"realm_edit_topic_policy",
|
||||
|
|
|
@ -26,13 +26,20 @@ from zerver.actions.message_send import (
|
|||
internal_send_stream_message_by_name,
|
||||
send_rate_limited_pm_notification_to_bot_owner,
|
||||
)
|
||||
from zerver.actions.realm_settings import do_set_realm_property
|
||||
from zerver.actions.realm_settings import (
|
||||
do_change_realm_permission_group_setting,
|
||||
do_set_realm_property,
|
||||
)
|
||||
from zerver.actions.streams import do_change_stream_post_policy
|
||||
from zerver.actions.user_groups import add_subgroups_to_user_group, check_add_user_group
|
||||
from zerver.actions.user_settings import do_change_user_setting
|
||||
from zerver.actions.users import do_change_can_forge_sender, do_deactivate_user
|
||||
from zerver.lib.addressee import Addressee
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.exceptions import (
|
||||
DirectMessageInitiationError,
|
||||
DirectMessagePermissionError,
|
||||
JsonableError,
|
||||
)
|
||||
from zerver.lib.message import get_raw_unread_data, get_recent_private_conversations
|
||||
from zerver.lib.message_cache import MessageDict
|
||||
from zerver.lib.per_request_cache import flush_per_request_caches
|
||||
|
@ -60,7 +67,7 @@ from zerver.models import (
|
|||
)
|
||||
from zerver.models.constants import MAX_TOPIC_NAME_LENGTH
|
||||
from zerver.models.groups import SystemGroups
|
||||
from zerver.models.realms import PrivateMessagePolicyEnum, WildcardMentionPolicyEnum, get_realm
|
||||
from zerver.models.realms import WildcardMentionPolicyEnum, get_realm
|
||||
from zerver.models.recipients import get_or_create_direct_message_group
|
||||
from zerver.models.streams import get_stream
|
||||
from zerver.models.users import get_system_bot, get_user
|
||||
|
@ -2412,27 +2419,169 @@ class PersonalMessageSendTest(ZulipTestCase):
|
|||
receiver=self.example_user("othello"),
|
||||
)
|
||||
|
||||
def test_private_message_policy(self) -> None:
|
||||
def test_direct_message_initiator_group_setting(self) -> None:
|
||||
"""
|
||||
Tests that PrivateMessagePolicyEnum.DISABLED works correctly.
|
||||
Tests that direct_message_initiator_group_setting works correctly.
|
||||
"""
|
||||
user_profile = self.example_user("hamlet")
|
||||
polonius = self.example_user("polonius")
|
||||
admin = self.example_user("iago")
|
||||
cordelia = self.example_user("cordelia")
|
||||
realm = user_profile.realm
|
||||
direct_message_group_1 = [user_profile, admin, polonius]
|
||||
direct_message_group_2 = [user_profile, admin, polonius, cordelia]
|
||||
administrators_system_group = NamedUserGroup.objects.get(
|
||||
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
|
||||
)
|
||||
self.login_user(user_profile)
|
||||
do_set_realm_property(
|
||||
user_profile.realm,
|
||||
"private_message_policy",
|
||||
PrivateMessagePolicyEnum.DISABLED,
|
||||
self.send_personal_message(user_profile, polonius)
|
||||
do_change_realm_permission_group_setting(
|
||||
realm,
|
||||
"direct_message_initiator_group",
|
||||
administrators_system_group,
|
||||
acting_user=None,
|
||||
)
|
||||
with self.assertRaises(JsonableError):
|
||||
self.send_personal_message(user_profile, self.example_user("cordelia"))
|
||||
|
||||
# We can send to Polonius because we'd previously messaged him.
|
||||
self.send_personal_message(user_profile, polonius)
|
||||
# Tests if we can send messages to self irrespective of the value of the setting.
|
||||
self.send_personal_message(user_profile, user_profile)
|
||||
|
||||
# We cannot send to users with whom we does not have any direct message conversation.
|
||||
with self.assertRaises(DirectMessageInitiationError) as direct_message_initiation_error:
|
||||
self.send_personal_message(user_profile, cordelia)
|
||||
self.assertEqual(
|
||||
str(direct_message_initiation_error.exception),
|
||||
"You do not have permission to initiate direct message conversations.",
|
||||
)
|
||||
with self.assertRaises(DirectMessageInitiationError):
|
||||
self.send_personal_message(user_profile, admin)
|
||||
|
||||
# Have the administrator send a message, and verify that allows the user to reply.
|
||||
self.send_personal_message(admin, user_profile)
|
||||
with self.assert_database_query_count(16):
|
||||
self.send_personal_message(user_profile, admin)
|
||||
|
||||
# Tests that user cannot initiate direct message thread in groups.
|
||||
with self.assertRaises(DirectMessageInitiationError):
|
||||
self.send_group_direct_message(user_profile, direct_message_group_1)
|
||||
|
||||
# Have the administrator send a message to the direct message group, and verify
|
||||
# that allows the user to reply.
|
||||
self.send_group_direct_message(admin, direct_message_group_1)
|
||||
with self.assert_database_query_count(20):
|
||||
self.send_group_direct_message(user_profile, direct_message_group_1)
|
||||
|
||||
# We cannot sent to `direct_message_group_2` as no message has been sent to this group yet.
|
||||
with self.assertRaises(DirectMessageInitiationError):
|
||||
self.send_group_direct_message(user_profile, direct_message_group_2)
|
||||
|
||||
bot_profile = self.create_test_bot("testbot", user_profile)
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com", user_profile.realm_id)
|
||||
# Tests if messages to and from bots are allowed irrespective of the value of the setting.
|
||||
self.send_personal_message(user_profile, notification_bot)
|
||||
self.send_personal_message(user_profile, bot_profile)
|
||||
self.send_personal_message(bot_profile, user_profile)
|
||||
|
||||
# Tests if the permission works when the setting is set to a combination of
|
||||
# groups and users.
|
||||
user_group = self.create_or_update_anonymous_group_for_setting(
|
||||
[user_profile],
|
||||
[administrators_system_group],
|
||||
)
|
||||
do_change_realm_permission_group_setting(
|
||||
realm,
|
||||
"direct_message_initiator_group",
|
||||
user_group,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_personal_message(user_profile, cordelia)
|
||||
|
||||
def test_direct_message_permission_group_setting(self) -> None:
|
||||
"""
|
||||
Tests that direct_message_permission_group_setting works correctly.
|
||||
"""
|
||||
user_profile = self.example_user("hamlet")
|
||||
cordelia = self.example_user("cordelia")
|
||||
polonius = self.example_user("polonius")
|
||||
admin = self.example_user("iago")
|
||||
realm = user_profile.realm
|
||||
direct_message_group = [user_profile, cordelia, admin]
|
||||
direct_message_group_without_admin = [user_profile, cordelia, polonius]
|
||||
administrators_system_group = NamedUserGroup.objects.get(
|
||||
name=SystemGroups.ADMINISTRATORS, realm=realm, is_system_group=True
|
||||
)
|
||||
nobody_system_group = NamedUserGroup.objects.get(
|
||||
name=SystemGroups.NOBODY, realm=realm, is_system_group=True
|
||||
)
|
||||
self.login_user(user_profile)
|
||||
do_change_realm_permission_group_setting(
|
||||
realm,
|
||||
"direct_message_permission_group",
|
||||
administrators_system_group,
|
||||
acting_user=None,
|
||||
)
|
||||
# Tests if the user is allowed to send to administrators.
|
||||
with self.assert_database_query_count(16):
|
||||
self.send_personal_message(user_profile, admin)
|
||||
self.send_personal_message(admin, user_profile)
|
||||
# Tests if we can send messages to self irrespective of the value of the setting.
|
||||
self.send_personal_message(user_profile, user_profile)
|
||||
|
||||
# We cannot send direct messages unless one of the recipient is in the
|
||||
# `direct_message_permission_group` (in this case, the
|
||||
# `administrators_system_group`).
|
||||
with self.assertRaises(DirectMessagePermissionError) as direct_message_permission_error:
|
||||
self.send_personal_message(user_profile, cordelia)
|
||||
self.assertEqual(
|
||||
str(direct_message_permission_error.exception),
|
||||
"You do not have permission to send direct messages to this recipient.",
|
||||
)
|
||||
|
||||
# We can send to this direct message group as it has administrator as one of the
|
||||
# recipient.
|
||||
with self.assert_database_query_count(24):
|
||||
self.send_group_direct_message(user_profile, direct_message_group)
|
||||
self.send_group_direct_message(admin, direct_message_group)
|
||||
|
||||
# But this one does not have an administrator. So, it should throw an error.
|
||||
with self.assertRaises(DirectMessagePermissionError):
|
||||
self.send_group_direct_message(user_profile, direct_message_group_without_admin)
|
||||
|
||||
bot_profile = self.create_test_bot("testbot", user_profile)
|
||||
notification_bot = get_system_bot("notification-bot@zulip.com", user_profile.realm_id)
|
||||
# Tests if messages to and from bots are allowed irrespective of the value of the setting.
|
||||
self.send_personal_message(user_profile, notification_bot)
|
||||
self.send_personal_message(user_profile, bot_profile)
|
||||
self.send_personal_message(bot_profile, user_profile)
|
||||
|
||||
# Tests if the permission works when the setting is set to a combination of
|
||||
# groups and users.
|
||||
user_group = self.create_or_update_anonymous_group_for_setting(
|
||||
[user_profile],
|
||||
[administrators_system_group],
|
||||
)
|
||||
do_change_realm_permission_group_setting(
|
||||
realm,
|
||||
"direct_message_permission_group",
|
||||
user_group,
|
||||
acting_user=None,
|
||||
)
|
||||
self.send_personal_message(user_profile, cordelia)
|
||||
|
||||
do_change_realm_permission_group_setting(
|
||||
realm,
|
||||
"direct_message_permission_group",
|
||||
nobody_system_group,
|
||||
acting_user=None,
|
||||
)
|
||||
with self.assertRaises(DirectMessagePermissionError) as direct_message_permission_error:
|
||||
self.send_personal_message(user_profile, cordelia)
|
||||
self.assertEqual(
|
||||
str(direct_message_permission_error.exception),
|
||||
"Direct messages are disabled in this organization.",
|
||||
)
|
||||
|
||||
def test_non_ascii_personal(self) -> None:
|
||||
"""
|
||||
Sending a direct message containing non-ASCII characters succeeds.
|
||||
|
@ -2678,7 +2827,15 @@ class InternalPrepTest(ZulipTestCase):
|
|||
Test that a user can send a direct message to themselves and to a bot in a DM disabled organization
|
||||
"""
|
||||
sender = self.example_user("hamlet")
|
||||
sender.realm.private_message_policy = PrivateMessagePolicyEnum.DISABLED
|
||||
nobody_system_group = NamedUserGroup.objects.get(
|
||||
name=SystemGroups.NOBODY, realm=sender.realm, is_system_group=True
|
||||
)
|
||||
do_change_realm_permission_group_setting(
|
||||
sender.realm,
|
||||
"direct_message_permission_group",
|
||||
nobody_system_group,
|
||||
acting_user=None,
|
||||
)
|
||||
sender.realm.save()
|
||||
|
||||
# Create a non-bot user
|
||||
|
|
|
@ -149,6 +149,8 @@ def update_realm(
|
|||
bot_creation_policy: Optional[Json[BotCreationPolicyEnum]] = None,
|
||||
can_create_public_channel_group: Optional[Json[GroupSettingChangeRequest]] = None,
|
||||
can_create_private_channel_group: Optional[Json[GroupSettingChangeRequest]] = None,
|
||||
direct_message_initiator_group: Optional[Json[GroupSettingChangeRequest]] = None,
|
||||
direct_message_permission_group: Optional[Json[GroupSettingChangeRequest]] = None,
|
||||
create_web_public_stream_policy: Optional[Json[CreateWebPublicStreamPolicyEnum]] = None,
|
||||
invite_to_stream_policy: Optional[Json[CommonPolicyEnum]] = None,
|
||||
move_messages_between_streams_policy: Optional[
|
||||
|
|
Loading…
Reference in New Issue