コード例 #1
0
ファイル: streams.py プロジェクト: synicalsyntax/zulip
def update_stream_backend(
        request: HttpRequest, user_profile: UserProfile,
        stream_id: int,
        description: Optional[str]=REQ(validator=check_capped_string(
            Stream.MAX_DESCRIPTION_LENGTH), default=None),
        is_private: Optional[bool]=REQ(validator=check_bool, default=None),
        is_announcement_only: Optional[bool]=REQ(validator=check_bool, default=None),
        stream_post_policy: Optional[int]=REQ(validator=check_int_in(
            Stream.STREAM_POST_POLICY_TYPES), default=None),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        new_name: Optional[str]=REQ(validator=check_string, default=None),
        message_retention_days: Optional[Union[int, str]]=REQ(validator=check_string_or_int, default=None),
) -> HttpResponse:
    # We allow realm administrators to to update the stream name and
    # description even for private streams.
    stream = access_stream_for_delete_or_update(user_profile, stream_id)

    if message_retention_days is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        user_profile.realm.ensure_not_on_limited_plan()
        message_retention_days_value = parse_message_retention_days(
            message_retention_days, Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)
        do_change_stream_message_retention_days(stream, message_retention_days_value)

    if description is not None:
        if '\n' in description:
            # We don't allow newline characters in stream descriptions.
            description = description.replace("\n", " ")
        do_change_stream_description(stream, description)
    if new_name is not None:
        new_name = new_name.strip()
        if stream.name == new_name:
            return json_error(_("Stream already has that name!"))
        if stream.name.lower() != new_name.lower():
            # Check that the stream name is available (unless we are
            # are only changing the casing of the stream name).
            check_stream_name_available(user_profile.realm, new_name)
        do_rename_stream(stream, new_name, user_profile)
    if is_announcement_only is not None:
        # is_announcement_only is a legacy way to specify
        # stream_post_policy.  We can probably just delete this code,
        # since we're not aware of clients that used it, but we're
        # keeping it for backwards-compatibility for now.
        stream_post_policy = Stream.STREAM_POST_POLICY_EVERYONE
        if is_announcement_only:
            stream_post_policy = Stream.STREAM_POST_POLICY_ADMINS
    if stream_post_policy is not None:
        do_change_stream_post_policy(stream, stream_post_policy)

    # But we require even realm administrators to be actually
    # subscribed to make a private stream public.
    if is_private is not None:
        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        do_change_stream_invite_only(stream, is_private, history_public_to_subscribers)
    return json_success()
