docbrown/app/controllers/users_controller.rb
Michael Kohl 9377eee4f5
[deploy] /profile/settings page rendering (Ridhwana/Michael) (#10733)
* chore: update the profile to be more dynamic. Maps out the group + field name   + value for field

* feat: add the correct input type to the form tying it to the correct method to update

* feat: update the value and the label

* feat: update the profile with a submit

* refactor: ensure we dont show empty groups on the settings/profile

* fix: this shoudl be dribbble

* fix: use attributes! to refresh the cache to ensure that we font run into the case where Profile.attributes is []

* feat: show group description only when it exists

* chore: add some classes

* feat: use the profile update service to update the relevant user information

* chore: rename variable

* chore: use a scope to not return empty groups

* fix: only update user if the profile sync is update is successful. Also capture errors

* feat: style the crayons field checkbox

* refactor: rename the attribute to user instead of profile

* refactor: service object

* Fix CodeClimate warning and rearrange code

* Fix service object, add missing attribute

* Fix flash and add redirect

* Fix service object + spec

* Fix user edits profile spec

* Disable spec

* Fix more specs

* Undo vim fail

* Add data update scripts

* Update where clause in data update script

* Rename method

* Make sync_to_user conditional

* Add after update actions and Honeycomb

* feat: handle checkbox allow chcekbox to check

* Don't set experience cookie in ProfilesController

* Clean up Profiles::Update object

* Fix spec

* Remove Twitch integration (#10732)

* Remove Twitch integration

* Ignore columns

* Change admin password in docs (#10735)

Update default admin password in documentation

* Do not require meta_keywords to be set (#10721)

* Remove extra character from meta_keywords in /listings/index.html.erb

* Remove meta_keywords from MANDATORY_CONFIGS

* Add and use meta_keywords_tag helper

* Use modern tag syntax instead of deprecated syntax

* Add and use meta_keywords_default helper

* Add and use meta_keywords_article helper

* Remove * from meta_keywords_field.label

* Update meta_keyword specs to account for no keywords being set

* Generalizes Connect welcome message and settings nav tab and refactors tests (#10741) [deploy]

* [deploy] fix: Clear chat input after Enter key submit (#10487)

* fix: Clear chat input after Enter key submit

* fix: Rename Composer component

* test: Add integration tests for input value

* After pressing Enter
* After clicking Send
* After clicking Save edit
* After clicking Close edit

* Remove tabindex of -1 on upload image input in the write a post editor (#10543)

* Remove tabindex of -1 on upload image input in the write a post editor

* Remove negative tabindex on image uploader's outer button

* Give tabindex of -1 to outer button on image uploader

* Update buttons.scss

* [deploy] Add recaptcha keys to as site-configurable keys (#10736)

* Add recaptcha keys to as site-configurable keys

* Add recaptcha to site config view

* Use site config key

* Make ReCAPTCHA tag optional if keys are blank

* Use proper capitalization of recaptcha

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Refactor logic for readability

* Use proper capitalization of recaptcha

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Use proper capitalization of recaptcha

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Use proper capitalization of recaptcha

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>

* Add Section for Series/Collections (#10719)

* Add Section for Series/Collections

I added a small section in this area of the docs to share some information about collections/ series. 

There is not much information in the documentation about collections or series and I felt like this would be beneficial to others looking to help with the Series feature of the website.

* Updated wording of collections section.

* Articles API: filtering by tags (#3654) (#10614)

* Articles API: filtering by tags (#3654)

* Address CR comments (#3654)

* Add test case
* Move tests
* Update docs

* Use _any_ option for querying by tags (#3654)

* Move Imgproxy endpoint config to Images::Optimizer (#10742)

* Link Email Account creation UI to existing Create Account UI (#10746) [deploy]

* Conditional button to link to email accout creation

* Rename email signup state param

* Test for email account creation button scenarios

* Add forem meta tags (#10747)

* Add forem meta tags

* Add docs

* Update docs/technical-overview/compatibility.md

Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>

* Update docs/technical-overview/compatibility.md

Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>

* Update docs/technical-overview/compatibility.md

Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>

* Update docs/technical-overview/compatibility.md

Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>

* Remove Twitch-related columns from users table (#10750)

* [deploy] Long text goes outside cards (#10740)

* Add word-break to crayons-card

Add word-break to crayons-card this will allow words to be broken if the word is too long but will wrap normally otherwise.

* Use overflow-wrap: anywhere;

`break-word` is deprecated though `anywhere` does not work in IE

* Removes superfluous sign_in from users_onboarding_spec.rb (#10757) [deploy]

* [deploy] Allow glitch id starting with ~ (#10544)

* [deploy] crayons prep (#10737)

* Add DEV special case for following hiring tag

* Move image validation to Profiles::Update object

* spec: move over the tests for validating images

* Move follow_hiring_tag into the service object

Co-authored-by: Ridhwana <ridhwana.khan16@gmail.com>
Co-authored-by: Andrew Bone <AndrewB05@gmail.com>
Co-authored-by: Vaidehi Joshi <vaidehi.sj@gmail.com>
Co-authored-by: Julianna Tetreault <32834804+juliannatetreault@users.noreply.github.com>
Co-authored-by: Robin Gagnon <me@reobin.dev>
Co-authored-by: Manaswini <58681405+Manaswini1832@users.noreply.github.com>
Co-authored-by: Andy Zhao <17884966+Zhao-Andy@users.noreply.github.com>
Co-authored-by: Christopher Wray <53663762+cwray-tech@users.noreply.github.com>
Co-authored-by: Rafal Trojanowski <rt.trojanowski@gmail.com>
Co-authored-by: Mac Siri <mac@dev.to>
Co-authored-by: Josh Puetz <josh@dev.to>
Co-authored-by: Ben Halpern <bendhalpern@gmail.com>
Co-authored-by: Jacob Herrington <jacobherringtondeveloper@gmail.com>
Co-authored-by: irmela <irmela@berlin-coding.de>
Co-authored-by: ludwiczakpawel <ludwiczakpawel@gmail.com>
2020-10-20 09:18:13 -04:00

346 lines
11 KiB
Ruby

class UsersController < ApplicationController
before_action :set_no_cache_header
before_action :raise_suspended, only: %i[update]
before_action :set_user, only: %i[
update update_language_settings confirm_destroy request_destroy full_delete remove_identity
]
after_action :verify_authorized, except: %i[index signout_confirm add_org_admin remove_org_admin remove_from_org]
before_action :authenticate_user!, only: %i[onboarding_update onboarding_checkbox_update]
before_action :set_suggested_users, only: %i[index]
before_action :initialize_stripe, only: %i[edit]
INDEX_ATTRIBUTES_FOR_SERIALIZATION = %i[id name username summary profile_image].freeze
private_constant :INDEX_ATTRIBUTES_FOR_SERIALIZATION
def index
@users =
case params[:state]
when "follow_suggestions"
determine_follow_suggestions(current_user)
when "sidebar_suggestions"
Suggester::Users::Sidebar.new(current_user, params[:tag]).suggest.sample(3)
else
User.none
end
end
# GET /settings/@tab
def edit
unless current_user
skip_authorization
return redirect_to sign_up_path
end
set_user
set_current_tab(params["tab"] || "profile")
handle_settings_tab
end
# PATCH/PUT /users/:id.:format
def update
set_current_tab(params["user"]["tab"])
if @user.update(permitted_attributes(@user))
RssReaderFetchUserWorker.perform_async(@user.id) if @user.feed_url.present?
notice = "Your profile was successfully updated."
if config_changed?
notice = "Your config has been updated. Refresh to see all changes."
end
if @user.export_requested?
notice += " The export will be emailed to you shortly."
ExportContentWorker.perform_async(@user.id)
end
cookies.permanent[:user_experience_level] = @user.experience_level.to_s if @user.experience_level.present?
flash[:settings_notice] = notice
@user.touch(:profile_updated_at)
redirect_to "/settings/#{@tab}"
else
Honeycomb.add_field("error", @user.errors.messages.reject { |_, v| v.empty? })
Honeycomb.add_field("errored", true)
if @tab
render :edit, status: :bad_request
else
flash[:error] = @user.errors.full_messages.join(", ")
redirect_to "/settings"
end
end
end
def update_language_settings
set_current_tab("misc")
@user.language_settings["preferred_languages"] = Languages::LIST.keys & params[:user][:preferred_languages].to_a
if @user.save
flash[:settings_notice] = "Your language settings were successfully updated."
@user.touch(:profile_updated_at)
redirect_to "/settings/#{@tab}"
else
render :edit
end
end
def request_destroy
set_current_tab("account")
if destroy_request_in_progress?
notice = "You have already requested account deletion. Please, check your email for further instructions."
flash[:settings_notice] = notice
redirect_to user_settings_path(@tab)
elsif @user.email?
Users::RequestDestroy.call(@user)
notice = "You have requested account deletion. Please, check your email for further instructions."
flash[:settings_notice] = notice
redirect_to user_settings_path(@tab)
else
flash[:settings_notice] = "Please, provide an email to delete your account."
redirect_to user_settings_path("account")
end
end
def confirm_destroy
destroy_token = Rails.cache.read("user-destroy-token-#{@user.id}")
raise ActionController::RoutingError, "Not Found" unless destroy_token.present? && destroy_token == params[:token]
set_current_tab("account")
end
def full_delete
set_current_tab("account")
if @user.email?
Users::DeleteWorker.perform_async(@user.id)
sign_out @user
flash[:global_notice] = "Your account deletion is scheduled. You'll be notified when it's deleted."
redirect_to root_path
else
flash[:settings_notice] = "Please, provide an email to delete your account"
redirect_to user_settings_path("account")
end
end
def remove_identity
set_current_tab("account")
error_message = "An error occurred. Please try again or send an email to: #{SiteConfig.email_addresses[:default]}"
unless Authentication::Providers.enabled?(params[:provider])
flash[:error] = error_message
redirect_to user_settings_path(@tab)
return
end
provider = Authentication::Providers.get!(params[:provider])
identity = @user.identities.find_by(provider: provider.provider_name)
if identity && @user.identities.size > 1
identity.destroy
@user.update(
provider.user_username_field => nil,
:profile_updated_at => Time.current,
)
flash[:settings_notice] = "Your #{provider.official_name} account was successfully removed."
else
flash[:error] = error_message
end
redirect_to user_settings_path(@tab)
end
def onboarding_update
if params[:user]
sanitize_user_params
permitted_params = %i[summary location employment_title employer_name last_onboarding_page]
current_user.assign_attributes(params[:user].permit(permitted_params))
current_user.profile_updated_at = Time.current
end
current_user.saw_onboarding = true
authorize User
render_update_response
end
def onboarding_checkbox_update
if params[:user]
permitted_params = %i[
checked_code_of_conduct checked_terms_and_conditions email_newsletter email_digest_periodic
]
current_user.assign_attributes(params[:user].permit(permitted_params))
end
current_user.saw_onboarding = true
authorize User
render_update_response
end
def join_org
authorize User
if (@organization = Organization.find_by(secret: params[:org_secret].strip))
OrganizationMembership.create(user_id: current_user.id, organization_id: @organization.id, type_of_user: "member")
flash[:settings_notice] = "You have joined the #{@organization.name} organization."
redirect_to "/settings/organization/#{@organization.id}"
else
flash[:error] = "The given organization secret was invalid."
redirect_to "/settings/organization/new"
end
end
def leave_org
org = Organization.find_by(id: params[:organization_id])
authorize org
OrganizationMembership.find_by(organization_id: org.id, user_id: current_user.id)&.delete
flash[:settings_notice] = "You have left your organization."
redirect_to "/settings/organization/new"
end
def add_org_admin
adminable = User.find(params[:user_id])
org = Organization.find_by(id: params[:organization_id])
not_authorized unless current_user.org_admin?(org) && OrganizationMembership.exists?(user: adminable,
organization: org)
OrganizationMembership.find_by(user_id: adminable.id, organization_id: org.id).update(type_of_user: "admin")
flash[:settings_notice] = "#{adminable.name} is now an admin."
redirect_to "/settings/organization/#{org.id}"
end
def remove_org_admin
unadminable = User.find(params[:user_id])
org = Organization.find_by(id: params[:organization_id])
not_authorized unless current_user.org_admin?(org) && unadminable.org_admin?(org)
OrganizationMembership.find_by(user_id: unadminable.id, organization_id: org.id).update(type_of_user: "member")
flash[:settings_notice] = "#{unadminable.name} is no longer an admin."
redirect_to "/settings/organization/#{org.id}"
end
def remove_from_org
removable = User.find(params[:user_id])
org = Organization.find_by(id: params[:organization_id])
removable_org_membership = OrganizationMembership.find_by(user_id: removable.id, organization_id: org.id)
not_authorized unless current_user.org_admin?(org) && removable_org_membership
removable_org_membership.delete
flash[:settings_notice] = "#{removable.name} is no longer part of your organization."
redirect_to "/settings/organization/#{org.id}"
end
def signout_confirm; end
def handle_settings_tab
return @tab = "profile" if @tab.blank?
case @tab
when "organization"
handle_organization_tab
when "integrations"
handle_integrations_tab
when "billing"
handle_billing_tab
when "response-templates"
handle_response_templates_tab
else
not_found unless @tab.in?(Constants::Settings::TAB_LIST.map { |t| t.downcase.tr(" ", "-") })
end
end
private
def sanitize_user_params
params[:user].delete_if { |_k, v| v.blank? }
end
def set_suggested_users
@suggested_users = SiteConfig.suggested_users
end
def default_suggested_users
@default_suggested_users ||= User.where(username: @suggested_users)
end
def determine_follow_suggestions(current_user)
recent_suggestions = Suggester::Users::Recent.new(
current_user,
attributes_to_select: INDEX_ATTRIBUTES_FOR_SERIALIZATION,
).suggest
recent_suggestions.presence || default_suggested_users
end
def render_update_response
if current_user.save
respond_to do |format|
format.json { render json: { outcome: "updated successfully" } }
end
else
respond_to do |format|
format.json { render json: { outcome: "update failed" } }
end
end
end
def handle_organization_tab
@organizations = @current_user.organizations.order(name: :asc)
if params[:org_id] == "new" || params[:org_id].blank? && @organizations.size.zero?
@organization = Organization.new
elsif params[:org_id].blank? || params[:org_id].match?(/\d/)
@organization = Organization.find_by(id: params[:org_id]) || @organizations.first
authorize @organization, :part_of_org?
@org_organization_memberships = @organization.organization_memberships.includes(:user)
@organization_membership = OrganizationMembership.find_by(user_id: current_user.id,
organization_id: @organization.id)
end
end
def handle_integrations_tab
@github_repositories_show = current_user.authenticated_through?(:github)
end
def handle_billing_tab
stripe_code = current_user.stripe_id_code
return if stripe_code == "special"
@customer = Payments::Customer.get(stripe_code) if stripe_code.present?
end
def handle_response_templates_tab
@response_templates = current_user.response_templates
@response_template = ResponseTemplate.find_or_initialize_by(id: params[:id], user: current_user)
end
def set_user
@user = current_user
not_found unless @user
authorize @user
end
def set_current_tab(current_tab = "profile")
@tab = current_tab
end
def config_changed?
params[:user].include?(:config_theme)
end
def less_than_one_day_old?(user)
# we check all the `_created_at` fields for all available providers
# we use `.available` and not `.enabled` to avoid a situation in which
# an admin disables an authentication method after users have already
# registered, risking that they would be flagged as new
# the last one is a fallback in case all created_at fields are nil
user_identity_age = Authentication::Providers.available.map do |provider|
user.public_send("#{provider}_created_at")
end.detect(&:present?)
user_identity_age = user_identity_age.presence || 8.days.ago
range = 1.day.ago.beginning_of_day..Time.current
range.cover?(user_identity_age)
end
def destroy_request_in_progress?
Rails.cache.exist?("user-destroy-token-#{@user.id}")
end
end