Skip to content
This repository was archived by the owner on Mar 14, 2025. It is now read-only.

Commit 51e5285

Browse files
authored
Merge pull request #158 from loftwah/dl/user-model-fixes
Dl/awful-changes-too-many
2 parents e9a1239 + 61a10ba commit 51e5285

21 files changed

+429
-297
lines changed

app/controllers/analytics_controller.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ def set_user
2626
end
2727

2828
def check_analytics_visibility
29-
unless @user == current_user || @user.public_analytics?
29+
unless current_user == @user || @user.public_analytics?
3030
flash[:alert] = "This user's analytics are not public."
31-
redirect_to root_path
31+
redirect_to root_path and return
3232
end
3333
end
3434

app/controllers/users/registrations_controller.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ def update
4242
@user = current_user
4343
@user.tags = JSON.parse(@user.tags) if @user.tags.is_a?(String)
4444

45-
is_password_change = params[:user][:password].present? || params[:user][:password_confirmation].present?
46-
is_email_change = params[:user][:email].present? && params[:user][:email] != @user.email
47-
48-
if is_password_change || is_email_change
45+
# Check if the user is trying to change their password
46+
if params[:user][:password].present? || params[:user][:password_confirmation].present?
47+
# If password change is requested, use Devise's `update_with_password`
4948
successfully_updated = @user.update_with_password(account_update_params)
5049
else
50+
# If password change is not requested, remove the current_password requirement
5151
params[:user].delete(:current_password)
52-
successfully_updated = @user.update_without_password(account_update_params)
52+
successfully_updated = @user.update(account_update_params)
5353
end
5454

5555
if successfully_updated
@@ -75,7 +75,7 @@ def valid_invite_code?(invite_code)
7575

7676
# Check if the provided invite code matches any of the valid codes, case-insensitive
7777
valid_codes.any? { |code| code.casecmp(invite_code).zero? }
78-
end
78+
end
7979

8080
protected
8181

app/helpers/open_graph_helper.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,22 @@ module OpenGraphHelper
22
include Rails.application.routes.url_helpers
33

44
def set_open_graph_tags(user)
5-
twitter_handle = user.username.downcase
5+
# Fallback values for Open Graph
6+
default_title = 'Linkarooie - Simplify Your Online Presence'
7+
default_description = 'Manage all your links in one place with Linkarooie. Create a central hub for your social and professional profiles.'
8+
default_image = image_url('default_og_image.png')
9+
default_image_alt = 'Linkarooie logo'
10+
default_url = root_url
11+
twitter_handle = user.username&.downcase || '@loftwah'
612

7-
# Open Graph tags
8-
content_for :og_title, user.full_name
9-
content_for :og_description, user.description.truncate(160)
10-
content_for :og_image, url_for("/uploads/og_images/#{user.username}_og.png")
11-
content_for :og_image_alt, "#{user.full_name}'s profile image"
12-
content_for :og_url, user_links_url(user.username)
13+
# Open Graph tags with fallback values
14+
content_for :og_title, user.full_name || default_title
15+
content_for :og_description, (user.description || default_description).truncate(160)
16+
content_for :og_image, user.username.present? ? url_for("/uploads/og_images/#{user.username}_og.png") : default_image
17+
content_for :og_image_alt, user.full_name.present? ? "#{user.full_name}'s profile image" : default_image_alt
18+
content_for :og_url, user_links_url(user.username || default_url)
1319

14-
# Twitter Card tags
20+
# Twitter Card tags with fallback values
1521
content_for :twitter_card, 'summary_large_image'
1622
content_for :twitter_site, "@#{twitter_handle}"
1723
content_for :twitter_creator, "@#{twitter_handle}"

app/models/user.rb

Lines changed: 88 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class User < ApplicationRecord
2-
3-
FALLBACK_AVATAR_URL = 'https://pbs.twimg.com/profile_images/1581014308397502464/NPogKMyk_400x400.jpg'
2+
FALLBACK_AVATAR_URL = '/avatars/default_avatar.jpg'
3+
FALLBACK_BANNER_URL = '/banners/default_banner.jpg'
44

55
attr_accessor :invite_code
66
devise :database_authenticatable, :registerable,
@@ -16,15 +16,16 @@ class User < ApplicationRecord
1616
VALID_USERNAME_REGEX = /\A[a-zA-Z0-9_]+\z/
1717

1818
validates :username, presence: true, uniqueness: true, format: { with: VALID_USERNAME_REGEX, message: 'can only contain letters, numbers, and underscores' }
19-
20-
validates :username, uniqueness: true, allow_blank: true
2119
validates :full_name, presence: true
22-
validate :ensure_username_presence
2320
validates :avatar_border, inclusion: { in: ['white', 'black', 'none', 'rainbow', 'rainbow-overlay'] }
2421
validates :avatar, format: { with: /\A(https?:\/\/).*\z/i, message: "must be a valid URL" }, allow_blank: true
22+
validates :banner, format: { with: /\A(https?:\/\/).*\z/i, message: "must be a valid URL" }, allow_blank: true
2523

26-
after_save :generate_open_graph_image, unless: -> { Rails.env.test? }
27-
after_save :download_and_store_avatar
24+
before_validation :ensure_username_presence
25+
before_create :set_default_images
26+
after_create :generate_open_graph_image, unless: -> { Rails.env.test? }
27+
after_save :download_and_store_avatar, if: -> { saved_change_to_avatar? && avatar.present? }
28+
after_save :download_and_store_banner, if: -> { saved_change_to_banner? && banner.present? }
2829

