about summary refs log tree commit diff
path: root/app/controllers/api/v1
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-06-27 14:22:30 -0500
committerFire Demon <firedemon@creature.cafe>2020-09-08 03:37:04 -0500
commit9d4f18b984d6699bdf96e5f5963edfe80063426c (patch)
treee00fb54963769a259cd9bbe97754a2a872d028be /app/controllers/api/v1
parent437d71bddf967573df3912ee5976f7c5a5a7b4c7 (diff)
Monsterfork v2 Kaiju Commit 2020.06.27.1 - 2020.09.05.5
Diffstat (limited to 'app/controllers/api/v1')
-rw-r--r--app/controllers/api/v1/accounts/credentials_controller.rb4
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb77
-rw-r--r--app/controllers/api/v1/accounts_controller.rb2
-rw-r--r--app/controllers/api/v1/admin/domain_allows_controller.rb54
-rw-r--r--app/controllers/api/v1/admin/domain_blocks_controller.rb54
-rw-r--r--app/controllers/api/v1/domain_permissions_controller.rb81
-rw-r--r--app/controllers/api/v1/instances/activity_controller.rb4
-rw-r--r--app/controllers/api/v1/instances/peers_controller.rb4
-rw-r--r--app/controllers/api/v1/instances_controller.rb2
-rw-r--r--app/controllers/api/v1/polls/votes_controller.rb1
-rw-r--r--app/controllers/api/v1/polls_controller.rb1
-rw-r--r--app/controllers/api/v1/statuses/hides_controller.rb28
-rw-r--r--app/controllers/api/v1/statuses/mutes_controller.rb4
-rw-r--r--app/controllers/api/v1/statuses/pins_controller.rb2
-rw-r--r--app/controllers/api/v1/statuses/publishing_controller.rb26
-rw-r--r--app/controllers/api/v1/statuses_controller.rb100
-rw-r--r--app/controllers/api/v1/timelines/public_controller.rb3
17 files changed, 403 insertions, 44 deletions
diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index 64b5cb747..3c8187a99 100644
--- a/app/controllers/api/v1/accounts/credentials_controller.rb
+++ b/app/controllers/api/v1/accounts/credentials_controller.rb
@@ -21,7 +21,9 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController
   private
 
   def account_params
-    params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, fields_attributes: [:name, :value])
+    params.permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable,
+                  :require_dereference, :show_replies, :show_unlisted,
+                  fields_attributes: [:name, :value])
   end
 
   def user_settings_params
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index 85a9133e3..d7e973e31 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -8,7 +8,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
 
   def index
     @statuses = load_statuses
-    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id)
+    render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_account&.id)
   end
 
   private
@@ -17,17 +17,17 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
     @account = Account.find(params[:account_id])
   end
 
+  def owner?
+    @account.id == current_account&.id
+  end
+
   def load_statuses
     cached_account_statuses
   end
 
   def cached_account_statuses
     statuses = truthy_param?(:pinned) ? pinned_scope : permitted_account_statuses
-
     statuses.merge!(only_media_scope) if truthy_param?(:only_media)
