mirror of
https://github.com/discourse/discourse.git
synced 2024-11-26 10:50:26 -06:00
1062dbc3e9
Previously the image was imported from a Discourse hosted CDN but the URL has since become invalid. However, it was not caught since all errors are rescued. This commit fixes the issue by shipping the user avatar with the plugin.
179 lines
5.3 KiB
Ruby
179 lines
5.3 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
|
|
|
|
create_custom_avatar(
|
|
user,
|
|
tempfile,
|
|
override_gravatar: options&.dig(:override_gravatar),
|
|
origin: avatar_url
|
|
)
|
|
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.create_custom_avatar(user, file, override_gravatar: false, origin: nil)
|
|
ext = FastImage.type(file).to_s
|
|
file.rewind
|
|
|
|
upload = UploadCreator.new(file, "external-avatar.#{ext}",
|
|
origin: origin,
|
|
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)
|
|
|
|
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
|
|
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)
|
|
#
|