Example #1
0
    def get_member(row):
        # type: (Dict[str, Any]) -> Dict[str, Any]
        email = row['email']
        user_id = row['id']

        result = dict(
            user_id=user_id,
            email=email,
            full_name=row['full_name'],
            is_bot=row['is_bot'],
            is_active=row['is_active'],
            bot_type=row['bot_type'],
        )

        result['is_admin'] = user_id in admin_ids

        result['avatar_url'] = get_avatar_field(
            user_id=user_id,
            email=email,
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            realm_id=row['realm_id'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        if row['bot_owner__email']:
            result['bot_owner'] = row['bot_owner__email']

        return result
Example #2
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']
        is_bot = row['is_bot']
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active=row['is_active'],
        )
        if not is_bot:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #3
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id= realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']
        is_bot = row['is_bot']
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active = row['is_active'],
        )
        if not is_bot:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #4
0
    def get_member(row: Dict[str, Any]) -> Dict[str, Any]:
        email = row['email']
        user_id = row['id']

        result = dict(
            user_id=user_id,
            email=email,
            full_name=row['full_name'],
            is_bot=row['is_bot'],
            is_active=row['is_active'],
            is_admin=row['is_realm_admin'],
            bot_type=row['bot_type'],
            is_guest=row['is_guest'],
            timezone=row['timezone'],
        )

        result['avatar_url'] = get_avatar_field(
            user_id=user_id,
            email=email,
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            realm_id=row['realm_id'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        if row['bot_owner__email']:
            result['bot_owner'] = row['bot_owner__email']

        if include_delivery_email:
            result['delivery_email'] = row['delivery_email']
            if row['bot_owner__delivery_email']:
                result['bot_owner_delivery_email'] = row['bot_owner__delivery_email']

        return result
Example #5
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm.id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['role'] == UserProfile.ROLE_REALM_ADMINISTRATOR
        is_guest = row['role'] == UserProfile.ROLE_GUEST
        is_bot = row['is_bot']
        # This format should align with get_cross_realm_dicts() and notify_created_user
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_guest=is_guest,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active = row['is_active'],
            date_joined = row['date_joined'].isoformat(),
        )
        if is_bot:
            if row['email'] in settings.CROSS_REALM_BOT_EMAILS:
                result['is_cross_realm_bot'] = True
            elif row['bot_owner_id'] is not None:
                result['bot_owner_id'] = row['bot_owner_id']
        else:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #6
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id= realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']
        is_guest = row['is_guest']
        is_bot = row['is_bot']
        # This format should align with get_cross_realm_dicts() and notify_created_user
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_guest=is_guest,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active = row['is_active'],
            date_joined = row['date_joined'].isoformat(),
        )
        if not is_bot:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #7