-    statuses.merge!(no_replies_scope) if truthy_param?(:exclude_replies)
-    statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
-    statuses.merge!(hashtag_scope)    if params[:tagged].present?
 
     cache_collection_paginated_by_id(
       statuses,
@@ -38,39 +38,66 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def permitted_account_statuses
-    @account.statuses.permitted_for(@account, current_account)
+    return mentions_scope if truthy_param?(:mentions)
+    return Status.none if unauthorized?
+
+    @account.statuses.permitted_for(
+      @account,
+      current_account,
+      include_semiprivate: true,
+      include_reblogs: include_reblogs?,
+      include_replies: include_replies?,
+      only_reblogs: only_reblogs?,
+      only_replies: only_replies?,
+      include_unpublished: owner?,
+      tag: params[:tagged]
+    )
   end
 
   def only_media_scope
     Status.joins(:media_attachments).merge(@account.media_attachments.reorder(nil)).group(:id)
   end
 
-  def pinned_scope
-    return Status.none if @account.blocking?(current_account)
+  def unauthorized?
+    (@account.private && !following?(@account)) || (@account.require_auth && !current_account?)
+  end
 
-    @account.pinned_statuses
+  def include_reblogs?
+    params[:include_reblogs].present? ? truthy_param?(:include_reblogs) : !truthy_param?(:exclude_reblogs)
+  end
+
+  def include_replies?
+    return false unless owner? || @account.show_replies?
+
+    params[:include_replies].present? ? truthy_param?(:include_replies) : !truthy_param?(:exclude_replies)
   end
 
-  def no_replies_scope
-    Status.without_replies
+  def only_reblogs?
+    truthy_param?(:only_reblogs).presence || false
   end
 
-  def no_reblogs_scope
-    Status.without_reblogs
+  def only_replies?
+    return false unless owner? || @account.show_replies?
+
+    truthy_param?(:only_replies).presence || false
   end
 
-  def hashtag_scope
-    tag = Tag.find_normalized(params[:tagged])
+  def mentions_scope
+    return Status.none unless current_account?
+
+    Status.mentions_between(@account, current_account)
+  end
 
-    if tag
-      Status.tagged_with(tag.id)
-    else
-      Status.none
-    end
+  def pinned_scope
+    return Status.none if @account.blocking?(current_account)
+
+    @account.pinned_statuses
   end
 
   def pagination_params(core_params)
-    params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params)
+    params.slice(:limit, :only_media, :include_replies, :exclude_replies, :only_replies, :include_reblogs, :exclude_reblogs, :only_relogs, :mentions)
+          .permit(:limit, :only_media, :include_replies, :exclude_replies, :only_replies, :include_reblogs, :exclude_reblogs, :only_relogs, :mentions)
+          .merge(core_params)
   end
 
   def insert_pagination_headers
@@ -78,15 +105,11 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def next_path
-    if records_continue?
-      api_v1_account_statuses_url pagination_params(max_id: pagination_max_id)
-    end
+    api_v1_account_statuses_url pagination_params(max_id: pagination_max_id) if records_continue?
   end
 
   def prev_path
-    unless @statuses.empty?
-      api_v1_account_statuses_url pagination_params(min_id: pagination_since_id)
-    end
+    api_v1_account_statuses_url pagination_params(min_id: pagination_since_id) unless @statuses.empty?
   end
 
   def records_continue?
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 0080faf33..e9f848ac9 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -44,7 +44,7 @@ class Api::V1::AccountsController < Api::BaseController
   end
 
   def mute
-    MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications))
+    MuteService.new.call(current_user.account, @account, notifications: truthy_param?(:notifications), timelines_only: truthy_param?(:timelines_only))
     render json: @account, serializer: REST::RelationshipSerializer, relationships: relationships
   end
 
