diff --git a/statuses_controller.rb b/statuses_controller.rb new file mode 100644 index 0000000..fa0214e --- /dev/null +++ b/statuses_controller.rb @@ -0,0 +1,111 @@ +class StatusesController < ApplicationController + before_action :users_only, except: [:index, :show, :timeline] + before_action :set_status, only: [:show, :edit, :update, :destroy] + before_action :set_user, except: [:timeline] + before_action :check_ownership, only: [:edit, :update, :destroy] + + + def redirect_to_current_user + if current_user + redirect_to user_status_path(current_user) + else + redirect_to new_user_session_path, alert: "Please log in to view statuses." + end + end + def check_ownership + unless @status && @status.user == current_user + redirect_to user_status_path(@user || current_user), alert: "You don't have permission to do that silly!" + end +end + + def edit + @user = current_user + end + + def update + @user = current_user + if @status.update(status_params) + redirect_to user_status_path(@user, @status), notice: "Status updated!" + else + render :edit + end + end + + def show + end + def destroy + @status.destroy + redirect_to user_statuses_path(@user), notice: "Status deleted." + end + + def redirect_to_current_user_new + if current_user + redirect_to new_user_status_path(current_user) + else + redirect_to new_user_session_path, alert: "Please log in to create a status." + end + end + def index + if params[:user_id].present? + @user = User.find_by(id: params[:user_id]) || User.find_by(login: params[:user_id]) + end + if @user + @statuses = @user.statuses.order(created_at: :desc) + else + @statuses = [] + end +end + def new + if @user != current_user + redirect_to user_statuses_path(current_user, @status), alert: "You can't do that silly!" + return + end + @status = current_user.statuses.new +end + def create + if @user != current_user + redirect_to user_status_path(current_user), alert: "You can't do that silly!" + return + end + @status = current_user.statuses.new(status_params) + if @status.save + redirect_to user_statuses_path(current_user, @status), notice: "Status created!" + else + render :new + end + end + def timeline + @statuses = Status.includes(:user, :icon_attachment) + .order(created_at: :desc) + end + private + +def set_status + return unless params[:id].present? + + @status = Status.find_by(id: params[:id]) + unless @status + redirect_to user_statuses_path(current_user), alert: "Status not found." + return + end + + @user = @status.user +end + +def set_user + return if @user.present? + @user = if params[:user_id].present? + User.find_by(id: params[:user_id]) || User.find_by(login: params[:user_id]) + else + current_user + end + + unless @user + redirect_to root_path, alert: "User not found." + end +end + +def status_params + params.require(:status).permit(:icon, :text, :mood, :music) + end +end diff --git a/users_controller.rb b/users_controller.rb new file mode 100644 index 0000000..321e480 --- /dev/null +++ b/users_controller.rb @@ -0,0 +1,418 @@ +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