コード例 #2
0
def add_subscriptions_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    streams_raw: Sequence[Mapping[str, str]] = REQ(
        "subscriptions", json_validator=add_subscriptions_schema),
    invite_only: bool = REQ(json_validator=check_bool, default=False),
    stream_post_policy: int = REQ(
        json_validator=check_int_in(Stream.STREAM_POST_POLICY_TYPES),
        default=Stream.STREAM_POST_POLICY_EVERYONE,
    ),
    history_public_to_subscribers: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    message_retention_days: Union[str, int] = REQ(
        json_validator=check_string_or_int, default=RETENTION_DEFAULT),
    announce: bool = REQ(json_validator=check_bool, default=False),
    principals: Union[Sequence[str], Sequence[int]] = REQ(
        json_validator=check_principals,
        default=EMPTY_PRINCIPALS,
    ),
    authorization_errors_fatal: bool = REQ(json_validator=check_bool,
                                           default=True),
) -> HttpResponse:
    realm = user_profile.realm
    stream_dicts = []
    color_map = {}
    for stream_dict in streams_raw:
        # 'color' field is optional
        # check for its presence in the streams_raw first
        if "color" in stream_dict:
            color_map[stream_dict["name"]] = stream_dict["color"]

        stream_dict_copy: StreamDict = {}
        stream_dict_copy["name"] = stream_dict["name"].strip()

        # We don't allow newline characters in stream descriptions.
        if "description" in stream_dict:
            stream_dict_copy["description"] = stream_dict[
                "description"].replace("\n", " ")

        stream_dict_copy["invite_only"] = invite_only
        stream_dict_copy["stream_post_policy"] = stream_post_policy
        stream_dict_copy[
            "history_public_to_subscribers"] = history_public_to_subscribers
        stream_dict_copy[
            "message_retention_days"] = parse_message_retention_days(
                message_retention_days,
                Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)

        stream_dicts.append(stream_dict_copy)

    # Validation of the streams arguments, including enforcement of
    # can_create_streams policy and check_stream_name policy is inside
    # list_to_streams.
    existing_streams, created_streams = list_to_streams(stream_dicts,
                                                        user_profile,
                                                        autocreate=True)
    authorized_streams, unauthorized_streams = filter_stream_authorization(
        user_profile, existing_streams)
    if len(unauthorized_streams) > 0 and authorization_errors_fatal:
        return json_error(
            _("Unable to access stream ({stream_name}).").format(
                stream_name=unauthorized_streams[0].name, ))
    # Newly created streams are also authorized for the creator
    streams = authorized_streams + created_streams

    if len(principals) > 0:
        if realm.is_zephyr_mirror_realm and not all(stream.invite_only
                                                    for stream in streams):
            return json_error(
                _("You can only invite other Zephyr mirroring users to private streams."
                  ))
        if not user_profile.can_subscribe_other_users():
            # Guest users case will not be handled here as it will
            # be handled by the decorator above.
            raise JsonableError(_("Insufficient permission"))
        subscribers = {
            principal_to_user_profile(user_profile, principal)
            for principal in principals
        }
    else:
        subscribers = {user_profile}

    (subscribed,
     already_subscribed) = bulk_add_subscriptions(realm,
                                                  streams,
                                                  subscribers,
                                                  acting_user=user_profile,
                                                  color_map=color_map)

    # We can assume unique emails here for now, but we should eventually
    # convert this function to be more id-centric.
    email_to_user_profile: Dict[str, UserProfile] = {}

    result: Dict[str, Any] = dict(subscribed=defaultdict(list),
                                  already_subscribed=defaultdict(list))
    for sub_info in subscribed:
        subscriber = sub_info.user
        stream = sub_info.stream
        result["subscribed"][subscriber.email].append(stream.name)
        email_to_user_profile[subscriber.email] = subscriber
    for sub_info in already_subscribed:
        subscriber = sub_info.user
        stream = sub_info.stream
        result["already_subscribed"][subscriber.email].append(stream.name)

    result["subscribed"] = dict(result["subscribed"])
    result["already_subscribed"] = dict(result["already_subscribed"])

    send_messages_for_new_subscribers(
        user_profile=user_profile,
        subscribers=subscribers,
        new_subscriptions=result["subscribed"],
        email_to_user_profile=email_to_user_profile,
        created_streams=created_streams,
        announce=announce,
    )

    result["subscribed"] = dict(result["subscribed"])
    result["already_subscribed"] = dict(result["already_subscribed"])
    if not authorization_errors_fatal:
        result["unauthorized"] = [s.name for s in unauthorized_streams]
    return json_success(result)
