コード例 #1
0
def fetch_initial_state_data(
        user_profile: UserProfile,
        event_types: Optional[Iterable[str]],
        queue_id: str,
        client_gravatar: bool,
        user_avatar_url_field_optional: bool,
        slim_presence: bool = False,
        include_subscribers: bool = True) -> Dict[str, Any]:
    state: Dict[str, Any] = {'queue_id': queue_id}
    realm = user_profile.realm

    if event_types is None:
        # return True always
        want: Callable[[str], bool] = always_want
    else:
        want = set(event_types).__contains__

    # Show the version info unconditionally.
    state['zulip_version'] = ZULIP_VERSION
    state['zulip_feature_level'] = API_FEATURE_LEVEL

    if want('alert_words'):
        state['alert_words'] = user_alert_words(user_profile)

    if want('custom_profile_fields'):
        fields = custom_profile_fields_for_realm(realm.id)
        state['custom_profile_fields'] = [f.as_dict() for f in fields]
        state[
            'custom_profile_field_types'] = CustomProfileField.FIELD_TYPE_CHOICES_DICT

    if want('hotspots'):
        state['hotspots'] = get_next_hotspots(user_profile)

    if want('message'):
        # The client should use get_messages() to fetch messages
        # starting with the max_message_id.  They will get messages
        # newer than that ID via get_events()
        user_messages = UserMessage.objects \
            .filter(user_profile=user_profile) \
            .order_by('-message_id') \
            .values('message_id')[:1]
        if user_messages:
            state['max_message_id'] = user_messages[0]['message_id']
        else:
            state['max_message_id'] = -1

    if want('muted_topics'):
        state['muted_topics'] = get_topic_mutes(user_profile)

    if want('presence'):
        state['presences'] = get_presences_for_realm(realm, user_profile,
                                                     slim_presence)

    if want('realm'):
        for property_name in Realm.property_types:
            state['realm_' + property_name] = getattr(realm, property_name)

        # Most state is handled via the property_types framework;
        # these manual entries are for those realm settings that don't
        # fit into that framework.
        state[
            'realm_authentication_methods'] = realm.authentication_methods_dict(
            )
        state['realm_allow_message_editing'] = realm.allow_message_editing
        state[
            'realm_allow_community_topic_editing'] = realm.allow_community_topic_editing
        state['realm_allow_message_deleting'] = realm.allow_message_deleting
        state[
            'realm_message_content_edit_limit_seconds'] = realm.message_content_edit_limit_seconds
        state[
            'realm_message_content_delete_limit_seconds'] = realm.message_content_delete_limit_seconds
        state['realm_community_topic_editing_limit_seconds'] = \
            Realm.DEFAULT_COMMUNITY_TOPIC_EDITING_LIMIT_SECONDS
        state['realm_icon_url'] = realm_icon_url(realm)
        state['realm_icon_source'] = realm.icon_source
        state['max_icon_file_size'] = settings.MAX_ICON_FILE_SIZE
        add_realm_logo_fields(state, realm)
        state['realm_bot_domain'] = realm.get_bot_domain()
        state['realm_uri'] = realm.uri
        state[
            'realm_available_video_chat_providers'] = realm.VIDEO_CHAT_PROVIDERS
        state['realm_presence_disabled'] = realm.presence_disabled
        state['settings_send_digest_emails'] = settings.SEND_DIGEST_EMAILS
        state[
            'realm_digest_emails_enabled'] = realm.digest_emails_enabled and settings.SEND_DIGEST_EMAILS
        state['realm_is_zephyr_mirror_realm'] = realm.is_zephyr_mirror_realm
        state['realm_email_auth_enabled'] = email_auth_enabled(realm)
        state['realm_password_auth_enabled'] = password_auth_enabled(realm)
        state['realm_push_notifications_enabled'] = push_notifications_enabled(
        )
        state['realm_upload_quota'] = realm.upload_quota_bytes()
        state['realm_plan_type'] = realm.plan_type
        state['zulip_plan_is_not_limited'] = realm.plan_type != Realm.LIMITED
        state['upgrade_text_for_wide_organization_logo'] = str(
            Realm.UPGRADE_TEXT_STANDARD)
        state['realm_default_external_accounts'] = DEFAULT_EXTERNAL_ACCOUNTS
        state['jitsi_server_url'] = settings.JITSI_SERVER_URL
        state['development_environment'] = settings.DEVELOPMENT
        state['server_generation'] = settings.SERVER_GENERATION
        state['password_min_length'] = settings.PASSWORD_MIN_LENGTH
        state['password_min_guesses'] = settings.PASSWORD_MIN_GUESSES
        state['max_file_upload_size_mib'] = settings.MAX_FILE_UPLOAD_SIZE
        state['max_avatar_file_size_mib'] = settings.MAX_AVATAR_FILE_SIZE
        state['server_inline_image_preview'] = settings.INLINE_IMAGE_PREVIEW
        state[
            'server_inline_url_embed_preview'] = settings.INLINE_URL_EMBED_PREVIEW
        state[
            'server_avatar_changes_disabled'] = settings.AVATAR_CHANGES_DISABLED
        state['server_name_changes_disabled'] = settings.NAME_CHANGES_DISABLED

        if realm.notifications_stream and not realm.notifications_stream.deactivated:
            notifications_stream = realm.notifications_stream
            state['realm_notifications_stream_id'] = notifications_stream.id
        else:
            state['realm_notifications_stream_id'] = -1

        signup_notifications_stream = realm.get_signup_notifications_stream()
        if signup_notifications_stream:
            state[
                'realm_signup_notifications_stream_id'] = signup_notifications_stream.id
        else:
            state['realm_signup_notifications_stream_id'] = -1

    if want('realm_domains'):
        state['realm_domains'] = get_realm_domains(realm)

    if want('realm_emoji'):
        state['realm_emoji'] = realm.get_emoji()

    if want('realm_filters'):
        state['realm_filters'] = realm_filters_for_realm(realm.id)

    if want('realm_user_groups'):
        state['realm_user_groups'] = user_groups_in_realm_serialized(realm)

    if want('realm_filtered_user'):
        state['filtered_users'] = get_filtered_user_data(
            realm,
            user_profile,
            client_gravatar=client_gravatar,
            user_avatar_url_field_optional=user_avatar_url_field_optional)
        # For the user's own avatar URL, we force
        # client_gravatar=False, since that saves some unnecessary
        # client-side code for handing medium-size avatars.  See #8253
        # for details.
        state['avatar_source'] = user_profile.avatar_source
        state['avatar_url_medium'] = avatar_url(
            user_profile,
            medium=True,
            client_gravatar=False,
        )
        state['avatar_url'] = avatar_url(
            user_profile,
            medium=False,
            client_gravatar=False,
        )

        state['can_create_streams'] = user_profile.can_create_streams()
        state[
            'can_subscribe_other_users'] = user_profile.can_subscribe_other_users(
            )
        state['cross_realm_bots'] = list(get_cross_realm_dicts())
        state['is_admin'] = user_profile.is_realm_admin
        state['is_owner'] = user_profile.is_realm_owner
        state['is_guest'] = user_profile.is_guest
        state['user_id'] = user_profile.id
        state['enter_sends'] = user_profile.enter_sends
        state['email'] = user_profile.email
        state['delivery_email'] = user_profile.delivery_email
        state['full_name'] = user_profile.full_name

    if want('realm_user'):
        state['raw_users'] = get_raw_user_data(
            realm,
            user_profile,
            client_gravatar=client_gravatar,
            user_avatar_url_field_optional=user_avatar_url_field_optional)

        # For the user's own avatar URL, we force
        # client_gravatar=False, since that saves some unnecessary
        # client-side code for handing medium-size avatars.  See #8253
        # for details.
        state['avatar_source'] = user_profile.avatar_source
        state['avatar_url_medium'] = avatar_url(
            user_profile,
            medium=True,
            client_gravatar=False,
        )
        state['avatar_url'] = avatar_url(
            user_profile,
            medium=False,
            client_gravatar=False,
        )

        state['can_create_streams'] = user_profile.can_create_streams()
        state[
            'can_subscribe_other_users'] = user_profile.can_subscribe_other_users(
            )
        state['cross_realm_bots'] = list(get_cross_realm_dicts())
        state['is_admin'] = user_profile.is_realm_admin
        state['is_owner'] = user_profile.is_realm_owner
        state['is_guest'] = user_profile.is_guest
        state['user_id'] = user_profile.id
        state['enter_sends'] = user_profile.enter_sends
        state['email'] = user_profile.email
        state['delivery_email'] = user_profile.delivery_email
        state['full_name'] = user_profile.full_name

    if want('realm_bot'):
        state['realm_bots'] = get_owned_bot_dicts(user_profile)

    # This does not yet have an apply_event counterpart, since currently,
    # new entries for EMBEDDED_BOTS can only be added directly in the codebase.
    if want('realm_embedded_bots'):
        realm_embedded_bots = []
        for bot in EMBEDDED_BOTS:
            realm_embedded_bots.append({
                'name':
                bot.name,
                'config':
                load_bot_config_template(bot.name)
            })
        state['realm_embedded_bots'] = realm_embedded_bots

    # This does not have an apply_events counterpart either since
    # this data is mostly static.
    if want('realm_incoming_webhook_bots'):
        realm_incoming_webhook_bots = []
        for integration in WEBHOOK_INTEGRATIONS:
            realm_incoming_webhook_bots.append({
                'name': integration.name,
                'config': {c[1]: c[0]
                           for c in integration.config_options},
            })
        state['realm_incoming_webhook_bots'] = realm_incoming_webhook_bots

    if want('recent_private_conversations'):
        # A data structure containing records of this form:
        #
        #   [{'max_message_id': 700175, 'user_ids': [801]}]
        #
        # for all recent private message conversations, ordered by the
        # highest message ID in the conversation.  The user_ids list
        # is the list of users other than the current user in the
        # private message conversation (so it is [] for PMs to self).
        # Note that raw_recent_private_conversations is an
        # intermediate form as a dictionary keyed by recipient_id,
        # which is more efficient to update, and is rewritten to the
        # final format in post_process_state.
        state[
            'raw_recent_private_conversations'] = get_recent_private_conversations(
                user_profile)

    if want('subscription'):
        subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper(
            user_profile, include_subscribers=include_subscribers)
        state['subscriptions'] = subscriptions
        state['unsubscribed'] = unsubscribed
        state['never_subscribed'] = never_subscribed

    if want('update_message_flags') and want('message'):
        # Keeping unread_msgs updated requires both message flag updates and
        # message updates. This is due to the fact that new messages will not
        # generate a flag update so we need to use the flags field in the
        # message event.
        state['raw_unread_msgs'] = get_raw_unread_data(user_profile)

    if want('starred_messages'):
        state['starred_messages'] = get_starred_message_ids(user_profile)

    if want('stream'):
        state['streams'] = do_get_streams(user_profile)
        state['stream_name_max_length'] = Stream.MAX_NAME_LENGTH
        state['stream_description_max_length'] = Stream.MAX_DESCRIPTION_LENGTH
    if want('default_streams'):
        if user_profile.is_guest:
            state['realm_default_streams'] = []
        else:
            state['realm_default_streams'] = streams_to_dicts_sorted(
                get_default_streams_for_realm(realm.id))
    if want('default_stream_groups'):
        if user_profile.is_guest:
            state['realm_default_stream_groups'] = []
        else:
            state[
                'realm_default_stream_groups'] = default_stream_groups_to_dicts_sorted(
                    get_default_stream_groups(realm))

    if want('stop_words'):
        state['stop_words'] = read_stop_words()

    if want('update_display_settings'):
        for prop in UserProfile.property_types:
            state[prop] = getattr(user_profile, prop)
        state['emojiset_choices'] = user_profile.emojiset_choices()

    if want('update_global_notifications'):
        for notification in UserProfile.notification_setting_types:
            state[notification] = getattr(user_profile, notification)
        state[
            'available_notification_sounds'] = get_available_notification_sounds(
            )

    if want('user_status'):
        state['user_status'] = get_user_info_dict(realm_id=realm.id)

    if want('video_calls'):
        state['has_zoom_token'] = user_profile.zoom_token is not None

    return state
