Пример #1
0
def get_subscribers_backend(request, user_profile,
                            stream_id=REQ('stream', converter=to_non_negative_int)):
    # type: (HttpRequest, UserProfile, int) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
    subscribers = get_subscriber_emails(stream, user_profile)

    return json_success({'subscribers': subscribers})
Пример #2
0
def update_stream_backend(
        request: HttpRequest, user_profile: UserProfile,
        stream_id: int,
        description: Optional[Text]=REQ(validator=check_string, default=None),
        is_private: Optional[bool]=REQ(validator=check_bool, default=None),
        new_name: Optional[Text]=REQ(validator=check_string, 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 description is not None:
        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)

    # 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)
    return json_success()
Пример #3
0
def get_subscribers_backend(request: HttpRequest, user_profile: UserProfile,
                            stream_id: int=REQ('stream', converter=to_non_negative_int)) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id,
                                                   allow_realm_admin=True)
    subscribers = get_subscriber_emails(stream, user_profile)

    return json_success({'subscribers': subscribers})
Пример #4
0
def get_topics_backend(request: HttpRequest, user_profile: UserProfile,
                       stream_id: int=REQ(converter=to_non_negative_int)) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    result = get_topic_history_for_stream(
        user_profile=user_profile,
        recipient=recipient,
    )

    return json_success(dict(topics=result))
Пример #5
0
def get_topics_backend(request: HttpRequest, user_profile: UserProfile,
                       stream_id: int=REQ(converter=to_non_negative_int)) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    result = get_topic_history_for_stream(
        user_profile=user_profile,
        recipient=recipient,
        public_history=stream.is_history_public_to_subscribers(),
    )

    return json_success(dict(topics=result))
Пример #6
0
    def consume(self, event: Mapping[str, Any]) -> None:
        if event['type'] == 'mark_stream_messages_as_read':
            user_profile = get_user_profile_by_id(event['user_profile_id'])

            for stream_id in event['stream_ids']:
                # Since the user just unsubscribed, we don't require
                # an active Subscription object (otherwise, private
                # streams would never be accessible)
                (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id,
                                                               require_active=False)
                do_mark_stream_messages_as_read(user_profile, stream)
Пример #7
0
def get_topics_backend(request, user_profile,
                       stream_id=REQ(converter=to_non_negative_int)):
    # type: (HttpRequest, UserProfile, int) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    result = get_topic_history_for_stream(
        user_profile=user_profile,
        recipient=recipient,
    )

    return json_success(dict(topics=result))
Пример #8
0
def get_subscribers_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ('stream', converter=to_non_negative_int)
) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile,
                                                   stream_id,
                                                   allow_realm_admin=True)
    subscribers = get_subscriber_emails(stream, user_profile)

    return json_success({'subscribers': subscribers})
Пример #9
0
    def consume(self, event: Mapping[str, Any]) -> None:
        if event['type'] == 'mark_stream_messages_as_read':
            user_profile = get_user_profile_by_id(event['user_profile_id'])

            for stream_id in event['stream_ids']:
                # Since the user just unsubscribed, we don't require
                # an active Subscription object (otherwise, private
                # streams would never be accessible)
                (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id,
                                                               require_active=False)
                do_mark_stream_messages_as_read(user_profile, stream)
Пример #10
0
def update_subscription_properties_backend(
        request: HttpRequest, user_profile: UserProfile,
        subscription_data: List[Dict[str, Any]]=REQ(
            validator=check_list(
                check_dict([("stream_id", check_int),
                            ("property", check_string),
                            ("value", check_variable_type([check_string, check_bool]))])
            )
        ),
) -> HttpResponse:
    """
    This is the entry point to changing subscription properties. This
    is a bulk endpoint: requestors always provide a subscription_data
    list containing dictionaries for each stream of interest.

    Requests are of the form:

    [{"stream_id": "1", "property": "is_muted", "value": False},
     {"stream_id": "1", "property": "color", "value": "#c2c2c2"}]
    """
    property_converters = {"color": check_color, "in_home_view": check_bool,
                           "is_muted": check_bool,
                           "desktop_notifications": check_bool,
                           "audible_notifications": check_bool,
                           "push_notifications": check_bool,
                           "email_notifications": check_bool,
                           "pin_to_top": check_bool}
    response_data = []

    for change in subscription_data:
        stream_id = change["stream_id"]
        property = change["property"]
        value = change["value"]

        if property not in property_converters:
            return json_error(_("Unknown subscription property: %s") % (property,))

        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        if sub is None:
            return json_error(_("Not subscribed to stream id %d") % (stream_id,))

        property_conversion = property_converters[property](property, value)
        if property_conversion:
            return json_error(property_conversion)

        do_change_subscription_property(user_profile, sub, stream,
                                        property, value)

        response_data.append({'stream_id': stream_id,
                              'property': property,
                              'value': value})

    return json_success({"subscription_data": response_data})