コード例 #3
0
ファイル: realm.py プロジェクト: ItsMeWithTheFace/zulip
def update_realm(
    request: HttpRequest,
    user_profile: UserProfile,
    name: Optional[str] = REQ(str_validator=check_capped_string(
        Realm.MAX_REALM_NAME_LENGTH),
                              default=None),
    description: Optional[str] = REQ(str_validator=check_capped_string(
        Realm.MAX_REALM_DESCRIPTION_LENGTH),
                                     default=None),
    emails_restricted_to_domains: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    disallow_disposable_email_addresses: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    invite_required: Optional[bool] = REQ(json_validator=check_bool,
                                          default=None),
    invite_to_realm_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.INVITE_TO_REALM_POLICY_TYPES),
                                                default=None),
    name_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    email_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                 default=None),
    avatar_changes_disabled: Optional[bool] = REQ(json_validator=check_bool,
                                                  default=None),
    inline_image_preview: Optional[bool] = REQ(json_validator=check_bool,
                                               default=None),
    inline_url_embed_preview: Optional[bool] = REQ(json_validator=check_bool,
                                                   default=None),
    add_custom_emoji_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    delete_own_message_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_MESSAGE_POLICY_TYPES),
                                                   default=None),
    message_content_delete_limit_seconds_raw: Optional[Union[int, str]] = REQ(
        "message_content_delete_limit_seconds",
        json_validator=check_string_or_int,
        default=None),
    allow_message_editing: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    edit_topic_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_MESSAGE_POLICY_TYPES),
                                           default=None),
    mandatory_topics: Optional[bool] = REQ(json_validator=check_bool,
                                           default=None),
    message_content_edit_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_edit_history: Optional[bool] = REQ(json_validator=check_bool,
                                             default=None),
    default_language: Optional[str] = REQ(default=None),
    waiting_period_threshold: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    authentication_methods: Optional[Dict[str, Any]] = REQ(
        json_validator=check_dict([]), default=None),
    notifications_stream_id: Optional[int] = REQ(json_validator=check_int,
                                                 default=None),
    signup_notifications_stream_id: Optional[int] = REQ(
        json_validator=check_int, default=None),
    message_retention_days_raw: Optional[Union[int, str]] = REQ(
        "message_retention_days",
        json_validator=check_string_or_int,
        default=None),
    send_welcome_emails: Optional[bool] = REQ(json_validator=check_bool,
                                              default=None),
    digest_emails_enabled: Optional[bool] = REQ(json_validator=check_bool,
                                                default=None),
    message_content_allowed_in_email_notifications: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    bot_creation_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.BOT_CREATION_POLICY_TYPES),
                                             default=None),
    create_public_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    create_private_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    create_web_public_stream_policy: Optional[int] = REQ(
        json_validator=check_int_in(
            Realm.CREATE_WEB_PUBLIC_STREAM_POLICY_TYPES),
        default=None),
    invite_to_stream_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    move_messages_between_streams_policy: Optional[int] = REQ(
        json_validator=check_int_in(Realm.COMMON_POLICY_TYPES), default=None),
    user_group_edit_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                default=None),
    private_message_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.PRIVATE_MESSAGE_POLICY_TYPES),
                                                default=None),
    wildcard_mention_policy: Optional[int] = REQ(json_validator=check_int_in(
        Realm.WILDCARD_MENTION_POLICY_TYPES),
                                                 default=None),
    email_address_visibility: Optional[int] = REQ(json_validator=check_int_in(
        Realm.EMAIL_ADDRESS_VISIBILITY_TYPES),
                                                  default=None),
    video_chat_provider: Optional[int] = REQ(json_validator=check_int,
                                             default=None),
    giphy_rating: Optional[int] = REQ(json_validator=check_int, default=None),
    default_code_block_language: Optional[str] = REQ(default=None),
    digest_weekday: Optional[int] = REQ(json_validator=check_int_in(
        Realm.DIGEST_WEEKDAY_VALUES),
                                        default=None),
    string_id: Optional[str] = REQ(
        str_validator=check_capped_string(Realm.MAX_REALM_SUBDOMAIN_LENGTH),
        default=None,
    ),
    enable_spectator_access: Optional[bool] = REQ(json_validator=check_bool,
                                                  default=None),
) -> HttpResponse:
    realm = user_profile.realm

    # Additional validation/error checking beyond types go here, so
    # the entire request can succeed or fail atomically.
    if default_language is not None and default_language not in get_available_language_codes(
    ):
        raise JsonableError(
            _("Invalid language '{}'").format(default_language))
    if authentication_methods is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        if True not in list(authentication_methods.values()):
            raise JsonableError(
                _("At least one authentication method must be enabled."))
    if video_chat_provider is not None and video_chat_provider not in {
            p["id"]
            for p in Realm.VIDEO_CHAT_PROVIDERS.values()
    }:
        raise JsonableError(
            _("Invalid video_chat_provider {}").format(video_chat_provider))
    if giphy_rating is not None and giphy_rating not in {
            p["id"]
            for p in Realm.GIPHY_RATING_OPTIONS.values()
    }:
        raise JsonableError(_("Invalid giphy_rating {}").format(giphy_rating))

    message_retention_days: Optional[int] = None
    if message_retention_days_raw is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        realm.ensure_not_on_limited_plan()
        message_retention_days = parse_message_retention_days(
            message_retention_days_raw,
            Realm.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)

    if invite_to_realm_policy is not None and not user_profile.is_realm_owner:
        raise OrganizationOwnerRequired()

    data: Dict[str, Any] = {}

    message_content_delete_limit_seconds: Optional[int] = None
    if message_content_delete_limit_seconds_raw is not None:
        message_content_delete_limit_seconds = parse_message_content_delete_limit(
            message_content_delete_limit_seconds_raw,
            Realm.MESSAGE_CONTENT_DELETE_LIMIT_SPECIAL_VALUES_MAP,
        )
        do_set_realm_property(
            realm,
            "message_content_delete_limit_seconds",
            message_content_delete_limit_seconds,
            acting_user=user_profile,
        )
        data[
            "message_content_delete_limit_seconds"] = message_content_delete_limit_seconds

    # The user of `locals()` here is a bit of a code smell, but it's
    # restricted to the elements present in realm.property_types.
    #
    # TODO: It should be possible to deduplicate this function up
    # further by some more advanced usage of the
    # `REQ/has_request_variables` extraction.
    req_vars = {
        k: v
        for k, v in list(locals().items()) if k in realm.property_types
    }

    for k, v in list(req_vars.items()):
        if v is not None and getattr(realm, k) != v:
            do_set_realm_property(realm, k, v, acting_user=user_profile)
            if isinstance(v, str):
                data[k] = "updated"
            else:
                data[k] = v

    # The following realm properties do not fit the pattern above
    # authentication_methods is not supported by the do_set_realm_property
    # framework because of its bitfield.
    if authentication_methods is not None and (
            realm.authentication_methods_dict() != authentication_methods):
        do_set_realm_authentication_methods(realm,
                                            authentication_methods,
                                            acting_user=user_profile)
        data["authentication_methods"] = authentication_methods
    # The message_editing settings are coupled to each other, and thus don't fit
    # into the do_set_realm_property framework.
    if ((allow_message_editing is not None
         and realm.allow_message_editing != allow_message_editing)
            or (message_content_edit_limit_seconds is not None
                and realm.message_content_edit_limit_seconds !=
                message_content_edit_limit_seconds)
            or (edit_topic_policy is not None
                and realm.edit_topic_policy != edit_topic_policy)):
        if allow_message_editing is None:
            allow_message_editing = realm.allow_message_editing
        if message_content_edit_limit_seconds is None:
            message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds
        if edit_topic_policy is None:
            edit_topic_policy = realm.edit_topic_policy
        do_set_realm_message_editing(
            realm,
            allow_message_editing,
            message_content_edit_limit_seconds,
            edit_topic_policy,
            acting_user=user_profile,
        )
        data["allow_message_editing"] = allow_message_editing
        data[
            "message_content_edit_limit_seconds"] = message_content_edit_limit_seconds
        data["edit_topic_policy"] = edit_topic_policy

    # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean,
    # str or integer field, and thus doesn't fit into the do_set_realm_property framework.
    if notifications_stream_id is not None:
        if realm.notifications_stream is None or (realm.notifications_stream.id
                                                  != notifications_stream_id):
            new_notifications_stream = None
            if notifications_stream_id >= 0:
                (new_notifications_stream,
                 sub) = access_stream_by_id(user_profile,
                                            notifications_stream_id)
            do_set_realm_notifications_stream(realm,
                                              new_notifications_stream,
                                              notifications_stream_id,
                                              acting_user=user_profile)
            data["notifications_stream_id"] = notifications_stream_id

    if signup_notifications_stream_id is not None:
        if realm.signup_notifications_stream is None or (
                realm.signup_notifications_stream.id !=
                signup_notifications_stream_id):
            new_signup_notifications_stream = None
            if signup_notifications_stream_id >= 0:
                (new_signup_notifications_stream,
                 sub) = access_stream_by_id(user_profile,
                                            signup_notifications_stream_id)
            do_set_realm_signup_notifications_stream(
                realm,
                new_signup_notifications_stream,
                signup_notifications_stream_id,
                acting_user=user_profile,
            )
            data[
                "signup_notifications_stream_id"] = signup_notifications_stream_id

    if default_code_block_language is not None:
        # Migrate '', used in the API to encode the default/None behavior of this feature.
        if default_code_block_language == "":
            data["default_code_block_language"] = None
        else:
            data["default_code_block_language"] = default_code_block_language

    if string_id is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()

        if realm.demo_organization_scheduled_deletion_date is None:
            raise JsonableError(_("Must be a demo organization."))

        try:
            check_subdomain(string_id)
        except ValidationError as err:
            raise JsonableError(str(err.message))

        do_change_realm_subdomain(realm, string_id, acting_user=user_profile)
        data["realm_uri"] = realm.uri

    return json_success(data)