0
    def get_member(row: Dict[str, Any]) -> Dict[str, Any]:
        email = row['email']
        user_id = row['id']

        result = dict(
            user_id=user_id,
            email=email,
            full_name=row['full_name'],
            is_bot=row['is_bot'],
            is_active=row['is_active'],
            bot_type=row['bot_type'],
        )

        result['is_admin'] = user_id in admin_ids

        result['avatar_url'] = get_avatar_field(
            user_id=user_id,
            email=email,
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            realm_id=row['realm_id'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        if row['bot_owner__email']:
            result['bot_owner'] = row['bot_owner__email']

        return result
Example #8
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']
        is_bot = row['is_bot']
        # This format should align with get_cross_realm_dicts() and notify_created_user
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active=row['is_active'],
            date_joined=row['date_joined'].isoformat(),
        )
        if not is_bot:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #9
0
    def test_get_avatar_field(self):
        # type: () -> None
        with self.settings(AVATAR_SALT="salt"):
            url = get_avatar_field(
                user_id=17,
                realm_id=5,
                email='*****@*****.**',
                avatar_source=UserProfile.AVATAR_FROM_USER,
                avatar_version=2,
                medium=True,
                client_gravatar=False,
            )

        self.assertEqual(
            url,
            '/user_avatars/5/fc2b9f1a81f4508a4df2d95451a2a77e0524ca0e-medium.png?x=x&version=2'
        )

        url = get_avatar_field(
            user_id=9999,
            realm_id=9999,
            email='*****@*****.**',
            avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
            avatar_version=2,
            medium=True,
            client_gravatar=False,
        )

        self.assertEqual(
            url,
            'https://secure.gravatar.com/avatar/b48def645758b95537d4424c84d1a9ff?d=identicon&s=500&version=2'
        )

        url = get_avatar_field(
            user_id=9999,
            realm_id=9999,
            email='*****@*****.**',
            avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
            avatar_version=2,
            medium=True,
            client_gravatar=True,
        )

        self.assertEqual(url, None)
Example #10
0
    def test_get_avatar_field(self):
        # type: () -> None
        with self.settings(AVATAR_SALT="salt"):
            url = get_avatar_field(
                user_id=17,
                realm_id=5,
                email='*****@*****.**',
                avatar_source=UserProfile.AVATAR_FROM_USER,
                avatar_version=2,
                medium=True,
                client_gravatar=False,
            )

        self.assertEqual(
            url,
            '/user_avatars/5/fc2b9f1a81f4508a4df2d95451a2a77e0524ca0e-medium.png?x=x&version=2'
        )

        url = get_avatar_field(
            user_id=9999,
            realm_id=9999,
            email='*****@*****.**',
            avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
            avatar_version=2,
            medium=True,
            client_gravatar=False,
        )

        self.assertEqual(
            url,
            'https://secure.gravatar.com/avatar/b48def645758b95537d4424c84d1a9ff?d=identicon&s=500&version=2'
        )

        url = get_avatar_field(
            user_id=9999,
            realm_id=9999,
            email='*****@*****.**',
            avatar_source=UserProfile.AVATAR_FROM_GRAVATAR,
            avatar_version=2,
            medium=True,
            client_gravatar=True,
        )

        self.assertEqual(url, None)
Example #11
0
def format_user_row(
    realm: Realm,
    acting_user: UserProfile,
    row: Dict[str, Any],
    client_gravatar: bool,
    custom_profile_field_data: Optional[Dict[str,
                                             Any]] = None) -> Dict[str, Any]:
    """Formats a user row returned by a database fetch using
    .values(*realm_user_dict_fields) into a dictionary representation
    of that user for API delivery to clients.  The acting_user
    argument is used for permissions checks.
    """

    avatar_url = get_avatar_field(
        user_id=row['id'],
        realm_id=realm.id,
        email=row['delivery_email'],
        avatar_source=row['avatar_source'],
        avatar_version=row['avatar_version'],
        medium=False,
        client_gravatar=client_gravatar,
    )

    is_admin = is_administrator_role(row['role'])
    is_owner = row['role'] == UserProfile.ROLE_REALM_OWNER
    is_guest = row['role'] == UserProfile.ROLE_GUEST
    is_bot = row['is_bot']
    # This format should align with get_cross_realm_dicts() and notify_created_user
    result = dict(
        email=row['email'],
        user_id=row['id'],
        avatar_url=avatar_url,
        avatar_version=row['avatar_version'],
        is_admin=is_admin,
        is_owner=is_owner,
        is_guest=is_guest,
        is_bot=is_bot,
        full_name=row['full_name'],
        timezone=row['timezone'],
        is_active=row['is_active'],
        date_joined=row['date_joined'].isoformat(),
    )
    if (realm.email_address_visibility == Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS
            and acting_user.is_realm_admin):
        result['delivery_email'] = row['delivery_email']

    if is_bot:
        result["bot_type"] = row["bot_type"]
        if row['email'] in settings.CROSS_REALM_BOT_EMAILS:
            result['is_cross_realm_bot'] = True

        # Note that bot_owner_id can be None with legacy data.
        result['bot_owner_id'] = row['bot_owner_id']
    elif custom_profile_field_data is not None:
        result['profile_data'] = custom_profile_field_data
    return result
Example #12
0
    def set_sender_avatar(obj: Dict[str, Any], client_gravatar: bool) -> None:
        sender_id = obj['sender_id']
        sender_realm_id = obj['sender_realm_id']
        sender_email = obj['sender_email']
        sender_avatar_source = obj['sender_avatar_source']
        sender_avatar_version = obj['sender_avatar_version']

        obj['avatar_url'] = get_avatar_field(
            user_id=sender_id,
            realm_id=sender_realm_id,
            email=sender_email,
            avatar_source=sender_avatar_source,
            avatar_version=sender_avatar_version,
            medium=False,
            client_gravatar=client_gravatar,
        )
Example #13
0
    def set_sender_avatar(obj: Dict[str, Any], client_gravatar: bool) -> None:
        sender_id = obj['sender_id']
        sender_realm_id = obj['sender_realm_id']
        sender_delivery_email = obj['sender_delivery_email']
        sender_avatar_source = obj['sender_avatar_source']
        sender_avatar_version = obj['sender_avatar_version']

        obj['avatar_url'] = get_avatar_field(
            user_id=sender_id,
            realm_id=sender_realm_id,
            email=sender_delivery_email,
            avatar_source=sender_avatar_source,
            avatar_version=sender_avatar_version,
            medium=False,
            client_gravatar=client_gravatar,
        )
Example #14
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm.id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['role'] == UserProfile.ROLE_REALM_ADMINISTRATOR
        is_guest = row['role'] == UserProfile.ROLE_GUEST
        is_bot = row['is_bot']
        # This format should align with get_cross_realm_dicts() and notify_created_user
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_guest=is_guest,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active=row['is_active'],
            date_joined=row['date_joined'].isoformat(),
        )

        if (realm.email_address_visibility
                == Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS
                and user_profile.is_realm_admin):
            result['delivery_email'] = row['delivery_email']

        if for_api:
            # The API currently has a quirk that it expects to include
            # a bot_type field even for human users; this field is
            # invalid so we plan to eventually remove this.
            result['bot_type'] = row['bot_type']
        if is_bot:
            if row['email'] in settings.CROSS_REALM_BOT_EMAILS:
                result['is_cross_realm_bot'] = True
            elif row['bot_owner_id'] is not None:
                result['bot_owner_id'] = row['bot_owner_id']
        elif include_custom_profile_fields:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #15
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm.id,
            email=row['delivery_email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['role'] == UserProfile.ROLE_REALM_ADMINISTRATOR
        is_guest = row['role'] == UserProfile.ROLE_GUEST
        is_bot = row['is_bot']
        # This format should align with get_cross_realm_dicts() and notify_created_user
        result = dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_guest=is_guest,
            is_bot=is_bot,
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active=row['is_active'],
            date_joined=row['date_joined'].isoformat(),
        )

        if (realm.email_address_visibility
                == Realm.EMAIL_ADDRESS_VISIBILITY_ADMINS
                and user_profile.is_realm_admin):
            result['delivery_email'] = row['delivery_email']

        if is_bot:
            result["bot_type"] = row["bot_type"]
            if row['email'] in settings.CROSS_REALM_BOT_EMAILS:
                result['is_cross_realm_bot'] = True

            # Note that bot_owner_id can be None with legacy data.
            result['bot_owner_id'] = row['bot_owner_id']
        elif include_custom_profile_fields:
            result['profile_data'] = profiles_by_user_id.get(row['id'], {})
        return result
