about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/components/actions/compose.jsx2
-rw-r--r--app/controllers/accounts_controller.rb4
-rw-r--r--app/controllers/api/v1/accounts_controller.rb5
-rw-r--r--app/controllers/api/v1/statuses_controller.rb3
-rw-r--r--app/controllers/stream_entries_controller.rb6
-rw-r--r--app/models/block.rb22
-rw-r--r--app/models/concerns/streamable.rb6
-rw-r--r--app/models/status.rb29
-rw-r--r--app/models/stream_entry.rb5
-rw-r--r--app/services/post_status_service.rb2
-rw-r--r--app/services/reblog_service.rb2
-rw-r--r--app/views/api/v1/statuses/_show.rabl6
12 files changed, 68 insertions, 24 deletions
diff --git a/app/assets/javascripts/components/actions/compose.jsx b/app/assets/javascripts/components/actions/compose.jsx
index a9fbe6b91..fdb0abdcd 100644
--- a/app/assets/javascripts/components/actions/compose.jsx
+++ b/app/assets/javascripts/components/actions/compose.jsx
@@ -67,7 +67,7 @@ export function submitCompose() {
       in_reply_to_id: getState().getIn(['compose', 'in_reply_to'], null),
       media_ids: getState().getIn(['compose', 'media_attachments']).map(item => item.get('id')),
       sensitive: getState().getIn(['compose', 'sensitive']),
-      unlisted: getState().getIn(['compose', 'unlisted'])
+      visibility: getState().getIn(['compose', 'unlisted']) ? 'unlisted' : 'public'
     }).then(function (response) {
       dispatch(submitComposeSuccess({ ...response.data }));
 
diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb
index 57f25a273..411a41ccc 100644
--- a/app/controllers/accounts_controller.rb
+++ b/app/controllers/accounts_controller.rb
@@ -11,12 +11,12 @@ class AccountsController < ApplicationController
   def show
     respond_to do |format|
       format.html do
-        @statuses = @account.statuses.order('id desc').paginate_by_max_id(20, params[:max_id], params[:since_id])
+        @statuses = @account.statuses.permitted_for(@account, current_account).order('id desc').paginate_by_max_id(20, params[:max_id], params[:since_id])
         @statuses = cache_collection(@statuses, Status)
       end
 
       format.atom do
-        @entries = @account.stream_entries.order('id desc').with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
+        @entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
       end
     end
   end
diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb
index 0abdfd9fa..de53a9602 100644
--- a/app/controllers/api/v1/accounts_controller.rb
+++ b/app/controllers/api/v1/accounts_controller.rb
@@ -8,8 +8,7 @@ class Api::V1::AccountsController < ApiController
 
   respond_to :json
 
-  def show
-  end
+  def show; end
 
   def verify_credentials
     @account = current_user.account
@@ -47,7 +46,7 @@ class Api::V1::AccountsController < ApiController
   end
 
   def statuses
-    @statuses = @account.statuses.paginate_by_max_id(DEFAULT_STATUSES_LIMIT, params[:max_id], params[:since_id])
+    @statuses = @account.statuses.permitted_for(@account, current_account).paginate_by_max_id(DEFAULT_STATUSES_LIMIT, params[:max_id], params[:since_id])
     @statuses = cache_collection(@statuses, Status)
 
     set_maps(@statuses)
diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb
index 453d003da..f7b4ed610 100644
--- a/app/controllers/api/v1/statuses_controller.rb
+++ b/app/controllers/api/v1/statuses_controller.rb
@@ -52,7 +52,7 @@ 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], unlisted: params[:unlisted])
+    @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], visibility: params[:visibility])
     render action: :show
   end
 
@@ -95,5 +95,6 @@ class Api::V1::StatusesController < ApiController
 
   def set_status
     @status = Status.find(params[:id])
+    raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
   end
 end
diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb
index 58dd423f7..438d51a84 100644
--- a/app/controllers/stream_entries_controller.rb
+++ b/app/controllers/stream_entries_controller.rb
@@ -14,8 +14,8 @@ class StreamEntriesController < ApplicationController
         return gone if @stream_entry.activity.nil?
 
         if @stream_entry.activity_type == 'Status'
-          @ancestors   = @stream_entry.activity.ancestors
-          @descendants = @stream_entry.activity.descendants
+          @ancestors   = @stream_entry.activity.ancestors(current_account)
+          @descendants = @stream_entry.activity.descendants(current_account)
         end
       end
 
