about summary refs log tree commit diff
path: root/app/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/about_controller.rb7
-rw-r--r--app/controllers/accounts_controller.rb4
-rw-r--r--app/controllers/admin/accounts_controller.rb23
-rw-r--r--app/controllers/admin/domain_blocks_controller.rb18
-rw-r--r--app/controllers/admin/reports_controller.rb45
-rw-r--r--app/controllers/admin/settings_controller.rb14
-rw-r--r--app/controllers/api/v1/accounts_controller.rb34
-rw-r--r--app/controllers/api/v1/apps_controller.rb8
-rw-r--r--app/controllers/api/v1/blocks_controller.rb2
-rw-r--r--app/controllers/api/v1/favourites_controller.rb2
-rw-r--r--app/controllers/api/v1/follow_requests_controller.rb6
-rw-r--r--app/controllers/api/v1/follows_controller.rb8
-rw-r--r--app/controllers/api/v1/instances_controller.rb7
-rw-r--r--app/controllers/api/v1/media_controller.rb8
-rw-r--r--app/controllers/api/v1/mutes_controller.rb21
-rw-r--r--app/controllers/api/v1/notifications_controller.rb6
-rw-r--r--app/controllers/api/v1/reports_controller.rb30
-rw-r--r--app/controllers/api/v1/search_controller.rb9
-rw-r--r--app/controllers/api/v1/statuses_controller.rb21
-rw-r--r--app/controllers/api/v1/timelines_controller.rb18
-rw-r--r--app/controllers/api_controller.rb5
-rw-r--r--app/controllers/application_controller.rb15
-rw-r--r--app/controllers/auth/registrations_controller.rb10
-rw-r--r--app/controllers/authorize_follow_controller.rb2
-rw-r--r--app/controllers/concerns/obfuscate_filename.rb7
-rw-r--r--app/controllers/oauth/authorizations_controller.rb7
-rw-r--r--app/controllers/remote_follow_controller.rb3
-rw-r--r--app/controllers/settings/exports_controller.rb46
-rw-r--r--app/controllers/settings/imports_controller.rb34
-rw-r--r--app/controllers/settings/preferences_controller.rb3
-rw-r--r--app/controllers/settings/two_factor_auths_controller.rb3
-rw-r--r--app/controllers/statuses_controller.rb39
-rw-r--r--app/controllers/stream_entries_controller.rb4
-rw-r--r--app/controllers/xrd_controller.rb11
34 files changed, 408 insertions, 72 deletions
diff --git a/app/controllers/about_controller.rb b/app/controllers/about_controller.rb
index 491036db2..7fd43489f 100644
--- a/app/controllers/about_controller.rb
+++ b/app/controllers/about_controller.rb
@@ -4,7 +4,12 @@ class AboutController < ApplicationController
   before_action :set_body_classes
 
   def index
-    @description = Setting.site_description
+    @description                  = Setting.site_description
+    @open_registrations           = Setting.open_registrations
+    @closed_registrations_message = Setting.closed_registrations_message
+
+    @user = User.new
+    @user.build_account
   end
 
   def more
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index b837f006e..dc1aeb5ea 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -16,7 +16,7 @@ class AccountsController < ApplicationController
       end
 
       format.atom do
-        @entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
+        @entries = @account.stream_entries.order('id desc').where(activity_type: 'Status').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
       end
 
       format.activitystreams2
@@ -56,6 +56,6 @@ class AccountsController < ApplicationController
   end
 
   def check_account_suspension
-    head 410 if @account.suspended?
+    gone if @account.suspended?
   end
 end
diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb
index 95107b3dc..df2c7bebf 100644
--- a/app/controllers/admin/accounts_controller.rb
+++ b/app/controllers/admin/accounts_controller.rb
@@ -19,19 +19,26 @@ class Admin::AccountsController < ApplicationController
 
   def show; end
 
-  def update
-    if @account.update(account_params)
-      redirect_to admin_accounts_path
-    else
-      render :show
-    end
-  end
-
   def suspend
     Admin::SuspensionWorker.perform_async(@account.id)
     redirect_to admin_accounts_path
   end
 
+  def unsuspend
+    @account.update(suspended: false)
+    redirect_to admin_accounts_path
+  end
+
+  def silence
+    @account.update(silenced: true)
+    redirect_to admin_accounts_path
+  end
+
+  def unsilence
+    @account.update(silenced: false)
+    redirect_to admin_accounts_path
+  end
+
   private
 
   def set_account
diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb
index e362957e7..1f4432847 100644
--- a/app/controllers/admin/domain_blocks_controller.rb
+++ b/app/controllers/admin/domain_blocks_controller.rb
@@ -9,6 +9,24 @@ class Admin::DomainBlocksController < ApplicationController
     @blocks = DomainBlock.paginate(page: params[:page], per_page: 40)
   end
 