Пример #11
0
def mark_stream_as_read(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ(json_validator=check_int)
) -> HttpResponse:
    stream, sub = access_stream_by_id(user_profile, stream_id)
    count = do_mark_stream_messages_as_read(user_profile, stream.recipient_id)

    log_data_str = f"[{count} updated]"
    request._log_data["extra"] = log_data_str

    return json_success({"result": "success", "msg": ""})
Пример #12
0
def mark_stream_as_read(
    request: HttpRequest, user_profile: UserProfile, stream_id: int = REQ(json_validator=check_int)
) -> HttpResponse:
    stream, sub = access_stream_by_id(user_profile, stream_id)
    count = do_mark_stream_messages_as_read(user_profile, stream.recipient_id)

    log_data_str = f"[{count} updated]"
    log_data = RequestNotes.get_notes(request).log_data
    assert log_data is not None
    log_data["extra"] = log_data_str

    return json_success(request)
Пример #13
0
def remove_default_stream(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ(json_validator=check_int)
) -> HttpResponse:
    (stream, sub) = access_stream_by_id(
        user_profile,
        stream_id,
        allow_realm_admin=True,
    )
    do_remove_default_stream(stream)
    return json_success()
Пример #14
0
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),
) -> 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 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()
Пример #15
0
def update_subscription_properties_backend(
        request: HttpRequest, user_profile: UserProfile,
        subscription_data: List[Dict[str, Any]]=REQ(
            validator=check_list(
                check_dict([("stream_id", check_int),
                            ("property", check_string),
                            ("value", check_variable_type([check_string, check_bool]))])
            )
        ),
) -> HttpResponse:
    """
    This is the entry point to changing subscription properties. This
    is a bulk endpoint: requestors always provide a subscription_data
    list containing dictionaries for each stream of interest.

    Requests are of the form:

    [{"stream_id": "1", "property": "in_home_view", "value": False},
     {"stream_id": "1", "property": "color", "value": "#c2c2c2"}]
    """
    property_converters = {"color": check_color, "in_home_view": check_bool,
                           "desktop_notifications": check_bool,
                           "audible_notifications": check_bool,
                           "push_notifications": check_bool,
                           "email_notifications": check_bool,
                           "pin_to_top": check_bool}
    response_data = []

    for change in subscription_data:
        stream_id = change["stream_id"]
        property = change["property"]
        value = change["value"]

        if property not in property_converters:
            return json_error(_("Unknown subscription property: %s") % (property,))

        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        if sub is None:
            return json_error(_("Not subscribed to stream id %d") % (stream_id,))

        property_conversion = property_converters[property](property, value)
        if property_conversion:
            return json_error(property_conversion)

        do_change_subscription_property(user_profile, sub, stream,
                                        property, value)

        response_data.append({'stream_id': stream_id,
                              'property': property,
                              'value': value})

    return json_success({"subscription_data": response_data})