diff --git a/app/controllers/api/v1/admin/domain_allows_controller.rb b/app/controllers/api/v1/admin/domain_allows_controller.rb
new file mode 100644
index 000000000..1b150d480
--- /dev/null
+++ b/app/controllers/api/v1/admin/domain_allows_controller.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+class Api::V1::Admin::DomainAllowsController < Api::BaseController
+  include Authorization
+
+  LIMIT = 100
+
+  before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:domain_allows' }, only: :show
+  before_action :require_staff!
+  after_action :insert_pagination_headers, only: :show
+
+  def show
+    @allows = load_domain_allows
+    render json: @allows
+  end
+
+  private
+
+  def load_domain_allows
+    DomainAllow.paginate_by_max_id(
+      limit_param(LIMIT),
+      params[:max_id],
+      params[:since_id]
+    )
+  end
+
+  def insert_pagination_headers
+    set_pagination_headers(next_path, prev_path)
+  end
+
+  def next_path
+    api_v1_admin_domain_allows_url pagination_params(max_id: pagination_max_id) if records_continue?
+  end
+
+  def prev_path
+    api_v1_admin_domain_allows_url pagination_params(since_id: pagination_since_id) unless @allows.empty?
+  end
+
+  def pagination_max_id
+    @allows.last.id
+  end
+
+  def pagination_since_id
+    @allows.first.id
+  end
+
+  def records_continue?
+    @allows.size == limit_param(LIMIT)
+  end
+
+  def pagination_params(core_params)
+    params.slice(:limit).permit(:limit).merge(core_params)
+  end
+end
diff --git a/app/controllers/api/v1/admin/domain_blocks_controller.rb b/app/controllers/api/v1/admin/domain_blocks_controller.rb
new file mode 100644
index 000000000..c0ce0da25
--- /dev/null
+++ b/app/controllers/api/v1/admin/domain_blocks_controller.rb
@@ -0,0 +1,54 @@
+# frozen_string_literal: true
+
+class Api::V1::Admin::DomainBlocksController < Api::BaseController
+  include Authorization
+
+  LIMIT = 100
+
+  before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:domain_blocks' }, only: :show
+  before_action :require_staff!
+  after_action :insert_pagination_headers, only: :show
+
+  def show
+    @blocks = load_domain_blocks
+    render json: @blocks
+  end
+
+  private
+
+  def load_domain_blocks
+    DomainBlock.paginate_by_max_id(
+      limit_param(LIMIT),
+      params[:max_id],
+      params[:since_id]
+    )
+  end
+
+  def insert_pagination_headers
+    set_pagination_headers(next_path, prev_path)
+  end
+
+  def next_path
+    api_v1_admin_domain_blocks_url pagination_params(max_id: pagination_max_id) if records_continue?
+  end
+
+  def prev_path
+    api_v1_admin_domain_blocks_url pagination_params(since_id: pagination_since_id) unless @blocks.empty?
+  end
+
+  def pagination_max_id
+    @blocks.last.id
+  end
+
+  def pagination_since_id
+    @blocks.first.id
+  end
+
+  def records_continue?
+    @blocks.size == limit_param(LIMIT)
+  end
+
+  def pagination_params(core_params)
+    params.slice(:limit).permit(:limit).merge(core_params)
+  end
+end
diff --git a/app/controllers/api/v1/domain_permissions_controller.rb b/app/controllers/api/v1/domain_permissions_controller.rb
new file mode 100644
index 000000000..1b0e37135
--- /dev/null
+++ b/app/controllers/api/v1/domain_permissions_controller.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+class Api::V1::DomainPermissionsController < Api::BaseController
+  before_action -> { doorkeeper_authorize! :read, :'read:domain_permissions', :'read:domain_permissions:account' }, only: :show
+  before_action -> { doorkeeper_authorize! :write, :'write:domain_permissions', :'write:domain_permissions:account' }, only: [:create, :update, :destroy]
+  before_action :require_user!
+  before_action :set_permission, except: [:show, :create]
+  after_action :insert_pagination_headers
+
+  LIMIT = 100
+
+  def show
+    @permissions = load_account_domain_permissions
+    render json: @permissions, each_serializer: REST::AccountDomainPermissionSerializer
+  end
+
+  def create
+    @permission = current_account.domain_permissions.create!(domain_permission_params)
+    render json: @permission, serializer: REST::AccountDomainPermissionSerializer
+  end
+
+  def update
+    @permission.update!(domain_permission_params)
+    render json: @permission, serializer: REST::AccountDomainPermissionSerializer
+  end
+
+  def destroy
+    @permission.destroy!
+    render_empty
+  end
+
+  private
+
+  def load_account_domain_permissions
+    account_domain_permissions.paginate_by_max_id(
+      limit_param(LIMIT),
+      params[:max_id],
+      params[:since_id]
+    )
+  end
+
+  def set_permission
+    @permission = current_account.domain_permissions.find(params[:id])
+  end
+
+  def account_domain_permissions
+    current_account.domain_permissions
+  end
+
+  def insert_pagination_headers
+    set_pagination_headers(next_path, prev_path)
+  end
+
+  def next_path
+    api_v1_domain_permissions_url pagination_params(max_id: pagination_max_id) if records_continue?
+  end
+
+  def prev_path
+    api_v1_domain_permissions_url pagination_params(since_id: pagination_since_id) unless @permissions.empty?
+  end
+
+  def pagination_max_id
+    @permissions.last.id
+  end
+
+  def pagination_since_id
+    @permissions.first.id
+  end
+
+  def records_continue?
+    @permissions.size == limit_param(LIMIT)
+  end
+
+  def pagination_params(core_params)
+    params.slice(:limit).permit(:limit).merge(core_params)
+  end
+
+  def domain_permission_params
+    params.permit(:domain, :visibility)
+  end
+end
diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb
index 4f6b4bcbf..f2ac902e1 100644
--- a/app/controllers/api/v1/instances/activity_controller.rb
+++ b/app/controllers/api/v1/instances/activity_controller.rb
@@ -4,7 +4,7 @@ class Api::V1::Instances::ActivityController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def show
     expires_in 1.day, public: true