+  def new
+    @domain_block = DomainBlock.new
+  end
+
   def create
+    @domain_block = DomainBlock.new(resource_params)
+
+    if @domain_block.save
+      DomainBlockWorker.perform_async(@domain_block.id)
+      redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed'
+    else
+      render action: :new
+    end
+  end
+
+  private
+
+  def resource_params
+    params.require(:domain_block).permit(:domain, :severity)
   end
 end
diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb
new file mode 100644
index 000000000..2b3b1809f
--- /dev/null
+++ b/app/controllers/admin/reports_controller.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+class Admin::ReportsController < ApplicationController
+  before_action :require_admin!
+  before_action :set_report, except: [:index]
+
+  layout 'admin'
+
+  def index
+    @reports = Report.includes(:account, :target_account).order('id desc').paginate(page: params[:page], per_page: 40)
+    @reports = params[:action_taken].present? ? @reports.resolved : @reports.unresolved
+  end
+
+  def show
+    @statuses = Status.where(id: @report.status_ids)
+  end
+
+  def resolve
+    @report.update(action_taken: true, action_taken_by_account_id: current_account.id)
+    redirect_to admin_report_path(@report)
+  end
+
+  def suspend
+    Admin::SuspensionWorker.perform_async(@report.target_account.id)
+    Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
+    redirect_to admin_report_path(@report)
+  end
+
+  def silence
+    @report.target_account.update(silenced: true)
+    Report.unresolved.where(target_account: @report.target_account).update_all(action_taken: true, action_taken_by_account_id: current_account.id)
+    redirect_to admin_report_path(@report)
+  end
+
+  def remove
+    RemovalWorker.perform_async(params[:status_id])
+    redirect_to admin_report_path(@report)
+  end
+
+  private
+
+  def set_report
+    @report = Report.find(params[:id])
+  end
+end
diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb
index af0be8823..7615c781d 100644
--- a/app/controllers/admin/settings_controller.rb
+++ b/app/controllers/admin/settings_controller.rb
@@ -11,9 +11,13 @@ class Admin::SettingsController < ApplicationController
 
   def update
     @setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id])
+    value    = settings_params[:value]
 
-    if @setting.value != params[:setting][:value]
-      @setting.value = params[:setting][:value]
+    # Special cases
+    value = value == 'true' if @setting.var == 'open_registrations'
+
+    if @setting.value != value
+      @setting.value = value
       @setting.save
     end
 
@@ -22,4 +26,10 @@ class Admin::SettingsController < ApplicationController
       format.json { respond_with_bip(@setting) }
     end
   end
+
+  private
+
+  def settings_params
+    params.require(:setting).permit(:value)
+  end
 end
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index d97010c0e..da18474cb 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -1,8 +1,8 @@
 # frozen_string_literal: true
 
 class Api::V1::AccountsController < ApiController
-  before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock]
-  before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock]
+  before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
+  before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
   before_action :require_user!, except: [:show, :following, :followers, :statuses]
   before_action :set_account, except: [:verify_credentials, :suggestions, :search]
 
@@ -20,7 +20,7 @@ class Api::V1::AccountsController < ApiController
     accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |f| accounts[f.target_account_id] }
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = following_api_v1_account_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
     prev_path = following_api_v1_account_url(since_id: results.first.id) unless results.empty?
@@ -35,7 +35,7 @@ class Api::V1::AccountsController < ApiController
     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |f| accounts[f.account_id] }
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = followers_api_v1_account_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
     prev_path = followers_api_v1_account_url(since_id: results.first.id) unless results.empty?
@@ -47,12 +47,15 @@ class Api::V1::AccountsController < ApiController
 
   def statuses
     @statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(limit_param(DEFAULT_STATUSES_LIMIT), params[:max_id], params[:since_id])
+    @statuses = @statuses.where(id: MediaAttachment.where(account: @account).where.not(status_id: nil).reorder('').select('distinct status_id')) if params[:only_media]
+    @statuses = @statuses.without_replies if params[:exclude_replies]
     @statuses = cache_collection(@statuses, Status)
 
     set_maps(@statuses)
-    set_counters_maps(@statuses)
+    # set_counters_maps(@statuses)
+    # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
 
-    next_path = statuses_api_v1_account_url(max_id: @statuses.last.id)    if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
+    next_path = statuses_api_v1_account_url(max_id: @statuses.last.id)    unless @statuses.empty?
     prev_path = statuses_api_v1_account_url(since_id: @statuses.first.id) unless @statuses.empty?
 
     set_pagination_headers(next_path, prev_path)
