about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorFire Demon <firedemon@creature.cafe>2020-07-18 23:59:04 -0500
committerFire Demon <firedemon@creature.cafe>2020-08-30 05:43:08 -0500
commitdc5526f4ae8c9d3a6f132b2bc72914b95e5286cc (patch)
tree511631c5b0b87d0b8c4dae207af03aa80d54be73 /app
parentc6ede2d0eef48df6f9a70bb9c2036d688bda35af (diff)
[Privacy, Federation, UI] Add options to allow Fediverse users to decide whether to include replies and unlisted posts on their profiles
Diffstat (limited to 'app')
-rw-r--r--app/controllers/accounts_controller.rb4
-rw-r--r--app/controllers/activitypub/outboxes_controller.rb6
-rw-r--r--app/controllers/api/v1/accounts/credentials_controller.rb4
-rw-r--r--app/controllers/api/v1/accounts/statuses_controller.rb4
-rw-r--r--app/controllers/settings/profiles_controller.rb4
-rw-r--r--app/javascript/flavours/glitch/components/status.js1
-rw-r--r--app/javascript/flavours/glitch/features/account_timeline/components/header.js3
-rw-r--r--app/lib/activitypub/adapter.rb2
-rw-r--r--app/models/account.rb2
-rw-r--r--app/models/status.rb6
-rw-r--r--app/serializers/activitypub/actor_serializer.rb4
-rw-r--r--app/serializers/rest/account_serializer.rb2
-rw-r--r--app/services/activitypub/process_account_service.rb2
-rw-r--r--app/views/accounts/show.html.haml2
-rw-r--r--app/views/settings/profiles/show.html.haml14
15 files changed, 45 insertions, 15 deletions
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 21209cf12..81b8f8985 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -69,12 +69,12 @@ class AccountsController < ApplicationController
     default_statuses.tap do |statuses|
       statuses.merge!(hashtag_scope)    if tag_requested?
       statuses.merge!(only_media_scope) if media_requested?
-      statuses.merge!(no_replies_scope) unless (current_account&.id == @account.id) && replies_requested?
+      statuses.merge!(no_replies_scope) unless (current_account&.id == @account.id || @account.show_replies?) && replies_requested?
     end
   end
 
   def default_statuses
-    visibility_scopes = user_signed_in? ? [:public, :unlisted] : :public
+    visibility_scopes = user_signed_in? || @account.show_unlisted? ? [:public, :unlisted] : :public
     @account.statuses.not_local_only.where(visibility: visibility_scopes)
   end
 
diff --git a/app/controllers/activitypub/outboxes_controller.rb b/app/controllers/activitypub/outboxes_controller.rb
index e25a4bc07..4d4f5e364 100644
--- a/app/controllers/activitypub/outboxes_controller.rb
+++ b/app/controllers/activitypub/outboxes_controller.rb
@@ -49,7 +49,7 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   def set_statuses
     return unless page_requested?
 
-    @statuses = @account.statuses.permitted_for(@account, signed_request_account)
+    @statuses = @account.statuses.permitted_for(@account, signed_request_account, user_signed_in: known_visitor?)
     @statuses = @statuses.paginate_by_id(LIMIT, params_slice(:max_id, :min_id, :since_id))
     @statuses = cache_collection(@statuses, Status)
   end
@@ -61,4 +61,8 @@ class ActivityPub::OutboxesController < ActivityPub::BaseController
   def page_params
     { page: true, max_id: params[:max_id], min_id: params[:min_id] }.compact
   end
+
+  def known_visitor?
+    user_signed_in? || (signed_request_account.present? && signed_request_account.following?(@account))
+  end
 end
diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb
index dbafc3cc2..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, :require_dereference, 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 8a7a3a04d..4735fea8c 100644
--- a/app/controllers/api/v1/accounts/statuses_controller.rb
+++ b/app/controllers/api/v1/accounts/statuses_controller.rb
@@ -29,7 +29,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
     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 (current_account&.id != @account.id) || truthy_param?(:exclude_replies)