コード例 #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
ファイル: streams.py プロジェクト: pratishkatiyar/zulip
def add_subscriptions_backend(
    request: HttpRequest,
    user_profile: UserProfile,
    streams_raw: Iterable[Dict[str, str]] = REQ(
        "subscriptions",
        validator=check_list(
            check_dict_only([('name', check_string)],
                            optional_keys=[
                                ('color', check_color),
                                ('description',
                                 check_capped_string(
                                     Stream.MAX_DESCRIPTION_LENGTH)),
                            ]), )),
    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="realm_default"),
    announce: bool = REQ(validator=check_bool, default=False),
    principals: Union[Sequence[str],
                      Sequence[int]] = REQ(validator=check_union(
                          [check_list(check_string),
                           check_list(check_int)]),
                                           default=[]),
    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_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

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

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            notifications.append(
                internal_prep_private_message(
                    realm=user_profile.realm,
                    sender=sender,
                    recipient_user=email_to_user_profile[email],
                    content=msg))

    if announce and len(created_streams) > 0:
        notifications_stream = user_profile.realm.get_notifications_stream()
        if notifications_stream is not None:
            if len(created_streams) > 1:
                content = _(
                    "@_**%(user_name)s|%(user_id)d** created the following streams: %(stream_str)s."
                )
            else:
                content = _(
                    "@_**%(user_name)s|%(user_id)d** created a new stream %(stream_str)s."
                )
            content = content % {
                'user_name': user_profile.full_name,
                'user_id': user_profile.id,
                'stream_str': ", ".join(f'#**{s.name}**'
                                        for s in created_streams)
            }

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            topic = _('new streams')

            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:
            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}|{user_id}**.'
                              ).format(
                                  user_name=user_profile.full_name,
                                  user_id=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)
コード例 #4
0
def apply_event(state: Dict[str, Any], event: Dict[str, Any],
                user_profile: UserProfile, client_gravatar: bool,
                slim_presence: bool, include_subscribers: bool) -> None:
    if event['type'] == "message":
        state['max_message_id'] = max(state['max_message_id'],
                                      event['message']['id'])
        if 'raw_unread_msgs' in state:
            apply_unread_message_event(
                user_profile,
                state['raw_unread_msgs'],
                event['message'],
                event['flags'],
            )

        if event['message']['type'] != "stream":
            if 'raw_recent_private_conversations' in state:
                # Handle maintaining the recent_private_conversations data structure.
                conversations = state['raw_recent_private_conversations']
                recipient_id = get_recent_conversations_recipient_id(
                    user_profile, event['message']['recipient_id'],
                    event['message']["sender_id"])

                if recipient_id not in conversations:
                    conversations[recipient_id] = dict(user_ids=sorted([
                        user_dict['id']
                        for user_dict in event['message']['display_recipient']
                        if user_dict['id'] != user_profile.id
                    ]), )
                conversations[recipient_id]['max_message_id'] = event[
                    'message']['id']
            return

        # Below, we handle maintaining first_message_id.
        for sub_dict in state.get('subscriptions', []):
            if event['message']['stream_id'] == sub_dict['stream_id']:
                if sub_dict['first_message_id'] is None:
                    sub_dict['first_message_id'] = event['message']['id']
        for stream_dict in state.get('streams', []):
            if event['message']['stream_id'] == stream_dict['stream_id']:
                if stream_dict['first_message_id'] is None:
                    stream_dict['first_message_id'] = event['message']['id']

    elif event['type'] == "hotspots":
        state['hotspots'] = event['hotspots']
    elif event['type'] == "custom_profile_fields":
        state['custom_profile_fields'] = event['fields']
    elif event['type'] == "realm_user":
        person = event['person']
        person_user_id = person['user_id']

        if event['op'] == "add":
            person = copy.deepcopy(person)
            if client_gravatar:
                if person['avatar_url'].startswith(
                        "https://secure.gravatar.com"):
                    person['avatar_url'] = None
            person['is_active'] = True
            if not person['is_bot']:
                person['profile_data'] = {}
            state['raw_users'][person_user_id] = person
        elif event['op'] == "remove":
            state['raw_users'][person_user_id]['is_active'] = False
        elif event['op'] == 'update':
            is_me = (person_user_id == user_profile.id)

            if is_me:
                if ('avatar_url' in person and 'avatar_url' in state):
                    state['avatar_source'] = person['avatar_source']
                    state['avatar_url'] = person['avatar_url']
                    state['avatar_url_medium'] = person['avatar_url_medium']

                if 'role' in person:
                    state['is_admin'] = is_administrator_role(person['role'])
                    state['is_owner'] = person[
                        'role'] == UserProfile.ROLE_REALM_OWNER
                    state['is_guest'] = person[
                        'role'] == UserProfile.ROLE_GUEST
                    # Recompute properties based on is_admin/is_guest
                    state[
                        'can_create_streams'] = user_profile.can_create_streams(
                        )
                    state[
                        'can_subscribe_other_users'] = user_profile.can_subscribe_other_users(
                        )

                    # TODO: Probably rather than writing the perfect
                    # live-update code for the case of racing with the
                    # current user changing roles, we should just do a
                    # full refetch.
                    if 'never_subscribed' in state:
                        subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper(
                            user_profile,
                            include_subscribers=include_subscribers)
                        state['subscriptions'] = subscriptions
                        state['unsubscribed'] = unsubscribed
                        state['never_subscribed'] = never_subscribed
                    if 'streams' in state:
                        state['streams'] = do_get_streams(user_profile)

                for field in ['delivery_email', 'email', 'full_name']:
                    if field in person and field in state:
                        state[field] = person[field]

                # In the unlikely event that the current user
                # just changed to/from being an admin, we need
                # to add/remove the data on all bots in the
                # realm.  This is ugly and probably better
                # solved by removing the all-realm-bots data
                # given to admin users from this flow.
                if ('role' in person and 'realm_bots' in state):
                    prev_state = state['raw_users'][user_profile.id]
                    was_admin = prev_state['is_admin']
                    now_admin = is_administrator_role(person['role'])

                    if was_admin and not now_admin:
                        state['realm_bots'] = []
                    if not was_admin and now_admin:
                        state['realm_bots'] = get_owned_bot_dicts(user_profile)

            if client_gravatar and 'avatar_url' in person:
                # Respect the client_gravatar setting in the `users` data.
                if person['avatar_url'].startswith(
                        "https://secure.gravatar.com"):
                    person['avatar_url'] = None
                    person['avatar_url_medium'] = None

            if person_user_id in state['raw_users']:
                p = state['raw_users'][person_user_id]
                for field in p:
                    if field in person:
                        p[field] = person[field]
                    if 'role' in person:
                        p['is_admin'] = is_administrator_role(person['role'])
                        p['is_owner'] = person[
                            'role'] == UserProfile.ROLE_REALM_OWNER
                        p['is_guest'] = person[
                            'role'] == UserProfile.ROLE_GUEST
                    if 'custom_profile_field' in person:
                        custom_field_id = person['custom_profile_field']['id']
                        custom_field_new_value = person[
                            'custom_profile_field']['value']
                        if 'rendered_value' in person['custom_profile_field']:
                            p['profile_data'][custom_field_id] = {
                                'value':
                                custom_field_new_value,
                                'rendered_value':
                                person['custom_profile_field']
                                ['rendered_value'],
                            }
                        else:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value,
                            }

    elif event['type'] == 'realm_bot':
        if event['op'] == 'add':
            state['realm_bots'].append(event['bot'])

        if event['op'] == 'remove':
            user_id = event['bot']['user_id']
            for bot in state['realm_bots']:
                if bot['user_id'] == user_id:
                    bot['is_active'] = False

        if event['op'] == 'delete':
            state['realm_bots'] = [
                item for item in state['realm_bots']
                if item['user_id'] != event['bot']['user_id']
            ]

        if event['op'] == 'update':
            for bot in state['realm_bots']:
                if bot['user_id'] == event['bot']['user_id']:
                    if 'owner_id' in event['bot']:
                        bot_owner_id = event['bot']['owner_id']
                        bot['owner_id'] = bot_owner_id
                    else:
                        bot.update(event['bot'])

    elif event['type'] == 'stream':
        if event['op'] == 'create':
            for stream in event['streams']:
                if not stream['invite_only']:
                    stream_data = copy.deepcopy(stream)
                    if include_subscribers:
                        stream_data['subscribers'] = []

                    # We know the stream has no traffic, and this
                    # field is not present in the event.
                    #
                    # TODO: Probably this should just be added to the event.
                    stream_data['stream_weekly_traffic'] = None

                    # Add stream to never_subscribed (if not invite_only)
                    state['never_subscribed'].append(stream_data)
                state['streams'].append(stream)
            state['streams'].sort(key=lambda elt: elt["name"])

        if event['op'] == 'delete':
            deleted_stream_ids = {
                stream['stream_id']
                for stream in event['streams']
            }
            state['streams'] = [
                s for s in state['streams']
                if s['stream_id'] not in deleted_stream_ids
            ]
            state['never_subscribed'] = [
                stream for stream in state['never_subscribed']
                if stream['stream_id'] not in deleted_stream_ids
            ]

        if event['op'] == 'update':
            # For legacy reasons, we call stream data 'subscriptions' in
            # the state var here, for the benefit of the JS code.
            for obj in state['subscriptions']:
                if obj['name'].lower() == event['name'].lower():
                    obj[event['property']] = event['value']
                    if event['property'] == "description":
                        obj['rendered_description'] = event[
                            'rendered_description']
            # Also update the pure streams data
            for stream in state['streams']:
                if stream['name'].lower() == event['name'].lower():
                    prop = event['property']
                    if prop in stream:
                        stream[prop] = event['value']
                        if prop == 'description':
                            stream['rendered_description'] = event[
                                'rendered_description']
        elif event['op'] == "occupy":
            state['streams'] += event['streams']
        elif event['op'] == "vacate":
            stream_ids = [s["stream_id"] for s in event['streams']]
            state['streams'] = [
                s for s in state['streams'] if s["stream_id"] not in stream_ids
            ]
    elif event['type'] == 'default_streams':
        state['realm_default_streams'] = event['default_streams']
    elif event['type'] == 'default_stream_groups':
        state['realm_default_stream_groups'] = event['default_stream_groups']
    elif event['type'] == 'realm':
        if event['op'] == "update":
            field = 'realm_' + event['property']
            state[field] = event['value']

            if event['property'] == 'plan_type':
                # Then there are some extra fields that also need to be set.
                state['zulip_plan_is_not_limited'] = event[
                    'value'] != Realm.LIMITED
                state['realm_upload_quota'] = event['extra_data'][
                    'upload_quota']

            policy_permission_dict = {
                'create_stream_policy': 'can_create_streams',
                'invite_to_stream_policy': 'can_subscribe_other_users'
            }

            # Tricky interaction: Whether we can create streams and can subscribe other users
            # can get changed here.

            if field == 'realm_waiting_period_threshold':
                for policy, permission in policy_permission_dict.items():
                    if permission in state:
                        state[permission] = user_profile.has_permission(policy)

            if event['property'] in policy_permission_dict.keys():
                if policy_permission_dict[event['property']] in state:
                    state[policy_permission_dict[
                        event['property']]] = user_profile.has_permission(
                            event['property'])

        elif event['op'] == "update_dict":
            for key, value in event['data'].items():
                state['realm_' + key] = value
                # It's a bit messy, but this is where we need to
                # update the state for whether password authentication
                # is enabled on this server.
                if key == 'authentication_methods':
                    state['realm_password_auth_enabled'] = (value['Email']
                                                            or value['LDAP'])
                    state['realm_email_auth_enabled'] = value['Email']
    elif event['type'] == "subscription":
        if not include_subscribers and event['op'] in [
                'peer_add', 'peer_remove'
        ]:
            return

        if event['op'] in ["add"]:
            if not include_subscribers:
                # Avoid letting 'subscribers' entries end up in the list
                for i, sub in enumerate(event['subscriptions']):
                    event['subscriptions'][i] = copy.deepcopy(
                        event['subscriptions'][i])
                    del event['subscriptions'][i]['subscribers']

        def name(sub: Dict[str, Any]) -> str:
            return sub['name'].lower()

        if event['op'] == "add":
            added_names = set(map(name, event["subscriptions"]))
            was_added = lambda s: name(s) in added_names

            # add the new subscriptions
            state['subscriptions'] += event['subscriptions']

            # remove them from unsubscribed if they had been there
            state['unsubscribed'] = [
                s for s in state['unsubscribed'] if not was_added(s)
            ]

            # remove them from never_subscribed if they had been there
            state['never_subscribed'] = [
                s for s in state['never_subscribed'] if not was_added(s)
            ]

        elif event['op'] == "remove":
            removed_names = set(map(name, event["subscriptions"]))
            was_removed = lambda s: name(s) in removed_names

            # Find the subs we are affecting.
            removed_subs = list(filter(was_removed, state['subscriptions']))

            # Remove our user from the subscribers of the removed subscriptions.
            if include_subscribers:
                for sub in removed_subs:
                    sub['subscribers'].remove(user_profile.id)

            # We must effectively copy the removed subscriptions from subscriptions to
            # unsubscribe, since we only have the name in our data structure.
            state['unsubscribed'] += removed_subs

            # Now filter out the removed subscriptions from subscriptions.
            state['subscriptions'] = [
                s for s in state['subscriptions'] if not was_removed(s)
            ]

        elif event['op'] == 'update':
            for sub in state['subscriptions']:
                if sub['name'].lower() == event['name'].lower():
                    sub[event['property']] = event['value']
        elif event['op'] == 'peer_add':
            stream_id = event['stream_id']
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['stream_id'] == stream_id
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
            for sub in state['never_subscribed']:
                if (sub['stream_id'] == stream_id
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
        elif event['op'] == 'peer_remove':
            stream_id = event['stream_id']
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['stream_id'] == stream_id
                        and user_id in sub['subscribers']):
                    sub['subscribers'].remove(user_id)
    elif event['type'] == "presence":
        if slim_presence:
            user_key = str(event['user_id'])
        else:
            user_key = event['email']
        state['presences'][user_key] = get_presence_for_user(
            event['user_id'], slim_presence)[user_key]
    elif event['type'] == "update_message":
        # We don't return messages in /register, so we don't need to
        # do anything for content updates, but we may need to update
        # the unread_msgs data if the topic of an unread message changed.
        if 'new_stream_id' in event:
            stream_dict = state['raw_unread_msgs']['stream_dict']
            stream_id = event['new_stream_id']
            for message_id in event['message_ids']:
                if message_id in stream_dict:
                    stream_dict[message_id]['stream_id'] = stream_id

        if TOPIC_NAME in event:
            stream_dict = state['raw_unread_msgs']['stream_dict']
            topic = event[TOPIC_NAME]
            for message_id in event['message_ids']:
                if message_id in stream_dict:
                    stream_dict[message_id]['topic'] = topic
    elif event['type'] == "delete_message":
        if 'message_id' in event:
            message_ids = [event['message_id']]
        else:
            message_ids = event['message_ids']  # nocoverage
        max_message = Message.objects.filter(
            usermessage__user_profile=user_profile).order_by('-id').first()
        if max_message:
            state['max_message_id'] = max_message.id
        else:
            state['max_message_id'] = -1

        if 'raw_unread_msgs' in state:
            for remove_id in message_ids:
                remove_message_id_from_unread_mgs(state['raw_unread_msgs'],
                                                  remove_id)

        # The remainder of this block is about maintaining recent_private_conversations
        if 'raw_recent_private_conversations' not in state or event[
                'message_type'] != 'private':
            return

        recipient_id = get_recent_conversations_recipient_id(
            user_profile, event['recipient_id'], event['sender_id'])

        # Ideally, we'd have test coverage for these two blocks.  To
        # do that, we'll need a test where we delete not-the-latest
        # messages or delete a private message not in
        # recent_private_conversations.
        if recipient_id not in state[
                'raw_recent_private_conversations']:  # nocoverage
            return

        old_max_message_id = state['raw_recent_private_conversations'][
            recipient_id]['max_message_id']
        if old_max_message_id not in message_ids:  # nocoverage
            return

        # OK, we just deleted what had been the max_message_id for
        # this recent conversation; we need to recompute that value
        # from scratch.  Definitely don't need to re-query everything,
        # but this case is likely rare enough that it's reasonable to do so.
        state['raw_recent_private_conversations'] = \
            get_recent_private_conversations(user_profile)
    elif event['type'] == "reaction":
        # The client will get the message with the reactions directly
        pass
    elif event['type'] == "submessage":
        # The client will get submessages with their messages
        pass
    elif event['type'] == 'typing':
        # Typing notification events are transient and thus ignored
        pass
    elif event['type'] == "attachment":
        # Attachment events are just for updating the "uploads" UI;
        # they are not sent directly.
        pass
    elif event['type'] == "update_message_flags":
        # We don't return messages in `/register`, so most flags we
        # can ignore, but we do need to update the unread_msgs data if
        # unread state is changed.
        if 'raw_unread_msgs' in state and event['flag'] == 'read' and event[
                'operation'] == 'add':
            for remove_id in event['messages']:
                remove_message_id_from_unread_mgs(state['raw_unread_msgs'],
                                                  remove_id)
        if event['flag'] == 'starred' and 'starred_messages' in state:
            if event['operation'] == 'add':
                state['starred_messages'] += event['messages']
            if event['operation'] == 'remove':
                state['starred_messages'] = [
                    message for message in state['starred_messages']
                    if not (message in event['messages'])
                ]
    elif event['type'] == "realm_domains":
        if event['op'] == 'add':
            state['realm_domains'].append(event['realm_domain'])
        elif event['op'] == 'change':
            for realm_domain in state['realm_domains']:
                if realm_domain['domain'] == event['realm_domain']['domain']:
                    realm_domain['allow_subdomains'] = event['realm_domain'][
                        'allow_subdomains']
        elif event['op'] == 'remove':
            state['realm_domains'] = [
                realm_domain for realm_domain in state['realm_domains']
                if realm_domain['domain'] != event['domain']
            ]
    elif event['type'] == "realm_emoji":
        state['realm_emoji'] = event['realm_emoji']
    elif event['type'] == 'realm_export':
        # These realm export events are only available to
        # administrators, and aren't included in page_params.
        pass
    elif event['type'] == "alert_words":
        state['alert_words'] = event['alert_words']
    elif event['type'] == "muted_topics":
        state['muted_topics'] = event["muted_topics"]
    elif event['type'] == "realm_filters":
        state['realm_filters'] = event["realm_filters"]
    elif event['type'] == "update_display_settings":
        assert event['setting_name'] in UserProfile.property_types
        state[event['setting_name']] = event['setting']
    elif event['type'] == "update_global_notifications":
        assert event[
            'notification_name'] in UserProfile.notification_setting_types
        state[event['notification_name']] = event['setting']
    elif event['type'] == "invites_changed":
        pass
    elif event['type'] == "user_group":
        if event['op'] == 'add':
            state['realm_user_groups'].append(event['group'])
            state['realm_user_groups'].sort(key=lambda group: group['id'])
        elif event['op'] == 'update':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group.update(event['data'])
        elif event['op'] == 'add_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group['members'].extend(event['user_ids'])
                    user_group['members'].sort()
        elif event['op'] == 'remove_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    members = set(user_group['members'])
                    user_group['members'] = list(members -
                                                 set(event['user_ids']))
                    user_group['members'].sort()
        elif event['op'] == 'remove':
            state['realm_user_groups'] = [
                ug for ug in state['realm_user_groups']
                if ug['id'] != event['group_id']
            ]
    elif event['type'] == 'user_status':
        user_id = event['user_id']
        user_status = state['user_status']
        away = event.get('away')
        status_text = event.get('status_text')

        if user_id not in user_status:
            user_status[user_id] = dict()

        if away is not None:
            if away:
                user_status[user_id]['away'] = True
            else:
                user_status[user_id].pop('away', None)

        if status_text is not None:
            if status_text == '':
                user_status[user_id].pop('status_text', None)
            else:
                user_status[user_id]['status_text'] = status_text

        if not user_status[user_id]:
            user_status.pop(user_id, None)

        state['user_status'] = user_status
    elif event['type'] == 'has_zoom_token':
        state['has_zoom_token'] = event['value']
    else:
        raise AssertionError("Unexpected event type {}".format(event['type']))