@@ -71,10 +74,17 @@ class Api::V1::AccountsController < ApiController
     @followed_by = { @account.id => false }
     @blocking    = { @account.id => true }
     @requested   = { @account.id => false }
+    @muting      = { @account.id => current_user.account.muting?(@account.id) }
 
     render action: :relationship
   end
 
+  def mute
+    MuteService.new.call(current_user.account, @account)
+    set_relationship
+    render action: :relationship
+  end
+
   def unfollow
     UnfollowService.new.call(current_user.account, @account)
     set_relationship
@@ -87,6 +97,12 @@ class Api::V1::AccountsController < ApiController
     render action: :relationship
   end
 
+  def unmute
+    UnmuteService.new.call(current_user.account, @account)
+    set_relationship
+    render action: :relationship
+  end
+
   def relationships
     ids = params[:id].is_a?(Enumerable) ? params[:id].map(&:to_i) : [params[:id].to_i]
 
@@ -94,13 +110,14 @@ class Api::V1::AccountsController < ApiController
     @following   = Account.following_map(ids, current_user.account_id)
     @followed_by = Account.followed_by_map(ids, current_user.account_id)
     @blocking    = Account.blocking_map(ids, current_user.account_id)
+    @muting      = Account.muting_map(ids, current_user.account_id)
     @requested   = Account.requested_map(ids, current_user.account_id)
   end
 
   def search
-    @accounts = SearchService.new.call(params[:q], limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:resolve] == 'true')
+    @accounts = AccountSearchService.new.call(params[:q], limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:resolve] == 'true', current_account)
 
-    set_account_counters_maps(@accounts) unless @accounts.nil?
+    # set_account_counters_maps(@accounts) unless @accounts.nil?
 
     render action: :index
   end
@@ -115,6 +132,7 @@ class Api::V1::AccountsController < ApiController
     @following   = Account.following_map([@account.id], current_user.account_id)
     @followed_by = Account.followed_by_map([@account.id], current_user.account_id)
     @blocking    = Account.blocking_map([@account.id], current_user.account_id)
+    @muting      = Account.muting_map([@account.id], current_user.account_id)
     @requested   = Account.requested_map([@account.id], current_user.account_id)
   end
 end
diff --git a/app/controllers/api/v1/apps_controller.rb b/app/controllers/api/v1/apps_controller.rb
index ca9dd0b7e..2ec7280af 100644
--- a/app/controllers/api/v1/apps_controller.rb
+++ b/app/controllers/api/v1/apps_controller.rb
@@ -4,6 +4,12 @@ class Api::V1::AppsController < ApiController
   respond_to :json
 
   def create
-    @app = Doorkeeper::Application.create!(name: params[:client_name], redirect_uri: params[:redirect_uris], scopes: (params[:scopes] || Doorkeeper.configuration.default_scopes), website: params[:website])
+    @app = Doorkeeper::Application.create!(name: app_params[:client_name], redirect_uri: app_params[:redirect_uris], scopes: (app_params[:scopes] || Doorkeeper.configuration.default_scopes), website: app_params[:website])
+  end
+
+  private
+
+  def app_params
+    params.permit(:client_name, :redirect_uris, :scopes, :website)
   end
 end
diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb
index 08aefc175..dadf21265 100644
--- a/app/controllers/api/v1/blocks_controller.rb
+++ b/app/controllers/api/v1/blocks_controller.rb
@@ -11,7 +11,7 @@ class Api::V1::BlocksController < ApiController
     accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |f| accounts[f.target_account_id] }.compact
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = api_v1_blocks_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
     prev_path = api_v1_blocks_url(since_id: results.first.id) unless results.empty?
diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb
index ef0a4854a..8a5b81e63 100644
--- a/app/controllers/api/v1/favourites_controller.rb
+++ b/app/controllers/api/v1/favourites_controller.rb
@@ -11,7 +11,7 @@ class Api::V1::FavouritesController < ApiController
     @statuses = cache_collection(Status.where(id: results.map(&:status_id)), Status)
 
     set_maps(@statuses)
-    set_counters_maps(@statuses)
+    # set_counters_maps(@statuses)
 
     next_path = api_v1_favourites_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
     prev_path = api_v1_favourites_url(since_id: results.first.id) unless results.empty?
diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb
index a30e97e71..3b8e8c078 100644
--- a/app/controllers/api/v1/follow_requests_controller.rb
+++ b/app/controllers/api/v1/follow_requests_controller.rb
@@ -9,7 +9,7 @@ class Api::V1::FollowRequestsController < ApiController
     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |f| accounts[f.account_id] }
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = api_v1_follow_requests_url(max_id: results.last.id)    if results.size == DEFAULT_ACCOUNTS_LIMIT
     prev_path = api_v1_follow_requests_url(since_id: results.first.id) unless results.empty?