@@ -33,6 +33,6 @@ class Api::V1::Instances::ActivityController < Api::BaseController
   end
 
   def require_enabled_api!
-    head 404 unless Setting.activity_api_enabled && !whitelist_mode?
+    head 404 unless Setting.activity_api_enabled #&& !whitelist_mode?
   end
 end
diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb
index 9fa440935..d30ef1fe9 100644
--- a/app/controllers/api/v1/instances/peers_controller.rb
+++ b/app/controllers/api/v1/instances/peers_controller.rb
@@ -4,7 +4,7 @@ class Api::V1::Instances::PeersController < Api::BaseController
   before_action :require_enabled_api!
 
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def index
     expires_in 1.day, public: true
@@ -14,6 +14,6 @@ class Api::V1::Instances::PeersController < Api::BaseController
   private
 
   def require_enabled_api!
-    head 404 unless Setting.peers_api_enabled && !whitelist_mode?
+    head 404 unless Setting.peers_api_enabled #&& !whitelist_mode?
   end
 end
diff --git a/app/controllers/api/v1/instances_controller.rb b/app/controllers/api/v1/instances_controller.rb
index 5b5058a7b..844bab68a 100644
--- a/app/controllers/api/v1/instances_controller.rb
+++ b/app/controllers/api/v1/instances_controller.rb
@@ -2,7 +2,7 @@
 
 class Api::V1::InstancesController < Api::BaseController
   skip_before_action :set_cache_headers
-  skip_before_action :require_authenticated_user!, unless: :whitelist_mode?
+  skip_before_action :require_authenticated_user! #, unless: :whitelist_mode?
 
   def show
     expires_in 3.minutes, public: true
diff --git a/app/controllers/api/v1/polls/votes_controller.rb b/app/controllers/api/v1/polls/votes_controller.rb
index 513b937ef..91ca96ef0 100644
--- a/app/controllers/api/v1/polls/votes_controller.rb
+++ b/app/controllers/api/v1/polls/votes_controller.rb
@@ -17,6 +17,7 @@ class Api::V1::Polls::VotesController < Api::BaseController
   def set_poll
     @poll = Poll.attached.find(params[:poll_id])
     authorize @poll.status, :show?
+    authorize @poll.status.reblog, :show? if @poll.status.reblog?
   rescue Mastodon::NotPermittedError
     not_found
   end
diff --git a/app/controllers/api/v1/polls_controller.rb b/app/controllers/api/v1/polls_controller.rb
index 6435e9f0d..75f5a9f08 100644
--- a/app/controllers/api/v1/polls_controller.rb
+++ b/app/controllers/api/v1/polls_controller.rb
@@ -16,6 +16,7 @@ class Api::V1::PollsController < Api::BaseController
   def set_poll
     @poll = Poll.attached.find(params[:id])
     authorize @poll.status, :show?
+    authorize @poll.status.reblog, :show? if @poll.status.reblog?
   rescue Mastodon::NotPermittedError
     not_found
   end
diff --git a/app/controllers/api/v1/statuses/hides_controller.rb b/app/controllers/api/v1/statuses/hides_controller.rb
new file mode 100644
index 000000000..8c5457c82
--- /dev/null
+++ b/app/controllers/api/v1/statuses/hides_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+class Api::V1::Statuses::HidesController < Api::BaseController
+  include Authorization
+
+  before_action -> { doorkeeper_authorize! :write, :'write:mutes' }
+  before_action :require_user!
+  before_action :set_status
+
+  def create
+    MuteStatusService.new.call(current_account, @status)
+    render json: @status, serializer: REST::StatusSerializer
+  end
+
+  def destroy
+    current_account.unmute_status!(@status)
+    render json: @status, serializer: REST::StatusSerializer
+  end
+
+  private
+
+  def set_status
+    @status = Status.find(params[:status_id])
+    authorize @status, :show?
+  rescue Mastodon::NotPermittedError
+    not_found
+  end
+end
diff --git a/app/controllers/api/v1/statuses/mutes_controller.rb b/app/controllers/api/v1/statuses/mutes_controller.rb
index 87071a2b9..73d9df734 100644
--- a/app/controllers/api/v1/statuses/mutes_controller.rb
+++ b/app/controllers/api/v1/statuses/mutes_controller.rb
@@ -9,12 +9,14 @@ class Api::V1::Statuses::MutesController < Api::BaseController
   before_action :set_conversation
 
   def create