コード例 #5
0
ファイル: events.py プロジェクト: akashnimare/zulip
def fetch_initial_state_data(user_profile: UserProfile,
                             event_types: Optional[Iterable[str]],
                             queue_id: str, client_gravatar: bool,
                             include_subscribers: bool = True) -> Dict[str, Any]:
    state = {'queue_id': queue_id}  # type: Dict[str, Any]
    realm = user_profile.realm

    if event_types is None:
        # return True always
        want = always_want  # type: Callable[[str], bool]
    else:
        want = set(event_types).__contains__

    if want('alert_words'):
        state['alert_words'] = user_alert_words(user_profile)

    if want('custom_profile_fields'):
        fields = custom_profile_fields_for_realm(realm.id)
        state['custom_profile_fields'] = [f.as_dict() for f in fields]
        state['custom_profile_field_types'] = CustomProfileField.FIELD_TYPE_CHOICES_DICT

    if want('hotspots'):
        state['hotspots'] = get_next_hotspots(user_profile)

    if want('message'):
        # The client should use get_messages() to fetch messages
        # starting with the max_message_id.  They will get messages
        # newer than that ID via get_events()
        messages = Message.objects.filter(usermessage__user_profile=user_profile).order_by('-id')[:1]
        if messages:
            state['max_message_id'] = messages[0].id
        else:
            state['max_message_id'] = -1

    if want('muted_topics'):
        state['muted_topics'] = get_topic_mutes(user_profile)

    if want('pointer'):
        state['pointer'] = user_profile.pointer

    if want('presence'):
        state['presences'] = get_status_dict(user_profile)

    if want('realm'):
        for property_name in Realm.property_types:
            state['realm_' + property_name] = getattr(realm, property_name)

        # Most state is handled via the property_types framework;
        # these manual entries are for those realm settings that don't
        # fit into that framework.
        state['realm_authentication_methods'] = realm.authentication_methods_dict()
        state['realm_allow_message_editing'] = realm.allow_message_editing
        state['realm_allow_community_topic_editing'] = realm.allow_community_topic_editing
        state['realm_allow_message_deleting'] = realm.allow_message_deleting
        state['realm_message_content_edit_limit_seconds'] = realm.message_content_edit_limit_seconds
        state['realm_message_content_delete_limit_seconds'] = realm.message_content_delete_limit_seconds
        state['realm_icon_url'] = realm_icon_url(realm)
        state['realm_icon_source'] = realm.icon_source
        state['max_icon_file_size'] = settings.MAX_ICON_FILE_SIZE
        state['realm_logo_url'] = realm_logo_url(realm)
        state['realm_logo_source'] = realm.logo_source
        state['max_logo_file_size'] = settings.MAX_LOGO_FILE_SIZE
        state['realm_bot_domain'] = realm.get_bot_domain()
        state['realm_uri'] = realm.uri
        state['realm_available_video_chat_providers'] = realm.VIDEO_CHAT_PROVIDERS
        state['realm_presence_disabled'] = realm.presence_disabled
        state['realm_digest_emails_enabled'] = realm.digest_emails_enabled and settings.SEND_DIGEST_EMAILS
        state['realm_is_zephyr_mirror_realm'] = realm.is_zephyr_mirror_realm
        state['realm_email_auth_enabled'] = email_auth_enabled(realm)
        state['realm_password_auth_enabled'] = password_auth_enabled(realm)
        state['realm_push_notifications_enabled'] = push_notifications_enabled()
        if realm.notifications_stream and not realm.notifications_stream.deactivated:
            notifications_stream = realm.notifications_stream
            state['realm_notifications_stream_id'] = notifications_stream.id
        else:
            state['realm_notifications_stream_id'] = -1

        signup_notifications_stream = realm.get_signup_notifications_stream()
        if signup_notifications_stream:
            state['realm_signup_notifications_stream_id'] = signup_notifications_stream.id
        else:
            state['realm_signup_notifications_stream_id'] = -1

    if want('realm_domains'):
        state['realm_domains'] = get_realm_domains(realm)

    if want('realm_emoji'):
        state['realm_emoji'] = realm.get_emoji()

    if want('realm_filters'):
        state['realm_filters'] = realm_filters_for_realm(realm.id)

    if want('realm_user_groups'):
        state['realm_user_groups'] = user_groups_in_realm_serialized(realm)

    if want('realm_user'):
        state['raw_users'] = get_raw_user_data(
            realm_id=realm.id,
            client_gravatar=client_gravatar,
        )

        # For the user's own avatar URL, we force
        # client_gravatar=False, since that saves some unnecessary
        # client-side code for handing medium-size avatars.  See #8253
        # for details.
        state['avatar_source'] = user_profile.avatar_source
        state['avatar_url_medium'] = avatar_url(
            user_profile,
            medium=True,
            client_gravatar=False,
        )
        state['avatar_url'] = avatar_url(
            user_profile,
            medium=False,
            client_gravatar=False,
        )

        state['can_create_streams'] = user_profile.can_create_streams()
        state['can_subscribe_other_users'] = user_profile.can_subscribe_other_users()
        state['cross_realm_bots'] = list(get_cross_realm_dicts())
        state['is_admin'] = user_profile.is_realm_admin
        state['is_guest'] = user_profile.is_guest
        state['user_id'] = user_profile.id
        state['enter_sends'] = user_profile.enter_sends
        state['email'] = user_profile.email
        state['delivery_email'] = user_profile.delivery_email
        state['full_name'] = user_profile.full_name

    if want('realm_bot'):
        state['realm_bots'] = get_owned_bot_dicts(user_profile)

    # This does not yet have an apply_event counterpart, since currently,
    # new entries for EMBEDDED_BOTS can only be added directly in the codebase.
    if want('realm_embedded_bots'):
        realm_embedded_bots = []
        for bot in EMBEDDED_BOTS:
            realm_embedded_bots.append({'name': bot.name,
                                        'config': load_bot_config_template(bot.name)})
        state['realm_embedded_bots'] = realm_embedded_bots

    if want('subscription'):
        subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper(
            user_profile, include_subscribers=include_subscribers)
        state['subscriptions'] = subscriptions
        state['unsubscribed'] = unsubscribed
        state['never_subscribed'] = never_subscribed

    if want('update_message_flags') and want('message'):
        # Keeping unread_msgs updated requires both message flag updates and
        # message updates. This is due to the fact that new messages will not
        # generate a flag update so we need to use the flags field in the
        # message event.
        state['raw_unread_msgs'] = get_raw_unread_data(user_profile)

    if want('starred_messages'):
        state['starred_messages'] = get_starred_message_ids(user_profile)

    if want('stream'):
        state['streams'] = do_get_streams(user_profile)
        state['stream_name_max_length'] = Stream.MAX_NAME_LENGTH
        state['stream_description_max_length'] = Stream.MAX_DESCRIPTION_LENGTH
    if want('default_streams'):
        state['realm_default_streams'] = streams_to_dicts_sorted(
            get_default_streams_for_realm(realm.id))
    if want('default_stream_groups'):
        state['realm_default_stream_groups'] = default_stream_groups_to_dicts_sorted(
            get_default_stream_groups(realm))

    if want('update_display_settings'):
        for prop in UserProfile.property_types:
            state[prop] = getattr(user_profile, prop)
        state['emojiset_choices'] = user_profile.emojiset_choices()

    if want('update_global_notifications'):
        for notification in UserProfile.notification_setting_types:
            state[notification] = getattr(user_profile, notification)
        state['available_notification_sounds'] = get_available_notification_sounds()

    if want('user_status'):
        state['away_user_ids'] = sorted(list(get_away_user_ids(realm_id=realm.id)))

    if want('zulip_version'):
        state['zulip_version'] = ZULIP_VERSION

    return state