@@ -18,12 +18,12 @@ class Api::V1::FollowRequestsController < ApiController
   end
 
   def authorize
-    FollowRequest.find_by!(account_id: params[:id], target_account: current_account).authorize!
+    AuthorizeFollowService.new.call(Account.find(params[:id]), current_account)
     render_empty
   end
 
   def reject
-    FollowRequest.find_by!(account_id: params[:id], target_account: current_account).reject!
+    RejectFollowService.new.call(Account.find(params[:id]), current_account)
     render_empty
   end
 end
diff --git a/app/controllers/api/v1/follows_controller.rb b/app/controllers/api/v1/follows_controller.rb
index c22dacbaa..7c0f44f03 100644
--- a/app/controllers/api/v1/follows_controller.rb
+++ b/app/controllers/api/v1/follows_controller.rb
@@ -7,7 +7,7 @@ class Api::V1::FollowsController < ApiController
   respond_to :json
 
   def create
-    raise ActiveRecord::RecordNotFound if params[:uri].blank?
+    raise ActiveRecord::RecordNotFound if follow_params[:uri].blank?
 
     @account = FollowService.new.call(current_user.account, target_uri).try(:target_account)
     render action: :show
@@ -16,6 +16,10 @@ class Api::V1::FollowsController < ApiController
   private
 
   def target_uri
-    params[:uri].strip.gsub(/\A@/, '')
+    follow_params[:uri].strip.gsub(/\A@/, '')
+  end
+
+  def follow_params
+    params.permit(:uri)
   end
 end
diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb
new file mode 100644
index 000000000..51d92838a
--- /dev/null
+++ b/app/controllers/api/v1/instances_controller.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+class Api::V1::InstancesController < ApiController
+  respond_to :json
+
+  def show; end
+end
diff --git a/app/controllers/api/v1/media_controller.rb b/app/controllers/api/v1/media_controller.rb
index f8139ade7..aed3578d7 100644
--- a/app/controllers/api/v1/media_controller.rb
+++ b/app/controllers/api/v1/media_controller.rb
@@ -10,10 +10,16 @@ class Api::V1::MediaController < ApiController
   respond_to :json
 
   def create
-    @media = MediaAttachment.create!(account: current_user.account, file: params[:file])
+    @media = MediaAttachment.create!(account: current_user.account, file: media_params[:file])
   rescue Paperclip::Errors::NotIdentifiedByImageMagickError
     render json: { error: 'File type of uploaded media could not be verified' }, status: 422
   rescue Paperclip::Error
     render json: { error: 'Error processing thumbnail for uploaded media' }, status: 500
   end
+
+  private
+
+  def media_params
+    params.permit(:file)
+  end
 end
diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb
new file mode 100644
index 000000000..6f48de040
--- /dev/null
+++ b/app/controllers/api/v1/mutes_controller.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class Api::V1::MutesController < ApiController
+  before_action -> { doorkeeper_authorize! :follow }
+  before_action :require_user!
+
+  respond_to :json
+
+  def index
+    results   = Mute.where(account: current_account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
+    accounts  = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
+    @accounts = results.map { |f| accounts[f.target_account_id] }
+
+    # set_account_counters_maps(@accounts)
+
+    next_path = api_v1_mutes_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
+    prev_path = api_v1_mutes_url(since_id: results.first.id) unless results.empty?
+
+    set_pagination_headers(next_path, prev_path)
+  end
+end
diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb
index 877356a75..7bbc5419c 100644
--- a/app/controllers/api/v1/notifications_controller.rb
+++ b/app/controllers/api/v1/notifications_controller.rb
@@ -14,10 +14,10 @@ class Api::V1::NotificationsController < ApiController
     statuses       = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
 
     set_maps(statuses)
-    set_counters_maps(statuses)
-    set_account_counters_maps(@notifications.map(&:from_account))
+    # set_counters_maps(statuses)
+    # set_account_counters_maps(@notifications.map(&:from_account))
 
-    next_path = api_v1_notifications_url(max_id: @notifications.last.id)    if @notifications.size == limit_param(DEFAULT_NOTIFICATIONS_LIMIT)
+    next_path = api_v1_notifications_url(max_id: @notifications.last.id)    unless @notifications.empty?
     prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty?
 
     set_pagination_headers(next_path, prev_path)
diff --git a/app/controllers/api/v1/reports_controller.rb b/app/controllers/api/v1/reports_controller.rb
new file mode 100644
index 000000000..f83c573cb
--- /dev/null
+++ b/app/controllers/api/v1/reports_controller.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+class Api::V1::ReportsController < ApiController
+  before_action -> { doorkeeper_authorize! :read }, except: [:create]
+  before_action -> { doorkeeper_authorize! :write }, only:  [:create]
+  before_action :require_user!
+
+  respond_to :json
+
+  def index
+    @reports = Report.where(account: current_account)
+  end
+
+  def create
+    status_ids = report_params[:status_ids].is_a?(Enumerable) ? report_params[:status_ids] : [report_params[:status_ids]]
+
+    @report = Report.create!(account: current_account,
+                             target_account: Account.find(report_params[:account_id]),
+                             status_ids: Status.find(status_ids).pluck(:id),
+                             comment: report_params[:comment])
+
+    render :show
+  end
+
+  private
+
+  def report_params
+    params.permit(:account_id, :comment, status_ids: [])
+  end
+end
diff --git a/app/controllers/api/v1/search_controller.rb b/app/controllers/api/v1/search_controller.rb
new file mode 100644
index 000000000..6b1292458
--- /dev/null
+++ b/app/controllers/api/v1/search_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class Api::V1::SearchController < ApiController
+  respond_to :json
+
+  def index
+    @search = OpenStruct.new(SearchService.new.call(params[:q], 5, params[:resolve] == 'true', current_account))
+  end
+end
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 69cbdce5d..4ece7e702 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -23,7 +23,7 @@ class Api::V1::StatusesController < ApiController
     statuses = [@status] + @context[:ancestors] + @context[:descendants]
 
     set_maps(statuses)
-    set_counters_maps(statuses)
+    # set_counters_maps(statuses)
   end
 
   def card
@@ -36,7 +36,7 @@ class Api::V1::StatusesController < ApiController
     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |r| accounts[r.account_id] }
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = reblogged_by_api_v1_status_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
     prev_path = reblogged_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
