class UsersController < ApplicationController cache_sweeper :pseud_sweeper before_action :check_user_status, only: [:edit, :update, :change_username, :changed_username] before_action :load_user, except: [:activate, :delete_confirmation, :index] before_action :check_ownership, except: [:activate, :change_username, :changed_username, :delete_confirmation, :edit, :index, :show, :update] before_action :check_ownership_or_admin, only: [:change_username, :changed_username, :edit, :update] skip_before_action :store_location, only: [:end_first_login] def load_user Rails.logger.info "DEBUG params: #{params.inspect}" @user = User.find_by!(login: params[:id]) @check_ownership_of = @user end def index flash.keep redirect_to controller: :people, action: :index end # GET /users/1 def show @page_subtitle = @user.login @status = @user.statuses.last visible = visible_items(current_user) @works = visible[:works].order('revised_at DESC').limit(ArchiveConfig.NUMBER_OF_ITEMS_VISIBLE_IN_DASHBOARD) @series = visible[:series].order('updated_at DESC').limit(ArchiveConfig.NUMBER_OF_ITEMS_VISIBLE_IN_DASHBOARD) @bookmarks = visible[:bookmarks].order('updated_at DESC').limit(ArchiveConfig.NUMBER_OF_ITEMS_VISIBLE_IN_DASHBOARD) if current_user.respond_to?(:subscriptions) @subscription = current_user.subscriptions.where(subscribable_id: @user.id, subscribable_type: 'User').first || current_user.subscriptions.build(subscribable: @user) end end # GET /users/1/edit def edit @page_subtitle = t(".browser_title") authorize @user.profile if logged_in_as_admin? end def change_email @page_subtitle = t(".browser_title") end def change_password @page_subtitle = t(".browser_title") end def change_username authorize @user if logged_in_as_admin? @page_subtitle = t(".browser_title") end def changed_password unless params[:password] && reauthenticate render(:change_password) && return end @user.password = params[:password] @user.password_confirmation = params[:password_confirmation] if @user.save flash[:notice] = ts("Your password has been changed. To protect your account, you have been logged out of all active sessions. Please log in with your new password.") @user.create_log_item(action: ArchiveConfig.ACTION_PASSWORD_CHANGE) redirect_to(user_profile_path(@user)) && return else render(:change_password) && return end end def changed_username authorize @user if logged_in_as_admin? render(:change_username) && return if params[:new_login].blank? @new_login = params[:new_login] unless logged_in_as_admin? || @user.valid_password?(params[:password]) flash[:error] = t(".user.incorrect_password") render(:change_username) && return end if @new_login == @user.login flash.now[:error] = t(".new_username_must_be_different") render :change_username and return end old_login = @user.login @user.login = @new_login @user.ticket_number = params[:ticket_number] if @user.save if logged_in_as_admin? flash[:notice] = t(".admin.successfully_updated") redirect_to admin_user_path(@user) else I18n.with_locale(@user.preference.locale_for_mails) do UserMailer.change_username(@user, old_login).deliver_later end flash[:notice] = t(".user.successfully_updated") redirect_to @user end else @user.reload render :change_username end end def activate if params[:id].blank? flash[:error] = ts('Your activation key is missing.') redirect_to root_path return end @user = User.find_by(confirmation_token: params[:id]) unless @user flash[:error] = ts("Your activation key is invalid. If you didn't activate within #{AdminSetting.current.days_to_purge_unactivated * 7} days, your account was deleted. Please sign up again, or contact support via the link in our footer for more help.").html_safe redirect_to root_path return end if @user.active? flash[:error] = ts("Your account has already been activated.") redirect_to @user return end @user.activate flash[:notice] = ts("Account activation complete! Please log in.") @user.create_log_item(action: ArchiveConfig.ACTION_ACTIVATE) # assign over any external authors that belong to this user external_authors = [] external_authors << ExternalAuthor.find_by(email: @user.email) @invitation = @user.invitation external_authors << @invitation.external_author if @invitation external_authors.compact! unless external_authors.empty? external_authors.each do |external_author| external_author.claim!(@user) end flash[:notice] += ts(" We found some works already uploaded to the Archive of Our Own that we think belong to you! You'll see them on your homepage when you've logged in.") end redirect_to(new_user_session_path) end def update authorize @user.profile if logged_in_as_admin? if @user.profile.update(profile_params) if logged_in_as_admin? && @user.profile.ticket_url.present? link = view_context.link_to("Ticket ##{@user.profile.ticket_number}", @user.profile.ticket_url) AdminActivity.log_action(current_admin, @user, action: "edit profile", summary: link) end flash[:notice] = ts('Your profile has been successfully updated') redirect_to user_profile_path(@user) else render :edit end end def confirm_change_email @page_subtitle = t(".browser_title") render :change_email and return unless reauthenticate if params[:new_email].blank? flash.now[:error] = t("users.confirm_change_email.blank_email") render :change_email and return end @new_email = params[:new_email] # Please note: This comparison is not technically correct. According to # RFC 5321, the local part of an email address is case sensitive, while the # domain is case insensitive. That said, all major email providers treat # the local part as case insensitive, so it would probably cause more # confusion if we did this correctly. # # Also, email addresses are validated on the client, and will only contain # a limited subset of ASCII, so we don't need to do a unicode casefolding pass. if @new_email.downcase == @user.email.downcase flash.now[:error] = t("users.confirm_change_email.same_as_current") render :change_email and return end if @new_email.downcase != params[:email_confirmation].downcase flash.now[:error] = t("users.confirm_change_email.nonmatching_email") render :change_email and return end old_email = @user.email @user.email = @new_email return if @user.valid?(:update) # Make sure that on failure, the form doesn't show the new invalid email as the current one @user.email = old_email render :change_email end def changed_email new_email = params[:new_email] old_email = @user.email @user.email = new_email if @user.save I18n.with_locale(@user.preference.locale_for_mails) do UserMailer.change_email(@user.id, old_email, new_email).deliver_later end else # Make sure that on failure, the form still shows the old email as the "current" one. @user.email = old_email end render :change_email end # GET /users/1/reconfirm_email?confirmation_token=abcdef def reconfirm_email confirmed_user = User.confirm_by_token(params[:confirmation_token]) if confirmed_user.errors.empty? flash[:notice] = t(".success") else flash[:error] = t(".invalid_token") end redirect_to change_email_user_path(@user) end # DELETE /users/1 # DELETE /users/1.xml def destroy @hide_dashboard = true @works = @user.works.where(posted: true) @sole_owned_collections = @user.sole_owned_collections if @works.empty? && @sole_owned_collections.empty? @user.wipeout_unposted_works @user.destroy_empty_series @user.destroy flash[:notice] = ts('You have successfully deleted your account.') redirect_to(delete_confirmation_path) elsif params[:coauthor].blank? && params[:sole_author].blank? @sole_authored_works = @user.sole_authored_works @coauthored_works = @user.coauthored_works render('delete_preview') && return elsif params[:coauthor] || params[:sole_author] destroy_author end end def delete_confirmation end def end_first_login @user.preference.update_attribute(:first_login, false) respond_to do |format| format.html { redirect_to(@user) && return } format.js end end def end_banner @user.preference.update_attribute(:banner_seen, true) respond_to do |format| format.html { redirect_to(request.env['HTTP_REFERER'] || root_path) && return } format.js end end def end_tos_prompt @user.update_attribute(:accepted_tos_version, @current_tos_version) head :no_content end private def reauthenticate if params[:password_check].blank? return wrong_password!(params[:new_email], t("users.confirm_change_email.blank_password"), t("users.changed_password.blank_password")) end if @user.valid_password?(params[:password_check]) true else wrong_password!(params[:new_email], t("users.confirm_change_email.wrong_password_html", contact_support_link: helpers.link_to(t("users.confirm_change_email.contact_support"), new_feedback_report_path)), t("users.changed_password.wrong_password")) end end def wrong_password!(condition, if_true, if_false) flash.now[:error] = condition ? if_true : if_false @wrong_password = true false end def visible_items(current_user) # NOTE: When current_user is nil, we use .visible_to_all, otherwise we use # .visible_to_registered_user. visible_method = current_user.nil? && current_admin.nil? ? :visible_to_all : :visible_to_registered_user visible_works = @user.works.send(visible_method) visible_series = @user.series.send(visible_method) visible_bookmarks = @user.bookmarks.send(visible_method) visible_works = visible_works.revealed.non_anon visible_series = visible_series.exclude_anonymous @fandoms = if @user == User.orphan_account [] else Fandom.select("tags.*, count(DISTINCT works.id) as work_count"). joins(:filtered_works).group("tags.id").merge(visible_works). where(filter_taggings: { inherited: false }). order('work_count DESC').load end { works: visible_works, series: visible_series, bookmarks: visible_bookmarks } end def destroy_author @sole_authored_works = @user.sole_authored_works @coauthored_works = @user.coauthored_works if params[:cancel_button] flash[:notice] = ts('Account deletion canceled.') redirect_to user_profile_path(@user) return end if params[:coauthor] == 'keep_pseud' || params[:coauthor] == 'orphan_pseud' # Orphans co-authored works. pseuds = @user.pseuds works = @coauthored_works # We change the pseud to the default orphan pseud if use_default is true. use_default = params[:use_default] == 'true' || params[:coauthor] == 'orphan_pseud' Creatorship.orphan(pseuds, works, use_default) elsif params[:coauthor] == 'remove' # Removes user as an author from co-authored works @coauthored_works.each do |w| w.remove_author(@user) end end if params[:sole_author] == 'keep_pseud' || params[:sole_author] == 'orphan_pseud' # Orphans works where user is the sole author. pseuds = @user.pseuds works = @sole_authored_works # We change the pseud to default orphan pseud if use_default is true. use_default = params[:use_default] == 'true' || params[:sole_author] == 'orphan_pseud' Creatorship.orphan(pseuds, works, use_default) Collection.orphan(pseuds, @sole_owned_collections, default: use_default) elsif params[:sole_author] == 'delete' # Deletes works where user is sole author @sole_authored_works.each(&:destroy) # Deletes collections where user is sole author @sole_owned_collections.each(&:destroy) end @works = @user.works.where(posted: true) if @works.blank? @user.wipeout_unposted_works @user.destroy_empty_series @user.destroy flash[:notice] = ts('You have successfully deleted your account.') redirect_to(delete_confirmation_path) else flash[:error] = ts('Sorry, something went wrong! Please try again.') redirect_to(@user) end end private def profile_params params.require(:profile_attributes).permit( :title, :about_me, :ticket_number ) end end