Пример #16
0
def invite_users_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    invitee_emails_raw: str = REQ("invitee_emails"),
    invite_expires_in_minutes: Optional[int] = REQ(
        json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_MINUTES
    ),
    invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
    stream_ids: List[int] = REQ(json_validator=check_list(check_int)),
) -> HttpResponse:

    if not user_profile.can_invite_others_to_realm():
        # Guest users case will not be handled here as it will
        # be handled by the decorator above.
        raise JsonableError(_("Insufficient permission"))
    if invite_as not in PreregistrationUser.INVITE_AS.values():
        raise JsonableError(_("Must be invited as an valid type of user"))
    check_if_owner_required(invite_as, user_profile)
    if (
        invite_as
        in [
            PreregistrationUser.INVITE_AS["REALM_ADMIN"],
            PreregistrationUser.INVITE_AS["MODERATOR"],
        ]
        and not user_profile.is_realm_admin
    ):
        raise JsonableError(_("Must be an organization administrator"))
    if not invitee_emails_raw:
        raise JsonableError(_("You must specify at least one email address."))
    if not stream_ids:
        raise JsonableError(_("You must specify at least one stream for invitees to join."))

    invitee_emails = get_invitee_emails_set(invitee_emails_raw)

    streams: List[Stream] = []
    for stream_id in stream_ids:
        try:
            (stream, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            raise JsonableError(
                _("Stream does not exist with id: {}. No invites were sent.").format(stream_id)
            )
        streams.append(stream)

    do_invite_users(
        user_profile,
        invitee_emails,
        streams,
        invite_expires_in_minutes=invite_expires_in_minutes,
        invite_as=invite_as,
    )
    return json_success(request)
Пример #17
0
def generate_multiuse_invite_backend(request: HttpRequest, user_profile: UserProfile,
                                     stream_ids: List[int]=REQ(validator=check_list(check_int),
                                                               default=[])) -> HttpResponse:
    streams = []
    for stream_id in stream_ids:
        try:
            (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(_("Invalid stream id {}. No invites were sent.".format(stream_id)))
        streams.append(stream)

    invite_link = do_create_multiuse_invite_link(user_profile, streams)
    return json_success({'invite_link': invite_link})
Пример #18
0
def generate_multiuse_invite_backend(request: HttpRequest, user_profile: UserProfile,
                                     stream_ids: List[int]=REQ(validator=check_list(check_int),
                                                               default=[])) -> HttpResponse:
    streams = []
    for stream_id in stream_ids:
        try:
            (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(_("Invalid stream id {}. No invites were sent.".format(stream_id)))
        streams.append(stream)

    invite_link = do_create_multiuse_invite_link(user_profile, streams)
    return json_success({'invite_link': invite_link})
Пример #19
0
def mark_stream_as_read(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ(validator=check_int)
) -> HttpResponse:
    stream, recipient, sub = access_stream_by_id(user_profile, stream_id)
    count = do_mark_stream_messages_as_read(user_profile, request.client,
                                            stream)

    log_data_str = f"[{count} updated]"
    request._log_data["extra"] = log_data_str

    return json_success({'result': 'success', 'msg': ''})
Пример #20
0
def get_subscribers_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ("stream", converter=to_non_negative_int, path_only=True),
) -> HttpResponse:
    (stream, sub) = access_stream_by_id(
        user_profile,
        stream_id,
        allow_realm_admin=True,
    )
    subscribers = get_subscriber_ids(stream, user_profile)

    return json_success({"subscribers": list(subscribers)})
Пример #21
0
    def consume(self, event: Mapping[str, Any]) -> None:
        if event['type'] == 'mark_stream_messages_as_read':
            user_profile = get_user_profile_by_id(event['user_profile_id'])
            client = Client.objects.get(id=event['client_id'])

            for stream_id in event['stream_ids']:
                # Since the user just unsubscribed, we don't require
                # an active Subscription object (otherwise, private
                # streams would never be accessible)
                (stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            stream_id,
                                            require_active=False)
                do_mark_stream_messages_as_read(user_profile, client, stream)
        elif event['type'] == 'clear_push_device_tokens':
            clear_push_device_tokens(event["user_profile_id"])
        elif event['type'] == 'realm_export':
            start = time.time()
            realm = Realm.objects.get(id=event['realm_id'])
            output_dir = tempfile.mkdtemp(prefix="zulip-export-")

            public_url = export_realm_wrapper(realm=realm,
                                              output_dir=output_dir,
                                              threads=6,
                                              upload=True,
                                              public_only=True,
                                              delete_after_upload=True)
            assert public_url is not None

            # Update the extra_data field now that the export is complete.
            export_event = RealmAuditLog.objects.get(id=event['id'])
            export_event.extra_data = ujson.dumps(
                dict(export_path=urllib.parse.urlparse(public_url).path, ))
            export_event.save(update_fields=['extra_data'])

            # Send a private message notification letting the user who
            # triggered the export know the export finished.
            user_profile = get_user_profile_by_id(event['user_profile_id'])
            content = "Your data export is complete and has been uploaded here:\n\n%s" % (
                public_url, )
            internal_send_private_message(realm=user_profile.realm,
                                          sender=get_system_bot(
                                              settings.NOTIFICATION_BOT),
                                          recipient_user=user_profile,
                                          content=content)

            # For future frontend use, also notify administrator
            # clients that the export happened.
            notify_realm_export(user_profile)
            logging.info("Completed data export for %s in %s" %
                         (user_profile.realm.string_id, time.time() - start))
Пример #22
0
def get_topics_backend(request, user_profile,
                       stream_id=REQ(converter=to_non_negative_int)):
    # type: (HttpRequest, UserProfile, int) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    result = get_topic_history_for_stream(
        user_profile=user_profile,
        recipient=recipient,
    )

    # Our data structure here is a list of tuples of
    # (topic name, unread count), and it's reverse chronological,
    # so the most recent topic is the first element of the list.
    return json_success(dict(topics=result))
Пример #23
0
def get_topics_backend(request, user_profile,
                       stream_id=REQ(converter=to_non_negative_int)):
    # type: (HttpRequest, UserProfile, int) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    result = get_topic_history_for_stream(
        user_profile=user_profile,
        recipient=recipient,
    )

    # Our data structure here is a list of tuples of
    # (topic name, unread count), and it's reverse chronological,
    # so the most recent topic is the first element of the list.
    return json_success(dict(topics=result))
Пример #24
0
def mute_topic(user_profile: UserProfile, stream_id: Optional[int],
               stream_name: Optional[str], topic_name: str) -> HttpResponse:
    if stream_name is not None:
        (stream, recipient,
         sub) = access_stream_by_name(user_profile, stream_name)
    else:
        assert stream_id is not None
        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    if topic_is_muted(user_profile, stream.id, topic_name):
        return json_error(_("Topic already muted"))

    do_mute_topic(user_profile, stream, recipient, topic_name)
    return json_success()
