about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/accounts_controller.rb13
-rw-r--r--app/controllers/activitypub/collections_controller.rb1
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb4
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js3
-rw-r--r--app/javascript/flavours/glitch/features/status/components/action_bar.js3
-rw-r--r--app/javascript/mastodon/components/status_action_bar.js3
-rw-r--r--app/javascript/mastodon/features/status/components/action_bar.js3
-rw-r--r--app/lib/activitypub/activity/accept.rb13
-rw-r--r--app/lib/activitypub/activity/add.rb3
-rw-r--r--app/serializers/rest/status_serializer.rb2
-rw-r--r--app/services/activitypub/fetch_featured_collection_service.rb6
-rw-r--r--app/validators/status_pin_validator.rb2
-rw-r--r--app/workers/remote_account_refresh_worker.rb24
13 files changed, 65 insertions, 15 deletions
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index f9bd616e4..03c07c50b 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -29,7 +29,7 @@ class AccountsController < ApplicationController
           return
         end
 
-        @pinned_statuses = cache_collection(@account.pinned_statuses.not_local_only, Status) if show_pinned_statuses?
+        @pinned_statuses = cached_filtered_status_pins if show_pinned_statuses?
         @statuses        = cached_filtered_status_page
         @rss_url         = rss_url
 
@@ -65,6 +65,10 @@ class AccountsController < ApplicationController
     [replies_requested?, media_requested?, tag_requested?, params[:max_id].present?, params[:min_id].present?].none?
   end
 
+  def filtered_pinned_statuses
+    @account.pinned_statuses.not_local_only.where(visibility: [:public, :unlisted])
+  end
+
   def filtered_statuses
     default_statuses.tap do |statuses|
       statuses.merge!(hashtag_scope)    if tag_requested?
@@ -143,6 +147,13 @@ class AccountsController < ApplicationController
     request.path.split('.').first.end_with?(Addressable::URI.parse("/tagged/#{params[:tag]}").normalize)
   end
 