-    current_account.mute_conversation!(@conversation)
+    MuteConversationService.new.call(current_account, @status.conversation, hidden: truthy_param?(:hide))
     @mutes_map = { @conversation.id => true }
 
     render json: @status, serializer: REST::StatusSerializer
   end
 
+  alias update create
+
   def destroy
     current_account.unmute_conversation!(@conversation)
     @mutes_map = { @conversation.id => false }
diff --git a/app/controllers/api/v1/statuses/pins_controller.rb b/app/controllers/api/v1/statuses/pins_controller.rb
index 51b1621b6..187b6145c 100644
--- a/app/controllers/api/v1/statuses/pins_controller.rb
+++ b/app/controllers/api/v1/statuses/pins_controller.rb
@@ -9,7 +9,7 @@ class Api::V1::Statuses::PinsController < Api::BaseController
 
   def create
     StatusPin.create!(account: current_account, status: @status)
-    distribute_add_activity!
+    distribute_add_activity! unless @status.semiprivate?
     render json: @status, serializer: REST::StatusSerializer
   end
 
diff --git a/app/controllers/api/v1/statuses/publishing_controller.rb b/app/controllers/api/v1/statuses/publishing_controller.rb
new file mode 100644
index 000000000..97c052e22
--- /dev/null
+++ b/app/controllers/api/v1/statuses/publishing_controller.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+class Api::V1::Statuses::PublishingController < Api::BaseController
+  include Authorization
+
+  before_action -> { doorkeeper_authorize! :write, :'write:statuses:publish' }
+  before_action :require_user!
+  before_action :set_status
+
+  def create
+    PublishStatusService.new.call(@status)
+
+    render json: @status,
+           serializer: (@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer),
+           source_requested: truthy_param?(:source)
+  end
+
+  private
+
+  def set_status
+    @status = Status.unpublished.find(params[:status_id])
+    authorize @status, :destroy?
+  rescue Mastodon::NotPermittedError
+    not_found
+  end
+end
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index c8529318f..cbd232a50 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -19,7 +19,7 @@ class Api::V1::StatusesController < Api::BaseController
 
   def show
     @status = cache_collection([@status], Status).first
-    render json: @status, serializer: REST::StatusSerializer
+    render json: @status, serializer: REST::StatusSerializer, source_requested: truthy_param?(:source)
   end
 
   def context
@@ -31,7 +31,7 @@ class Api::V1::StatusesController < Api::BaseController
     @context = Context.new(ancestors: loaded_ancestors, descendants: loaded_descendants)
     statuses = [@status] + @context.ancestors + @context.descendants
 
-    render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id)
+    render json: @context, serializer: REST::ContextSerializer, relationships: StatusRelationshipsPresenter.new(statuses, current_user&.account_id), current_account_id: current_user&.account_id
   end
 
   def create
@@ -41,24 +41,80 @@ class Api::V1::StatusesController < Api::BaseController
                                          media_ids: status_params[:media_ids],
                                          sensitive: status_params[:sensitive],
                                          spoiler_text: status_params[:spoiler_text],
+                                         title: status_params[:title],
+                                         footer: status_params[:footer],
+                                         notify: status_params[:notify],
+                                         publish: status_params[:publish],
                                          visibility: status_params[:visibility],
                                          scheduled_at: status_params[:scheduled_at],
                                          application: doorkeeper_token.application,
                                          poll: status_params[:poll],
                                          content_type: status_params[:content_type],
+                                         tags: parse_tags_param(status_params[:tags]),
+                                         mentions: parse_mentions_param(status_params[:mentions]),
                                          idempotency: request.headers['Idempotency-Key'],
-                                         with_rate_limit: true)
+                                         with_rate_limit: true,
+                                         expires_at: status_params[:expires_at],
+                                         publish_at: status_params[:publish_at])
 