コード例 #6
0
ファイル: events.py プロジェクト: akashnimare/zulip
def apply_event(state: Dict[str, Any],
                event: Dict[str, Any],
                user_profile: UserProfile,
                client_gravatar: bool,
                include_subscribers: bool) -> None:
    if event['type'] == "message":
        state['max_message_id'] = max(state['max_message_id'], event['message']['id'])
        if 'raw_unread_msgs' in state:
            apply_unread_message_event(
                user_profile,
                state['raw_unread_msgs'],
                event['message'],
                event['flags'],
            )

    elif event['type'] == "hotspots":
        state['hotspots'] = event['hotspots']
    elif event['type'] == "custom_profile_fields":
        state['custom_profile_fields'] = event['fields']
    elif event['type'] == "pointer":
        state['pointer'] = max(state['pointer'], event['pointer'])
    elif event['type'] == "realm_user":
        person = event['person']
        person_user_id = person['user_id']

        if event['op'] == "add":
            person = copy.deepcopy(person)
            if client_gravatar:
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
            person['is_active'] = True
            if not person['is_bot']:
                person['profile_data'] = {}
            state['raw_users'][person_user_id] = person
        elif event['op'] == "remove":
            state['raw_users'][person_user_id]['is_active'] = False
        elif event['op'] == 'update':
            is_me = (person_user_id == user_profile.id)

            if is_me:
                if ('avatar_url' in person and 'avatar_url' in state):
                    state['avatar_source'] = person['avatar_source']
                    state['avatar_url'] = person['avatar_url']
                    state['avatar_url_medium'] = person['avatar_url_medium']

                for field in ['is_admin', 'delivery_email', 'email', 'full_name']:
                    if field in person and field in state:
                        state[field] = person[field]

                # In the unlikely event that the current user
                # just changed to/from being an admin, we need
                # to add/remove the data on all bots in the
                # realm.  This is ugly and probably better
                # solved by removing the all-realm-bots data
                # given to admin users from this flow.
                if ('is_admin' in person and 'realm_bots' in state):
                    prev_state = state['raw_users'][user_profile.id]
                    was_admin = prev_state['is_admin']
                    now_admin = person['is_admin']

                    if was_admin and not now_admin:
                        state['realm_bots'] = []
                    if not was_admin and now_admin:
                        state['realm_bots'] = get_owned_bot_dicts(user_profile)

            if client_gravatar and 'avatar_url' in person:
                # Respect the client_gravatar setting in the `users` data.
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
                    person['avatar_url_medium'] = None

            if person_user_id in state['raw_users']:
                p = state['raw_users'][person_user_id]
                for field in p:
                    if field in person:
                        p[field] = person[field]
                    if 'custom_profile_field' in person:
                        custom_field_id = person['custom_profile_field']['id']
                        custom_field_new_value = person['custom_profile_field']['value']
                        if 'rendered_value' in person['custom_profile_field']:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value,
                                'rendered_value': person['custom_profile_field']['rendered_value']
                            }
                        else:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value
                            }

    elif event['type'] == 'realm_bot':
        if event['op'] == 'add':
            state['realm_bots'].append(event['bot'])

        if event['op'] == 'remove':
            email = event['bot']['email']
            for bot in state['realm_bots']:
                if bot['email'] == email:
                    bot['is_active'] = False

        if event['op'] == 'delete':
            state['realm_bots'] = [item for item
                                   in state['realm_bots'] if item['email'] != event['bot']['email']]

        if event['op'] == 'update':
            for bot in state['realm_bots']:
                if bot['email'] == event['bot']['email']:
                    if 'owner_id' in event['bot']:
                        bot['owner'] = get_user_profile_by_id(event['bot']['owner_id']).email
                    else:
                        bot.update(event['bot'])

    elif event['type'] == 'stream':
        if event['op'] == 'create':
            for stream in event['streams']:
                if not stream['invite_only']:
                    stream_data = copy.deepcopy(stream)
                    if include_subscribers:
                        stream_data['subscribers'] = []
                    stream_data['stream_weekly_traffic'] = None
                    stream_data['is_old_stream'] = False
                    stream_data['is_announcement_only'] = False
                    # Add stream to never_subscribed (if not invite_only)
                    state['never_subscribed'].append(stream_data)
                state['streams'].append(stream)
            state['streams'].sort(key=lambda elt: elt["name"])

        if event['op'] == 'delete':
            deleted_stream_ids = {stream['stream_id'] for stream in event['streams']}
            state['streams'] = [s for s in state['streams'] if s['stream_id'] not in deleted_stream_ids]
            state['never_subscribed'] = [stream for stream in state['never_subscribed'] if
                                         stream['stream_id'] not in deleted_stream_ids]

        if event['op'] == 'update':
            # For legacy reasons, we call stream data 'subscriptions' in
            # the state var here, for the benefit of the JS code.
            for obj in state['subscriptions']:
                if obj['name'].lower() == event['name'].lower():
                    obj[event['property']] = event['value']
            # Also update the pure streams data
            for stream in state['streams']:
                if stream['name'].lower() == event['name'].lower():
                    prop = event['property']
                    if prop in stream:
                        stream[prop] = event['value']
        elif event['op'] == "occupy":
            state['streams'] += event['streams']
        elif event['op'] == "vacate":
            stream_ids = [s["stream_id"] for s in event['streams']]
            state['streams'] = [s for s in state['streams'] if s["stream_id"] not in stream_ids]
    elif event['type'] == 'default_streams':
        state['realm_default_streams'] = event['default_streams']
    elif event['type'] == 'default_stream_groups':
        state['realm_default_stream_groups'] = event['default_stream_groups']
    elif event['type'] == 'realm':
        if event['op'] == "update":
            field = 'realm_' + event['property']
            state[field] = event['value']

            # Tricky interaction: Whether we can create streams can get changed here.
            if (field in ['realm_create_stream_by_admins_only',
                          'realm_waiting_period_threshold']) and 'can_create_streams' in state:
                state['can_create_streams'] = user_profile.can_create_streams()
                state['can_subscribe_other_users'] = user_profile.can_subscribe_other_users()
        elif event['op'] == "update_dict":
            for key, value in event['data'].items():
                state['realm_' + key] = value
                # It's a bit messy, but this is where we need to
                # update the state for whether password authentication
                # is enabled on this server.
                if key == 'authentication_methods':
                    state['realm_password_auth_enabled'] = (value['Email'] or value['LDAP'])
                    state['realm_email_auth_enabled'] = value['Email']
    elif event['type'] == "subscription":
        if not include_subscribers and event['op'] in ['peer_add', 'peer_remove']:
            return

        if event['op'] in ["add"]:
            if not include_subscribers:
                # Avoid letting 'subscribers' entries end up in the list
                for i, sub in enumerate(event['subscriptions']):
                    event['subscriptions'][i] = copy.deepcopy(event['subscriptions'][i])
                    del event['subscriptions'][i]['subscribers']

        def name(sub: Dict[str, Any]) -> str:
            return sub['name'].lower()

        if event['op'] == "add":
            added_names = set(map(name, event["subscriptions"]))
            was_added = lambda s: name(s) in added_names

            # add the new subscriptions
            state['subscriptions'] += event['subscriptions']

            # remove them from unsubscribed if they had been there
            state['unsubscribed'] = [s for s in state['unsubscribed'] if not was_added(s)]

            # remove them from never_subscribed if they had been there
            state['never_subscribed'] = [s for s in state['never_subscribed'] if not was_added(s)]

        elif event['op'] == "remove":
            removed_names = set(map(name, event["subscriptions"]))
            was_removed = lambda s: name(s) in removed_names

            # Find the subs we are affecting.
            removed_subs = list(filter(was_removed, state['subscriptions']))

            # Remove our user from the subscribers of the removed subscriptions.
            if include_subscribers:
                for sub in removed_subs:
                    sub['subscribers'] = [id for id in sub['subscribers'] if id != user_profile.id]

            # We must effectively copy the removed subscriptions from subscriptions to
            # unsubscribe, since we only have the name in our data structure.
            state['unsubscribed'] += removed_subs

            # Now filter out the removed subscriptions from subscriptions.
            state['subscriptions'] = [s for s in state['subscriptions'] if not was_removed(s)]

        elif event['op'] == 'update':
            for sub in state['subscriptions']:
                if sub['name'].lower() == event['name'].lower():
                    sub[event['property']] = event['value']
        elif event['op'] == 'peer_add':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions'] and
                        user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
            for sub in state['never_subscribed']:
                if (sub['name'] in event['subscriptions'] and
                        user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
        elif event['op'] == 'peer_remove':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions'] and
                        user_id in sub['subscribers']):
                    sub['subscribers'].remove(user_id)
    elif event['type'] == "presence":
        # TODO: Add user_id to presence update events / state format!
        presence_user_profile = get_user(event['email'], user_profile.realm)
        state['presences'][event['email']] = UserPresence.get_status_dict_by_user(
            presence_user_profile)[event['email']]
    elif event['type'] == "update_message":
        # We don't return messages in /register, so we don't need to
        # do anything for content updates, but we may need to update
        # the unread_msgs data if the topic of an unread message changed.
        if TOPIC_NAME in event:
            stream_dict = state['raw_unread_msgs']['stream_dict']
            topic = event[TOPIC_NAME]
            for message_id in event['message_ids']:
                if message_id in stream_dict:
                    stream_dict[message_id]['topic'] = topic
    elif event['type'] == "delete_message":
        max_message = Message.objects.filter(
            usermessage__user_profile=user_profile).order_by('-id').first()
        if max_message:
            state['max_message_id'] = max_message.id
        else:
            state['max_message_id'] = -1

        remove_id = event['message_id']
        remove_message_id_from_unread_mgs(state, remove_id)
    elif event['type'] == "reaction":
        # The client will get the message with the reactions directly
        pass
    elif event['type'] == "submessage":
        # The client will get submessages with their messages
        pass
    elif event['type'] == 'typing':
        # Typing notification events are transient and thus ignored
        pass
    elif event['type'] == "attachment":
        # Attachment events are just for updating the "uploads" UI;
        # they are not sent directly.
        pass
    elif event['type'] == "update_message_flags":
        # We don't return messages in `/register`, so most flags we
        # can ignore, but we do need to update the unread_msgs data if
        # unread state is changed.
        if event['flag'] == 'read' and event['operation'] == 'add':
            for remove_id in event['messages']:
                remove_message_id_from_unread_mgs(state, remove_id)
        if event['flag'] == 'starred' and event['operation'] == 'add':
            state['starred_messages'] += event['messages']
        if event['flag'] == 'starred' and event['operation'] == 'remove':
            state['starred_messages'] = [message for message in state['starred_messages']
                                         if not (message in event['messages'])]
    elif event['type'] == "realm_domains":
        if event['op'] == 'add':
            state['realm_domains'].append(event['realm_domain'])
        elif event['op'] == 'change':
            for realm_domain in state['realm_domains']:
                if realm_domain['domain'] == event['realm_domain']['domain']:
                    realm_domain['allow_subdomains'] = event['realm_domain']['allow_subdomains']
        elif event['op'] == 'remove':
            state['realm_domains'] = [realm_domain for realm_domain in state['realm_domains']
                                      if realm_domain['domain'] != event['domain']]
    elif event['type'] == "realm_emoji":
        state['realm_emoji'] = event['realm_emoji']
    elif event['type'] == "alert_words":
        state['alert_words'] = event['alert_words']
    elif event['type'] == "muted_topics":
        state['muted_topics'] = event["muted_topics"]
    elif event['type'] == "realm_filters":
        state['realm_filters'] = event["realm_filters"]
    elif event['type'] == "update_display_settings":
        assert event['setting_name'] in UserProfile.property_types
        state[event['setting_name']] = event['setting']
    elif event['type'] == "update_global_notifications":
        assert event['notification_name'] in UserProfile.notification_setting_types
        state[event['notification_name']] = event['setting']
    elif event['type'] == "invites_changed":
        pass
    elif event['type'] == "user_group":
        if event['op'] == 'add':
            state['realm_user_groups'].append(event['group'])
            state['realm_user_groups'].sort(key=lambda group: group['id'])
        elif event['op'] == 'update':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group.update(event['data'])
        elif event['op'] == 'add_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group['members'].extend(event['user_ids'])
                    user_group['members'].sort()
        elif event['op'] == 'remove_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    members = set(user_group['members'])
                    user_group['members'] = list(members - set(event['user_ids']))
                    user_group['members'].sort()
        elif event['op'] == 'remove':
            state['realm_user_groups'] = [ug for ug in state['realm_user_groups']
                                          if ug['id'] != event['group_id']]
    elif event['type'] == 'user_status':
        away_user_ids = set(state['away_user_ids'])
        user_id = event['user_id']

        if event['away']:
            away_user_ids.add(user_id)
        else:
            away_user_ids.discard(user_id)

        state['away_user_ids'] = sorted(list(away_user_ids))
    else:
        raise AssertionError("Unexpected event type %s" % (event['type'],))
