Skip to content

Commit

Permalink
Merge pull request #275 from bullet-train-co/features/photos-with-act…
Browse files Browse the repository at this point in the history
…ive-storage

Upload profile photo with ActiveStorage when Cloudinary isn't enabled
  • Loading branch information
jagthedrummer authored Aug 9, 2023
2 parents f8b2578 + 166b39e commit ede9b6b
Show file tree
Hide file tree
Showing 10 changed files with 98 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ def user_params
:last_name,
:time_zone,
:locale,
:profile_photo_id
:profile_photo_id, # For Cloudinary
:profile_photo, # For ActiveStorage
:profile_photo_removal
]

selected_fields = if params.is_a?(BulletTrain::Api::StrongParametersReporter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ other_options ||= {}
<% end %>
<% end %>
<% if form.object.send(method).attached? %>
<div class="button-alternative cursor-pointer mr-3" data-action="click->fields--file-field#removeFile" data-fields--file-field-target="removeFileButton">
<% if form.object.send(method).representable? %>
<div class="mt-2">
<%= image_tag(form.object.send(method).representation(resize_to_limit: [200, 200])) %>
</div>
<% end %>
<div class="mt-2 button-alternative cursor-pointer mr-3" data-action="click->fields--file-field#removeFile" data-fields--file-field-target="removeFileButton">
<i class="leading-none mr-2 text-base ti ti-trash"></i>
<span><%= t('fields.remove_document') %></span>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,8 @@ def membership_params
strong_params = params.require(:membership).permit(
:user_first_name,
:user_last_name,
:user_profile_photo_id,
:user_profile_photo_id, # For Cloudinary
:user_profile_photo, # For ActiveStorage
*permitted_fields,
*permitted_arrays,
)
Expand Down
67 changes: 43 additions & 24 deletions bullet_train/app/helpers/account/users_helper.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
module Account::UsersHelper
def profile_photo_for(url: nil, email: nil, first_name: nil, last_name: nil)
def profile_photo_for(url: nil, email: nil, first_name: nil, last_name: nil, profile_header: false)
size_details = profile_header ? {width: 700, height: 200} : {width: 100, height: 100}
size_details[:crop] = :fill

if cloudinary_enabled? && !url.blank?
cl_image_path(url, width: 100, height: 100, crop: :fill)
cl_image_path(url, size_details[:width], size_details[:height], size_details[:crop])
elsif !url.blank?
url + "?" + size_details.to_param
else
background_color = Colorizer.colorize_similarly(email.to_s, 0.5, 0.6).delete("#")
"https://ui-avatars.com/api/?" + {
color: "ffffff",
background: background_color,
bold: true,
# email.to_s should not be necessary once we fix the edge case of cancelling an unclaimed membership
name: [first_name, last_name].join(" ").strip.presence || email,
size: 200,
}.to_param
ui_avatar_params(email, first_name, last_name)
end
end

def user_profile_photo_url(user)
profile_photo_for(
url: user.profile_photo_id,
url: get_photo_url_from(user),
email: user.email,
first_name: user.first_name,
last_name: user.last_name
Expand All @@ -29,33 +26,29 @@ def membership_profile_photo_url(membership)
user_profile_photo_url(membership.user)
else
profile_photo_for(
url: membership.user_profile_photo_id,
url: get_photo_url_from(membership),
email: membership.invitation&.email || membership.user_email,
first_name: membership.user_first_name,
last_name: membership.user_last_name
)
end
end

# TODO: We can do away with these three `profile_header` methods, I'm just
# leaving them in case we have other developers depending on these methods.
def profile_header_photo_for(url: nil, email: nil, first_name: nil, last_name: nil)
if cloudinary_enabled? && !url.blank?
cl_image_path(url, width: 700, height: 200, crop: :fill)
elsif !url.blank?
url + "?" + {size: 200}.to_param
else
background_color = Colorizer.colorize_similarly(email.to_s, 0.5, 0.6).delete("#")
"https://ui-avatars.com/api/?" + {
color: "ffffff",
background: background_color,
bold: true,
# email.to_s should not be necessary once we fix the edge case of cancelling an unclaimed membership
name: "#{first_name&.first || email.to_s[0]} #{last_name&.first || email.to_s[1]}",
size: 200,
}.to_param
ui_avatar_params(email, first_name, last_name)
end
end

def user_profile_header_photo_url(user)
profile_header_photo_for(
url: user.profile_photo_id,
url: get_photo_url_from(user),
email: user.email,
first_name: user.first_name,
last_name: user.last_name
Expand All @@ -67,14 +60,40 @@ def membership_profile_header_photo_url(membership)
user_profile_header_photo_url(membership.user)
else
profile_header_photo_for(
url: membership.user_profile_photo_id,
url: get_photo_url_from(membership),
email: membership.invitation&.email || membership.user_email,
first_name: membership.user_first_name,
last_name: membership.user&.last_name || membership.user_last_name
)
end
end

def get_photo_url_from(resource)
photo_method = if resource.is_a?(User)
:profile_photo
elsif resource.is_a?(Membership)
:user_profile_photo
end

if cloudinary_enabled?
resource.send("#{photo_method}_id".to_sym)
elsif resource.send(photo_method).attached?
url_for(resource.send(photo_method))
end
end

def ui_avatar_params(email, first_name, last_name)
background_color = Colorizer.colorize_similarly(email.to_s, 0.5, 0.6).delete("#")
"https://ui-avatars.com/api/?" + {
color: "ffffff",
background: background_color,
bold: true,
# email.to_s should not be necessary once we fix the edge case of cancelling an unclaimed membership
name: "#{first_name&.first || email.to_s[0]} #{last_name&.first || email.to_s[1]}",
size: 200,
}.to_param
end

def current_membership
current_user.memberships.where(team: current_team).first
end
Expand Down
15 changes: 15 additions & 0 deletions bullet_train/app/models/concerns/memberships/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Memberships::Base
extend ActiveSupport::Concern

included do
attr_accessor :user_profile_photo_removal

# See `docs/permissions.md` for details.
include Roles::Support

Expand All @@ -16,6 +18,9 @@ module Memberships::Base

has_many :scaffolding_absolutely_abstract_creative_concepts_collaborators, class_name: "Scaffolding::AbsolutelyAbstract::CreativeConcepts::Collaborator", dependent: :destroy

# Image uploading
has_one_attached :user_profile_photo

after_destroy do
# if we're destroying a user's membership to the team they have set as
# current, then we need to remove that so they don't get an error.
Expand All @@ -25,6 +30,8 @@ module Memberships::Base
end
end

after_validation :remove_user_profile_photo, if: :user_profile_photo_removal?

scope :excluding_platform_agents, -> { where(platform_agent_of: nil) }
scope :platform_agents, -> { where.not(platform_agent_of: nil) }
scope :current_and_invited, -> { includes(:invitation).where("user_id IS NOT NULL OR invitations.id IS NOT NULL").references(:invitation) }
Expand Down Expand Up @@ -141,5 +148,13 @@ def should_receive_notifications?
invitation.present? || user.present?
end

def user_profile_photo_removal?
user_profile_photo_removal.present?
end

def remove_user_profile_photo
user_profile_photo.purge
end

ActiveSupport.run_load_hooks :bullet_train_memberships_base, self
end
14 changes: 14 additions & 0 deletions bullet_train/app/models/concerns/users/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module Users::Base
extend ActiveSupport::Concern

included do
attr_accessor :profile_photo_removal

if two_factor_authentication_enabled?
devise :two_factor_authenticatable, :two_factor_backupable
else
Expand All @@ -27,6 +29,9 @@ module Users::Base
# oauth providers
has_many :oauth_stripe_accounts, class_name: "Oauth::StripeAccount" if stripe_enabled?

# Image uploading
has_one_attached :profile_photo

# platform functionality.
belongs_to :platform_agent_of, class_name: "Platform::Application", optional: true

Expand All @@ -35,6 +40,7 @@ module Users::Base
validates :time_zone, inclusion: {in: ActiveSupport::TimeZone.all.map(&:name)}, allow_nil: true

# callbacks
after_validation :remove_profile_photo, if: :profile_photo_removal?
after_update :set_teams_time_zone
end

Expand Down Expand Up @@ -166,4 +172,12 @@ def set_teams_time_zone
team.update(time_zone: time_zone) if team.users.count == 1
end
end

def profile_photo_removal?
profile_photo_removal.present?
end

def remove_profile_photo
profile_photo.purge
end
end
10 changes: 6 additions & 4 deletions bullet_train/app/views/account/users/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@
<%= render 'shared/fields/text_field', method: :last_name %>
</div>

<% if cloudinary_enabled? %>
<div class="sm:col-span-2">
<div class="sm:col-span-2">
<% if cloudinary_enabled? %>
<%= render 'shared/fields/cloudinary_image', method: :profile_photo_id %>
</div>
<% end %>
<% else %>
<%= render 'shared/fields/file_field', method: :profile_photo %>
<% end %>
</div>

<div class="sm:col-span-2">
<%= render 'shared/fields/super_select', method: :time_zone,
Expand Down
1 change: 1 addition & 0 deletions bullet_train/bullet_train.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "bullet_train-routes"
spec.add_dependency "devise"
spec.add_dependency "xxhash"
spec.add_dependency "image_processing"

spec.add_dependency "cancancan"

Expand Down
3 changes: 3 additions & 0 deletions bullet_train/config/locales/en/memberships.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ en:
user_profile_photo_id:
name: &user_profile_photo_id Profile Photo
label: *user_profile_photo_id
user_profile_photo:
name: &user_profile_photo Profile Photo
label: *user_profile_photo
role_ids:
name: &roles Special Privileges
label: *roles
Expand Down
5 changes: 5 additions & 0 deletions bullet_train/config/locales/en/users.en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ en:
_: &profile_photo_id Profile Photo
label: *profile_photo_id
heading: *profile_photo_id
profile_photo:
_: &profile_photo Profile Photo
label: *profile_photo
heading: *profile_photo
current_password:
label: Current Password
password:
Expand Down Expand Up @@ -116,6 +120,7 @@ en:
last_name: *last_name
email: *email
profile_photo_id: *profile_photo_id
profile_photo: *profile_photo
time_zone: *time_zone
locale: *locale
# 🚅 super scaffolding will insert new activerecord attributes above this line.
Expand Down

0 comments on commit ede9b6b

Please sign in to comment.