@@ -51,7 +51,7 @@ class Api::V1::StatusesController < ApiController
     accounts  = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
     @accounts = results.map { |f| accounts[f.account_id] }
 
-    set_account_counters_maps(@accounts)
+    # set_account_counters_maps(@accounts)
 
     next_path = favourited_by_api_v1_status_url(max_id: results.last.id)    if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
     prev_path = favourited_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
@@ -62,12 +62,11 @@ class Api::V1::StatusesController < ApiController
   end
 
   def create
-    @status = PostStatusService.new.call(current_user.account, params[:status], params[:in_reply_to_id].blank? ? nil : Status.find(params[:in_reply_to_id]), media_ids: params[:media_ids],
-                                                                                                                                                             sensitive: params[:sensitive],
-                                                                                                                                                             spoiler_text: params[:spoiler_text],
-                                                                                                                                                             visibility: params[:visibility],
-                                                                                                                                                             application: doorkeeper_token.application)
-
+    @status = PostStatusService.new.call(current_user.account, status_params[:status], status_params[:in_reply_to_id].blank? ? nil : Status.find(status_params[:in_reply_to_id]), media_ids: status_params[:media_ids],
+                                                                                                                                                                                  sensitive: status_params[:sensitive],
+                                                                                                                                                                                  spoiler_text: status_params[:spoiler_text],
+                                                                                                                                                                                  visibility: status_params[:visibility],
+                                                                                                                                                                                  application: doorkeeper_token.application)
     render action: :show
   end
 
@@ -112,4 +111,8 @@ class Api::V1::StatusesController < ApiController
     @status = Status.find(params[:id])
     raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
   end
+
+  def status_params
+    params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
+  end
 end
diff --git a/app/controllers/api/v1/timelines_controller.rb b/app/controllers/api/v1/timelines_controller.rb
index a8cc2b288..0446b9e4d 100644
--- a/app/controllers/api/v1/timelines_controller.rb
+++ b/app/controllers/api/v1/timelines_controller.rb
@@ -11,10 +11,10 @@ class Api::V1::TimelinesController < ApiController
     @statuses = cache_collection(@statuses)
 
     set_maps(@statuses)
-    set_counters_maps(@statuses)
-    set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
+    # set_counters_maps(@statuses)
+    # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
 
-    next_path = api_v1_home_timeline_url(max_id: @statuses.last.id)    if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
+    next_path = api_v1_home_timeline_url(max_id: @statuses.last.id)    unless @statuses.empty?
     prev_path = api_v1_home_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
 
     set_pagination_headers(next_path, prev_path)
@@ -27,10 +27,10 @@ class Api::V1::TimelinesController < ApiController
     @statuses = cache_collection(@statuses)
 
     set_maps(@statuses)
-    set_counters_maps(@statuses)
-    set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
+    # set_counters_maps(@statuses)
+    # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
 