+    statuses.merge!(no_replies_scope) if (current_account&.id != @account.id && !@account.show_replies?) || truthy_param?(:exclude_replies)
     statuses.merge!(no_reblogs_scope) if truthy_param?(:exclude_reblogs)
     statuses.merge!(hashtag_scope)    if params[:tagged].present?
 
@@ -37,7 +37,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
   end
 
   def permitted_account_statuses
-    @account.statuses.permitted_for(@account, current_account)
+    @account.statuses.permitted_for(@account, current_account, user_signed_in: user_signed_in?)
   end
 
   def only_media_scope
diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb
index 33d93a233..d6e3c9863 100644
--- a/app/controllers/settings/profiles_controller.rb
+++ b/app/controllers/settings/profiles_controller.rb
@@ -23,7 +23,9 @@ class Settings::ProfilesController < Settings::BaseController
   private
 
   def account_params
-    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :require_dereference, fields_attributes: [:name, :value])
+    params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable,
+                                    :require_dereference, :show_replies, :show_unlisted,
+                                    fields_attributes: [:name, :value])
   end
 
   def set_account
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index 021c75c76..3a6029b96 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -674,6 +674,7 @@ class Status extends ImmutablePureComponent {
     const selectorAttribs = {
       'data-status-by': `@${status.getIn(['account', 'acct'])}`,
       'data-nest-level': status.get('nest_level'),
+      'data-nest-deep': status.get('nest_level') >= 15,
     };
 
     if (prepend && account) {
diff --git a/app/javascript/flavours/glitch/features/account_timeline/components/header.js b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
index d7edd43ab..527352497 100644
--- a/app/javascript/flavours/glitch/features/account_timeline/components/header.js
+++ b/app/javascript/flavours/glitch/features/account_timeline/components/header.js
@@ -125,7 +125,8 @@ export default class Header extends ImmutablePureComponent {
         {!hideTabs && (
           <div className='account__section-headline'>
             <NavLink exact to={`/accounts/${account.get('id')}`}><FormattedMessage id='account.posts' defaultMessage='Toots' /></NavLink>
-            { account.get('id') === me && (<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>) }
+            { (account.get('id') === me || account.get('show_replies')) &&
+                (<NavLink exact to={`/accounts/${account.get('id')}/with_replies`}><FormattedMessage id='account.posts_with_replies' defaultMessage='Toots with replies' /></NavLink>) }
             <NavLink exact to={`/accounts/${account.get('id')}/media`}><FormattedMessage id='account.media' defaultMessage='Media' /></NavLink>
           </div>
         )}
diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb
index 6ecce7fe9..ef46ae4ae 100644
--- a/app/lib/activitypub/adapter.rb
+++ b/app/lib/activitypub/adapter.rb
@@ -10,6 +10,8 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base
     direct_message: { 'litepub': 'http://litepub.social/ns#', 'directMessage': 'litepub:directMessage' },
     edited: { 'mp' => 'http://the.monsterpit.net/ns#', 'edited' => 'mp:edited' },
     require_dereference: { 'mp' => 'http://the.monsterpit.net/ns#', 'requireDereference' => 'mp:requireDereference' },
+    show_replies: { 'mp' => 'http://the.monsterpit.net/ns#', 'showReplies' => 'mp:showReplies' },
+    show_unlisted: { 'mp' => 'http://the.monsterpit.net/ns#', 'showUnlisted' => 'mp:showUnlisted' },
     manually_approves_followers: { 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers' },
     sensitive: { 'sensitive' => 'as:sensitive' },
     hashtag: { 'Hashtag' => 'as:Hashtag' },
diff --git a/app/models/account.rb b/app/models/account.rb
index 48e6e8532..8b384f212 100644
--- a/app/models/account.rb
+++ b/app/models/account.rb
@@ -51,6 +51,8 @@
 #  header_storage_schema_version :integer
 #  devices_url                   :string
 #  require_dereference           :boolean          default(FALSE), not null
+#  show_replies                  :boolean          default(TRUE), not null
+#  show_unlisted                 :boolean          default(TRUE), not null
 #
 
 class Account < ApplicationRecord
diff --git a/app/models/status.rb b/app/models/status.rb
index bee7d1e67..54023c24c 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -390,8 +390,8 @@ class Status < ApplicationRecord
       end
     end
 
-    def permitted_for(target_account, account)
-      visibility = [:public, :unlisted]
+    def permitted_for(target_account, account, user_signed_in: false)
+      visibility = user_signed_in || target_account.show_unlisted? ? [:public, :unlisted] : :public
 
       if account.nil?
         where(visibility: visibility).not_local_only
@@ -402,7 +402,7 @@ class Status < ApplicationRecord
       else
         # followers can see followers-only stuff, but also things they are mentioned in.
         # non-followers can see everything that isn't private/direct, but can see stuff they are mentioned in.
-        visibility.push(:private) if account.following?(target_account)
+        visibility.push(:private) if account.following?(target_account) && (user_signed_in || target_account.show_unlisted?)
 
         scope = left_outer_joins(:reblog)
 
diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb
index f3ed70490..5a63d15ea 100644
--- a/app/serializers/activitypub/actor_serializer.rb
+++ b/app/serializers/activitypub/actor_serializer.rb
@@ -24,8 +24,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
   attribute :moved_to, if: :moved?
   attribute :also_known_as, if: :also_known_as?
 
-  context_extensions :require_dereference
-  attribute :require_dereference
+  context_extensions :require_dereference, :show_replies
+  attributes :require_dereference, :show_replies, :show_unlisted
 
   class EndpointsSerializer < ActivityPub::Serializer
     include RoutingHelper
diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb
index bc941d3e7..e425c34a0 100644
--- a/app/serializers/rest/account_serializer.rb
+++ b/app/serializers/rest/account_serializer.rb
@@ -7,7 +7,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
              :note, :url, :avatar, :avatar_static, :header, :header_static,
              :followers_count, :following_count, :statuses_count, :last_status_at
 
-  attribute :require_dereference
+  attributes :require_dereference, :show_replies, :show_unlisted
 
   has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested?
   has_many :emojis, serializer: REST::CustomEmojiSerializer
diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb
index 56c70cfa0..2a5980c79 100644
--- a/app/services/activitypub/process_account_service.rb
+++ b/app/services/activitypub/process_account_service.rb
@@ -87,6 +87,8 @@ class ActivityPub::ProcessAccountService < BaseService
     @account.actor_type              = actor_type
     @account.discoverable            = @json['discoverable'] || false
     @account.require_dereference     = @json['requireDereference'] || false
+    @account.show_replies            = @json['showReplies'] || true
+    @account.show_unlisted           = @json['showUnlisted'] || true
   end
 
   def set_fetchable_attributes!
diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml
index ef84f7304..b1f237618 100644
--- a/app/views/accounts/show.html.haml
+++ b/app/views/accounts/show.html.haml
@@ -26,6 +26,8 @@
 
       .account__section-headline
         = active_link_to t('accounts.posts_tab_heading'), short_account_url(@account)
+        - if @account.show_replies?
+          = active_link_to t('accounts.posts_with_replies'), short_account_with_replies_url(@account)
         = active_link_to t('accounts.media'), short_account_media_url(@account)
 
       - if user_signed_in? && @account.blocking?(current_account)
diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml
index d2f6dc9ba..828b3ee4c 100644
--- a/app/views/settings/profiles/show.html.haml
+++ b/app/views/settings/profiles/show.html.haml
@@ -28,13 +28,25 @@
 
   .fields-group
     = f.input :bot, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.bot')
+  
+  %h4= t 'settings.profiles.privacy'
+
+  %p.hint= t 'settings.profiles.privacy_html'
 
   - if Setting.profile_directory
     .fields-group
-      = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable'), recommended: true
+      = f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable')
+
+  .fields-group
+    = f.input :show_replies, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.show_replies')
   
+  .fields-group
+    = f.input :show_unlisted, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.show_unlisted')
+
   %h4= t 'settings.profiles.advanced_privacy'
 
+  %p.hint= t 'settings.profiles.advanced_privacy_html'
+
   .fields-group
     = f.input :require_dereference, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.require_dereference_html')