Allow users to upload avatars on the Settings page.

We were using Gravatar for user avatars, but now users can
upload their avatars directly to Zulip, and we will store
their avatar for them.  This removes the old Gravatar-related
interface and polling code.

This commit does not attempt to update the avatars in
messages that have already been loaded, either for the user
making the change or other users.

(imported from commit 301dc48f96f83de0136c93de57055638c79e0961)
This commit is contained in:
Steve Howell
2013-10-28 10:49:38 -04:00
parent af6de2053e
commit 7cf66b5bb8
7 changed files with 152 additions and 60 deletions

View File

@@ -144,6 +144,76 @@ exports.build_widget = function (
};
};
exports.build_user_avatar_widget = function (upload_function) {
var get_file_input = function () {
return $('#user_avatar_file_input').expectOne();
};
return exports.build_direct_upload_widget(
get_file_input,
$("#user_avatar_file_input_error").expectOne(),
$("#user_avatar_upload_button").expectOne(),
upload_function
);
};
exports.build_direct_upload_widget = function (
get_file_input, // function returns a jQuery file input object
input_error, // jQuery object for error text
upload_button, // jQuery button to open file dialog
upload_function
) {
function accept(file) {
input_error.hide();
upload_function(get_file_input());
}
function clear() {
var control = get_file_input();
var new_control = control.clone(true);
control.replaceWith(new_control);
}
upload_button.on('drop', function (e) {
var files = e.dataTransfer.files;
if (files === null || files === undefined || files.length === 0) {
return false;
}
get_file_input().get(0).files = files;
e.preventDefault();
return false;
});
get_file_input().on('change', function (e) {
if (e.target.files.length === 0) {
input_error.hide();
} else if (e.target.files.length === 1) {
var file = e.target.files[0];
if (file.size > 5*1024*1024) {
input_error.text('File size must be < 5Mb.');
input_error.show();
clear();
}
else if (!is_image_format(file)) {
input_error.text('File type is not supported.');
input_error.show();
clear();
} else {
accept(file);
}
}
else {
input_error.text('Please just upload one file.');
}
});
upload_button.on('click', function (e) {
get_file_input().trigger('click');
e.preventDefault();
});
};
return exports;
}());

View File