-    next_path = api_v1_public_timeline_url(max_id: @statuses.last.id)    if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
+    next_path = api_v1_public_timeline_url(max_id: @statuses.last.id)    unless @statuses.empty?
     prev_path = api_v1_public_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
 
     set_pagination_headers(next_path, prev_path)
@@ -44,10 +44,10 @@ class Api::V1::TimelinesController < ApiController
     @statuses = cache_collection(@statuses)
 
     set_maps(@statuses)
-    set_counters_maps(@statuses)
-    set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
+    # set_counters_maps(@statuses)
+    # set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
 
-    next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id)    if @statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
+    next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id)    unless @statuses.empty?
     prev_path = api_v1_hashtag_timeline_url(params[:id], since_id: @statuses.first.id) unless @statuses.empty?
 
     set_pagination_headers(next_path, prev_path)
diff --git a/app/controllers/api_controller.rb b/app/controllers/api_controller.rb
index 5d2bd9a22..db16f82e5 100644
--- a/app/controllers/api_controller.rb
+++ b/app/controllers/api_controller.rb
@@ -10,7 +10,7 @@ class ApiController < ApplicationController
 
   before_action :set_rate_limit_headers
 
-  rescue_from ActiveRecord::RecordInvalid do |e|
+  rescue_from ActiveRecord::RecordInvalid, Mastodon::ValidationError do |e|
     render json: { error: e.to_s }, status: 422
   end
 
@@ -30,7 +30,7 @@ class ApiController < ApplicationController
     render json: { error: 'Remote SSL certificate could not be verified' }, status: 503
   end
 
-  rescue_from Mastodon::NotPermitted do
+  rescue_from Mastodon::NotPermittedError do
     render json: { error: 'This action is not allowed' }, status: 403
   end
 
@@ -79,6 +79,7 @@ class ApiController < ApplicationController
 
   def require_user!
     current_resource_owner
+    set_user_activity
   rescue ActiveRecord::RecordNotFound
     render json: { error: 'This method requires an authenticated user' }, status: 422
   end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index e4b6d0faf..c06142fd4 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -39,7 +39,14 @@ class ApplicationController < ActionController::Base
   end
 
   def set_user_activity
-    current_user.touch(:current_sign_in_at) if !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
+    return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
+
+    # Mark user as signed-in today
+    current_user.update_tracked_fields(request)
+
+    # If the sign in is after a two week break, we need to regenerate their feed
+    RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
+    return
   end
 
   def check_suspension
@@ -51,21 +58,21 @@ class ApplicationController < ActionController::Base
   def not_found
     respond_to do |format|
       format.any  { head 404 }
-      format.html { render 'errors/404', layout: 'error' }
+      format.html { render 'errors/404', layout: 'error', status: 404 }
     end
   end
 
   def gone
     respond_to do |format|
       format.any  { head 410 }
-      format.html { render 'errors/410', layout: 'error' }
+      format.html { render 'errors/410', layout: 'error', status: 410 }
     end
   end
 
   def unprocessable_entity
     respond_to do |format|
       format.any  { head 422 }
-      format.html { render 'errors/422', layout: 'error' }
+      format.html { render 'errors/422', layout: 'error', status: 422 }
     end
   end
 
diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb
index 501e66807..4881c074a 100644
--- a/app/controllers/auth/registrations_controller.rb
+++ b/app/controllers/auth/registrations_controller.rb
@@ -3,7 +3,7 @@
 class Auth::RegistrationsController < Devise::RegistrationsController
   layout :determine_layout
 
-  before_action :check_single_user_mode
+  before_action :check_enabled_registrations, only: [:new, :create]
   before_action :configure_sign_up_params, only: [:create]
 
   protected
@@ -27,12 +27,12 @@ class Auth::RegistrationsController < Devise::RegistrationsController
     new_user_session_path
   end
 
-  def check_single_user_mode
-    redirect_to root_path if Rails.configuration.x.single_user_mode
+  def check_enabled_registrations
+    redirect_to root_path if Rails.configuration.x.single_user_mode || !Setting.open_registrations
   end
-  
+
   private
-  
+
   def determine_layout
     %w(edit update).include?(action_name) ? 'admin' : 'auth'
   end
diff --git a/app/controllers/authorize_follow_controller.rb b/app/controllers/authorize_follow_controller.rb
index e866b5599..c98a5f45f 100644
--- a/app/controllers/authorize_follow_controller.rb
+++ b/app/controllers/authorize_follow_controller.rb
@@ -25,7 +25,7 @@ class AuthorizeFollowController < ApplicationController
     else
       redirect_to web_url("accounts/#{@account.id}")
     end
-  rescue ActiveRecord::RecordNotFound, Mastodon::NotPermitted
+  rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError
     render :error
   end
 