2930
serialize :tags, coder: JSON
3031

@@ -36,7 +37,7 @@ def parsed_tags
3637
[]
3738
end
3839
else
39-
tags
40+
tags || []
4041
end
4142
end
4243

@@ -45,43 +46,93 @@ def generate_open_graph_image
4546
end
4647

4748
def download_and_store_avatar
48-
if avatar.blank?
49-
self.avatar = FALLBACK_AVATAR_URL
50-
save(validate: false)
49+
download_and_store_image(:avatar, FALLBACK_AVATAR_URL)
50+
end
51+
52+
def download_and_store_banner
53+
download_and_store_image(:banner, FALLBACK_BANNER_URL)
54+
end
55+
56+
def avatar_url
57+
avatar_local_path.present? ? "/#{avatar_local_path}" : (avatar.presence || FALLBACK_AVATAR_URL)
58+
end
59+
60+
def banner_url
61+
banner_local_path.present? ? "/#{banner_local_path}" : (banner.presence || FALLBACK_BANNER_URL)
62+
end
63+
64+
def valid_url?(url)
65+
uri = URI.parse(url)
66+
uri.is_a?(URI::HTTP) && !uri.host.nil?
67+
rescue URI::InvalidURIError
68+
false
69+
end
70+
71+
private
72+
73+
def ensure_username_presence
74+
if username.blank?
75+
self.username = email.present? ? email.split('@').first : "user#{SecureRandom.hex(4)}"
76+
end
77+
end
78+
79+
def set_default_images
80+
self.avatar ||= FALLBACK_AVATAR_URL
81+
self.banner ||= FALLBACK_BANNER_URL
82+
end
83+
84+
def download_and_store_image(type, fallback_url)
85+
url = send(type)
86+
87+
Rails.logger.info "Downloading #{type} from #{url}"
88+
89+
if url.blank? || !valid_url?(url)
90+
Rails.logger.warn "#{type.capitalize} URL invalid or blank. Using fallback."
91+
update_column("#{type}_local_path", fallback_url)
5192
return
5293
end
5394

5495
begin
55-
avatar_dir = Rails.root.join('public', 'avatars')
56-
FileUtils.mkdir_p(avatar_dir) unless File.directory?(avatar_dir)
96+
uri = URI.parse(url)
97+
Rails.logger.info "Attempting to download #{type} from #{uri}"
98+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
99+
request = Net::HTTP::Get.new(uri)
100+
response = http.request(request)
101+
102+
if response.is_a?(Net::HTTPSuccess)
103+
content_type = response['Content-Type']
104+
Rails.logger.info "Downloaded #{type}, content type: #{content_type}"
105+
106+
unless content_type.start_with?('image/')
107+
raise "Invalid content type: #{content_type}"
108+
end
109+
110+
extension = case content_type
111+
when 'image/jpeg' then '.jpg'
112+
when 'image/png' then '.png'
113+
when 'image/gif' then '.gif'
114+
else ''
115+
end
116+
117+
image_dir = Rails.root.join('public', type.to_s.pluralize)
118+
FileUtils.mkdir_p(image_dir) unless File.directory?(image_dir)
119+
120+
filename = "#{username}_#{type}#{extension}"
121+
filepath = File.join(image_dir, filename)
57122

58-
uri = URI.parse(avatar)
59-
filename = "#{username}_avatar#{File.extname(avatar)}"
60-
filepath = File.join(avatar_dir, filename)
123+
File.open(filepath, 'wb') { |file| file.write(response.body) }
61124

62-
response = Net::HTTP.get_response(uri)
63-
if response.is_a?(Net::HTTPSuccess)
64-
File.open(filepath, 'wb') do |local_file|
65-
local_file.write(response.body)
125+
update_column("#{type}_local_path", "#{type.to_s.pluralize}/#{filename}")
126+
Rails.logger.info "#{type.capitalize} successfully downloaded for user #{username}"
127+
128+
else
129+
Rails.logger.warn "Failed to download #{type} for user #{username}: HTTP Error: #{response.code} #{response.message}. Using local fallback."
130+
update_column(type, fallback_url)
66131
end
67-
Rails.logger.info "Avatar downloaded for user #{username}"
68-
else
69-
Rails.logger.error "Failed to download avatar for user #{username}. HTTP Error: #{response.code} #{response.message}. Using fallback avatar."
70-
self.avatar = FALLBACK_AVATAR_URL
71-
save(validate: false)
72132
end
73133
rescue StandardError => e
74-
Rails.logger.error "Failed to download avatar for user #{username}: #{e.message}. Using fallback avatar."
75-
self.avatar = FALLBACK_AVATAR_URL
76-
save(validate: false)
134+
Rails.logger.error "Failed to download #{type} for user #{username}: #{e.message}. Using fallback."
135+
update_column(type, fallback_url)
77136
end
78137
end
79-
80-
private
81-
82-
def ensure_username_presence
83-
if username.blank?
84-
self.username = email.present? ? email.split('@').first : "user#{SecureRandom.hex(4)}"
85-
end
86-
end
87-
end
138+
end

0 commit comments

Comments
 (0)