mirror of
https://github.com/discourse/discourse.git
synced 2024-11-24 18:29:54 +08:00
a811976023
Users noticed that sometimes, avatar from Gravatar is not correctly updated - https://meta.discourse.org/t/updated-image-on-gravatar-not-seeing-it-update-on-site/54357 A potential reason for that is that even if you update your avatar in Gravatar, URL stays the same and if the cache is involved, service is still receiving the old photo. For example. In my case, when I click the button to refresh avatar the new Upload record is created with `origin` URL to new avatar, and `url` to old one I made some tests in the rails console and adding random param to Gravatar URL is deceiving cache and correct, the newest avatar is downloaded
169 lines
5.1 KiB
Ruby
169 lines
5.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
class UserAvatar < ActiveRecord::Base
|
|
belongs_to :user
|
|
belongs_to :gravatar_upload, class_name: 'Upload'
|
|
belongs_to :custom_upload, class_name: 'Upload'
|
|
|
|
def contains_upload?(id)
|
|
gravatar_upload_id == id || custom_upload_id == id
|
|
end
|
|
|
|
def update_gravatar!
|
|
DistributedMutex.synchronize("update_gravatar_#{user_id}") do
|
|
begin
|
|
self.update!(last_gravatar_download_attempt: Time.now)
|
|
|
|
max = Discourse.avatar_sizes.max
|
|
|
|
# The user could be deleted before this executes
|
|
return if user.blank? || user.primary_email.blank?
|
|
|
|
email_hash = user_id == Discourse::SYSTEM_USER_ID ? User.email_hash("info@discourse.org") : user.email_hash
|
|
gravatar_url = "https://#{SiteSetting.gravatar_base_url}/avatar/#{email_hash}.png?s=#{max}&d=404&reset_cache=#{SecureRandom.urlsafe_base64(5)}"
|
|
|
|
# follow redirects in case gravatar change rules on us
|
|
tempfile = FileHelper.download(
|
|
gravatar_url,
|
|
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
|
|
tmp_file_name: "gravatar",
|
|
skip_rate_limit: true,
|
|
verbose: false,
|
|
follow_redirect: true
|
|
)
|
|
|
|
if tempfile
|
|
ext = File.extname(tempfile)
|
|
ext = '.png' if ext.blank?
|
|
|
|
upload = UploadCreator.new(
|
|
tempfile,
|
|
"gravatar#{ext}",
|
|
origin: gravatar_url,
|
|
type: "avatar",
|
|
for_gravatar: true
|
|
).create_for(user_id)
|
|
|
|
if gravatar_upload_id != upload.id
|
|
User.transaction do
|
|
if gravatar_upload_id && user.uploaded_avatar_id == gravatar_upload_id
|
|
user.update!(uploaded_avatar_id: upload.id)
|
|
end
|
|
|
|
self.update!(gravatar_upload: upload)
|
|
end
|
|
end
|
|
end
|
|
rescue OpenURI::HTTPError => e
|
|
if e.io&.status[0].to_i != 404
|
|
raise e
|
|
end
|
|
ensure
|
|
tempfile&.close!
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.local_avatar_url(hostname, username, upload_id, size)
|
|
self.local_avatar_template(hostname, username, upload_id).gsub("{size}", size.to_s)
|
|
end
|
|
|
|
def self.local_avatar_template(hostname, username, upload_id)
|
|
version = self.version(upload_id)
|
|
"#{Discourse.base_uri}/user_avatar/#{hostname}/#{username}/{size}/#{version}.png"
|
|
end
|
|
|
|
def self.external_avatar_url(user_id, upload_id, size)
|
|
self.external_avatar_template(user_id, upload_id).gsub("{size}", size.to_s)
|
|
end
|
|
|
|
def self.external_avatar_template(user_id, upload_id)
|
|
version = self.version(upload_id)
|
|
"#{Discourse.store.absolute_base_url}/avatars/#{user_id}/{size}/#{version}.png"
|
|
end
|
|
|
|
def self.version(upload_id)
|
|
"#{upload_id}_#{OptimizedImage::VERSION}"
|
|
end
|
|
|
|
def self.import_url_for_user(avatar_url, user, options = nil)
|
|
tempfile = FileHelper.download(
|
|
avatar_url,
|
|
max_file_size: SiteSetting.max_image_size_kb.kilobytes,
|
|
tmp_file_name: "sso-avatar",
|
|
follow_redirect: true
|
|
)
|
|
|
|
return unless tempfile
|
|
|
|
ext = FastImage.type(tempfile).to_s
|
|
tempfile.rewind
|
|
|
|
upload = UploadCreator.new(tempfile, "external-avatar." + ext, origin: avatar_url, type: "avatar").create_for(user.id)
|
|
|
|
user.create_user_avatar! unless user.user_avatar
|
|
|
|
if !user.user_avatar.contains_upload?(upload.id)
|
|
user.user_avatar.update!(custom_upload_id: upload.id)
|
|
override_gravatar = !options || options[:override_gravatar]
|
|
|
|
if user.uploaded_avatar_id.nil? ||
|
|
!user.user_avatar.contains_upload?(user.uploaded_avatar_id) ||
|
|
override_gravatar
|
|
|
|
user.update!(uploaded_avatar_id: upload.id)
|
|
end
|
|
end
|
|
|
|
rescue Net::ReadTimeout, OpenURI::HTTPError
|
|
# skip saving, we are not connected to the net
|
|
ensure
|
|
tempfile.close! if tempfile && tempfile.respond_to?(:close!)
|
|
end
|
|
|
|
def self.ensure_consistency!
|
|
DB.exec <<~SQL
|
|
UPDATE user_avatars
|
|
SET gravatar_upload_id = NULL
|
|
WHERE gravatar_upload_id IN (
|
|
SELECT u1.gravatar_upload_id FROM user_avatars u1
|
|
LEFT JOIN uploads up
|
|
ON u1.gravatar_upload_id = up.id
|
|
WHERE u1.gravatar_upload_id IS NOT NULL AND
|
|
up.id IS NULL
|
|
)
|
|
SQL
|
|
|
|
DB.exec <<~SQL
|
|
UPDATE user_avatars
|
|
SET custom_upload_id = NULL
|
|
WHERE custom_upload_id IN (
|
|
SELECT u1.custom_upload_id FROM user_avatars u1
|
|
LEFT JOIN uploads up
|
|
ON u1.custom_upload_id = up.id
|
|
WHERE u1.custom_upload_id IS NOT NULL AND
|
|
up.id IS NULL
|
|
)
|
|
SQL
|
|
end
|
|
end
|
|
|
|
# == Schema Information
|
|
#
|
|
# Table name: user_avatars
|
|
#
|
|
# id :integer not null, primary key
|
|
# user_id :integer not null
|
|
# custom_upload_id :integer
|
|
# gravatar_upload_id :integer
|
|
# last_gravatar_download_attempt :datetime
|
|
# created_at :datetime not null
|
|
# updated_at :datetime not null
|
|
#
|
|
# Indexes
|
|
#
|
|
# index_user_avatars_on_custom_upload_id (custom_upload_id)
|
|
# index_user_avatars_on_gravatar_upload_id (gravatar_upload_id)
|
|
# index_user_avatars_on_user_id (user_id)
|
|
#
|