@@ -43,7 +43,7 @@ class StreamEntriesController < ApplicationController
   end
 
   def set_stream_entry
-    @stream_entry = @account.stream_entries.find(params[:id])
+    @stream_entry = @account.stream_entries.where(hidden: false).find(params[:id])
     @type         = @stream_entry.activity_type.downcase
   end
 
diff --git a/app/models/block.rb b/app/models/block.rb
index dc05bce87..ad225d180 100644
--- a/app/models/block.rb
+++ b/app/models/block.rb
@@ -1,9 +1,31 @@
 # frozen_string_literal: true
 
 class Block < ApplicationRecord
+  include Streamable
+
   belongs_to :account
   belongs_to :target_account, class_name: 'Account'
 
   validates :account, :target_account, presence: true
   validates :account_id, uniqueness: { scope: :target_account_id }
+
+  def verb
+    destroyed? ? :unblock : :block
+  end
+
+  def target
+    target_account
+  end
+
+  def object_type
+    :person
+  end
+
+  def hidden?
+    true
+  end
+
+  def title
+    destroyed? ? "#{account.acct} is no longer blocking #{target_account.acct}" : "#{account.acct} blocked #{target_account.acct}"
+  end
 end
diff --git a/app/models/concerns/streamable.rb b/app/models/concerns/streamable.rb
index d9f5dc4d8..58c15cfbc 100644
--- a/app/models/concerns/streamable.rb
+++ b/app/models/concerns/streamable.rb
@@ -26,8 +26,12 @@ module Streamable
       super
     end
 
+    def hidden?
+      false
+    end
+
     after_create do
-      account.stream_entries.create!(activity: self) if account.local?
+      account.stream_entries.create!(activity: self, hidden: hidden?) if account.local?
     end
   end
 end
diff --git a/app/models/status.rb b/app/models/status.rb
index e87828e32..603f3b7a2 100644
--- a/app/models/status.rb
+++ b/app/models/status.rb
@@ -5,7 +5,7 @@ class Status < ApplicationRecord
   include Streamable
   include Cacheable
 
-  enum visibility: [:public, :unlisted], _suffix: :visibility
+  enum visibility: [:public, :unlisted, :private], _suffix: :visibility
 
   belongs_to :account, inverse_of: :statuses
 
@@ -66,19 +66,19 @@ class Status < ApplicationRecord
     content
   end
 
-  def reblogs_count
-    attributes['reblogs_count'] || reblogs.count
+  def hidden?
+    private_visibility?
   end
 
-  def favourites_count
-    attributes['favourites_count'] || favourites.count
+  def permitted?(other_account = nil)
+    private_visibility? ? (account.id == other_account&.id || other_account&.following?(account)) : true
   end
 
   def ancestors(account = nil)
     ids      = (Status.find_by_sql(['WITH RECURSIVE search_tree(id, in_reply_to_id, path) AS (SELECT id, in_reply_to_id, ARRAY[id] FROM statuses WHERE id = ? UNION ALL SELECT statuses.id, statuses.in_reply_to_id, path || statuses.id FROM search_tree JOIN statuses ON statuses.id = search_tree.in_reply_to_id WHERE NOT statuses.id = ANY(path)) SELECT id FROM search_tree ORDER BY path DESC', id]) - [self]).pluck(:id)
     statuses = Status.where(id: ids).with_includes.group_by(&:id)
     results  = ids.map { |id| statuses[id].first }
-    results  = results.reject { |status| account.blocking?(status.account) } unless account.nil?
+    results  = results.reject { |status| filter_from_context?(status, account) }
 
     results
   end
@@ -87,7 +87,7 @@ class Status < ApplicationRecord
     ids      = (Status.find_by_sql(['WITH RECURSIVE search_tree(id, path) AS (SELECT id, ARRAY[id] FROM statuses WHERE id = ? UNION ALL SELECT statuses.id, path || statuses.id FROM search_tree JOIN statuses ON statuses.in_reply_to_id = search_tree.id WHERE NOT statuses.id = ANY(path)) SELECT id FROM search_tree ORDER BY path', id]) - [self]).pluck(:id)
     statuses = Status.where(id: ids).with_includes.group_by(&:id)
     results  = ids.map { |id| statuses[id].first }
-    results  = results.reject { |status| account.blocking?(status.account) } unless account.nil?
+    results  = results.reject { |status| filter_from_context?(status, account) }
 
     results
   end
