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
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
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
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
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
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
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
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
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)
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)
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
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, )
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, )
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
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
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, )
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'], )
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'], )
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
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