コード例 #4
0
ファイル: streams.py プロジェクト: synicalsyntax/zulip
def add_subscriptions_backend(
        request: HttpRequest, user_profile: UserProfile,
        streams_raw: Iterable[Dict[str, str]]=REQ("subscriptions", validator=add_subscriptions_schema),
        invite_only: bool=REQ(validator=check_bool, default=False),
        stream_post_policy: int=REQ(validator=check_int_in(
            Stream.STREAM_POST_POLICY_TYPES), default=Stream.STREAM_POST_POLICY_EVERYONE),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        message_retention_days: Union[str, int]=REQ(validator=check_string_or_int,
                                                    default=RETENTION_DEFAULT),
        announce: bool=REQ(validator=check_bool, default=False),
        principals: Union[Sequence[str], Sequence[int]]=REQ(
            validator=check_principals, default=EMPTY_PRINCIPALS,
        ),
        authorization_errors_fatal: bool=REQ(validator=check_bool, default=True),
) -> HttpResponse:
    stream_dicts = []
    color_map = {}
    for stream_dict in streams_raw:
        # 'color' field is optional
        # check for its presence in the streams_raw first
        if 'color' in stream_dict:
            color_map[stream_dict['name']] = stream_dict['color']
        if 'description' in stream_dict:
            # We don't allow newline characters in stream descriptions.
            stream_dict['description'] = stream_dict['description'].replace("\n", " ")

        stream_dict_copy: Dict[str, Any] = {}
        for field in stream_dict:
            stream_dict_copy[field] = stream_dict[field]
        # Strip the stream name here.
        stream_dict_copy['name'] = stream_dict_copy['name'].strip()
        stream_dict_copy["invite_only"] = invite_only
        stream_dict_copy["stream_post_policy"] = stream_post_policy
        stream_dict_copy["history_public_to_subscribers"] = history_public_to_subscribers
        stream_dict_copy["message_retention_days"] = parse_message_retention_days(
            message_retention_days, Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)
        stream_dicts.append(stream_dict_copy)

    # Validation of the streams arguments, including enforcement of
    # can_create_streams policy and check_stream_name policy is inside
    # list_to_streams.
    existing_streams, created_streams = \
        list_to_streams(stream_dicts, user_profile, autocreate=True)
    authorized_streams, unauthorized_streams = \
        filter_stream_authorization(user_profile, existing_streams)
    if len(unauthorized_streams) > 0 and authorization_errors_fatal:
        return json_error(_("Unable to access stream ({stream_name}).").format(
            stream_name=unauthorized_streams[0].name,
        ))
    # Newly created streams are also authorized for the creator
    streams = authorized_streams + created_streams

    if len(principals) > 0:
        if user_profile.realm.is_zephyr_mirror_realm and not all(stream.invite_only for stream in streams):
            return json_error(_("You can only invite other Zephyr mirroring users to private streams."))
        if not user_profile.can_subscribe_other_users():
            if user_profile.realm.invite_to_stream_policy == Realm.POLICY_ADMINS_ONLY:
                return json_error(_("Only administrators can modify other users' subscriptions."))
            # Realm.POLICY_MEMBERS_ONLY only fails if the
            # user is a guest, which happens in the decorator above.
            assert user_profile.realm.invite_to_stream_policy == \
                Realm.POLICY_FULL_MEMBERS_ONLY
            return json_error(_("Your account is too new to modify other users' subscriptions."))
        subscribers = {principal_to_user_profile(user_profile, principal) for principal in principals}
    else:
        subscribers = {user_profile}

    (subscribed, already_subscribed) = bulk_add_subscriptions(streams, subscribers,
                                                              acting_user=user_profile, color_map=color_map)

    # We can assume unique emails here for now, but we should eventually
    # convert this function to be more id-centric.
    email_to_user_profile: Dict[str, UserProfile] = dict()

    result: Dict[str, Any] = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))
    for (subscriber, stream) in subscribed:
        result["subscribed"][subscriber.email].append(stream.name)
        email_to_user_profile[subscriber.email] = subscriber
    for (subscriber, stream) in already_subscribed:
        result["already_subscribed"][subscriber.email].append(stream.name)

    bots = {subscriber.email: subscriber.is_bot for subscriber in subscribers}

    newly_created_stream_names = {s.name for s in created_streams}

    # Inform the user if someone else subscribed them to stuff,
    # or if a new stream was created with the "announce" option.
    notifications = []
    if len(principals) > 0 and result["subscribed"]:
        for email, subscribed_stream_names in result["subscribed"].items():
            if email == user_profile.email:
                # Don't send a Zulip if you invited yourself.
                continue
            if bots[email]:
                # Don't send invitation Zulips to bots
                continue

            # For each user, we notify them about newly subscribed streams, except for
            # streams that were newly created.
            notify_stream_names = set(subscribed_stream_names) - newly_created_stream_names

            if not notify_stream_names:
                continue

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            recipient_user = email_to_user_profile[email]

            msg = you_were_just_subscribed_message(
                acting_user=user_profile,
                recipient_user=recipient_user,
                stream_names=notify_stream_names,
            )

            notifications.append(
                internal_prep_private_message(
                    realm=user_profile.realm,
                    sender=sender,
                    recipient_user=recipient_user,
                    content=msg))

    if announce and len(created_streams) > 0:
        notifications_stream = user_profile.realm.get_notifications_stream()
        if notifications_stream is not None:
            with override_language(notifications_stream.realm.default_language):
                if len(created_streams) > 1:
                    content = _("{user_name} created the following streams: {stream_str}.")
                else:
                    content = _("{user_name} created a new stream {stream_str}.")
                topic = _('new streams')

            content = content.format(
                user_name=f"@_**{user_profile.full_name}|{user_profile.id}**",
                stream_str=", ".join(f'#**{s.name}**' for s in created_streams)
            )

            sender = get_system_bot(settings.NOTIFICATION_BOT)

            notifications.append(
                internal_prep_stream_message(
                    realm=user_profile.realm,
                    sender=sender,
                    stream=notifications_stream,
                    topic=topic,
                    content=content,
                ),
            )

    if not user_profile.realm.is_zephyr_mirror_realm and len(created_streams) > 0:
        sender = get_system_bot(settings.NOTIFICATION_BOT)
        for stream in created_streams:
            with override_language(stream.realm.default_language):
                notifications.append(
                    internal_prep_stream_message(
                        realm=user_profile.realm,
                        sender=sender,
                        stream=stream,
                        topic=Realm.STREAM_EVENTS_NOTIFICATION_TOPIC,
                        content=_('Stream created by {user_name}.').format(
                            user_name=f"@_**{user_profile.full_name}|{user_profile.id}**",
                        ),
                    ),
                )

    if len(notifications) > 0:
        do_send_messages(notifications, mark_as_read=[user_profile.id])

    result["subscribed"] = dict(result["subscribed"])
    result["already_subscribed"] = dict(result["already_subscribed"])
    if not authorization_errors_fatal:
        result["unauthorized"] = [s.name for s in unauthorized_streams]
    return json_success(result)