Example #16
0
    def set_sender_avatar(obj):
        # type: (Dict[str, Any]) -> None
        sender_id = obj['sender_id']
        sender_realm_id = obj['sender_realm_id']
        sender_email = obj['sender_email']
        sender_avatar_source = obj['sender_avatar_source']
        sender_avatar_version = obj['sender_avatar_version']

        # TODO: Make client_gravatar configurable.
        client_gravatar = False

        obj['avatar_url'] = get_avatar_field(
            user_id=sender_id,
            realm_id=sender_realm_id,
            email=sender_email,
            avatar_source=sender_avatar_source,
            avatar_version=sender_avatar_version,
            medium=False,
            client_gravatar=client_gravatar,
        )
Example #17
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id= realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']

        return dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_bot=row['is_bot'],
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active = row['is_active'],
        )
Example #18
0
    def user_data(row: Dict[str, Any]) -> Dict[str, Any]:
        avatar_url = get_avatar_field(
            user_id=row['id'],
            realm_id=realm_id,
            email=row['email'],
            avatar_source=row['avatar_source'],
            avatar_version=row['avatar_version'],
            medium=False,
            client_gravatar=client_gravatar,
        )

        is_admin = row['is_realm_admin']

        return dict(
            email=row['email'],
            user_id=row['id'],
            avatar_url=avatar_url,
            is_admin=is_admin,
            is_bot=row['is_bot'],
            full_name=row['full_name'],
            timezone=row['timezone'],
            is_active=row['is_active'],
        )