diff --git a/app/controllers/concerns/obfuscate_filename.rb b/app/controllers/concerns/obfuscate_filename.rb
index 9f12cb7e9..9c896fb09 100644
--- a/app/controllers/concerns/obfuscate_filename.rb
+++ b/app/controllers/concerns/obfuscate_filename.rb
@@ -1,4 +1,5 @@
 # frozen_string_literal: true
+
 module ObfuscateFilename
   extend ActiveSupport::Concern
 
@@ -12,6 +13,10 @@ module ObfuscateFilename
     file = params.dig(*path)
     return if file.nil?
 
-    file.original_filename = 'media' + File.extname(file.original_filename)
+    file.original_filename = secure_token + File.extname(file.original_filename)
+  end
+
+  def secure_token(length = 16)
+    SecureRandom.hex(length / 2)
   end
 end
diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb
index feaad04f6..7c25266d8 100644
--- a/app/controllers/oauth/authorizations_controller.rb
+++ b/app/controllers/oauth/authorizations_controller.rb
@@ -3,6 +3,7 @@
 class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   skip_before_action :authenticate_resource_owner!
 
+  before_action :set_locale
   before_action :store_current_location
   before_action :authenticate_resource_owner!
 
@@ -11,4 +12,10 @@ class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
   def store_current_location
     store_location_for(:user, request.url)
   end
+
+  def set_locale
+    I18n.locale = current_user.try(:locale) || I18n.default_locale
+  rescue I18n::InvalidLocale
+    I18n.locale = I18n.default_locale
+  end
 end
diff --git a/app/controllers/remote_follow_controller.rb b/app/controllers/remote_follow_controller.rb
index 7d4bfe6ce..1e3f786ec 100644
--- a/app/controllers/remote_follow_controller.rb
+++ b/app/controllers/remote_follow_controller.rb
@@ -8,6 +8,7 @@ class RemoteFollowController < ApplicationController
 
   def new
     @remote_follow = RemoteFollow.new
+    @remote_follow.acct = session[:remote_follow] if session.key?(:remote_follow)
   end
 
   def create
@@ -22,6 +23,8 @@ class RemoteFollowController < ApplicationController
         render(:new) && return
       end
 
+      session[:remote_follow] = @remote_follow.acct
+
       redirect_to Addressable::Template.new(redirect_url_link.template).expand(uri: "#{@account.username}@#{Rails.configuration.x.local_domain}").to_s
     else
       render :new
diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb
new file mode 100644
index 000000000..4fcec5322
--- /dev/null
+++ b/app/controllers/settings/exports_controller.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require 'csv'
+
+class Settings::ExportsController < ApplicationController
+  layout 'admin'
+
+  before_action :authenticate_user!
+  before_action :set_account
+
+  def show
+    @total_storage = current_account.media_attachments.sum(:file_file_size)
+    @total_follows = current_account.following.count
+    @total_blocks  = current_account.blocking.count
+  end
+
+  def download_following_list
+    @accounts = current_account.following
+
+    respond_to do |format|
+      format.csv { render text: accounts_list_to_csv(@accounts) }
+    end
+  end
+
+  def download_blocking_list
+    @accounts = current_account.blocking
+
+    respond_to do |format|
+      format.csv { render text: accounts_list_to_csv(@accounts) }
+    end
+  end
+
+  private
+
+  def set_account
+    @account = current_user.account
+  end
+
+  def accounts_list_to_csv(list)
+    CSV.generate do |csv|
+      list.each do |account|
+        csv << [(account.local? ? "#{account.username}@#{Rails.configuration.x.local_domain}" : account.acct)]
+      end
+    end
+  end
+end
diff --git a/app/controllers/settings/imports_controller.rb b/app/controllers/settings/imports_controller.rb
new file mode 100644
index 000000000..cbb5e65da
--- /dev/null
+++ b/app/controllers/settings/imports_controller.rb
@@ -0,0 +1,34 @@
+# frozen_string_literal: true
+
+class Settings::ImportsController < ApplicationController
+  layout 'admin'
+
+  before_action :authenticate_user!
+  before_action :set_account
+
+  def show
+    @import = Import.new
+  end
+
+  def create
+    @import = Import.new(import_params)
+    @import.account = @account
+
+    if @import.save
+      ImportWorker.perform_async(@import.id)
+      redirect_to settings_import_path, notice: I18n.t('imports.success')
+    else
+      render action: :show
+    end
+  end
+
+  private
+
+  def set_account
+    @account = current_user.account
+  end
+
+  def import_params
+    params.require(:import).permit(:data, :type)
+  end
+end
diff --git a/app/controllers/settings/preferences_controller.rb b/app/controllers/settings/preferences_controller.rb
index b7479bf8c..60400e465 100644
--- a/app/controllers/settings/preferences_controller.rb
+++ b/app/controllers/settings/preferences_controller.rb
@@ -14,6 +14,7 @@ class Settings::PreferencesController < ApplicationController
       reblog:         user_params[:notification_emails][:reblog]         == '1',
       favourite:      user_params[:notification_emails][:favourite]      == '1',
       mention:        user_params[:notification_emails][:mention]        == '1',