コード例 #5
0
def update_stream_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int,
    description: Optional[str] = REQ(str_validator=check_capped_string(
        Stream.MAX_DESCRIPTION_LENGTH),
                                     default=None),
    is_private: Optional[bool] = REQ(json_validator=check_bool, default=None),
    is_announcement_only: Optional[bool] = REQ(json_validator=check_bool,
                                               default=None),
    stream_post_policy: Optional[int] = REQ(json_validator=check_int_in(
        Stream.STREAM_POST_POLICY_TYPES),
                                            default=None),
    history_public_to_subscribers: Optional[bool] = REQ(
        json_validator=check_bool, default=None),
    is_web_public: Optional[bool] = REQ(json_validator=check_bool,
                                        default=None),
    new_name: Optional[str] = REQ(default=None),
    message_retention_days: Optional[Union[int, str]] = REQ(
        json_validator=check_string_or_int, default=None),
) -> HttpResponse:
    # We allow realm administrators to to update the stream name and
    # description even for private streams.
    (stream, sub) = access_stream_for_delete_or_update(user_profile, stream_id)

    if message_retention_days is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        user_profile.realm.ensure_not_on_limited_plan()
        new_message_retention_days_value = parse_message_retention_days(
            message_retention_days,
            Stream.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)
        do_change_stream_message_retention_days(
            stream, user_profile, new_message_retention_days_value)

    if description is not None:
        if "\n" in description:
            # We don't allow newline characters in stream descriptions.
            description = description.replace("\n", " ")
        do_change_stream_description(stream,
                                     description,
                                     acting_user=user_profile)
    if new_name is not None:
        new_name = new_name.strip()
        if stream.name == new_name:
            raise JsonableError(_("Stream already has that name!"))
        if stream.name.lower() != new_name.lower():
            # Check that the stream name is available (unless we are
            # are only changing the casing of the stream name).
            check_stream_name_available(user_profile.realm, new_name)
        do_rename_stream(stream, new_name, user_profile)
    if is_announcement_only is not None:
        # is_announcement_only is a legacy way to specify
        # stream_post_policy.  We can probably just delete this code,
        # since we're not aware of clients that used it, but we're
        # keeping it for backwards-compatibility for now.
        stream_post_policy = Stream.STREAM_POST_POLICY_EVERYONE
        if is_announcement_only:
            stream_post_policy = Stream.STREAM_POST_POLICY_ADMINS
    if stream_post_policy is not None:
        do_change_stream_post_policy(stream,
                                     stream_post_policy,
                                     acting_user=user_profile)

    # But we require even realm administrators to be actually
    # subscribed to make a private stream public.
    if is_private is not None:
        default_stream_ids = {
            s.id
            for s in get_default_streams_for_realm(stream.realm_id)
        }
        (stream, sub) = access_stream_by_id(user_profile, stream_id)
        if is_private and stream.id in default_stream_ids:
            raise JsonableError(_("Default streams cannot be made private."))

    if is_web_public:
        # Enforce restrictions on creating web-public streams.
        if not user_profile.realm.web_public_streams_enabled():
            raise JsonableError(_("Web-public streams are not enabled."))
        if not user_profile.can_create_web_public_streams():
            raise JsonableError(_("Insufficient permission"))
        # Forbid parameter combinations that are inconsistent
        if is_private or history_public_to_subscribers is False:
            raise JsonableError(_("Invalid parameters"))

    if is_private is not None or is_web_public is not None:
        do_change_stream_permission(
            stream,
            invite_only=is_private,
            history_public_to_subscribers=history_public_to_subscribers,
            is_web_public=is_web_public,
            acting_user=user_profile,
        )
    return json_success(request)