-    render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer
+    render json: @status,
+           serializer: (@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer),
+           source_requested: truthy_param?(:source)
+  end
+
+  def update
+    @status = Status.where(account_id: current_user.account).find(params[:id])
+    authorize @status, :destroy?
+
+    @status = PostStatusService.new.call(current_user.account,
+                                         text: status_params[:status],
+                                         thread: @thread,
+                                         media_ids: status_params[:media_ids],
+                                         sensitive: status_params[:sensitive],
+                                         spoiler_text: status_params[:spoiler_text],
+                                         title: status_params[:title],
+                                         footer: status_params[:footer],
+                                         notify: status_params[:notify],
+                                         publish: status_params[:publish],
+                                         visibility: status_params[:visibility],
+                                         scheduled_at: status_params[:scheduled_at],
+                                         application: doorkeeper_token.application,
+                                         poll: status_params[:poll],
+                                         content_type: status_params[:content_type],
+                                         status: @status,
+                                         tags: parse_tags_param(status_params[:tags]),
+                                         mentions: parse_mentions_param(status_params[:mentions]),
+                                         idempotency: request.headers['Idempotency-Key'],
+                                         with_rate_limit: true,
+                                         expires_at: status_params[:expires_at],
+                                         publish_at: status_params[:publish_at])
+
+    render json: @status,
+           serializer: (@status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer),
+           source_requested: truthy_param?(:source)
   end
 
   def destroy
     @status = Status.where(account_id: current_user.account).find(params[:id])
     authorize @status, :destroy?
 
-    @status.discard
-    RemovalWorker.perform_async(@status.id, redraft: true)
-    @status.account.statuses_count = @status.account.statuses_count - 1
+    if !(current_user.setting_unpublish_on_delete && @status.published?) || truthy_param?(:redraft)
+      @status.discard
+      RemovalWorker.perform_async(@status.id, redraft: true)
+      @status.account.statuses_count = @status.account.statuses_count - 1
+    else
+      RemovalWorker.perform_async(@status.id, redraft: true, unpublish: true)
+      tag_script = "#!redraft #{@status.id}\n"
+      @status.text = "#{tag_script}#{@status.text.sub(/^\s*#!redraft \d+\n/, '')}"
+      @status.original_text = "#{tag_script}#{@status.original_text.sub(/^\s*#!redraft \d+\n/, '')}"
+    end
+
+    @status.local_only = @status.originally_local_only?
+    unless @status.original_text.match?(/^\s*#!\s*federate\b/i)
+      tag_script = "#!federate #{@status.originally_local_only? ? 'off' : 'on'}\n"
+      @status.text.prepend(tag_script)
+      @status.original_text.prepend(tag_script)
+    end
 
     render json: @status, serializer: REST::StatusSerializer, source_requested: true
   end
@@ -84,9 +140,17 @@ class Api::V1::StatusesController < Api::BaseController
       :in_reply_to_id,
       :sensitive,
       :spoiler_text,
+      :title,
+      :footer,
+      :notify,
+      :publish,
       :visibility,
       :scheduled_at,
       :content_type,
+      :expires_at,
+      :publish_at,
+      tags: [],
+      mentions: [],
       media_ids: [],
       poll: [
         :multiple,
@@ -100,4 +164,26 @@ class Api::V1::StatusesController < Api::BaseController
   def pagination_params(core_params)
     params.slice(:limit).permit(:limit).merge(core_params)
   end
+
+  def parse_tags_param(tags_param)
+    return if tags_param.blank?
+
+    tags_param.select { |value| value.respond_to?(:to_str) && value.present? }
+  end
+
+  def parse_mentions_param(mentions_param)
+    return if mentions_param.blank?
+
+    mentions_param.map do |value|
+      next if value.blank?
+
+      value = value.split('@', 3) if value.respond_to?(:to_str)
+      next unless value.is_a?(Enumerable)
+
+      mentioned_account = Account.find_by(username: value[0], domain: value[1])
+      next if mentioned_account.nil? || mentioned_account.suspended?
+
+      mentioned_account
+    end
+  end
 end
diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb
index 52b5cb323..b53f7750f 100644
--- a/app/controllers/api/v1/timelines/public_controller.rb
+++ b/app/controllers/api/v1/timelines/public_controller.rb
@@ -41,7 +41,8 @@ class Api::V1::Timelines::PublicController < Api::BaseController
   end
 
   def public_timeline_statuses
-    Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : truthy_param?(:local))
+    local = truthy_param?(:local) ? true : :local_reblogs
+    Status.as_public_timeline(current_account, truthy_param?(:remote) ? :remote : local)
   end
 
   def insert_pagination_headers