@@ -21,6 +21,40 @@ function is_local_part(value, element) {
}
$(function () {
var avatar_stamp = 1;
function upload_avatar(file_input) {
var form_data = new FormData();
form_data.append('csrfmiddlewaretoken', csrf_token);
jQuery.each(file_input[0].files, function (i, file) {
form_data.append('file-'+i, file);
});
var spinner = $("#upload_avatar_spinner").expectOne();
util.make_loading_indicator(spinner, {text: 'Uploading avatar.'});
$.ajax({
url: '/json/set_avatar',
type: 'POST',
data: form_data,
cache: false,
processData: false,
contentType: false,
success: function (data) {
util.destroy_loading_indicator($("#upload_avatar_spinner"));
var url = data.avatar_url + '&stamp=' + avatar_stamp;
$(".gravatar-profile").expectOne().css("background-image", "url('" + url + "')");
$("#user-settings-avatar").expectOne().attr("src", url);
avatar_stamp += 1;
}
});
}
avatar.build_user_avatar_widget(upload_avatar);
if (page_params.domain === "users.customer4.invalid") {
// At the request of the facilitators, CUSTOMER4 users
// can't change their names, so don't show that as a settings

View File

@@ -648,40 +648,6 @@ function toggle_star(row_id) {
change_message_star(message, message.starred);
}
function update_gravatars() {
_.each($(".gravatar-profile"), function (profile) {
// Avatar URLs will have at least one param, so & is safe here.
$(profile).attr('src', $(profile).attr('src') + '&stamp=' + gravatar_stamp);
});
gravatar_stamp += 1;
}
function poll_for_gravatar_update(start_time, url) {
// Give users 5 minutes to update their picture on gravatar.com,
// during which we try to auto-update their image on our site. If
// they take longer than that, we'll update when they press the
// save button.
$.ajax({
type: "HEAD",
url: url,
async: false,
cache: false,
success: function (resp, statusText, xhr) {
if (new Date(xhr.getResponseHeader('Last-Modified')) > start_time) {
update_gravatars();
}
else {
if (($.now() - start_time) < 1000 * 60 * 5) {
setTimeout(function () {
poll_for_gravatar_update(start_time, url);
}, 1500);
}
}
}
});
}
exports.small_avatar_url = function (message) {
// Try to call this function in all places where we need 25px
// gravatar images, so that the browser can help
@@ -701,10 +667,6 @@ exports.small_avatar_url = function (message) {
}
};
exports.wait_for_gravatar = function () {
poll_for_gravatar_update($.now(), $("img.gravatar-profile").attr("src"));
};
var loading_more_messages_indicator_showing = false;
exports.show_loading_more_messages_indicator = function () {
if (! loading_more_messages_indicator_showing) {
@@ -1013,7 +975,6 @@ $(function () {
if (result.full_name !== undefined) {
$(".my_fullname").text(result.full_name);
}
update_gravatars();
settings_status.removeClass(status_classes)
.addClass('alert-success')
@@ -1451,9 +1412,6 @@ $(function () {
$("#get_api_key_box").show();
$("#api_key_button_box").hide();
});
$('.change_gravatar_button').click(function (e) {
ui.wait_for_gravatar();
});
var notification_docs = $("#notification-docs");
notification_docs.popover({"placement": "right",

View File

@@ -206,6 +206,13 @@ li,
height: auto;
}
#user-settings-avatar {
width: 100px;
height: 100px;
padding-top: 10px;
border-radius: 15px;
}
.header-main .gravatar-profile {
width: 25px;
height: 25px;
@@ -2675,6 +2682,7 @@ div.edit_bot {
}
.edit_bot_avatar_clear_button,
#user_avatar_clear_button,
#bot_avatar_clear_button {
display: none;
}

View File

@@ -6,12 +6,12 @@
<div class="alert" id="settings-status"></div>
<form action="/json/settings/change" method="post"
class="form-horizontal your-account-settings">{% csrf_token %}
<div id="account-settings" class="settings-section">
<div class="settings-section-title"><i class="icon-vector-user settings-section-icon"></i>Your Account</div>
<div class="account-settings-form">
<form action="/json/settings/change" method="post"
class="form-horizontal your-account-settings">{% csrf_token %}
<div class="control-group" id="name_change_container">
<label for="full_name" class="control-label">Full name</label>
<div class="controls">
@@ -64,18 +64,6 @@
</div>
<div class="form-horizontal">
<div class="control-group">
<label class="control-label">Avatar</label>
<div class="controls">
<a href="https://en.gravatar.com/emails" target="_blank" class="change_gravatar_button">
<p><img class="img-rounded gravatar-profile user-image-settings"
src="{{ avatar_url }}&amp;s=120" /></p>
<p>Change at Gravatar.com</p>
</a>
</div>
</div>
</div>
<div class="control-group">
<div class="controls">
@@ -83,12 +71,29 @@
value="Save changes" class="btn btn-big btn-primary" />
</div>
</div>
</form>
<div id="user_avatar_file_input_error" class="text-error"></div>
<div class="control-group">
<div class="controls">
<div id="user_avatar_file"></div>
<input type="file" name="user_avatar_file_input" class="notdisplayed" id="user_avatar_file_input" value="Upload avatar" />
<button class="btn" id="user_avatar_upload_button">Customize avatar</button>
<p>
<img id="user-settings-avatar" src="{{ avatar_url }}" />
</p>
</div>
</div>
<div id="upload_avatar_spinner"></div>
</div>
</div>
</form>
<div class="alert" id="notify-settings-status"></div>

View File

@@ -2545,6 +2545,22 @@ def patch_bot_backend(request, user_profile, email, full_name=REQ):
)
return json_success(json_result)
@authenticated_json_post_view
def json_set_avatar(request, user_profile):
if len(request.FILES) != 1:
return json_error("You must upload exactly one avatar.")
user_file = request.FILES.values()[0]
upload_avatar_image(user_file, user_profile, user_profile.email)
user_profile.avatar_source = UserProfile.AVATAR_FROM_USER
user_profile.save(update_fields=["avatar_source"])
user_avatar_url = avatar_url(user_profile)
json_result = dict(
avatar_url = user_avatar_url
)
return json_success(json_result)
@has_request_variables
def regenerate_api_key(request, user_profile):
user_profile.api_key = random_api_key()

View File

@@ -126,6 +126,7 @@ urlpatterns += patterns('zerver.views',
url(r'^json/refer_friend$', 'json_refer_friend'),
url(r'^json/set_alert_words$', 'json_set_alert_words'),
url(r'^json/set_muted_topics$', 'json_set_muted_topics'),
url(r'^json/set_avatar$', 'json_set_avatar'),
# These are json format views used by the API. They require an API key.
url(r'^api/v1/get_profile$', 'api_get_profile'),