popovers.js: Add version to user avatar request.

When a user changes its avatar image, the user's avatar in popovers
wasn't being correctly updated, because of browser caching of the
avatar image.  We added a version on the request to get the image in
the same format we use elsewhere, so the browser knows when to use the
cached image or to make a new request to the server.

Edited by Tim to preserve/fix sort orders in some tests, and update
zulip_feature_level.

Fixes: #14290
This commit is contained in:
clarammdantas 2020-04-07 15:09:30 -03:00 committed by Tim Abbott
parent 9137726e38
commit 7e9024a39c
10 changed files with 38 additions and 12 deletions

View File

@ -50,6 +50,7 @@ const alice = {
email: 'alice@example.com', email: 'alice@example.com',
full_name: 'Alice Smith', full_name: 'Alice Smith',
user_id: 42, user_id: 42,
avatar_version: 5,
is_guest: false, is_guest: false,
is_admin: false, is_admin: false,
}; };
@ -184,13 +185,16 @@ run_test('sender_hover', () => {
}); });
$('.user_popover_email').each = noop; $('.user_popover_email').each = noop;
const image_stubber = make_image_stubber(); const image_stubber = make_image_stubber();
window.location = {
href: 'http://chat.zulip.org/',
};
const base_url = window.location.href;
handler.call(target, e); handler.call(target, e);
const avatar_img = image_stubber.get(0); const avatar_img = image_stubber.get(0);
assert.equal(avatar_img.src, 'avatar/42/medium'); const expected_url = new URL('avatar/42/medium?v=' + alice.avatar_version, base_url);
assert.equal(avatar_img.src.toString(), expected_url.toString());
// todo: load image // todo: load image
}); });

View File