コード例 #6
0
ファイル: realm.py プロジェクト: yuhuahu310/zulip
def update_realm(
    request: HttpRequest,
    user_profile: UserProfile,
    name: Optional[str] = REQ(validator=check_string, default=None),
    description: Optional[str] = REQ(validator=check_string, default=None),
    emails_restricted_to_domains: Optional[bool] = REQ(validator=check_bool,
                                                       default=None),
    disallow_disposable_email_addresses: Optional[bool] = REQ(
        validator=check_bool, default=None),
    invite_required: Optional[bool] = REQ(validator=check_bool, default=None),
    invite_by_admins_only: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    name_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    email_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                 default=None),
    avatar_changes_disabled: Optional[bool] = REQ(validator=check_bool,
                                                  default=None),
    inline_image_preview: Optional[bool] = REQ(validator=check_bool,
                                               default=None),
    inline_url_embed_preview: Optional[bool] = REQ(validator=check_bool,
                                                   default=None),
    add_emoji_by_admins_only: Optional[bool] = REQ(validator=check_bool,
                                                   default=None),
    allow_message_deleting: Optional[bool] = REQ(validator=check_bool,
                                                 default=None),
    message_content_delete_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_message_editing: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    allow_community_topic_editing: Optional[bool] = REQ(validator=check_bool,
                                                        default=None),
    mandatory_topics: Optional[bool] = REQ(validator=check_bool, default=None),
    message_content_edit_limit_seconds: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    allow_edit_history: Optional[bool] = REQ(validator=check_bool,
                                             default=None),
    default_language: Optional[str] = REQ(validator=check_string,
                                          default=None),
    waiting_period_threshold: Optional[int] = REQ(
        converter=to_non_negative_int, default=None),
    authentication_methods: Optional[Dict[str,
                                          Any]] = REQ(validator=check_dict([]),
                                                      default=None),
    notifications_stream_id: Optional[int] = REQ(validator=check_int,
                                                 default=None),
    signup_notifications_stream_id: Optional[int] = REQ(validator=check_int,
                                                        default=None),
    message_retention_days_raw: Optional[Union[int, str]] = REQ(
        "message_retention_days", validator=check_string_or_int, default=None),
    send_welcome_emails: Optional[bool] = REQ(validator=check_bool,
                                              default=None),
    digest_emails_enabled: Optional[bool] = REQ(validator=check_bool,
                                                default=None),
    message_content_allowed_in_email_notifications: Optional[bool] = REQ(
        validator=check_bool, default=None),
    bot_creation_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.BOT_CREATION_POLICY_TYPES),
                                             default=None),
    create_stream_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                              default=None),
    invite_to_stream_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.COMMON_POLICY_TYPES),
                                                 default=None),
    user_group_edit_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.USER_GROUP_EDIT_POLICY_TYPES),
                                                default=None),
    private_message_policy: Optional[int] = REQ(validator=check_int_in(
        Realm.PRIVATE_MESSAGE_POLICY_TYPES),
                                                default=None),
    email_address_visibility: Optional[int] = REQ(validator=check_int_in(
        Realm.EMAIL_ADDRESS_VISIBILITY_TYPES),
                                                  default=None),
    default_twenty_four_hour_time: Optional[bool] = REQ(validator=check_bool,
                                                        default=None),
    video_chat_provider: Optional[int] = REQ(validator=check_int,
                                             default=None),
    default_code_block_language: Optional[str] = REQ(validator=check_string,
                                                     default=None),
    digest_weekday: Optional[int] = REQ(validator=check_int_in(
        Realm.DIGEST_WEEKDAY_VALUES),
                                        default=None),
) -> HttpResponse:
    realm = user_profile.realm

    # Additional validation/error checking beyond types go here, so
    # the entire request can succeed or fail atomically.
    if default_language is not None and default_language not in get_available_language_codes(
    ):
        raise JsonableError(
            _("Invalid language '{}'").format(default_language))
    if description is not None and len(description) > 1000:
        return json_error(_("Organization description is too long."))
    if name is not None and len(name) > Realm.MAX_REALM_NAME_LENGTH:
        return json_error(_("Organization name is too long."))
    if authentication_methods is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        if True not in list(authentication_methods.values()):
            return json_error(
                _("At least one authentication method must be enabled."))
    if (video_chat_provider is not None and video_chat_provider
            not in {p['id']
                    for p in Realm.VIDEO_CHAT_PROVIDERS.values()}):
        return json_error(
            _("Invalid video_chat_provider {}").format(video_chat_provider))

    message_retention_days: Optional[int] = None
    if message_retention_days_raw is not None:
        if not user_profile.is_realm_owner:
            raise OrganizationOwnerRequired()
        realm.ensure_not_on_limited_plan()
        message_retention_days = parse_message_retention_days(
            message_retention_days_raw,
            Realm.MESSAGE_RETENTION_SPECIAL_VALUES_MAP)

    # The user of `locals()` here is a bit of a code smell, but it's
    # restricted to the elements present in realm.property_types.
    #
    # TODO: It should be possible to deduplicate this function up
    # further by some more advanced usage of the
    # `REQ/has_request_variables` extraction.
    req_vars = {
        k: v
        for k, v in list(locals().items()) if k in realm.property_types
    }
    data: Dict[str, Any] = {}

    for k, v in list(req_vars.items()):
        if v is not None and getattr(realm, k) != v:
            do_set_realm_property(realm, k, v, acting_user=user_profile)
            if isinstance(v, str):
                data[k] = 'updated'
            else:
                data[k] = v

    # The following realm properties do not fit the pattern above
    # authentication_methods is not supported by the do_set_realm_property
    # framework because of its bitfield.
    if authentication_methods is not None and (
            realm.authentication_methods_dict() != authentication_methods):
        do_set_realm_authentication_methods(realm,
                                            authentication_methods,
                                            acting_user=user_profile)
        data['authentication_methods'] = authentication_methods
    # The message_editing settings are coupled to each other, and thus don't fit
    # into the do_set_realm_property framework.
    if ((allow_message_editing is not None
         and realm.allow_message_editing != allow_message_editing)
            or (message_content_edit_limit_seconds is not None
                and realm.message_content_edit_limit_seconds !=
                message_content_edit_limit_seconds)
            or (allow_community_topic_editing is not None
                and realm.allow_community_topic_editing !=
                allow_community_topic_editing)):
        if allow_message_editing is None:
            allow_message_editing = realm.allow_message_editing
        if message_content_edit_limit_seconds is None:
            message_content_edit_limit_seconds = realm.message_content_edit_limit_seconds
        if allow_community_topic_editing is None:
            allow_community_topic_editing = realm.allow_community_topic_editing
        do_set_realm_message_editing(realm,
                                     allow_message_editing,
                                     message_content_edit_limit_seconds,
                                     allow_community_topic_editing,
                                     acting_user=user_profile)
        data['allow_message_editing'] = allow_message_editing
        data[
            'message_content_edit_limit_seconds'] = message_content_edit_limit_seconds
        data['allow_community_topic_editing'] = allow_community_topic_editing

    # Realm.notifications_stream and Realm.signup_notifications_stream are not boolean,
    # str or integer field, and thus doesn't fit into the do_set_realm_property framework.
    if notifications_stream_id is not None:
        if realm.notifications_stream is None or (realm.notifications_stream.id
                                                  != notifications_stream_id):
            new_notifications_stream = None
            if notifications_stream_id >= 0:
                (new_notifications_stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            notifications_stream_id)
            do_set_realm_notifications_stream(realm,
                                              new_notifications_stream,
                                              notifications_stream_id,
                                              acting_user=user_profile)
            data['notifications_stream_id'] = notifications_stream_id

    if signup_notifications_stream_id is not None:
        if realm.signup_notifications_stream is None or (
                realm.signup_notifications_stream.id !=
                signup_notifications_stream_id):
            new_signup_notifications_stream = None
            if signup_notifications_stream_id >= 0:
                (new_signup_notifications_stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            signup_notifications_stream_id)
            do_set_realm_signup_notifications_stream(
                realm,
                new_signup_notifications_stream,
                signup_notifications_stream_id,
                acting_user=user_profile)
            data[
                'signup_notifications_stream_id'] = signup_notifications_stream_id

    if default_code_block_language is not None:
        # Migrate '', used in the API to encode the default/None behavior of this feature.
        if default_code_block_language == '':
            data['default_code_block_language'] = None
        else:
            data['default_code_block_language'] = default_code_block_language

    return json_success(data)