Support email changes for !avatar syntax.

Significantly modified by tabbott to avoid calling
get_user_profile_by_email in bugdown, and have 100% test coverage of
the views code.

Fixes #2041.
This commit is contained in:
Igor Tokarev
2016-10-24 19:42:43 +05:00
committed by Tim Abbott
parent a1d296b802
commit 31dff09efa
5 changed files with 62 additions and 8 deletions

View File

@@ -37,7 +37,7 @@ from zerver.lib.timeout import timeout, TimeoutExpired
from zerver.lib.cache import ( from zerver.lib.cache import (
cache_with_key, cache_get_many, cache_set_many, NotFoundInCache) cache_with_key, cache_get_many, cache_set_many, NotFoundInCache)
from zerver.lib.url_preview import preview as link_preview from zerver.lib.url_preview import preview as link_preview
from zerver.models import Message, Realm from zerver.models import Message, Realm, UserProfile, get_user_profile_by_email
import zerver.lib.alert_words as alert_words import zerver.lib.alert_words as alert_words
import zerver.lib.mention as mention import zerver.lib.mention as mention
from zerver.lib.str_utils import force_text, force_str from zerver.lib.str_utils import force_text, force_str
@@ -635,10 +635,18 @@ class Avatar(markdown.inlinepatterns.Pattern):
# type: (Match[Text]) -> Optional[Element] # type: (Match[Text]) -> Optional[Element]
img = markdown.util.etree.Element('img') img = markdown.util.etree.Element('img')
email_address = match.group('email') email_address = match.group('email')
email = email_address.strip().lower()
profile_id = None
if db_data is not None:
user_dict = db_data['by_email'].get(email)
if user_dict is not None:
profile_id = user_dict['id']
img.set('class', 'message_body_gravatar') img.set('class', 'message_body_gravatar')
img.set('src', '/avatar/%s?s=30' % (email_address,)) img.set('src', '/avatar/{0}?s=30'.format(profile_id or email))
img.set('title', email_address) img.set('title', email)
img.set('alt', email_address) img.set('alt', email)
return img return img
emoji_tree = os.path.join(settings.STATIC_ROOT, "generated", "emoji", "images", "emoji") emoji_tree = os.path.join(settings.STATIC_ROOT, "generated", "emoji", "images", "emoji")
@@ -1350,6 +1358,7 @@ def do_convert(content, message=None, message_realm=None, possible_words=None, s
db_data = {'possible_words': possible_words, db_data = {'possible_words': possible_words,
'full_names': dict((user['full_name'].lower(), user) for user in realm_users), 'full_names': dict((user['full_name'].lower(), user) for user in realm_users),
'short_names': dict((user['short_name'].lower(), user) for user in realm_users), 'short_names': dict((user['short_name'].lower(), user) for user in realm_users),
'by_email': dict((user['email'].lower(), user) for user in realm_users),
'emoji': message_realm.get_emoji(), 'emoji': message_realm.get_emoji(),
'sent_by_bot': sent_by_bot, 'sent_by_bot': sent_by_bot,
'stream_names': dict((stream['name'], stream) for stream in realm_streams)} 'stream_names': dict((stream['name'], stream) for stream in realm_streams)}

View File

@@ -896,3 +896,30 @@ class BugdownErrorTests(ZulipTestCase):
# handle i18n properly here on some systems. # handle i18n properly here on some systems.
with self.assertRaises(JsonableError): with self.assertRaises(JsonableError):
self.send_message("othello@zulip.com", "Denmark", Recipient.STREAM, message) self.send_message("othello@zulip.com", "Denmark", Recipient.STREAM, message)
class BugdownAvatarTestCase(ZulipTestCase):
def test_avatar_with_id(self):
# type: () -> None
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
message = Message(sender=sender_user_profile, sending_client=get_client("test"))
user_profile = get_user_profile_by_email("hamlet@zulip.com")
msg = '!avatar({0})'.format(user_profile.email)
converted = bugdown.convert(msg, message=message)
values = {'email': user_profile.email, 'id': user_profile.id}
self.assertEqual(
converted,
'<p><img alt="{email}" class="message_body_gravatar" src="/avatar/{id}?s=30" title="{email}"></p>'.format(**values))
def test_avatar_of_unregistered_user(self):
# type: () -> None
sender_user_profile = get_user_profile_by_email("othello@zulip.com")
message = Message(sender=sender_user_profile, sending_client=get_client("test"))
email = 'fakeuser@example.com'
msg = '!avatar({0})'.format(email)
converted = bugdown.convert(msg, message=message)
self.assertEqual(
converted,
'<p><img alt="{0}" class="message_body_gravatar" src="/avatar/{0}?s=30" title="{0}"></p>'.format(email))

View File

@@ -381,6 +381,10 @@ class AvatarTest(UploadSerializeMixin, ZulipTestCase):
redirect_url = response['Location'] redirect_url = response['Location']
self.assertTrue(redirect_url.endswith(avatar_url(cordelia) + '&foo=bar')) self.assertTrue(redirect_url.endswith(avatar_url(cordelia) + '&foo=bar'))
response = self.client_get("/avatar/%s?foo=bar" % (cordelia.id))
redirect_url = response['Location']
self.assertTrue(redirect_url.endswith(avatar_url(cordelia) + '&foo=bar'))
def test_non_valid_user_avatar(self): def test_non_valid_user_avatar(self):
# type: () -> None # type: () -> None

View File

@@ -26,7 +26,7 @@ from zerver.lib.validator import check_bool, check_string
from zerver.lib.users import check_change_full_name, check_full_name from zerver.lib.users import check_change_full_name, check_full_name
from zerver.lib.utils import generate_random_token from zerver.lib.utils import generate_random_token
from zerver.models import UserProfile, Stream, Realm, Message, get_user_profile_by_email, \ from zerver.models import UserProfile, Stream, Realm, Message, get_user_profile_by_email, \
email_allowed_for_realm email_allowed_for_realm, get_user_profile_by_id
from zproject.jinja2 import render_to_response from zproject.jinja2 import render_to_response
@@ -112,12 +112,26 @@ def update_user_backend(request, user_profile, email,
return json_success() return json_success()
def avatar(request, email): # TODO: Since eventually we want to support using the same email with
# different organizations, we'll eventually want this to be a
# logged-in endpoint so that we can access the realm_id.
def avatar(request, email_or_id):
# type: (HttpRequest, str) -> HttpResponse # type: (HttpRequest, str) -> HttpResponse
"""Accepts an email address or user ID and returns the avatar"""
try: try:
user_profile = get_user_profile_by_email(email) int(email_or_id)
except ValueError:
get_user_func = get_user_profile_by_email
else:
get_user_func = get_user_profile_by_id
try:
# If there is a valid user account passed in, use its avatar
user_profile = get_user_func(email_or_id)
url = avatar_url(user_profile) url = avatar_url(user_profile)
except UserProfile.DoesNotExist: except UserProfile.DoesNotExist:
# If there is no such user, treat it as a new gravatar
email = email_or_id
avatar_source = 'G' avatar_source = 'G'
avatar_version = 1 avatar_version = 1
url = get_avatar_url(avatar_source, email, avatar_version) url = get_avatar_url(avatar_source, email, avatar_version)

View File

@@ -93,7 +93,7 @@ i18n_urls = [
{'template_name': 'zerver/reset_done.html'}), {'template_name': 'zerver/reset_done.html'}),
# Avatar # Avatar
url(r'^avatar/(?P<email>[\S]+)?', zerver.views.users.avatar, name='zerver.views.users.avatar'), url(r'^avatar/(?P<email_or_id>[\S]+)?', zerver.views.users.avatar, name='zerver.views.users.avatar'),
# Registration views, require a confirmation ID. # Registration views, require a confirmation ID.
url(r'^accounts/home/', zerver.views.registration.accounts_home, url(r'^accounts/home/', zerver.views.registration.accounts_home,