+  def cached_filtered_status_pins
+    cache_collection(
+      filtered_pinned_statuses,
+      Status
+    )
+  end
+
   def cached_filtered_status_page
     cache_collection_paginated_by_id(
       filtered_statuses,
diff --git a/app/controllers/activitypub/collections_controller.rb b/app/controllers/activitypub/collections_controller.rb
index 00f3d3cba..ac7ab8a0b 100644
--- a/app/controllers/activitypub/collections_controller.rb
+++ b/app/controllers/activitypub/collections_controller.rb
@@ -21,6 +21,7 @@ class ActivityPub::CollectionsController < ActivityPub::BaseController
     case params[:id]
     when 'featured'
       @items = for_signed_account { cache_collection(@account.pinned_statuses.not_local_only, Status) }
+      @items = @items.map { |item| item.distributable? ? item : ActivityPub::TagManager.instance.uri_for(item) }
     when 'tags'
       @items = for_signed_account { @account.featured_tags }
     when 'devices'
diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb
index 92ccb8061..2c027ea76 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -46,9 +46,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def pinned_scope
-    return Status.none if @account.blocking?(current_account)
-
-    @account.pinned_statuses
+    @account.pinned_statuses.permitted_for(@account, current_account)
   end
 
   def no_replies_scope
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index d63c6b142..650b33b62 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -196,6 +196,7 @@ class StatusActionBar extends ImmutablePureComponent {
     const anonymousAccess    = !me;
     const mutingConversation = status.get('muted');
     const publicStatus       = ['public', 'unlisted'].includes(status.get('visibility'));
+    const pinnableStatus     = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
     const writtenByMe        = status.getIn(['account', 'id']) === me;
 
     let menu = [];
@@ -212,7 +213,7 @@ class StatusActionBar extends ImmutablePureComponent {
 
     menu.push(null);
 
-    if (writtenByMe && publicStatus) {
+    if (writtenByMe && pinnableStatus) {
       menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
       menu.push(null);
     }
diff --git a/app/javascript/flavours/glitch/features/status/components/action_bar.js b/app/javascript/flavours/glitch/features/status/components/action_bar.js
index 6ed5f3865..eb4583026 100644
--- a/app/javascript/flavours/glitch/features/status/components/action_bar.js
+++ b/app/javascript/flavours/glitch/features/status/components/action_bar.js
@@ -146,6 +146,7 @@ class ActionBar extends React.PureComponent {
     const { status, intl } = this.props;
 
     const publicStatus       = ['public', 'unlisted'].includes(status.get('visibility'));
+    const pinnableStatus     = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
     const mutingConversation = status.get('muted');
     const writtenByMe        = status.getIn(['account', 'id']) === me;
 
@@ -158,7 +159,7 @@ class ActionBar extends React.PureComponent {
     }
 
     if (writtenByMe) {
-      if (publicStatus) {
+      if (pinnableStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
         menu.push(null);
       }
diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js
index 85c76edee..d125359e9 100644
--- a/app/javascript/mastodon/components/status_action_bar.js
+++ b/app/javascript/mastodon/components/status_action_bar.js
@@ -225,6 +225,7 @@ class StatusActionBar extends ImmutablePureComponent {
 
     const anonymousAccess    = !me;
     const publicStatus       = ['public', 'unlisted'].includes(status.get('visibility'));
+    const pinnableStatus     = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
     const mutingConversation = status.get('muted');
     const account            = status.get('account');
     const writtenByMe        = status.getIn(['account', 'id']) === me;
@@ -242,7 +243,7 @@ class StatusActionBar extends ImmutablePureComponent {
 
     menu.push({ text: intl.formatMessage(status.get('bookmarked') ? messages.removeBookmark : messages.bookmark), action: this.handleBookmarkClick });
 
-    if (writtenByMe && publicStatus) {
+    if (writtenByMe && pinnableStatus) {
       menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
     }
 
diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js
index ffa2510c0..e60119bc4 100644
--- a/app/javascript/mastodon/features/status/components/action_bar.js
+++ b/app/javascript/mastodon/features/status/components/action_bar.js
@@ -188,6 +188,7 @@ class ActionBar extends React.PureComponent {
     const { status, relationship, intl } = this.props;
 
     const publicStatus       = ['public', 'unlisted'].includes(status.get('visibility'));
+    const pinnableStatus     = ['public', 'unlisted', 'private'].includes(status.get('visibility'));
     const mutingConversation = status.get('muted');
     const account            = status.get('account');
     const writtenByMe        = status.getIn(['account', 'id']) === me;
@@ -201,7 +202,7 @@ class ActionBar extends React.PureComponent {
     }
 
     if (writtenByMe) {
-      if (publicStatus) {
+      if (pinnableStatus) {
         menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick });
         menu.push(null);
       }
diff --git a/app/lib/activitypub/activity/accept.rb b/app/lib/activitypub/activity/accept.rb
index 7010ff43e..5126e23c6 100644
--- a/app/lib/activitypub/activity/accept.rb
+++ b/app/lib/activitypub/activity/accept.rb
@@ -3,7 +3,7 @@
 class ActivityPub::Activity::Accept < ActivityPub::Activity
   def perform
     return accept_follow_for_relay if relay_follow?
-    return follow_request_from_object.authorize! unless follow_request_from_object.nil?
+    return accept_follow!(follow_request_from_object) unless follow_request_from_object.nil?
 
     case @object['type']
     when 'Follow'
@@ -19,7 +19,16 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
     return if target_account.nil? || !target_account.local?
 
     follow_request = FollowRequest.find_by(account: target_account, target_account: @account)
-    follow_request&.authorize!
+    accept_follow!(follow_request)
+  end
+
+  def accept_follow!(request)
+    return if request.nil?
+
+    is_first_follow = !request.target_account.followers.local.exists?
+    request.authorize!
+
+    RemoteAccountRefreshWorker.perform_async(request.target_account_id) if is_first_follow
   end
 
   def accept_follow_for_relay
diff --git a/app/lib/activitypub/activity/add.rb b/app/lib/activitypub/activity/add.rb
index 688ab00b3..845eeaef7 100644
--- a/app/lib/activitypub/activity/add.rb
+++ b/app/lib/activitypub/activity/add.rb
@@ -4,8 +4,7 @@ class ActivityPub::Activity::Add < ActivityPub::Activity
   def perform
     return unless @json['target'].present? && value_or_id(@json['target']) == @account.featured_collection_url
 
-    status   = status_from_uri(object_uri)
-    status ||= fetch_remote_original_status
+    status = status_from_object
 
     return unless !status.nil? && status.account_id == @account.id && !@account.pinned?(status)
 
diff --git a/app/serializers/rest/status_serializer.rb b/app/serializers/rest/status_serializer.rb
index b5dcf6208..45ecd392b 100644
--- a/app/serializers/rest/status_serializer.rb
+++ b/app/serializers/rest/status_serializer.rb
@@ -124,7 +124,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
     current_user? &&
       current_user.account_id == object.account_id &&
       !object.reblog? &&
-      %w(public unlisted).include?(object.visibility)
+      %w(public unlisted private).include?(object.visibility)
   end
 
   def source_requested?
diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb
index 72352aca6..9fce478c1 100644
--- a/app/services/activitypub/fetch_featured_collection_service.rb
+++ b/app/services/activitypub/fetch_featured_collection_service.rb
@@ -23,7 +23,7 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
 
   def process_items(items)
     status_ids = items.map { |item| value_or_id(item) }
-                      .filter_map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri) unless ActivityPub::TagManager.instance.local_uri?(uri) }
+                      .filter_map { |uri| ActivityPub::FetchRemoteStatusService.new.call(uri, on_behalf_of: local_follower) unless ActivityPub::TagManager.instance.local_uri?(uri) }
                       .filter_map { |status| status.id if status.account_id == @account.id }
     to_remove = []
     to_add    = status_ids
@@ -46,4 +46,8 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService
   def supported_context?
     super(@json)
   end
+
+  def local_follower
+    @local_follower ||= account.followers.local.without_suspended.first
+  end
 end
diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb
index 16353066c..35a101f1d 100644
--- a/app/validators/status_pin_validator.rb
+++ b/app/validators/status_pin_validator.rb
@@ -6,7 +6,7 @@ class StatusPinValidator < ActiveModel::Validator
   def validate(pin)
     pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog?
     pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id
-    pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility)
+    pin.errors.add(:base, I18n.t('statuses.pin_errors.direct')) if pin.status.direct_visibility?
     pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count >= MAX_PINNED  && pin.account.local?
   end
 end
diff --git a/app/workers/remote_account_refresh_worker.rb b/app/workers/remote_account_refresh_worker.rb
new file mode 100644
index 000000000..9632936b5
--- /dev/null
+++ b/app/workers/remote_account_refresh_worker.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+
+class RemoteAccountRefreshWorker
+  include Sidekiq::Worker
+  include ExponentialBackoff
+  include JsonLdHelper
+
+  sidekiq_options queue: 'pull', retry: 3
+
+  def perform(id)
+    account = Account.find_by(id: id)
+    return if account.nil? || account.local?
+
+    ActivityPub::FetchRemoteAccountService.new.call(account.uri)
+  rescue Mastodon::UnexpectedResponseError => e
+    response = e.response
+
+    if response_error_unsalvageable?(response)
+      # Give up
+    else
+      raise e
+    end
+  end
+end