+      digest:         user_params[:notification_emails][:digest]         == '1',
     }
 
     current_user.settings['interactions'] = {
@@ -33,6 +34,6 @@ class Settings::PreferencesController < ApplicationController
   private
 
   def user_params
-    params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention], interactions: [:must_be_follower, :must_be_following])
+    params.require(:user).permit(:locale, :setting_default_privacy, notification_emails: [:follow, :follow_request, :reblog, :favourite, :mention, :digest], interactions: [:must_be_follower, :must_be_following])
   end
 end
diff --git a/app/controllers/settings/two_factor_auths_controller.rb b/app/controllers/settings/two_factor_auths_controller.rb
index f34295cb9..cfee92391 100644
--- a/app/controllers/settings/two_factor_auths_controller.rb
+++ b/app/controllers/settings/two_factor_auths_controller.rb
@@ -8,7 +8,8 @@ class Settings::TwoFactorAuthsController < ApplicationController
   def show
     return unless current_user.otp_required_for_login
 
-    @qrcode = RQRCode::QRCode.new(current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain))
+    @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain)
+    @qrcode        = RQRCode::QRCode.new(@provision_url)
   end
 
   def enable
diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb
new file mode 100644
index 000000000..696bb4f52
--- /dev/null
+++ b/app/controllers/statuses_controller.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+
+class StatusesController < ApplicationController
+  layout 'public'
+
+  before_action :set_account
+  before_action :set_status
+  before_action :set_link_headers
+  before_action :check_account_suspension
+
+  def show
+    @ancestors   = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : []
+    @descendants = cache_collection(@status.descendants(current_account), Status)
+
+    render 'stream_entries/show'
+  end
+
+  private
+
+  def set_account
+    @account = Account.find_local!(params[:account_username])
+  end
+
+  def set_link_headers
+    response.headers['Link'] = LinkHeader.new([[account_stream_entry_url(@account, @status.stream_entry, format: 'atom'), [%w(rel alternate), %w(type application/atom+xml)]]])
+  end
+
+  def set_status
+    @status       = @account.statuses.find(params[:id])
+    @stream_entry = @status.stream_entry
+    @type         = @stream_entry.activity_type.downcase
+
+    raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
+  end
+
+  def check_account_suspension
+    gone if @account.suspended?
+  end
+end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
index da284d80e..de38b3602 100644
--- a/app/controllers/stream_entries_controller.rb
+++ b/app/controllers/stream_entries_controller.rb
@@ -43,13 +43,13 @@ class StreamEntriesController < ApplicationController
   end
 
   def set_stream_entry
-    @stream_entry = @account.stream_entries.find(params[:id])
+    @stream_entry = @account.stream_entries.where(activity_type: 'Status').find(params[:id])
     @type         = @stream_entry.activity_type.downcase
 
     raise ActiveRecord::RecordNotFound if @stream_entry.activity.nil? || (@stream_entry.hidden? && (@stream_entry.activity_type != 'Status' || (@stream_entry.activity_type == 'Status' && !@stream_entry.activity.permitted?(current_account))))
   end
 
   def check_account_suspension
-    head 410 if @account.suspended?
+    gone if @account.suspended?
   end
 end
diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb
index 9e0277860..6db87cefc 100644
--- a/app/controllers/xrd_controller.rb
+++ b/app/controllers/xrd_controller.rb
@@ -36,11 +36,14 @@ class XrdController < ApplicationController
   end
 
   def username_from_resource
-    if resource_param.start_with?('acct:') || resource_param.include?('@')
-      resource_param.split('@').first.gsub('acct:', '')
+    if resource_param =~ /\Ahttps?:\/\//
+      path_params = Rails.application.routes.recognize_path(resource_param)
+      raise ActiveRecord::RecordNotFound unless path_params[:controller] == 'users' && path_params[:action] == 'show'
+      path_params[:username]
     else
-      url = Addressable::URI.parse(resource_param)
-      url.path.gsub('/users/', '')
+      username, domain = resource_param.gsub(/\Aacct:/, '').split('@')
+      raise ActiveRecord::RecordNotFound unless TagManager.instance.local_domain?(domain)
+      username
     end
   end