Example #19
0
def format_user_row(
    realm: Realm,
    acting_user: Optional[UserProfile],
    row: Dict[str, Any],
    client_gravatar: bool,
    user_avatar_url_field_optional: bool,
    custom_profile_field_data: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """Formats a user row returned by a database fetch using
    .values(*realm_user_dict_fields) into a dictionary representation
    of that user for API delivery to clients.  The acting_user
    argument is used for permissions checks.
    """

    is_admin = is_administrator_role(row["role"])
    is_owner = row["role"] == UserProfile.ROLE_REALM_OWNER
    is_guest = row["role"] == UserProfile.ROLE_GUEST
    is_bot = row["is_bot"]
    result = dict(
        email=row["email"],
        user_id=row["id"],
        avatar_version=row["avatar_version"],
        is_admin=is_admin,
        is_owner=is_owner,
        is_guest=is_guest,
        is_billing_admin=row["is_billing_admin"],
        role=row["role"],
        is_bot=is_bot,
        full_name=row["full_name"],
        timezone=canonicalize_timezone(row["timezone"]),
        is_active=row["is_active"],
        date_joined=row["date_joined"].isoformat(),
    )

    # Zulip clients that support using `GET /avatar/{user_id}` as a
    # fallback if we didn't send an avatar URL in the user object pass
    # user_avatar_url_field_optional in client_capabilities.
    #
    # This is a major network performance optimization for
    # organizations with 10,000s of users where we would otherwise
    # send avatar URLs in the payload (either because most users have
    # uploaded avatars or because EMAIL_ADDRESS_VISIBILITY_ADMINS
    # prevents the older client_gravatar optimization from helping).
    # The performance impact is large largely because the hashes in
    # avatar URLs structurally cannot compress well.
    #
    # The user_avatar_url_field_optional gives the server sole
    # discretion in deciding for which users we want to send the
    # avatar URL (Which saves clients an RTT at the cost of some
    # bandwidth).  At present, the server looks at `long_term_idle` to
    # decide which users to include avatars for, piggy-backing on a
    # different optimization for organizations with 10,000s of users.
    include_avatar_url = not user_avatar_url_field_optional or not row["long_term_idle"]
    if include_avatar_url:
        result["avatar_url"] = get_avatar_field(
            user_id=row["id"],
            realm_id=realm.id,
            email=row["delivery_email"],
            avatar_source=row["avatar_source"],
            avatar_version=row["avatar_version"],
            medium=False,
            client_gravatar=client_gravatar,
        )

    if acting_user is not None and can_access_delivery_email(acting_user):
        result["delivery_email"] = row["delivery_email"]

    if is_bot:
        result["bot_type"] = row["bot_type"]
        if row["email"] in settings.CROSS_REALM_BOT_EMAILS:
            result["is_system_bot"] = True

        # Note that bot_owner_id can be None with legacy data.
        result["bot_owner_id"] = row["bot_owner_id"]
    elif custom_profile_field_data is not None:
        result["profile_data"] = custom_profile_field_data
    return result
Example #20
0
    def build_message_dict(apply_markdown, message, message_id, last_edit_time,
                           edit_history, content, subject, pub_date,
                           rendered_content, rendered_content_version,
                           sender_id, sender_email, sender_realm_id,
                           sender_realm_str, sender_avatar_source,
                           sender_avatar_version, sender_is_mirror_dummy,
                           sending_client_name, recipient_id, recipient_type,
                           recipient_type_id, reactions):
        # type: (bool, Optional[Message], int, Optional[datetime.datetime], Optional[Text], Text, Text, datetime.datetime, Optional[Text], Optional[int], int, Text, int, Text, Text, int, bool, Text, int, int, int, List[Dict[str, Any]]) -> Dict[str, Any]

        # TODO: Make client_gravatar configurable.
        client_gravatar = False

        avatar_url = get_avatar_field(
            user_id=sender_id,
            realm_id=sender_realm_id,
            email=sender_email,
            avatar_source=sender_avatar_source,
            avatar_version=sender_avatar_version,
            medium=False,
            client_gravatar=client_gravatar,
        )

        obj = dict(id=message_id,
                   sender_email=sender_email,
                   sender_realm_str=sender_realm_str,
                   sender_id=sender_id,
                   recipient_type_id=recipient_type_id,
                   recipient_type=recipient_type,
                   recipient_id=recipient_id,
                   subject=subject,
                   timestamp=datetime_to_timestamp(pub_date),
                   avatar_url=avatar_url,
                   client=sending_client_name)

        obj['raw_display_recipient'] = get_display_recipient_by_id(
            recipient_id, recipient_type, recipient_type_id)

        obj['sender_is_mirror_dummy'] = sender_is_mirror_dummy

        obj['subject_links'] = bugdown.subject_links(sender_realm_id, subject)

        if last_edit_time is not None:
            obj['last_edit_timestamp'] = datetime_to_timestamp(last_edit_time)
            assert edit_history is not None
            obj['edit_history'] = ujson.loads(edit_history)

        if apply_markdown:
            if Message.need_to_render_content(rendered_content,
                                              rendered_content_version,
                                              bugdown.version):
                if message is None:
                    # We really shouldn't be rendering objects in this method, but there is
                    # a scenario where we upgrade the version of bugdown and fail to run
                    # management commands to re-render historical messages, and then we
                    # need to have side effects.  This method is optimized to not need full
                    # blown ORM objects, but the bugdown renderer is unfortunately highly
                    # coupled to Message, and we also need to persist the new rendered content.
                    # If we don't have a message object passed in, we get one here.  The cost
                    # of going to the DB here should be overshadowed by the cost of rendering
                    # and updating the row.
                    # TODO: see #1379 to eliminate bugdown dependencies
                    message = Message.objects.select_related().get(
                        id=message_id)

                assert message is not None  # Hint for mypy.
                # It's unfortunate that we need to have side effects on the message
                # in some cases.
                rendered_content = render_markdown(message,
                                                   content,
                                                   realm=message.get_realm())
                message.rendered_content = rendered_content
                message.rendered_content_version = bugdown.version
                message.save_rendered_content()

            if rendered_content is not None:
                obj['content'] = rendered_content
            else:
                obj['content'] = u'<p>[Zulip note: Sorry, we could not understand the formatting of your message]</p>'

            obj['content_type'] = 'text/html'
        else:
            obj['content'] = content
            obj['content_type'] = 'text/x-markdown'

        if rendered_content is not None:
            obj['is_me_message'] = Message.is_status_message(
                content, rendered_content)
        else:
            obj['is_me_message'] = False

        obj['reactions'] = [
            ReactionDict.build_dict_from_raw_db_row(reaction)
            for reaction in reactions
        ]
        return obj