Пример #25
0
def get_subscription_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    user_id: int = REQ(json_validator=check_int, path_only=True),
    stream_id: int = REQ(json_validator=check_int, path_only=True),
) -> HttpResponse:
    target_user = access_user_by_id(user_profile, user_id, for_admin=False)
    (stream, sub) = access_stream_by_id(user_profile, stream_id)

    subscription_status = {
        "is_subscribed": subscribed_to_stream(target_user, stream_id)
    }

    return json_success(subscription_status)
Пример #26
0
def invite_users_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    invitee_emails_raw: str = REQ("invitee_emails"),
    invite_as: int = REQ(validator=check_int,
                         default=PreregistrationUser.INVITE_AS["MEMBER"]),
    stream_ids: List[int] = REQ(validator=check_list(check_int)),
) -> HttpResponse:

    if not user_profile.can_invite_others_to_realm():
        if user_profile.realm.invite_to_realm_policy == Realm.POLICY_ADMINS_ONLY:
            return json_error(
                _("Only administrators can invite others to this organization."
                  ))
        if user_profile.realm.invite_to_realm_policy == Realm.POLICY_MODERATORS_ONLY:
            return json_error(
                _("Only administrators and moderators can invite others to this organization."
                  ))
        if user_profile.realm.invite_to_realm_policy == Realm.POLICY_FULL_MEMBERS_ONLY:
            return json_error(
                _("Your account is too new to invite others to this organization."
                  ))
        # Guest case will be handled by require_member_or_admin decorator.
        raise AssertionError("Unexpected policy validation failure")
    if invite_as not in PreregistrationUser.INVITE_AS.values():
        return json_error(_("Must be invited as an valid type of user"))
    check_if_owner_required(invite_as, user_profile)
    if (invite_as == PreregistrationUser.INVITE_AS["REALM_ADMIN"]
            and not user_profile.is_realm_admin):
        return json_error(_("Must be an organization administrator"))
    if not invitee_emails_raw:
        return json_error(_("You must specify at least one email address."))
    if not stream_ids:
        return json_error(
            _("You must specify at least one stream for invitees to join."))

    invitee_emails = get_invitee_emails_set(invitee_emails_raw)

    streams: List[Stream] = []
    for stream_id in stream_ids:
        try:
            (stream, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(
                _("Stream does not exist with id: {}. No invites were sent.").
                format(stream_id))
        streams.append(stream)

    do_invite_users(user_profile, invitee_emails, streams, invite_as)
    return json_success()
Пример #27
0
def send_notification_backend(
        request: HttpRequest,
        user_profile: UserProfile,
        message_type: str = REQ(
            "type",
            str_validator=check_string_in(VALID_MESSAGE_TYPES),
            default="private"),
        operator: str = REQ(
            "op", str_validator=check_string_in(VALID_OPERATOR_TYPES)),
        notification_to: List[int] = REQ("to",
                                         json_validator=check_list(check_int)),
        topic: Optional[str] = REQ("topic", default=None),
) -> HttpResponse:
    to_length = len(notification_to)

    if to_length == 0:
        raise JsonableError(_("Empty 'to' list"))

    if message_type == "stream":
        if to_length > 1:
            raise JsonableError(_("Cannot send to multiple streams"))

        if topic is None:
            raise JsonableError(_("Missing topic"))

        if not user_profile.send_stream_typing_notifications:
            raise JsonableError(
                _("User has disabled typing notifications for stream messages")
            )

        stream_id = notification_to[0]
        # Verify that the user has access to the stream and has
        # permission to send messages to it.
        stream = access_stream_by_id(user_profile, stream_id)[0]
        access_stream_for_send_message(user_profile,
                                       stream,
                                       forwarder_user_profile=None)
        do_send_stream_typing_notification(user_profile, operator, stream,
                                           topic)
    else:
        if not user_profile.send_private_typing_notifications:
            raise JsonableError(
                _("User has disabled typing notifications for private messages"
                  ))

        user_ids = notification_to
        check_send_typing_notification(user_profile, user_ids, operator)

    return json_success()
Пример #28
0
def mute_topic(user_profile: UserProfile,
               stream_id: Optional[int],
               stream_name: Optional[str],
               topic_name: str) -> HttpResponse:
    if stream_name is not None:
        (stream, recipient, sub) = access_stream_by_name(user_profile, stream_name)
    else:
        assert stream_id is not None
        (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    if topic_is_muted(user_profile, stream.id, topic_name):
        return json_error(_("Topic already muted"))

    do_mute_topic(user_profile, stream, recipient, topic_name)
    return json_success()
Пример #29
0
def generate_multiuse_invite_backend(
        request: HttpRequest, user_profile: UserProfile,
        invite_as: int=REQ(validator=check_int, default=PreregistrationUser.INVITE_AS['MEMBER']),
        stream_ids: Sequence[int]=REQ(validator=check_list(check_int), default=[])) -> HttpResponse:
    check_if_owner_required(invite_as, user_profile)

    streams = []
    for stream_id in stream_ids:
        try:
            (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(_("Invalid stream id {}. No invites were sent.").format(stream_id))
        streams.append(stream)

    invite_link = do_create_multiuse_invite_link(user_profile, invite_as, streams)
    return json_success({'invite_link': invite_link})
Пример #30
0
def delete_in_topic(request: HttpRequest, user_profile: UserProfile,
                    stream_id: int=REQ(converter=to_non_negative_int),
                    topic_name: str=REQ("topic_name")) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    messages = messages_for_topic(stream.id, topic_name)
    if not stream.is_history_public_to_subscribers():
        # Don't allow the user to delete messages that they don't have access to.
        deletable_message_ids = UserMessage.objects.filter(
            user_profile=user_profile, message_id__in=messages).values_list("message_id", flat=True)
        messages = [message for message in messages if message.id in
                    deletable_message_ids]

    do_delete_messages(user_profile, messages)

    return json_success()
Пример #31
0
def delete_in_topic(request: HttpRequest, user_profile: UserProfile,
                    stream_id: int=REQ(converter=to_non_negative_int),
                    topic_name: str=REQ("topic_name")) -> HttpResponse:
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    messages = messages_for_topic(stream.id, topic_name)
    if not stream.is_history_public_to_subscribers():
        # Don't allow the user to delete messages that they don't have access to.
        deletable_message_ids = UserMessage.objects.filter(
            user_profile=user_profile, message_id__in=messages).values_list("message_id", flat=True)
        messages = [message for message in messages if message.id in
                    deletable_message_ids]

    do_delete_messages(user_profile, messages)

    return json_success()
Пример #32
0
    def consume(self, event: Mapping[str, Any]) -> None:
        if event['type'] == 'mark_stream_messages_as_read':
            user_profile = get_user_profile_by_id(event['user_profile_id'])
            client = Client.objects.get(id=event['client_id'])

            for stream_id in event['stream_ids']:
                # Since the user just unsubscribed, we don't require
                # an active Subscription object (otherwise, private
                # streams would never be accessible)
                (stream, recipient,
                 sub) = access_stream_by_id(user_profile,
                                            stream_id,
                                            require_active=False)
                do_mark_stream_messages_as_read(user_profile, client, stream)
        elif event['type'] == 'realm_exported':
            realm = Realm.objects.get(id=event['realm_id'])
            output_dir = tempfile.mkdtemp(prefix="zulip-export-")

            public_url = export_realm_wrapper(realm=realm,
                                              output_dir=output_dir,
                                              threads=6,
                                              upload=True,
                                              public_only=True,
                                              delete_after_upload=True)
            assert public_url is not None

            # TODO: This enables support for delete after access, and needs to be tested.
            export_event = RealmAuditLog.objects.get(id=event['id'])
            export_event.extra_data = public_url
            export_event.save(update_fields=['extra_data'])

            # Send a private message notification letting the user who
            # triggered the export know the export finished.
            user_profile = get_user_profile_by_id(event['user_profile_id'])
            content = "Your data export is complete and has been uploaded here:\n\n%s" % (
                public_url, )
            internal_send_private_message(realm=user_profile.realm,
                                          sender=get_system_bot(
                                              settings.NOTIFICATION_BOT),
                                          recipient_user=user_profile,
                                          content=content)

            # For future frontend use, also notify administrator
            # clients that the export happened, including sending the
            # url.
            notify_export_completed(user_profile, public_url)
Пример #33
0
def further_validated_draft_dict(draft_dict: Dict[str, Any],
                                 user_profile: UserProfile) -> Dict[str, Any]:
    """ Take a draft_dict that was already validated by draft_dict_validator then
    further sanitize, validate, and transform it. Ultimately return this "further
    validated" draft dict. It will have a slightly different set of keys the values
    for which can be used to directly create a Draft object. """

    content = truncate_body(draft_dict["content"])
    if "\x00" in content:
        raise JsonableError(_("Content must not contain null bytes"))

    timestamp = draft_dict.get("timestamp", time.time())
    timestamp = round(timestamp, 6)
    if timestamp < 0:
        # While it's not exactly an invalid timestamp, it's not something
        # we want to allow either.
        raise JsonableError(_("Timestamp must not be negative."))
    last_edit_time = timestamp_to_datetime(timestamp)

    topic = ""
    recipient = None
    to = draft_dict["to"]
    if draft_dict["type"] == "stream":
        topic = truncate_topic(draft_dict["topic"])
        if "\x00" in topic:
            raise JsonableError(_("Topic must not contain null bytes"))
        if len(to) != 1:
            raise JsonableError(
                _("Must specify exactly 1 stream ID for stream messages"))
        stream, sub = access_stream_by_id(user_profile, to[0])
        recipient = stream.recipient
    elif draft_dict["type"] == "private" and len(to) != 0:
        to_users = get_user_profiles_by_ids(set(to), user_profile.realm)
        try:
            recipient = recipient_for_user_profiles(to_users, False, None,
                                                    user_profile)
        except ValidationError as e:  # nocoverage
            raise JsonableError(e.messages[0])

    return {
        "recipient": recipient,
        "topic": topic,
        "content": content,
        "last_edit_time": last_edit_time,
    }
Пример #34
0
def mute_topic(
    user_profile: UserProfile,
    stream_id: Optional[int],
    stream_name: Optional[str],
    topic_name: str,
    date_muted: datetime.datetime,
) -> HttpResponse:
    if stream_name is not None:
        (stream, sub) = access_stream_by_name(user_profile, stream_name)
    else:
        assert stream_id is not None
        (stream, sub) = access_stream_by_id(user_profile, stream_id)

    if topic_is_muted(user_profile, stream.id, topic_name):
        raise JsonableError(_("Topic already muted"))

    do_mute_topic(user_profile, stream, topic_name, date_muted)
    return json_success()
Пример #35
0
def invite_users_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    invitee_emails_raw: str = REQ("invitee_emails"),
    invite_as: Optional[int] = REQ(
        validator=check_int, default=PreregistrationUser.INVITE_AS['MEMBER']),
    stream_ids: List[int] = REQ(validator=check_list(check_int)),
) -> HttpResponse:

    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
        return json_error(_("Must be an organization administrator"))
    if invite_as not in PreregistrationUser.INVITE_AS.values():
        return json_error(_("Must be invited as an valid type of user"))
    if invite_as == PreregistrationUser.INVITE_AS[
            'REALM_ADMIN'] and not user_profile.is_realm_admin:
        return json_error(_("Must be an organization administrator"))
    if not invitee_emails_raw:
        return json_error(_("You must specify at least one email address."))
    if not stream_ids:
        return json_error(
            _("You must specify at least one stream for invitees to join."))

    invitee_emails = get_invitee_emails_set(invitee_emails_raw)

    # We unconditionally sub you to the notifications stream if it
    # exists and is public.
    notifications_stream = user_profile.realm.notifications_stream  # type: Optional[Stream]
    if notifications_stream and not notifications_stream.invite_only:
        stream_ids.append(notifications_stream.id)

    streams = []  # type: List[Stream]
    for stream_id in stream_ids:
        try:
            (stream, recipient,
             sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(
                _("Stream does not exist with id: {}. No invites were sent.").
                format(stream_id))
        streams.append(stream)

    do_invite_users(user_profile, invitee_emails, streams, invite_as)
    return json_success()
Пример #36
0
def update_stream_backend(request, user_profile, stream_id,
                          description=REQ(validator=check_string, default=None),
                          is_private=REQ(validator=check_bool, default=None),
                          new_name=REQ(validator=check_string, default=None)):
    # type: (HttpRequest, UserProfile, int, Optional[Text], Optional[bool], Optional[Text]) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    if description is not None:
        do_change_stream_description(stream, description)
    if new_name is not None:
        new_name = new_name.strip()
        # Will raise if the new name has invalid characters.
        if stream.name.lower() == new_name.lower():
            return json_error(_("Stream already has that name!"))
        check_stream_name_available(user_profile.realm, new_name)
        do_rename_stream(stream, new_name)
    if is_private is not None:
        do_change_stream_invite_only(stream, is_private)
    return json_success()
Пример #37
0
def mute_topic(
    user_profile: UserProfile,
    stream_id: Optional[int],
    stream_name: Optional[str],
    topic_name: str,
    date_muted: datetime.datetime,
) -> None:
    if stream_name is not None:
        (stream, sub) = access_stream_by_name(user_profile, stream_name)
    else:
        assert stream_id is not None
        (stream, sub) = access_stream_by_id(user_profile, stream_id)

    if topic_is_muted(user_profile, stream.id, topic_name):
        raise JsonableError(_("Topic already muted"))

    try:
        do_mute_topic(user_profile, stream, topic_name, date_muted)
    except IntegrityError:
        raise JsonableError(_("Topic already muted"))
Пример #38
0
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),
    history_public_to_subscribers: Optional[bool] = REQ(validator=check_bool,
                                                        default=None),
    new_name: Optional[str] = REQ(validator=check_string, 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 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:
        do_change_stream_announcement_only(stream, is_announcement_only)

    # 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()
Пример #39
0
def delete_in_topic(
    request: HttpRequest,
    user_profile: UserProfile,
    stream_id: int = REQ(converter=to_non_negative_int, path_only=True),
    topic_name: str = REQ("topic_name"),
) -> HttpResponse:
    (stream, sub) = access_stream_by_id(user_profile, stream_id)

    messages = messages_for_topic(assert_is_not_none(stream.recipient_id), topic_name)
    if not stream.is_history_public_to_subscribers():
        # Don't allow the user to delete messages that they don't have access to.
        deletable_message_ids = UserMessage.objects.filter(
            user_profile=user_profile, message_id__in=messages
        ).values_list("message_id", flat=True)
        messages = messages.filter(id__in=deletable_message_ids)

    messages = messages.select_for_update(of=("self",))

    do_delete_messages(user_profile.realm, messages)

    return json_success()
Пример #40
0
def update_stream_backend(request, user_profile, stream_id,
                          description=REQ(validator=check_string, default=None),
                          is_private=REQ(validator=check_bool, default=None),
                          new_name=REQ(validator=check_string, default=None)):
    # type: (HttpRequest, UserProfile, int, Optional[Text], Optional[bool], Optional[Text]) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)

    if description is not None:
        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)
    if is_private is not None:
        do_change_stream_invite_only(stream, is_private)
    return json_success()
Пример #41
0
def generate_multiuse_invite_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    invite_expires_in_minutes: Optional[int] = REQ(
        json_validator=check_none_or(check_int), default=settings.INVITATION_LINK_VALIDITY_MINUTES
    ),
    invite_as: int = REQ(json_validator=check_int, default=PreregistrationUser.INVITE_AS["MEMBER"]),
    stream_ids: Sequence[int] = REQ(json_validator=check_list(check_int), default=[]),
) -> HttpResponse:
    check_if_owner_required(invite_as, user_profile)

    streams = []
    for stream_id in stream_ids:
        try:
            (stream, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            raise JsonableError(_("Invalid stream ID {}. No invites were sent.").format(stream_id))
        streams.append(stream)

    invite_link = do_create_multiuse_invite_link(
        user_profile, invite_as, invite_expires_in_minutes, streams
    )
    return json_success(request, data={"invite_link": invite_link})
Пример #42
0
def invite_users_backend(request: HttpRequest, user_profile: UserProfile,
                         invitee_emails_raw: str=REQ("invitee_emails"),
                         invite_as: Optional[int]=REQ(
                             validator=check_int, default=PreregistrationUser.INVITE_AS['MEMBER']),
                         stream_ids: List[int]=REQ(validator=check_list(check_int)),
                         ) -> HttpResponse:

    if user_profile.realm.invite_by_admins_only and not user_profile.is_realm_admin:
        return json_error(_("Must be an organization administrator"))
    if invite_as not in PreregistrationUser.INVITE_AS.values():
        return json_error(_("Must be invited as an valid type of user"))
    if invite_as == PreregistrationUser.INVITE_AS['REALM_ADMIN'] and not user_profile.is_realm_admin:
        return json_error(_("Must be an organization administrator"))
    if not invitee_emails_raw:
        return json_error(_("You must specify at least one email address."))
    if not stream_ids:
        return json_error(_("You must specify at least one stream for invitees to join."))

    invitee_emails = get_invitee_emails_set(invitee_emails_raw)

    # We unconditionally sub you to the notifications stream if it
    # exists and is public.
    notifications_stream = user_profile.realm.notifications_stream  # type: Optional[Stream]
    if notifications_stream and not notifications_stream.invite_only:
        stream_ids.append(notifications_stream.id)

    streams = []  # type: List[Stream]
    for stream_id in stream_ids:
        try:
            (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
        except JsonableError:
            return json_error(
                _("Stream does not exist with id: {}. No invites were sent.".format(stream_id)))
        streams.append(stream)

    do_invite_users(user_profile, invitee_emails, streams, invite_as)
    return json_success()
Пример #43
0
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),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        new_name: Optional[str]=REQ(validator=check_string, 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 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:
        do_change_stream_announcement_only(stream, is_announcement_only)

    # 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()
Пример #44
0
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),
        inline_image_preview: Optional[bool]=REQ(validator=check_bool, default=None),
        inline_url_embed_preview: Optional[bool]=REQ(validator=check_bool, default=None),
        create_stream_by_admins_only: 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[Any, 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: Optional[int]=REQ(converter=to_not_negative_int_or_none, default=None),
        send_welcome_emails: 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(converter=to_not_negative_int_or_none, default=None),
        email_address_visibility: Optional[int]=REQ(converter=to_not_negative_int_or_none, default=None),
        default_twenty_four_hour_time: Optional[bool]=REQ(validator=check_bool, default=None),
        video_chat_provider: Optional[str]=REQ(validator=check_string, default=None),
        google_hangouts_domain: Optional[str]=REQ(validator=check_string, default=None),
        zoom_user_id: Optional[str]=REQ(validator=check_string, default=None),
        zoom_api_key: Optional[str]=REQ(validator=check_string, default=None),
        zoom_api_secret: Optional[str]=REQ(validator=check_string, 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 '%s'" % (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 and True not in list(authentication_methods.values()):
        return json_error(_("At least one authentication method must be enabled."))
    if video_chat_provider == "Google Hangouts":
        try:
            validate_domain(google_hangouts_domain)
        except ValidationError as e:
            return json_error(_('Invalid domain: {}').format(e.messages[0]))
    if video_chat_provider == "Zoom":
        if not zoom_user_id:
            return json_error(_('Invalid user ID: user ID cannot be empty'))
        if not zoom_api_key:
            return json_error(_('Invalid API key: API key cannot be empty'))
        if not zoom_api_secret:
            return json_error(_('Invalid API secret: API secret cannot be empty'))
        # Technically, we could call some other API endpoint that
        # doesn't create a video call link, but this is a nicer
        # end-to-end test, since it verifies that the Zoom API user's
        # scopes includes the ability to create video calls, which is
        # the only capabiility we use.
        if not request_zoom_video_call_url(zoom_user_id, zoom_api_key, zoom_api_secret):
            return json_error(_('Invalid credentials for the %(third_party_service)s API.') % dict(
                third_party_service="Zoom"))

    # Additional validation of enum-style values
    if bot_creation_policy is not None and bot_creation_policy not in Realm.BOT_CREATION_POLICY_TYPES:
        return json_error(_("Invalid bot creation policy"))
    if email_address_visibility is not None and \
            email_address_visibility not in Realm.EMAIL_ADDRESS_VISIBILITY_TYPES:
        return json_error(_("Invalid email address visibility policy"))

    # 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 = {}  # type: 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)
            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)
        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)
        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

    if (message_content_delete_limit_seconds is not None and
            realm.message_content_delete_limit_seconds != message_content_delete_limit_seconds):
        do_set_realm_message_deleting(realm, message_content_delete_limit_seconds)
        data['message_content_delete_limit_seconds'] = message_content_delete_limit_seconds
    # 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)
            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)
            data['signup_notifications_stream_id'] = signup_notifications_stream_id

    return json_success(data)
Пример #45
0
def update_realm(request, user_profile, name=REQ(validator=check_string, default=None),
                 description=REQ(validator=check_string, default=None),
                 restricted_to_domain=REQ(validator=check_bool, default=None),
                 invite_required=REQ(validator=check_bool, default=None),
                 invite_by_admins_only=REQ(validator=check_bool, default=None),
                 name_changes_disabled=REQ(validator=check_bool, default=None),
                 email_changes_disabled=REQ(validator=check_bool, default=None),
                 inline_image_preview=REQ(validator=check_bool, default=None),
                 inline_url_embed_preview=REQ(validator=check_bool, default=None),
                 create_stream_by_admins_only=REQ(validator=check_bool, default=None),
                 add_emoji_by_admins_only=REQ(validator=check_bool, default=None),
                 allow_message_editing=REQ(validator=check_bool, default=None),
                 mandatory_topics=REQ(validator=check_bool, default=None),
                 message_content_edit_limit_seconds=REQ(converter=to_non_negative_int, default=None),
                 allow_edit_history=REQ(validator=check_bool, default=None),
                 default_language=REQ(validator=check_string, default=None),
                 waiting_period_threshold=REQ(converter=to_non_negative_int, default=None),
                 authentication_methods=REQ(validator=check_dict([]), default=None),
                 notifications_stream_id=REQ(validator=check_int, default=None),
                 message_retention_days=REQ(converter=to_not_negative_int_or_none, default=None)):
    # type: (HttpRequest, UserProfile, Optional[str], Optional[str], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[bool], Optional[int], Optional[bool], Optional[str], Optional[int], Optional[dict], Optional[int], Optional[int]) -> 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 '%s'" % (default_language,)))
    if description is not None and len(description) > 1000:
        return json_error(_("Realm description is too long."))
    if authentication_methods is not None and True not in list(authentication_methods.values()):
        return json_error(_("At least one authentication method must be enabled."))

    # 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 = {}  # type: 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)
            if isinstance(v, Text):
                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)
        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):
        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
        do_set_realm_message_editing(realm, allow_message_editing, message_content_edit_limit_seconds)
        data['allow_message_editing'] = allow_message_editing
        data['message_content_edit_limit_seconds'] = message_content_edit_limit_seconds
    # Realm.notifications_stream is not a boolean, Text 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)
            data['notifications_stream_id'] = notifications_stream_id

    return json_success(data)
Пример #46
0
def deactivate_stream_backend(request, user_profile, stream_id):
    # type: (HttpRequest, UserProfile, int) -> HttpResponse
    (stream, recipient, sub) = access_stream_by_id(user_profile, stream_id)
    do_deactivate_stream(stream)
    return json_success()