@@ -128,6 +128,14 @@ class Status < ApplicationRecord
       select('reblog_of_id').where(reblog_of_id: status_ids).where(account_id: account_id).map { |s| [s.reblog_of_id, true] }.to_h
     end
 
+    def permitted_for(target_account, account)
+      if account&.id == target_account.id || account&.following?(target_account)
+        self
+      else
+        where.not(visibility: :private)
+      end
+    end
+
     def reload_stale_associations!(cached_items)
       account_ids = []
 
@@ -161,5 +169,12 @@ class Status < ApplicationRecord
   before_validation do
     text.strip!
     self.in_reply_to_account_id = thread.account_id if reply?
+    self.visibility             = :public if visibility.nil?
+  end
+
+  private
+
+  def filter_from_context?(status, account)
+    account&.blocking?(status.account) || !status.permitted?(account)
   end
 end
diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb
index f6c8f461b..fcc691bef 100644
--- a/app/models/stream_entry.rb
+++ b/app/models/stream_entry.rb
@@ -9,6 +9,7 @@ class StreamEntry < ApplicationRecord
   belongs_to :status,    foreign_type: 'Status',    foreign_key: 'activity_id'
   belongs_to :follow,    foreign_type: 'Follow',    foreign_key: 'activity_id'
   belongs_to :favourite, foreign_type: 'Favourite', foreign_key: 'activity_id'
+  belongs_to :block,     foreign_type: 'Block',     foreign_key: 'activity_id'
 
   validates :account, :activity, presence: true
 
@@ -29,7 +30,7 @@ class StreamEntry < ApplicationRecord
   end
 
   def targeted?
-    [:follow, :share, :favorite].include? verb
+    [:follow, :unfollow, :block, :unblock, :share, :favorite].include? verb
   end
 
   def target
@@ -57,7 +58,7 @@ class StreamEntry < ApplicationRecord
   end
 
   def activity
-    send(activity_type.downcase.to_sym)
+    !new_record? ? send(activity_type.downcase) : super
   end
 
   private
diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb
index d5204151b..55405c0db 100644
--- a/app/services/post_status_service.rb
+++ b/app/services/post_status_service.rb
@@ -10,7 +10,7 @@ class PostStatusService < BaseService
   # @option [Enumerable] :media_ids Optional array of media IDs to attach
   # @return [Status]
   def call(account, text, in_reply_to = nil, options = {})
-    status = account.statuses.create!(text: text, thread: in_reply_to, sensitive: options[:sensitive], visibility: options[:unlisted] ? :unlisted : :public)
+    status = account.statuses.create!(text: text, thread: in_reply_to, sensitive: options[:sensitive], visibility: options[:visibility])
     attach_media(status, options[:media_ids])
     process_mentions_service.call(status)
     process_hashtags_service.call(status)
diff --git a/app/services/reblog_service.rb b/app/services/reblog_service.rb
index 7d0c90d2f..1a78b8f69 100644
--- a/app/services/reblog_service.rb
+++ b/app/services/reblog_service.rb
@@ -6,6 +6,8 @@ class ReblogService < BaseService
   # @param [Status] reblogged_status Status to be reblogged
   # @return [Status]
   def call(account, reblogged_status)
+    raise ActiveRecord::RecordInvalid if reblogged_status.private_visibility?
+
     reblog = account.statuses.create!(reblog: reblogged_status, text: '')
 
     DistributionWorker.perform_async(reblog.id)
diff --git a/app/views/api/v1/statuses/_show.rabl b/app/views/api/v1/statuses/_show.rabl
index 579c47b26..a3391a67e 100644
--- a/app/views/api/v1/statuses/_show.rabl
+++ b/app/views/api/v1/statuses/_show.rabl
@@ -1,10 +1,10 @@
-attributes :id, :created_at, :in_reply_to_id, :sensitive
+attributes :id, :created_at, :in_reply_to_id, :sensitive, :visibility
 
 node(:uri)              { |status| TagManager.instance.uri_for(status) }
 node(:content)          { |status| Formatter.instance.format(status) }
 node(:url)              { |status| TagManager.instance.url_for(status) }
-node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs_count }
-node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites_count }
+node(:reblogs_count)    { |status| defined?(@reblogs_counts_map)    ? (@reblogs_counts_map[status.id]    || 0) : status.reblogs.count }
+node(:favourites_count) { |status| defined?(@favourites_counts_map) ? (@favourites_counts_map[status.id] || 0) : status.favourites.count }
 
 child :account do
   extends 'api/v1/accounts/show'