@ -94,8 +94,10 @@ function init_email_clipboard() {
} }
function load_medium_avatar(user, elt) { function load_medium_avatar(user, elt) {
const user_avatar_url = "avatar/" + user.user_id + "/medium"; const avatar_path = "avatar/" + user.user_id + "/medium?v=" + user.avatar_version;
const user_avatar_url = new URL(avatar_path, window.location.href);
const sender_avatar_medium = new Image(); const sender_avatar_medium = new Image();
sender_avatar_medium.src = user_avatar_url; sender_avatar_medium.src = user_avatar_url;
$(sender_avatar_medium).on("load", function () { $(sender_avatar_medium).on("load", function () {
elt.css("background-image", "url(" + $(this).attr("src") + ")"); elt.css("background-image", "url(" + $(this).attr("src") + ")");

View File

@ -69,6 +69,7 @@ exports.update_person = function update(person) {
if (Object.prototype.hasOwnProperty.call(person, 'avatar_url')) { if (Object.prototype.hasOwnProperty.call(person, 'avatar_url')) {
const url = person.avatar_url; const url = person.avatar_url;
person_obj.avatar_url = url; person_obj.avatar_url = url;
person_obj.avatar_version = person.avatar_version;
if (people.is_my_user_id(person.user_id)) { if (people.is_my_user_id(person.user_id)) {
page_params.avatar_source = person.avatar_source; page_params.avatar_source = person.avatar_source;

View File

@ -10,6 +10,15 @@ below features are supported.
## Changes in Zulip 2.2 ## Changes in Zulip 2.2
**Feature level 6**
* [`GET /events`](/api/get-events-from-queue): `realm_user` events to
update a user's avatar now include the `avatar_version` field, which
is important for correctly refetching medium-size avatar images when
the user's avatar changes.
* [`GET /users`](/api/get-all-users) and [`GET
/users/{user_id}`](/api/get-user): User objects now contain the
`avatar_version` field as well.
**Feature level 5** **Feature level 5**
* [`GET /events`](/api/get-events-from-queue): `realm_bot` events, * [`GET /events`](/api/get-events-from-queue): `realm_bot` events,
sent when changes are made to bot users, now contain an sent when changes are made to bot users, now contain an

View File

@ -29,7 +29,7 @@ DESKTOP_WARNING_VERSION = "5.2.0"
# #
# Changes should be accompanied by documentation explaining what the # Changes should be accompanied by documentation explaining what the
# new level means in templates/zerver/api/changelog.md. # new level means in templates/zerver/api/changelog.md.
API_FEATURE_LEVEL = 5 API_FEATURE_LEVEL = 6
# Bump the minor PROVISION_VERSION to indicate that folks should provision # Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump # only when going from an old version of the code to a newer version. Bump

View File

@ -3263,10 +3263,11 @@ def notify_avatar_url_change(user_profile: UserProfile) -> None:
bot_owner_user_ids(user_profile)) bot_owner_user_ids(user_profile))
payload = dict( payload = dict(
email=user_profile.email,
avatar_source=user_profile.avatar_source, avatar_source=user_profile.avatar_source,
avatar_url=avatar_url(user_profile), avatar_url=avatar_url(user_profile),
avatar_url_medium=avatar_url(user_profile, medium=True), avatar_url_medium=avatar_url(user_profile, medium=True),
avatar_version=user_profile.avatar_version,
email=user_profile.email,
user_id=user_profile.id user_id=user_profile.id
) )

View File

@ -315,6 +315,7 @@ def format_user_row(realm: Realm, acting_user: UserProfile, row: Dict[str, Any],
email=row['email'], email=row['email'],
user_id=row['id'], user_id=row['id'],
avatar_url=avatar_url, avatar_url=avatar_url,
avatar_version=row['avatar_version'],
is_admin=is_admin, is_admin=is_admin,
is_guest=is_guest, is_guest=is_guest,
is_bot=is_bot, is_bot=is_bot,

View File

@ -672,14 +672,14 @@ class ListCustomProfileFieldTest(CustomProfileFieldTestCase):
expected_keys_for_iago = { expected_keys_for_iago = {
"delivery_email", "delivery_email",
"email", "user_id", "avatar_url", "is_admin", "is_guest", "is_bot", "email", "user_id", "avatar_url", "avatar_version", "is_admin", "is_guest", "is_bot",
"full_name", "timezone", "is_active", "date_joined", "profile_data"} "full_name", "timezone", "is_active", "date_joined", "profile_data"}
self.assertEqual(set(iago_raw_data.keys()), expected_keys_for_iago) self.assertEqual(set(iago_raw_data.keys()), expected_keys_for_iago)
self.assertNotEqual(iago_raw_data["profile_data"], {}) self.assertNotEqual(iago_raw_data["profile_data"], {})
expected_keys_for_test_bot = { expected_keys_for_test_bot = {
"delivery_email", "delivery_email",
"email", "user_id", "avatar_url", "is_admin", "is_guest", "is_bot", "full_name", "email", "user_id", "avatar_url", "avatar_version", "is_admin", "is_guest", "is_bot", "full_name",
"timezone", "is_active", "date_joined", "bot_type", "bot_owner_id"} "timezone", "is_active", "date_joined", "bot_type", "bot_owner_id"}
self.assertEqual(set(test_bot_raw_data.keys()), expected_keys_for_test_bot) self.assertEqual(set(test_bot_raw_data.keys()), expected_keys_for_test_bot)
self.assertEqual(test_bot_raw_data["bot_type"], 1) self.assertEqual(test_bot_raw_data["bot_type"], 1)

View File

@ -1247,6 +1247,7 @@ class EventsRegisterTest(ZulipTestCase):
('user_id', check_int), ('user_id', check_int),
('email', check_string), ('email', check_string),
('avatar_url', check_none_or(check_string)), ('avatar_url', check_none_or(check_string)),
('avatar_version', check_int),
('full_name', check_string), ('full_name', check_string),
('is_admin', check_bool), ('is_admin', check_bool),
('is_bot', check_bool), ('is_bot', check_bool),
@ -1273,6 +1274,7 @@ class EventsRegisterTest(ZulipTestCase):
('user_id', check_int), ('user_id', check_int),
('email', check_string), ('email', check_string),
('avatar_url', check_none_or(check_string)), ('avatar_url', check_none_or(check_string)),
('avatar_version', check_int),
('full_name', check_string), ('full_name', check_string),
('is_active', check_bool), ('is_active', check_bool),
('is_admin', check_bool), ('is_admin', check_bool),
@ -1534,6 +1536,7 @@ class EventsRegisterTest(ZulipTestCase):
('user_id', check_int), ('user_id', check_int),
('avatar_url', check_string), ('avatar_url', check_string),
('avatar_url_medium', check_string), ('avatar_url_medium', check_string),
('avatar_version', check_int),
('avatar_source', check_string), ('avatar_source', check_string),
])), ])),
]) ])
@ -1547,11 +1550,12 @@ class EventsRegisterTest(ZulipTestCase):
('type', equals('realm_user')), ('type', equals('realm_user')),
('op', equals('update')), ('op', equals('update')),
('person', check_dict_only([ ('person', check_dict_only([
('email', check_string), ('avatar_source', check_string),
('user_id', check_int),
('avatar_url', check_none_or(check_string)), ('avatar_url', check_none_or(check_string)),
('avatar_url_medium', check_none_or(check_string)), ('avatar_url_medium', check_none_or(check_string)),
('avatar_source', check_string), ('avatar_version', check_int),
('email', check_string),
('user_id', check_int),
])), ])),
]) ])
events = self.do_test( events = self.do_test(
@ -1589,9 +1593,10 @@ class EventsRegisterTest(ZulipTestCase):
('person', check_dict_only([ ('person', check_dict_only([
('email', check_string), ('email', check_string),
('user_id', check_int), ('user_id', check_int),
('avatar_source', check_string),
('avatar_url', check_string), ('avatar_url', check_string),
('avatar_url_medium', check_string), ('avatar_url_medium', check_string),
('avatar_source', check_string), ('avatar_version', check_int),
])), ])),
]) ])
do_set_realm_property(self.user_profile.realm, "email_address_visibility", do_set_realm_property(self.user_profile.realm, "email_address_visibility",

View File

@ -604,6 +604,7 @@ class HomeTest(ZulipTestCase):
self.assertEqual(sorted(cross_bots, key=by_email), sorted([ self.assertEqual(sorted(cross_bots, key=by_email), sorted([
dict( dict(
avatar_version=email_gateway_bot.avatar_version,
bot_owner_id=None, bot_owner_id=None,
bot_type=1, bot_type=1,
email=email_gateway_bot.email, email=email_gateway_bot.email,
@ -616,6 +617,7 @@ class HomeTest(ZulipTestCase):
is_guest=False is_guest=False
), ),
dict( dict(
avatar_version=email_gateway_bot.avatar_version,
bot_owner_id=None, bot_owner_id=None,
bot_type=1, bot_type=1,
email=notification_bot.email, email=notification_bot.email,
@ -628,6 +630,7 @@ class HomeTest(ZulipTestCase):
is_guest=False is_guest=False
), ),
dict( dict(
avatar_version=email_gateway_bot.avatar_version,
bot_owner_id=None, bot_owner_id=None,
bot_type=1, bot_type=1,
email=welcome_bot.email, email=welcome_bot.email,