コード例 #7
0
def add_subscriptions_backend(
        request: HttpRequest, user_profile: UserProfile,
        streams_raw: Iterable[Dict[str, str]]=REQ(
            "subscriptions", validator=check_list(check_dict_only(
                [('name', check_string)], optional_keys=[
                    ('color', check_color),
                    ('description', check_capped_string(Stream.MAX_DESCRIPTION_LENGTH)),
                ])
            )),
        invite_only: bool=REQ(validator=check_bool, default=False),
        is_announcement_only: bool=REQ(validator=check_bool, default=False),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        announce: bool=REQ(validator=check_bool, default=False),
        principals: List[str]=REQ(validator=check_list(check_string), default=[]),
        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 = {}  # type: 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["is_announcement_only"] = is_announcement_only
        stream_dict_copy["history_public_to_subscribers"] = history_public_to_subscribers
        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 (%s).") % 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():
            return json_error(_("Your account is too new to modify other users' subscriptions."))
        subscribers = set(principal_to_user_profile(user_profile, principal) for principal in principals)
    else:
        subscribers = set([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()  # type: Dict[str, UserProfile]

    result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))  # type: Dict[str, Any]
    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 = dict((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

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

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            notifications.append(
                internal_prep_private_message(
                    realm=user_profile.realm,
                    sender=sender,
                    recipient_user=email_to_user_profile[email],
                    content=msg))

    if announce and len(created_streams) > 0 and settings.NOTIFICATION_BOT is not None:
        notifications_stream = user_profile.realm.get_notifications_stream()
        if notifications_stream is not None:
            if len(created_streams) > 1:
                stream_strs = ", ".join('#**%s**' % s.name for s in created_streams)
                stream_msg = "the following streams: %s" % (stream_strs,)
            else:
                stream_msg = "a new stream #**%s**." % created_streams[0].name
            msg = ("@_**%s|%d** just created %s" % (user_profile.full_name, user_profile.id, stream_msg))

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            topic = 'Streams'

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

    if not user_profile.realm.is_zephyr_mirror_realm:
        for stream in created_streams:
            notifications.append(prep_stream_welcome_message(stream))

    if len(notifications) > 0:
        do_send_messages(notifications)

    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)
コード例 #8
0
def fetch_initial_state_data(
        user_profile: UserProfile,
        event_types: Optional[Iterable[str]],
        queue_id: str,
        client_gravatar: bool,
        include_subscribers: bool = True) -> Dict[str, Any]:
    state = {'queue_id': queue_id}  # type: Dict[str, Any]
    realm = user_profile.realm

    if event_types is None:
        # return True always
        want = always_want  # type: Callable[[str], bool]
    else:
        want = set(event_types).__contains__

    if want('alert_words'):
        state['alert_words'] = user_alert_words(user_profile)

    if want('custom_profile_fields'):
        fields = custom_profile_fields_for_realm(realm.id)
        state['custom_profile_fields'] = [f.as_dict() for f in fields]
        state[
            'custom_profile_field_types'] = CustomProfileField.FIELD_TYPE_CHOICES_DICT

    if want('hotspots'):
        state['hotspots'] = get_next_hotspots(user_profile)

    if want('message'):
        # The client should use get_messages() to fetch messages
        # starting with the max_message_id.  They will get messages
        # newer than that ID via get_events()
        messages = Message.objects.filter(
            usermessage__user_profile=user_profile).order_by('-id')[:1]
        if messages:
            state['max_message_id'] = messages[0].id
        else:
            state['max_message_id'] = -1

    if want('muted_topics'):
        state['muted_topics'] = get_topic_mutes(user_profile)

    if want('pointer'):
        state['pointer'] = user_profile.pointer

    if want('presence'):
        state['presences'] = get_status_dict(user_profile)

    if want('realm'):
        for property_name in Realm.property_types:
            state['realm_' + property_name] = getattr(realm, property_name)

        # Don't send the zoom API secret to clients.
        if state.get('realm_zoom_api_secret'):
            state['realm_zoom_api_secret'] = ''

        # Most state is handled via the property_types framework;
        # these manual entries are for those realm settings that don't
        # fit into that framework.
        state[
            'realm_authentication_methods'] = realm.authentication_methods_dict(
            )
        state['realm_allow_message_editing'] = realm.allow_message_editing
        state[
            'realm_allow_community_topic_editing'] = realm.allow_community_topic_editing
        state['realm_allow_message_deleting'] = realm.allow_message_deleting
        state[
            'realm_message_content_edit_limit_seconds'] = realm.message_content_edit_limit_seconds
        state[
            'realm_message_content_delete_limit_seconds'] = realm.message_content_delete_limit_seconds
        state['realm_icon_url'] = realm_icon_url(realm)
        state['realm_icon_source'] = realm.icon_source
        state['max_icon_file_size'] = settings.MAX_ICON_FILE_SIZE
        add_realm_logo_fields(state, realm)
        state['realm_bot_domain'] = realm.get_bot_domain()
        state['realm_uri'] = realm.uri
        state[
            'realm_available_video_chat_providers'] = realm.VIDEO_CHAT_PROVIDERS
        state['realm_presence_disabled'] = realm.presence_disabled
        state['settings_send_digest_emails'] = settings.SEND_DIGEST_EMAILS
        state[
            'realm_digest_emails_enabled'] = realm.digest_emails_enabled and settings.SEND_DIGEST_EMAILS
        state['realm_is_zephyr_mirror_realm'] = realm.is_zephyr_mirror_realm
        state['realm_email_auth_enabled'] = email_auth_enabled(realm)
        state['realm_password_auth_enabled'] = password_auth_enabled(realm)
        state['realm_push_notifications_enabled'] = push_notifications_enabled(
        )
        state['realm_upload_quota'] = realm.upload_quota_bytes()
        state['realm_plan_type'] = realm.plan_type

        if realm.notifications_stream and not realm.notifications_stream.deactivated:
            notifications_stream = realm.notifications_stream
            state['realm_notifications_stream_id'] = notifications_stream.id
        else:
            state['realm_notifications_stream_id'] = -1

        signup_notifications_stream = realm.get_signup_notifications_stream()
        if signup_notifications_stream:
            state[
                'realm_signup_notifications_stream_id'] = signup_notifications_stream.id
        else:
            state['realm_signup_notifications_stream_id'] = -1

    if want('realm_domains'):
        state['realm_domains'] = get_realm_domains(realm)

    if want('realm_emoji'):
        state['realm_emoji'] = realm.get_emoji()

    if want('realm_filters'):
        state['realm_filters'] = realm_filters_for_realm(realm.id)

    if want('realm_user_groups'):
        state['realm_user_groups'] = user_groups_in_realm_serialized(realm)

    if want('realm_user'):
        state['raw_users'] = get_raw_user_data(
            realm=realm,
            client_gravatar=client_gravatar,
        )

        # For the user's own avatar URL, we force
        # client_gravatar=False, since that saves some unnecessary
        # client-side code for handing medium-size avatars.  See #8253
        # for details.
        state['avatar_source'] = user_profile.avatar_source
        state['avatar_url_medium'] = avatar_url(
            user_profile,
            medium=True,
            client_gravatar=False,
        )
        state['avatar_url'] = avatar_url(
            user_profile,
            medium=False,
            client_gravatar=False,
        )

        state['can_create_streams'] = user_profile.can_create_streams()
        state[
            'can_subscribe_other_users'] = user_profile.can_subscribe_other_users(
            )
        state['cross_realm_bots'] = list(get_cross_realm_dicts())
        state['is_admin'] = user_profile.is_realm_admin
        state['is_guest'] = user_profile.is_guest
        state['user_id'] = user_profile.id
        state['enter_sends'] = user_profile.enter_sends
        state['email'] = user_profile.email
        state['delivery_email'] = user_profile.delivery_email
        state['full_name'] = user_profile.full_name

    if want('realm_bot'):
        state['realm_bots'] = get_owned_bot_dicts(user_profile)

    # This does not yet have an apply_event counterpart, since currently,
    # new entries for EMBEDDED_BOTS can only be added directly in the codebase.
    if want('realm_embedded_bots'):
        realm_embedded_bots = []
        for bot in EMBEDDED_BOTS:
            realm_embedded_bots.append({
                'name':
                bot.name,
                'config':
                load_bot_config_template(bot.name)
            })
        state['realm_embedded_bots'] = realm_embedded_bots

    if want('recent_private_conversations'):
        # A data structure containing records of this form:
        #
        #   [{'max_message_id': 700175, 'user_ids': [801]}]
        #
        # for all recent private message conversations, ordered by the
        # highest message ID in the conversation.  The user_ids list
        # is the list of users other than the current user in the
        # private message conversation (so it is [] for PMs to self).
        # Note that raw_recent_private_conversations is an
        # intermediate form as a dictionary keyed by recipient_id,
        # which is more efficient to update, and is rewritten to the
        # final format in post_process_state.
        state[
            'raw_recent_private_conversations'] = get_recent_private_conversations(
                user_profile)

    if want('subscription'):
        subscriptions, unsubscribed, never_subscribed = gather_subscriptions_helper(
            user_profile, include_subscribers=include_subscribers)
        state['subscriptions'] = subscriptions
        state['unsubscribed'] = unsubscribed
        state['never_subscribed'] = never_subscribed

    if want('update_message_flags') and want('message'):
        # Keeping unread_msgs updated requires both message flag updates and
        # message updates. This is due to the fact that new messages will not
        # generate a flag update so we need to use the flags field in the
        # message event.
        state['raw_unread_msgs'] = get_raw_unread_data(user_profile)

    if want('starred_messages'):
        state['starred_messages'] = get_starred_message_ids(user_profile)

    if want('stream'):
        state['streams'] = do_get_streams(user_profile)
        state['stream_name_max_length'] = Stream.MAX_NAME_LENGTH
        state['stream_description_max_length'] = Stream.MAX_DESCRIPTION_LENGTH
    if want('default_streams'):
        if user_profile.is_guest:
            state['realm_default_streams'] = []
        else:
            state['realm_default_streams'] = streams_to_dicts_sorted(
                get_default_streams_for_realm(realm.id))
    if want('default_stream_groups'):
        if user_profile.is_guest:
            state['realm_default_stream_groups'] = []
        else:
            state[
                'realm_default_stream_groups'] = default_stream_groups_to_dicts_sorted(
                    get_default_stream_groups(realm))

    if want('stop_words'):
        state['stop_words'] = read_stop_words()

    if want('update_display_settings'):
        for prop in UserProfile.property_types:
            state[prop] = getattr(user_profile, prop)
        state['emojiset_choices'] = user_profile.emojiset_choices()

    if want('update_global_notifications'):
        for notification in UserProfile.notification_setting_types:
            state[notification] = getattr(user_profile, notification)
        state[
            'available_notification_sounds'] = get_available_notification_sounds(
            )

    if want('user_status'):
        state['user_status'] = get_user_info_dict(realm_id=realm.id)

    if want('zulip_version'):
        state['zulip_version'] = ZULIP_VERSION

    return state
コード例 #9
0
ファイル: events.py プロジェクト: shashigowdak/zulip
def apply_event(state: Dict[str, Any], event: Dict[str, Any],
                user_profile: UserProfile, client_gravatar: bool,
                include_subscribers: bool) -> None:
    if event['type'] == "message":
        state['max_message_id'] = max(state['max_message_id'],
                                      event['message']['id'])
        if 'raw_unread_msgs' in state:
            apply_unread_message_event(
                user_profile,
                state['raw_unread_msgs'],
                event['message'],
                event['flags'],
            )

    elif event['type'] == "hotspots":
        state['hotspots'] = event['hotspots']
    elif event['type'] == "custom_profile_fields":
        state['custom_profile_fields'] = event['fields']
    elif event['type'] == "pointer":
        state['pointer'] = max(state['pointer'], event['pointer'])
    elif event['type'] == "realm_user":
        person = event['person']
        person_user_id = person['user_id']

        if event['op'] == "add":
            person = copy.deepcopy(person)
            if client_gravatar:
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
            person['is_active'] = True
            if not person['is_bot']:
                person['profile_data'] = {}
            state['raw_users'][person_user_id] = person
        elif event['op'] == "remove":
            state['raw_users'][person_user_id]['is_active'] = False
        elif event['op'] == 'update':
            is_me = (person_user_id == user_profile.id)

            if is_me:
                if ('avatar_url' in person and 'avatar_url' in state):
                    state['avatar_source'] = person['avatar_source']
                    state['avatar_url'] = person['avatar_url']
                    state['avatar_url_medium'] = person['avatar_url_medium']

                for field in [
                        'is_admin', 'delivery_email', 'email', 'full_name'
                ]:
                    if field in person and field in state:
                        state[field] = person[field]

                # In the unlikely event that the current user
                # just changed to/from being an admin, we need
                # to add/remove the data on all bots in the
                # realm.  This is ugly and probably better
                # solved by removing the all-realm-bots data
                # given to admin users from this flow.
                if ('is_admin' in person and 'realm_bots' in state):
                    prev_state = state['raw_users'][user_profile.id]
                    was_admin = prev_state['is_admin']
                    now_admin = person['is_admin']

                    if was_admin and not now_admin:
                        state['realm_bots'] = []
                    if not was_admin and now_admin:
                        state['realm_bots'] = get_owned_bot_dicts(user_profile)

            if client_gravatar and 'avatar_url' in person:
                # Respect the client_gravatar setting in the `users` data.
                if 'gravatar.com' in person['avatar_url']:
                    person['avatar_url'] = None
                    person['avatar_url_medium'] = None

            if person_user_id in state['raw_users']:
                p = state['raw_users'][person_user_id]
                for field in p:
                    if field in person:
                        p[field] = person[field]
                    if 'custom_profile_field' in person:
                        custom_field_id = person['custom_profile_field']['id']
                        custom_field_new_value = person[
                            'custom_profile_field']['value']
                        if 'rendered_value' in person['custom_profile_field']:
                            p['profile_data'][custom_field_id] = {
                                'value':
                                custom_field_new_value,
                                'rendered_value':
                                person['custom_profile_field']
                                ['rendered_value']
                            }
                        else:
                            p['profile_data'][custom_field_id] = {
                                'value': custom_field_new_value
                            }

    elif event['type'] == 'realm_bot':
        if event['op'] == 'add':
            state['realm_bots'].append(event['bot'])

        if event['op'] == 'remove':
            email = event['bot']['email']
            for bot in state['realm_bots']:
                if bot['email'] == email:
                    bot['is_active'] = False

        if event['op'] == 'delete':
            state['realm_bots'] = [
                item for item in state['realm_bots']
                if item['email'] != event['bot']['email']
            ]

        if event['op'] == 'update':
            for bot in state['realm_bots']:
                if bot['email'] == event['bot']['email']:
                    if 'owner_id' in event['bot']:
                        bot['owner'] = get_user_profile_by_id(
                            event['bot']['owner_id']).email
                    else:
                        bot.update(event['bot'])

    elif event['type'] == 'stream':
        if event['op'] == 'create':
            for stream in event['streams']:
                if not stream['invite_only']:
                    stream_data = copy.deepcopy(stream)
                    if include_subscribers:
                        stream_data['subscribers'] = []
                    stream_data['stream_weekly_traffic'] = None
                    stream_data['is_old_stream'] = False
                    stream_data['is_announcement_only'] = False
                    # Add stream to never_subscribed (if not invite_only)
                    state['never_subscribed'].append(stream_data)
                state['streams'].append(stream)
            state['streams'].sort(key=lambda elt: elt["name"])

        if event['op'] == 'delete':
            deleted_stream_ids = {
                stream['stream_id']
                for stream in event['streams']
            }
            state['streams'] = [
                s for s in state['streams']
                if s['stream_id'] not in deleted_stream_ids
            ]
            state['never_subscribed'] = [
                stream for stream in state['never_subscribed']
                if stream['stream_id'] not in deleted_stream_ids
            ]

        if event['op'] == 'update':
            # For legacy reasons, we call stream data 'subscriptions' in
            # the state var here, for the benefit of the JS code.
            for obj in state['subscriptions']:
                if obj['name'].lower() == event['name'].lower():
                    obj[event['property']] = event['value']
            # Also update the pure streams data
            for stream in state['streams']:
                if stream['name'].lower() == event['name'].lower():
                    prop = event['property']
                    if prop in stream:
                        stream[prop] = event['value']
        elif event['op'] == "occupy":
            state['streams'] += event['streams']
        elif event['op'] == "vacate":
            stream_ids = [s["stream_id"] for s in event['streams']]
            state['streams'] = [
                s for s in state['streams'] if s["stream_id"] not in stream_ids
            ]
    elif event['type'] == 'default_streams':
        state['realm_default_streams'] = event['default_streams']
    elif event['type'] == 'default_stream_groups':
        state['realm_default_stream_groups'] = event['default_stream_groups']
    elif event['type'] == 'realm':
        if event['op'] == "update":
            field = 'realm_' + event['property']
            state[field] = event['value']

            # Tricky interaction: Whether we can create streams can get changed here.
            if (field in [
                    'realm_create_stream_by_admins_only',
                    'realm_waiting_period_threshold'
            ]) and 'can_create_streams' in state:
                state['can_create_streams'] = user_profile.can_create_streams()
                state[
                    'can_subscribe_other_users'] = user_profile.can_subscribe_other_users(
                    )
        elif event['op'] == "update_dict":
            for key, value in event['data'].items():
                state['realm_' + key] = value
                # It's a bit messy, but this is where we need to
                # update the state for whether password authentication
                # is enabled on this server.
                if key == 'authentication_methods':
                    state['realm_password_auth_enabled'] = (value['Email']
                                                            or value['LDAP'])
                    state['realm_email_auth_enabled'] = value['Email']
    elif event['type'] == "subscription":
        if not include_subscribers and event['op'] in [
                'peer_add', 'peer_remove'
        ]:
            return

        if event['op'] in ["add"]:
            if not include_subscribers:
                # Avoid letting 'subscribers' entries end up in the list
                for i, sub in enumerate(event['subscriptions']):
                    event['subscriptions'][i] = copy.deepcopy(
                        event['subscriptions'][i])
                    del event['subscriptions'][i]['subscribers']

        def name(sub: Dict[str, Any]) -> str:
            return sub['name'].lower()

        if event['op'] == "add":
            added_names = set(map(name, event["subscriptions"]))
            was_added = lambda s: name(s) in added_names

            # add the new subscriptions
            state['subscriptions'] += event['subscriptions']

            # remove them from unsubscribed if they had been there
            state['unsubscribed'] = [
                s for s in state['unsubscribed'] if not was_added(s)
            ]

            # remove them from never_subscribed if they had been there
            state['never_subscribed'] = [
                s for s in state['never_subscribed'] if not was_added(s)
            ]

        elif event['op'] == "remove":
            removed_names = set(map(name, event["subscriptions"]))
            was_removed = lambda s: name(s) in removed_names

            # Find the subs we are affecting.
            removed_subs = list(filter(was_removed, state['subscriptions']))

            # Remove our user from the subscribers of the removed subscriptions.
            if include_subscribers:
                for sub in removed_subs:
                    sub['subscribers'] = [
                        id for id in sub['subscribers']
                        if id != user_profile.id
                    ]

            # We must effectively copy the removed subscriptions from subscriptions to
            # unsubscribe, since we only have the name in our data structure.
            state['unsubscribed'] += removed_subs

            # Now filter out the removed subscriptions from subscriptions.
            state['subscriptions'] = [
                s for s in state['subscriptions'] if not was_removed(s)
            ]

        elif event['op'] == 'update':
            for sub in state['subscriptions']:
                if sub['name'].lower() == event['name'].lower():
                    sub[event['property']] = event['value']
        elif event['op'] == 'peer_add':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions']
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
            for sub in state['never_subscribed']:
                if (sub['name'] in event['subscriptions']
                        and user_id not in sub['subscribers']):
                    sub['subscribers'].append(user_id)
        elif event['op'] == 'peer_remove':
            user_id = event['user_id']
            for sub in state['subscriptions']:
                if (sub['name'] in event['subscriptions']
                        and user_id in sub['subscribers']):
                    sub['subscribers'].remove(user_id)
    elif event['type'] == "presence":
        # TODO: Add user_id to presence update events / state format!
        presence_user_profile = get_user(event['email'], user_profile.realm)
        state['presences'][
            event['email']] = UserPresence.get_status_dict_by_user(
                presence_user_profile)[event['email']]
    elif event['type'] == "update_message":
        # We don't return messages in /register, so we don't need to
        # do anything for content updates, but we may need to update
        # the unread_msgs data if the topic of an unread message changed.
        if TOPIC_NAME in event:
            stream_dict = state['raw_unread_msgs']['stream_dict']
            topic = event[TOPIC_NAME]
            for message_id in event['message_ids']:
                if message_id in stream_dict:
                    stream_dict[message_id]['topic'] = topic
    elif event['type'] == "delete_message":
        max_message = Message.objects.filter(
            usermessage__user_profile=user_profile).order_by('-id').first()
        if max_message:
            state['max_message_id'] = max_message.id
        else:
            state['max_message_id'] = -1

        remove_id = event['message_id']
        remove_message_id_from_unread_mgs(state, remove_id)
    elif event['type'] == "reaction":
        # The client will get the message with the reactions directly
        pass
    elif event['type'] == "submessage":
        # The client will get submessages with their messages
        pass
    elif event['type'] == 'typing':
        # Typing notification events are transient and thus ignored
        pass
    elif event['type'] == "attachment":
        # Attachment events are just for updating the "uploads" UI;
        # they are not sent directly.
        pass
    elif event['type'] == "update_message_flags":
        # We don't return messages in `/register`, so most flags we
        # can ignore, but we do need to update the unread_msgs data if
        # unread state is changed.
        if event['flag'] == 'read' and event['operation'] == 'add':
            for remove_id in event['messages']:
                remove_message_id_from_unread_mgs(state, remove_id)
        if event['flag'] == 'starred' and event['operation'] == 'add':
            state['starred_messages'] += event['messages']
        if event['flag'] == 'starred' and event['operation'] == 'remove':
            state['starred_messages'] = [
                message for message in state['starred_messages']
                if not (message in event['messages'])
            ]
    elif event['type'] == "realm_domains":
        if event['op'] == 'add':
            state['realm_domains'].append(event['realm_domain'])
        elif event['op'] == 'change':
            for realm_domain in state['realm_domains']:
                if realm_domain['domain'] == event['realm_domain']['domain']:
                    realm_domain['allow_subdomains'] = event['realm_domain'][
                        'allow_subdomains']
        elif event['op'] == 'remove':
            state['realm_domains'] = [
                realm_domain for realm_domain in state['realm_domains']
                if realm_domain['domain'] != event['domain']
            ]
    elif event['type'] == "realm_emoji":
        state['realm_emoji'] = event['realm_emoji']
    elif event['type'] == "alert_words":
        state['alert_words'] = event['alert_words']
    elif event['type'] == "muted_topics":
        state['muted_topics'] = event["muted_topics"]
    elif event['type'] == "realm_filters":
        state['realm_filters'] = event["realm_filters"]
    elif event['type'] == "update_display_settings":
        assert event['setting_name'] in UserProfile.property_types
        state[event['setting_name']] = event['setting']
    elif event['type'] == "update_global_notifications":
        assert event[
            'notification_name'] in UserProfile.notification_setting_types
        state[event['notification_name']] = event['setting']
    elif event['type'] == "invites_changed":
        pass
    elif event['type'] == "user_group":
        if event['op'] == 'add':
            state['realm_user_groups'].append(event['group'])
            state['realm_user_groups'].sort(key=lambda group: group['id'])
        elif event['op'] == 'update':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group.update(event['data'])
        elif event['op'] == 'add_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    user_group['members'].extend(event['user_ids'])
                    user_group['members'].sort()
        elif event['op'] == 'remove_members':
            for user_group in state['realm_user_groups']:
                if user_group['id'] == event['group_id']:
                    members = set(user_group['members'])
                    user_group['members'] = list(members -
                                                 set(event['user_ids']))
                    user_group['members'].sort()
        elif event['op'] == 'remove':
            state['realm_user_groups'] = [
                ug for ug in state['realm_user_groups']
                if ug['id'] != event['group_id']
            ]
    elif event['type'] == 'user_status':
        user_id = event['user_id']
        user_status = state['user_status']
        away = event.get('away')
        status_text = event.get('status_text')

        if user_id not in user_status:
            user_status[user_id] = dict()

        if away is not None:
            if away:
                user_status[user_id]['away'] = True
            else:
                user_status[user_id].pop('away', None)

        if status_text is not None:
            if status_text == '':
                user_status[user_id].pop('status_text', None)
            else:
                user_status[user_id]['status_text'] = status_text

        if not user_status[user_id]:
            user_status.pop(user_id, None)

        state['user_status'] = user_status
    else:
        raise AssertionError("Unexpected event type %s" % (event['type'], ))
コード例 #10
0
def fetch_initial_state_data(
    user_profile: Optional[UserProfile],
    *,
    realm: Optional[Realm] = None,
    event_types: Optional[Iterable[str]] = None,
    queue_id: Optional[str] = "",
    client_gravatar: bool = False,
    user_avatar_url_field_optional: bool = False,
    slim_presence: bool = False,
    include_subscribers: bool = True,
    include_streams: bool = True,
) -> Dict[str, Any]:
    """When `event_types` is None, fetches the core data powering the
    webapp's `page_params` and `/api/v1/register` (for mobile/terminal
    apps).  Can also fetch a subset as determined by `event_types`.

    The user_profile=None code path is used for logged-out public
    access to streams with is_web_public=True.

    Whenever you add new code to this function, you should also add
    corresponding events for changes in the data structures and new
    code to apply_events (and add a test in test_events.py).
    """
    if realm is None:
        assert user_profile is not None
        realm = user_profile.realm

    state: Dict[str, Any] = {'queue_id': queue_id}

    if event_types is None:
        # return True always
        want: Callable[[str], bool] = always_want
    else:
        want = set(event_types).__contains__

    # Show the version info unconditionally.
    state['zulip_version'] = ZULIP_VERSION
    state['zulip_feature_level'] = API_FEATURE_LEVEL

    if want('alert_words'):
        state['alert_words'] = [] if user_profile is None else user_alert_words(user_profile)

    if want('custom_profile_fields'):
        fields = custom_profile_fields_for_realm(realm.id)
        state['custom_profile_fields'] = [f.as_dict() for f in fields]
        state['custom_profile_field_types'] = {
            item[4]: {"id": item[0], "name": str(item[1])} for item in CustomProfileField.ALL_FIELD_TYPES
        }

    if want('hotspots'):
        # Even if we offered special hotspots for guests without an
        # account, we'd maybe need to store their state using cookies
        # or local storage, rather than in the database.
        state['hotspots'] = [] if user_profile is None else get_next_hotspots(user_profile)

    if want('message'):
        # Since the introduction of `anchor="latest"` in the API,
        # `max_message_id` is primarily used for generating `local_id`
        # values that are higher than this.  We likely can eventually
        # remove this parameter from the API.
        user_messages = []
        if user_profile is not None:
            user_messages = UserMessage.objects \
                .filter(user_profile=user_profile) \
                .order_by('-message_id') \
                .values('message_id')[:1]
        if user_messages:
            state['max_message_id'] = user_messages[0]['message_id']
        else:
            state['max_message_id'] = -1

    if want('muted_topics'):
        state['muted_topics'] = [] if user_profile is None else get_topic_mutes(user_profile)

    if want('presence'):
        state['presences'] = {} if user_profile is None else get_presences_for_realm(realm, slim_presence)

    if want('realm'):
        for property_name in Realm.property_types:
            state['realm_' + property_name] = getattr(realm, property_name)

        # Most state is handled via the property_types framework;
        # these manual entries are for those realm settings that don't
        # fit into that framework.
        state['realm_authentication_methods'] = realm.authentication_methods_dict()

        # We pretend these features are disabled because guests can't
        # access them.  In the future, we may want to move this logic
        # to the frontends, so that we can correctly display what
        # these fields are in the settings.
        state['realm_allow_message_editing'] = False if user_profile is None else realm.allow_message_editing
        state['realm_allow_community_topic_editing'] = False if user_profile is None else realm.allow_community_topic_editing
        state['realm_allow_message_deleting'] = False if user_profile is None else realm.allow_message_deleting

        state['realm_message_content_edit_limit_seconds'] = realm.message_content_edit_limit_seconds
        state['realm_message_content_delete_limit_seconds'] = realm.message_content_delete_limit_seconds
        state['realm_community_topic_editing_limit_seconds'] = \
            Realm.DEFAULT_COMMUNITY_TOPIC_EDITING_LIMIT_SECONDS

        # This setting determines whether to send presence and also
        # whether to display of users list in the right sidebar; we
        # want both behaviors for logged-out users.  We may in the
        # future choose to move this logic to the frontend.
        state['realm_presence_disabled'] = True if user_profile is None else realm.presence_disabled

        state['realm_icon_url'] = realm_icon_url(realm)
        state['realm_icon_source'] = realm.icon_source
        state['max_icon_file_size'] = settings.MAX_ICON_FILE_SIZE
        add_realm_logo_fields(state, realm)
        state['realm_bot_domain'] = realm.get_bot_domain()
        state['realm_uri'] = realm.uri
        state['realm_available_video_chat_providers'] = realm.VIDEO_CHAT_PROVIDERS
        state['settings_send_digest_emails'] = settings.SEND_DIGEST_EMAILS
        state['realm_digest_emails_enabled'] = realm.digest_emails_enabled and settings.SEND_DIGEST_EMAILS
        state['realm_is_zephyr_mirror_realm'] = realm.is_zephyr_mirror_realm
        state['realm_email_auth_enabled'] = email_auth_enabled(realm)
        state['realm_password_auth_enabled'] = password_auth_enabled(realm)
        state['realm_push_notifications_enabled'] = push_notifications_enabled()
        state['realm_upload_quota'] = realm.upload_quota_bytes()
        state['realm_plan_type'] = realm.plan_type
        state['zulip_plan_is_not_limited'] = realm.plan_type != Realm.LIMITED
        state['upgrade_text_for_wide_organization_logo'] = str(Realm.UPGRADE_TEXT_STANDARD)
        state['realm_default_external_accounts'] = DEFAULT_EXTERNAL_ACCOUNTS
        state['jitsi_server_url']                = settings.JITSI_SERVER_URL.rstrip('/')
        state['development_environment']         = settings.DEVELOPMENT
        state['server_generation']               = settings.SERVER_GENERATION
        state['password_min_length']             = settings.PASSWORD_MIN_LENGTH
        state['password_min_guesses']            = settings.PASSWORD_MIN_GUESSES
        state['max_file_upload_size_mib']        = settings.MAX_FILE_UPLOAD_SIZE
        state['max_avatar_file_size_mib']        = settings.MAX_AVATAR_FILE_SIZE
        state['server_inline_image_preview']     = settings.INLINE_IMAGE_PREVIEW
        state['server_inline_url_embed_preview'] = settings.INLINE_URL_EMBED_PREVIEW
        state['server_avatar_changes_disabled']  = settings.AVATAR_CHANGES_DISABLED
        state['server_name_changes_disabled']    = settings.NAME_CHANGES_DISABLED

        if realm.notifications_stream and not realm.notifications_stream.deactivated:
            notifications_stream = realm.notifications_stream
            state['realm_notifications_stream_id'] = notifications_stream.id
        else:
            state['realm_notifications_stream_id'] = -1

        signup_notifications_stream = realm.get_signup_notifications_stream()
        if signup_notifications_stream:
            state['realm_signup_notifications_stream_id'] = signup_notifications_stream.id
        else:
            state['realm_signup_notifications_stream_id'] = -1

    if want('realm_domains'):
        state['realm_domains'] = get_realm_domains(realm)

    if want('realm_emoji'):
        state['realm_emoji'] = realm.get_emoji()

    if want('realm_filters'):
        state['realm_filters'] = realm_filters_for_realm(realm.id)

    if want('realm_user_groups'):
        state['realm_user_groups'] = user_groups_in_realm_serialized(realm)

    if user_profile is not None:
        settings_user = user_profile
    else:
        # When UserProfile=None, we want to serve the values for various
        # settings as the defaults.  Instead of copying the default values
        # from models.py here, we access these default values from a
        # temporary UserProfile object that will not be saved to the database.
        #
        # We also can set various fields to avoid duplicating code
        # unnecessarily.
        settings_user = UserProfile(
            full_name="Anonymous User",
            email="*****@*****.**",
            delivery_email="*****@*****.**",
            realm=realm,
            # We tag logged-out users as guests because most guest
            # restrictions apply to these users as well, and it lets
            # us avoid unnecessary conditionals.
            role=UserProfile.ROLE_GUEST,
            avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
            # ID=0 is not used in real Zulip databases, ensuring this is unique.
            id=0,
        )
    if want('realm_user'):
        state['raw_users'] = get_raw_user_data(realm, user_profile,
                                               client_gravatar=client_gravatar,
                                               user_avatar_url_field_optional=user_avatar_url_field_optional)
        state['cross_realm_bots'] = list(get_cross_realm_dicts())

        # For the user's own avatar URL, we force
        # client_gravatar=False, since that saves some unnecessary
        # client-side code for handing medium-size avatars.  See #8253
        # for details.
        state['avatar_source'] = settings_user.avatar_source
        state['avatar_url_medium'] = avatar_url(
            settings_user,
            medium=True,
            client_gravatar=False,
        )
        state['avatar_url'] = avatar_url(
            settings_user,
            medium=False,
            client_gravatar=False,
        )

        state['can_create_streams'] = settings_user.can_create_streams()
        state['can_subscribe_other_users'] = settings_user.can_subscribe_other_users()
        state['is_admin'] = settings_user.is_realm_admin
        state['is_owner'] = settings_user.is_realm_owner
        state['is_guest'] = settings_user.is_guest
        state['user_id'] = settings_user.id
        state['enter_sends'] = settings_user.enter_sends
        state['email'] = settings_user.email
        state['delivery_email'] = settings_user.delivery_email
        state['full_name'] = settings_user.full_name

    if want('realm_bot'):
        state['realm_bots'] = [] if user_profile is None else get_owned_bot_dicts(user_profile)

    # This does not yet have an apply_event counterpart, since currently,
    # new entries for EMBEDDED_BOTS can only be added directly in the codebase.
    if want('realm_embedded_bots'):
        realm_embedded_bots = []
        for bot in EMBEDDED_BOTS:
            realm_embedded_bots.append({'name': bot.name,
                                        'config': load_bot_config_template(bot.name)})
        state['realm_embedded_bots'] = realm_embedded_bots

    # This does not have an apply_events counterpart either since
    # this data is mostly static.
    if want('realm_incoming_webhook_bots'):
        realm_incoming_webhook_bots = []
        for integration in WEBHOOK_INTEGRATIONS:
            realm_incoming_webhook_bots.append({
                'name': integration.name,
                'config': {c[1]: c[0] for c in integration.config_options},
            })
        state['realm_incoming_webhook_bots'] = realm_incoming_webhook_bots

    if want('recent_private_conversations'):
        # A data structure containing records of this form:
        #
        #   [{'max_message_id': 700175, 'user_ids': [801]}]
        #
        # for all recent private message conversations, ordered by the
        # highest message ID in the conversation.  The user_ids list
        # is the list of users other than the current user in the
        # private message conversation (so it is [] for PMs to self).
        # Note that raw_recent_private_conversations is an
        # intermediate form as a dictionary keyed by recipient_id,
        # which is more efficient to update, and is rewritten to the
        # final format in post_process_state.
        state['raw_recent_private_conversations'] = {} if user_profile is None else get_recent_private_conversations(user_profile)

    if want('subscription'):
        if user_profile is not None:
            sub_info = gather_subscriptions_helper(
                user_profile,
                include_subscribers=include_subscribers,
            )
        else:
            sub_info = get_web_public_subs(realm)

        state['subscriptions'] = sub_info.subscriptions
        state['unsubscribed'] = sub_info.unsubscribed
        state['never_subscribed'] = sub_info.never_subscribed

    if want('update_message_flags') and want('message'):
        # Keeping unread_msgs updated requires both message flag updates and
        # message updates. This is due to the fact that new messages will not
        # generate a flag update so we need to use the flags field in the
        # message event.

        if user_profile is not None:
            state['raw_unread_msgs'] = get_raw_unread_data(user_profile)
        else:
            # For logged-out visitors, we treat all messages as read;
            # calling this helper lets us return empty objects in the
            # appropriate format.
            state['raw_unread_msgs'] = extract_unread_data_from_um_rows([], user_profile)

    if want('starred_messages'):
        state['starred_messages'] = [] if user_profile is None else get_starred_message_ids(user_profile)

    if want('stream'):
        if include_streams:
            # The webapp doesn't use the data from here; instead,
            # it uses data from state["subscriptions"] and other
            # places.
            if user_profile is not None:
                state['streams'] = do_get_streams(user_profile)
            else:
                # TODO: This line isn't used by the webapp because it
                # gets these data via the `subscriptions` key; it will
                # be used when the mobile apps support logged-out
                # access.
                state['streams'] = get_web_public_streams(realm)  # nocoverage
        state['stream_name_max_length'] = Stream.MAX_NAME_LENGTH
        state['stream_description_max_length'] = Stream.MAX_DESCRIPTION_LENGTH
    if want('default_streams'):
        if settings_user.is_guest:
            # Guest users and logged-out users don't have access to
            # all default streams, so we pretend the organization
            # doesn't have any.
            state['realm_default_streams'] = []
        else:
            state['realm_default_streams'] = streams_to_dicts_sorted(
                get_default_streams_for_realm(realm.id))
    if want('default_stream_groups'):
        if settings_user.is_guest:
            state['realm_default_stream_groups'] = []
        else:
            state['realm_default_stream_groups'] = default_stream_groups_to_dicts_sorted(
                get_default_stream_groups(realm))

    if want('stop_words'):
        state['stop_words'] = read_stop_words()

    if want('update_display_settings'):
        for prop in UserProfile.property_types:
            state[prop] = getattr(settings_user, prop)
            state['emojiset_choices'] = UserProfile.emojiset_choices()

    if want('update_global_notifications'):
        for notification in UserProfile.notification_setting_types:
            state[notification] = getattr(settings_user, notification)
        state['available_notification_sounds'] = get_available_notification_sounds()

    if want('user_status'):
        # We require creating an account to access statuses.
        state['user_status'] = {} if user_profile is None else get_user_info_dict(realm_id=realm.id)

    if want('video_calls'):
        state['has_zoom_token'] = settings_user.zoom_token is not None

    return state
コード例 #11
0
ファイル: streams.py プロジェクト: BakerWang/zulip
def add_subscriptions_backend(
        request: HttpRequest, user_profile: UserProfile,
        streams_raw: Iterable[Dict[str, str]]=REQ(
            "subscriptions", validator=check_list(check_dict_only(
                [('name', check_string)], optional_keys=[
                    ('color', check_color),
                    ('description', check_capped_string(Stream.MAX_DESCRIPTION_LENGTH)),
                ])
            )),
        invite_only: bool=REQ(validator=check_bool, default=False),
        is_announcement_only: bool=REQ(validator=check_bool, default=False),
        history_public_to_subscribers: Optional[bool]=REQ(validator=check_bool, default=None),
        announce: bool=REQ(validator=check_bool, default=False),
        principals: List[str]=REQ(validator=check_list(check_string), default=[]),
        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 = {}  # type: 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["is_announcement_only"] = is_announcement_only
        stream_dict_copy["history_public_to_subscribers"] = history_public_to_subscribers
        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 (%s).") % 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():
            return json_error(_("Your account is too new to modify other users' subscriptions."))
        subscribers = set(principal_to_user_profile(user_profile, principal) for principal in principals)
    else:
        subscribers = set([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()  # type: Dict[str, UserProfile]

    result = dict(subscribed=defaultdict(list), already_subscribed=defaultdict(list))  # type: Dict[str, Any]
    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 = dict((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

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

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            notifications.append(
                internal_prep_private_message(
                    realm=user_profile.realm,
                    sender=sender,
                    recipient_user=email_to_user_profile[email],
                    content=msg))

    if announce and len(created_streams) > 0 and settings.NOTIFICATION_BOT is not None:
        notifications_stream = user_profile.realm.get_notifications_stream()
        if notifications_stream is not None:
            if len(created_streams) > 1:
                stream_strs = ", ".join('#**%s**' % s.name for s in created_streams)
                stream_msg = "the following streams: %s" % (stream_strs,)
            else:
                stream_msg = "a new stream #**%s**." % created_streams[0].name
            msg = ("@_**%s|%d** just created %s" % (user_profile.full_name, user_profile.id, stream_msg))

            sender = get_system_bot(settings.NOTIFICATION_BOT)
            topic = 'Streams'

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

    if not user_profile.realm.is_zephyr_mirror_realm:
        for stream in created_streams:
            notifications.append(prep_stream_welcome_message(stream))

    if len(notifications) > 0:
        do_send_messages(notifications)

    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)